mirror of
https://github.com/EDCD/EDMarketConnector.git
synced 2025-04-12 23:37:14 +03:00
[2051] Update Supporting Files
Not touching any of the big ones, but some clarification updates to many of the supporting files.
This commit is contained in:
parent
a169fb5a18
commit
b2d5e13465
@ -79,7 +79,7 @@ def addcommodities(data) -> None: # noqa: CCR001
|
|||||||
|
|
||||||
commodities[key] = new
|
commodities[key] = new
|
||||||
|
|
||||||
if not len(commodities) > size_pre:
|
if len(commodities) <= size_pre:
|
||||||
return
|
return
|
||||||
|
|
||||||
if isfile(commodityfile):
|
if isfile(commodityfile):
|
||||||
@ -227,7 +227,7 @@ if __name__ == "__main__":
|
|||||||
print('Not docked!')
|
print('Not docked!')
|
||||||
continue
|
continue
|
||||||
|
|
||||||
elif not data.get('lastStarport'):
|
if not data.get('lastStarport'):
|
||||||
print('No starport!')
|
print('No starport!')
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
"""Simple HTTP listener to be used with debugging various EDMC sends."""
|
"""Simple HTTP listener to be used with debugging various EDMC sends."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import gzip
|
import gzip
|
||||||
import json
|
import json
|
||||||
import pathlib
|
import pathlib
|
||||||
@ -6,7 +8,7 @@ import tempfile
|
|||||||
import threading
|
import threading
|
||||||
import zlib
|
import zlib
|
||||||
from http import server
|
from http import server
|
||||||
from typing import Any, Callable, Literal, Tuple, Union
|
from typing import Any, Callable, Literal
|
||||||
from urllib.parse import parse_qs
|
from urllib.parse import parse_qs
|
||||||
|
|
||||||
from config import appname
|
from config import appname
|
||||||
@ -22,9 +24,6 @@ SAFE_TRANSLATE = str.maketrans({x: '_' for x in "!@#$%^&*()./\\\r\n[]-+='\";:?<>
|
|||||||
class LoggingHandler(server.BaseHTTPRequestHandler):
|
class LoggingHandler(server.BaseHTTPRequestHandler):
|
||||||
"""HTTP Handler implementation that logs to EDMCs logger and writes data to files on disk."""
|
"""HTTP Handler implementation that logs to EDMCs logger and writes data to files on disk."""
|
||||||
|
|
||||||
def __init__(self, request, client_address: Tuple[str, int], server) -> None:
|
|
||||||
super().__init__(request, client_address, server)
|
|
||||||
|
|
||||||
def log_message(self, format: str, *args: Any) -> None:
|
def log_message(self, format: str, *args: Any) -> None:
|
||||||
"""Override default handler logger with EDMC logger."""
|
"""Override default handler logger with EDMC logger."""
|
||||||
logger.info(format % args)
|
logger.info(format % args)
|
||||||
@ -45,7 +44,7 @@ class LoggingHandler(server.BaseHTTPRequestHandler):
|
|||||||
elif len(target_path) == 1 and target_path[0] == '/':
|
elif len(target_path) == 1 and target_path[0] == '/':
|
||||||
target_path = 'WEB_ROOT'
|
target_path = 'WEB_ROOT'
|
||||||
|
|
||||||
response: Union[Callable[[str], str], str, None] = DEFAULT_RESPONSES.get(target_path)
|
response: Callable[[str], str] | str | None = DEFAULT_RESPONSES.get(target_path)
|
||||||
if callable(response):
|
if callable(response):
|
||||||
response = response(to_save)
|
response = response(to_save)
|
||||||
|
|
||||||
@ -69,11 +68,11 @@ class LoggingHandler(server.BaseHTTPRequestHandler):
|
|||||||
target_file = output_data_path / (safe_file_name(target_path) + '.log')
|
target_file = output_data_path / (safe_file_name(target_path) + '.log')
|
||||||
if target_file.parent != output_data_path:
|
if target_file.parent != output_data_path:
|
||||||
logger.warning(f"REFUSING TO WRITE FILE THAT ISN'T IN THE RIGHT PLACE! {target_file=}")
|
logger.warning(f"REFUSING TO WRITE FILE THAT ISN'T IN THE RIGHT PLACE! {target_file=}")
|
||||||
logger.warning(f'DATA FOLLOWS\n{data}') # type: ignore # mypy thinks data is a byte string here
|
logger.warning(f'DATA FOLLOWS\n{data}')
|
||||||
return
|
return
|
||||||
|
|
||||||
with output_lock, target_file.open('a') as f:
|
with output_lock, target_file.open('a') as file:
|
||||||
f.write(to_save + "\n\n")
|
file.write(to_save + "\n\n")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_printable(data: bytes, compression: Literal['deflate'] | Literal['gzip'] | str | None = None) -> str:
|
def get_printable(data: bytes, compression: Literal['deflate'] | Literal['gzip'] | str | None = None) -> str:
|
||||||
|
@ -1,4 +1,11 @@
|
|||||||
"""Implements locking of Journal directory."""
|
"""
|
||||||
|
journal_lock.py - Locking of the Journal Directory.
|
||||||
|
|
||||||
|
Copyright (c) EDCD, All Rights Reserved
|
||||||
|
Licensed under the GNU General Public License.
|
||||||
|
See LICENSE file.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import pathlib
|
import pathlib
|
||||||
import sys
|
import sys
|
||||||
@ -6,7 +13,7 @@ import tkinter as tk
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
from os import getpid as os_getpid
|
from os import getpid as os_getpid
|
||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
from typing import TYPE_CHECKING, Callable, Optional
|
from typing import TYPE_CHECKING, Callable
|
||||||
|
|
||||||
from config import config
|
from config import config
|
||||||
from EDMCLogging import get_main_logger
|
from EDMCLogging import get_main_logger
|
||||||
@ -34,9 +41,9 @@ class JournalLock:
|
|||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
"""Initialise where the journal directory and lock file are."""
|
"""Initialise where the journal directory and lock file are."""
|
||||||
self.journal_dir: str | None = config.get_str('journaldir') or config.default_journal_dir
|
self.journal_dir: str | None = config.get_str('journaldir') or config.default_journal_dir
|
||||||
self.journal_dir_path: Optional[pathlib.Path] = None
|
self.journal_dir_path: pathlib.Path | None = None
|
||||||
self.set_path_from_journaldir()
|
self.set_path_from_journaldir()
|
||||||
self.journal_dir_lockfile_name: Optional[pathlib.Path] = None
|
self.journal_dir_lockfile_name: pathlib.Path | None = None
|
||||||
# We never test truthiness of this, so let it be defined when first assigned. Avoids type hint issues.
|
# We never test truthiness of this, so let it be defined when first assigned. Avoids type hint issues.
|
||||||
# self.journal_dir_lockfile: Optional[IO] = None
|
# self.journal_dir_lockfile: Optional[IO] = None
|
||||||
self.locked = False
|
self.locked = False
|
||||||
@ -58,7 +65,8 @@ class JournalLock:
|
|||||||
self.journal_dir_lockfile_name = self.journal_dir_path / 'edmc-journal-lock.txt' # type: ignore
|
self.journal_dir_lockfile_name = self.journal_dir_path / 'edmc-journal-lock.txt' # type: ignore
|
||||||
logger.trace_if('journal-lock', f'journal_dir_lockfile_name = {self.journal_dir_lockfile_name!r}')
|
logger.trace_if('journal-lock', f'journal_dir_lockfile_name = {self.journal_dir_lockfile_name!r}')
|
||||||
try:
|
try:
|
||||||
self.journal_dir_lockfile = open(self.journal_dir_lockfile_name, mode='w+', encoding='utf-8')
|
with open(self.journal_dir_lockfile_name, mode='w+', encoding='utf-8') as lockfile:
|
||||||
|
self.journal_dir_lockfile = lockfile
|
||||||
|
|
||||||
# Linux CIFS read-only mount throws: OSError(30, 'Read-only file system')
|
# Linux CIFS read-only mount throws: OSError(30, 'Read-only file system')
|
||||||
# Linux no-write-perm directory throws: PermissionError(13, 'Permission denied')
|
# Linux no-write-perm directory throws: PermissionError(13, 'Permission denied')
|
||||||
|
@ -10,10 +10,11 @@ OSX and Windows.
|
|||||||
|
|
||||||
Entire file may be imported by plugins.
|
Entire file may be imported by plugins.
|
||||||
"""
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
# Can't do this with styles on OSX - http://www.tkdocs.com/tutorial/styles.html#whydifficult
|
# Can't do this with styles on OSX - http://www.tkdocs.com/tutorial/styles.html#whydifficult
|
||||||
if sys.platform == 'darwin':
|
if sys.platform == 'darwin':
|
||||||
@ -29,7 +30,7 @@ elif sys.platform == 'win32':
|
|||||||
class Notebook(ttk.Notebook):
|
class Notebook(ttk.Notebook):
|
||||||
"""Custom ttk.Notebook class to fix some display issues."""
|
"""Custom ttk.Notebook class to fix some display issues."""
|
||||||
|
|
||||||
def __init__(self, master: Optional[ttk.Frame] = None, **kw):
|
def __init__(self, master: ttk.Frame | None = None, **kw):
|
||||||
|
|
||||||
ttk.Notebook.__init__(self, master, **kw)
|
ttk.Notebook.__init__(self, master, **kw)
|
||||||
style = ttk.Style()
|
style = ttk.Style()
|
||||||
@ -75,9 +76,9 @@ class Frame(sys.platform == 'darwin' and tk.Frame or ttk.Frame): # type: ignore
|
|||||||
class Label(tk.Label):
|
class Label(tk.Label):
|
||||||
"""Custom tk.Label class to fix some display issues."""
|
"""Custom tk.Label class to fix some display issues."""
|
||||||
|
|
||||||
def __init__(self, master: Optional[ttk.Frame] = None, **kw):
|
def __init__(self, master: ttk.Frame | None = None, **kw):
|
||||||
# This format chosen over `sys.platform in (...)` as mypy and friends dont understand that
|
# This format chosen over `sys.platform in (...)` as mypy and friends dont understand that
|
||||||
if sys.platform == 'darwin' or sys.platform == 'win32':
|
if sys.platform in ('darwin', 'win32'):
|
||||||
kw['foreground'] = kw.pop('foreground', PAGEFG)
|
kw['foreground'] = kw.pop('foreground', PAGEFG)
|
||||||
kw['background'] = kw.pop('background', PAGEBG)
|
kw['background'] = kw.pop('background', PAGEBG)
|
||||||
else:
|
else:
|
||||||
@ -89,7 +90,7 @@ class Label(tk.Label):
|
|||||||
class Entry(sys.platform == 'darwin' and tk.Entry or ttk.Entry): # type: ignore
|
class Entry(sys.platform == 'darwin' and tk.Entry or ttk.Entry): # type: ignore
|
||||||
"""Custom t(t)k.Entry class to fix some display issues."""
|
"""Custom t(t)k.Entry class to fix some display issues."""
|
||||||
|
|
||||||
def __init__(self, master: Optional[ttk.Frame] = None, **kw):
|
def __init__(self, master: ttk.Frame | None = None, **kw):
|
||||||
if sys.platform == 'darwin':
|
if sys.platform == 'darwin':
|
||||||
kw['highlightbackground'] = kw.pop('highlightbackground', PAGEBG)
|
kw['highlightbackground'] = kw.pop('highlightbackground', PAGEBG)
|
||||||
tk.Entry.__init__(self, master, **kw)
|
tk.Entry.__init__(self, master, **kw)
|
||||||
@ -100,7 +101,7 @@ class Entry(sys.platform == 'darwin' and tk.Entry or ttk.Entry): # type: ignore
|
|||||||
class Button(sys.platform == 'darwin' and tk.Button or ttk.Button): # type: ignore
|
class Button(sys.platform == 'darwin' and tk.Button or ttk.Button): # type: ignore
|
||||||
"""Custom t(t)k.Button class to fix some display issues."""
|
"""Custom t(t)k.Button class to fix some display issues."""
|
||||||
|
|
||||||
def __init__(self, master: Optional[ttk.Frame] = None, **kw):
|
def __init__(self, master: ttk.Frame | None = None, **kw):
|
||||||
if sys.platform == 'darwin':
|
if sys.platform == 'darwin':
|
||||||
kw['highlightbackground'] = kw.pop('highlightbackground', PAGEBG)
|
kw['highlightbackground'] = kw.pop('highlightbackground', PAGEBG)
|
||||||
tk.Button.__init__(self, master, **kw)
|
tk.Button.__init__(self, master, **kw)
|
||||||
@ -113,7 +114,7 @@ class Button(sys.platform == 'darwin' and tk.Button or ttk.Button): # type: ign
|
|||||||
class ColoredButton(sys.platform == 'darwin' and tk.Label or tk.Button): # type: ignore
|
class ColoredButton(sys.platform == 'darwin' and tk.Label or tk.Button): # type: ignore
|
||||||
"""Custom t(t)k.ColoredButton class to fix some display issues."""
|
"""Custom t(t)k.ColoredButton class to fix some display issues."""
|
||||||
|
|
||||||
def __init__(self, master: Optional[ttk.Frame] = None, **kw):
|
def __init__(self, master: ttk.Frame | None = None, **kw):
|
||||||
if sys.platform == 'darwin':
|
if sys.platform == 'darwin':
|
||||||
# Can't set Button background on OSX, so use a Label instead
|
# Can't set Button background on OSX, so use a Label instead
|
||||||
kw['relief'] = kw.pop('relief', tk.RAISED)
|
kw['relief'] = kw.pop('relief', tk.RAISED)
|
||||||
@ -131,7 +132,7 @@ class ColoredButton(sys.platform == 'darwin' and tk.Label or tk.Button): # type
|
|||||||
class Checkbutton(sys.platform == 'darwin' and tk.Checkbutton or ttk.Checkbutton): # type: ignore
|
class Checkbutton(sys.platform == 'darwin' and tk.Checkbutton or ttk.Checkbutton): # type: ignore
|
||||||
"""Custom t(t)k.Checkbutton class to fix some display issues."""
|
"""Custom t(t)k.Checkbutton class to fix some display issues."""
|
||||||
|
|
||||||
def __init__(self, master: Optional[ttk.Frame] = None, **kw):
|
def __init__(self, master: ttk.Frame | None = None, **kw):
|
||||||
if sys.platform == 'darwin':
|
if sys.platform == 'darwin':
|
||||||
kw['foreground'] = kw.pop('foreground', PAGEFG)
|
kw['foreground'] = kw.pop('foreground', PAGEFG)
|
||||||
kw['background'] = kw.pop('background', PAGEBG)
|
kw['background'] = kw.pop('background', PAGEBG)
|
||||||
@ -145,7 +146,7 @@ class Checkbutton(sys.platform == 'darwin' and tk.Checkbutton or ttk.Checkbutton
|
|||||||
class Radiobutton(sys.platform == 'darwin' and tk.Radiobutton or ttk.Radiobutton): # type: ignore
|
class Radiobutton(sys.platform == 'darwin' and tk.Radiobutton or ttk.Radiobutton): # type: ignore
|
||||||
"""Custom t(t)k.Radiobutton class to fix some display issues."""
|
"""Custom t(t)k.Radiobutton class to fix some display issues."""
|
||||||
|
|
||||||
def __init__(self, master: Optional[ttk.Frame] = None, **kw):
|
def __init__(self, master: ttk.Frame | None = None, **kw):
|
||||||
if sys.platform == 'darwin':
|
if sys.platform == 'darwin':
|
||||||
kw['foreground'] = kw.pop('foreground', PAGEFG)
|
kw['foreground'] = kw.pop('foreground', PAGEFG)
|
||||||
kw['background'] = kw.pop('background', PAGEBG)
|
kw['background'] = kw.pop('background', PAGEBG)
|
||||||
|
53
theme.py
53
theme.py
@ -1,9 +1,14 @@
|
|||||||
"""
|
"""
|
||||||
Theme support.
|
theme.py - Theme support.
|
||||||
|
|
||||||
|
Copyright (c) EDCD, All Rights Reserved
|
||||||
|
Licensed under the GNU General Public License.
|
||||||
|
See LICENSE file.
|
||||||
|
|
||||||
Because of various ttk limitations this app is an unholy mix of Tk and ttk widgets.
|
Because of various ttk limitations this app is an unholy mix of Tk and ttk widgets.
|
||||||
So can't use ttk's theme support. So have to change colors manually.
|
So can't use ttk's theme support. So have to change colors manually.
|
||||||
"""
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
@ -11,7 +16,7 @@ import tkinter as tk
|
|||||||
from os.path import join
|
from os.path import join
|
||||||
from tkinter import font as tk_font
|
from tkinter import font as tk_font
|
||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Set, Tuple
|
from typing import TYPE_CHECKING, Callable
|
||||||
|
|
||||||
from config import config
|
from config import config
|
||||||
from EDMCLogging import get_main_logger
|
from EDMCLogging import get_main_logger
|
||||||
@ -36,7 +41,7 @@ if sys.platform == 'win32':
|
|||||||
AddFontResourceEx.restypes = [LPCWSTR, DWORD, LPCVOID] # type: ignore
|
AddFontResourceEx.restypes = [LPCWSTR, DWORD, LPCVOID] # type: ignore
|
||||||
FR_PRIVATE = 0x10
|
FR_PRIVATE = 0x10
|
||||||
FR_NOT_ENUM = 0x20
|
FR_NOT_ENUM = 0x20
|
||||||
AddFontResourceEx(join(config.respath, u'EUROCAPS.TTF'), FR_PRIVATE, 0)
|
AddFontResourceEx(join(config.respath, 'EUROCAPS.TTF'), FR_PRIVATE, 0)
|
||||||
|
|
||||||
elif sys.platform == 'linux':
|
elif sys.platform == 'linux':
|
||||||
# pyright: reportUnboundVariable=false
|
# pyright: reportUnboundVariable=false
|
||||||
@ -121,7 +126,7 @@ elif sys.platform == 'linux':
|
|||||||
dpy = None
|
dpy = None
|
||||||
|
|
||||||
|
|
||||||
class _Theme(object):
|
class _Theme:
|
||||||
|
|
||||||
# Enum ? Remember these are, probably, based on 'value' of a tk
|
# Enum ? Remember these are, probably, based on 'value' of a tk
|
||||||
# RadioButton set. Looking in prefs.py, they *appear* to be hard-coded
|
# RadioButton set. Looking in prefs.py, they *appear* to be hard-coded
|
||||||
@ -132,18 +137,18 @@ class _Theme(object):
|
|||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.active: int | None = None # Starts out with no theme
|
self.active: int | None = None # Starts out with no theme
|
||||||
self.minwidth: Optional[int] = None
|
self.minwidth: int | None = None
|
||||||
self.widgets: Dict[tk.Widget | tk.BitmapImage, Set] = {}
|
self.widgets: dict[tk.Widget | tk.BitmapImage, set] = {}
|
||||||
self.widgets_pair: List = []
|
self.widgets_pair: list = []
|
||||||
self.defaults: Dict = {}
|
self.defaults: dict = {}
|
||||||
self.current: Dict = {}
|
self.current: dict = {}
|
||||||
self.default_ui_scale: float | None = None # None == not yet known
|
self.default_ui_scale: float | None = None # None == not yet known
|
||||||
self.startup_ui_scale: int | None = None
|
self.startup_ui_scale: int | None = None
|
||||||
|
|
||||||
def register(self, widget: tk.Widget | tk.BitmapImage) -> None: # noqa: CCR001, C901
|
def register(self, widget: tk.Widget | tk.BitmapImage) -> None: # noqa: CCR001, C901
|
||||||
# Note widget and children for later application of a theme. Note if
|
# Note widget and children for later application of a theme. Note if
|
||||||
# the widget has explicit fg or bg attributes.
|
# the widget has explicit fg or bg attributes.
|
||||||
assert isinstance(widget, tk.Widget) or isinstance(widget, tk.BitmapImage), widget
|
assert isinstance(widget, (tk.BitmapImage, tk.Widget)), widget
|
||||||
if not self.defaults:
|
if not self.defaults:
|
||||||
# Can't initialise this til window is created # Windows, MacOS
|
# Can't initialise this til window is created # Windows, MacOS
|
||||||
self.defaults = {
|
self.defaults = {
|
||||||
@ -169,14 +174,14 @@ class _Theme(object):
|
|||||||
attribs.add('fg')
|
attribs.add('fg')
|
||||||
if widget['background'] not in ['', self.defaults['bitmapbg']]:
|
if widget['background'] not in ['', self.defaults['bitmapbg']]:
|
||||||
attribs.add('bg')
|
attribs.add('bg')
|
||||||
elif isinstance(widget, tk.Entry) or isinstance(widget, ttk.Entry):
|
elif isinstance(widget, (tk.Entry, ttk.Entry)):
|
||||||
if widget['foreground'] not in ['', self.defaults['entryfg']]:
|
if widget['foreground'] not in ['', self.defaults['entryfg']]:
|
||||||
attribs.add('fg')
|
attribs.add('fg')
|
||||||
if widget['background'] not in ['', self.defaults['entrybg']]:
|
if widget['background'] not in ['', self.defaults['entrybg']]:
|
||||||
attribs.add('bg')
|
attribs.add('bg')
|
||||||
if 'font' in widget.keys() and str(widget['font']) not in ['', self.defaults['entryfont']]:
|
if 'font' in widget.keys() and str(widget['font']) not in ['', self.defaults['entryfont']]:
|
||||||
attribs.add('font')
|
attribs.add('font')
|
||||||
elif isinstance(widget, tk.Frame) or isinstance(widget, ttk.Frame) or isinstance(widget, tk.Canvas):
|
elif isinstance(widget, (tk.Canvas, tk.Frame, ttk.Frame)):
|
||||||
if (
|
if (
|
||||||
('background' in widget.keys() or isinstance(widget, tk.Canvas))
|
('background' in widget.keys() or isinstance(widget, tk.Canvas))
|
||||||
and widget['background'] not in ['', self.defaults['frame']]
|
and widget['background'] not in ['', self.defaults['frame']]
|
||||||
@ -200,21 +205,21 @@ class _Theme(object):
|
|||||||
attribs.add('font')
|
attribs.add('font')
|
||||||
self.widgets[widget] = attribs
|
self.widgets[widget] = attribs
|
||||||
|
|
||||||
if isinstance(widget, tk.Frame) or isinstance(widget, ttk.Frame):
|
if isinstance(widget, (tk.Frame, ttk.Frame)):
|
||||||
for child in widget.winfo_children():
|
for child in widget.winfo_children():
|
||||||
self.register(child)
|
self.register(child)
|
||||||
|
|
||||||
def register_alternate(self, pair: Tuple, gridopts: Dict) -> None:
|
def register_alternate(self, pair: tuple, gridopts: dict) -> None:
|
||||||
self.widgets_pair.append((pair, gridopts))
|
self.widgets_pair.append((pair, gridopts))
|
||||||
|
|
||||||
def button_bind(
|
def button_bind(
|
||||||
self, widget: tk.Widget, command: Callable, image: Optional[tk.BitmapImage] = None
|
self, widget: tk.Widget, command: Callable, image: tk.BitmapImage | None = None
|
||||||
) -> None:
|
) -> None:
|
||||||
widget.bind('<Button-1>', command)
|
widget.bind('<Button-1>', command)
|
||||||
widget.bind('<Enter>', lambda e: self._enter(e, image))
|
widget.bind('<Enter>', lambda e: self._enter(e, image))
|
||||||
widget.bind('<Leave>', lambda e: self._leave(e, image))
|
widget.bind('<Leave>', lambda e: self._leave(e, image))
|
||||||
|
|
||||||
def _enter(self, event: tk.Event, image: Optional[tk.BitmapImage]) -> None:
|
def _enter(self, event: tk.Event, image: tk.BitmapImage | None) -> None:
|
||||||
widget = event.widget
|
widget = event.widget
|
||||||
if widget and widget['state'] != tk.DISABLED:
|
if widget and widget['state'] != tk.DISABLED:
|
||||||
try:
|
try:
|
||||||
@ -231,7 +236,7 @@ class _Theme(object):
|
|||||||
except Exception:
|
except Exception:
|
||||||
logger.exception(f'Failure configuring image: {image=}')
|
logger.exception(f'Failure configuring image: {image=}')
|
||||||
|
|
||||||
def _leave(self, event: tk.Event, image: Optional[tk.BitmapImage]) -> None:
|
def _leave(self, event: tk.Event, image: tk.BitmapImage | None) -> None:
|
||||||
widget = event.widget
|
widget = event.widget
|
||||||
if widget and widget['state'] != tk.DISABLED:
|
if widget and widget['state'] != tk.DISABLED:
|
||||||
try:
|
try:
|
||||||
@ -299,13 +304,13 @@ class _Theme(object):
|
|||||||
Also, register it for future updates.
|
Also, register it for future updates.
|
||||||
:param widget: Target widget.
|
:param widget: Target widget.
|
||||||
"""
|
"""
|
||||||
assert isinstance(widget, tk.Widget) or isinstance(widget, tk.BitmapImage), widget
|
assert isinstance(widget, (tk.BitmapImage, tk.Widget)), widget
|
||||||
if not self.current:
|
if not self.current:
|
||||||
return # No need to call this for widgets created in plugin_app()
|
return # No need to call this for widgets created in plugin_app()
|
||||||
|
|
||||||
self.register(widget)
|
self.register(widget)
|
||||||
self._update_widget(widget)
|
self._update_widget(widget)
|
||||||
if isinstance(widget, tk.Frame) or isinstance(widget, ttk.Frame):
|
if isinstance(widget, (tk.Frame, ttk.Frame)):
|
||||||
for child in widget.winfo_children():
|
for child in widget.winfo_children():
|
||||||
self._update_widget(child)
|
self._update_widget(child)
|
||||||
|
|
||||||
@ -314,7 +319,7 @@ class _Theme(object):
|
|||||||
if widget not in self.widgets:
|
if widget not in self.widgets:
|
||||||
if isinstance(widget, tk.Widget):
|
if isinstance(widget, tk.Widget):
|
||||||
w_class = widget.winfo_class()
|
w_class = widget.winfo_class()
|
||||||
w_keys: List[str] = widget.keys()
|
w_keys: list[str] = widget.keys()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# There is no tk.BitmapImage.winfo_class()
|
# There is no tk.BitmapImage.winfo_class()
|
||||||
@ -325,7 +330,7 @@ class _Theme(object):
|
|||||||
assert_str = f'{w_class} {widget} "{"text" in w_keys and widget["text"]}"'
|
assert_str = f'{w_class} {widget} "{"text" in w_keys and widget["text"]}"'
|
||||||
raise AssertionError(assert_str)
|
raise AssertionError(assert_str)
|
||||||
|
|
||||||
attribs: Set = self.widgets.get(widget, set())
|
attribs: set = self.widgets.get(widget, set())
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if isinstance(widget, tk.BitmapImage):
|
if isinstance(widget, tk.BitmapImage):
|
||||||
@ -355,7 +360,7 @@ class _Theme(object):
|
|||||||
# e.g. tk.Button, tk.Label, tk.Menu
|
# e.g. tk.Button, tk.Label, tk.Menu
|
||||||
if 'fg' not in attribs:
|
if 'fg' not in attribs:
|
||||||
widget['foreground'] = self.current['foreground']
|
widget['foreground'] = self.current['foreground']
|
||||||
widget['activeforeground'] = self.current['activeforeground'],
|
widget['activeforeground'] = self.current['activeforeground']
|
||||||
widget['disabledforeground'] = self.current['disabledforeground']
|
widget['disabledforeground'] = self.current['disabledforeground']
|
||||||
|
|
||||||
if 'bg' not in attribs:
|
if 'bg' not in attribs:
|
||||||
@ -419,9 +424,7 @@ class _Theme(object):
|
|||||||
|
|
||||||
if self.active == theme:
|
if self.active == theme:
|
||||||
return # Don't need to mess with the window manager
|
return # Don't need to mess with the window manager
|
||||||
|
self.active = theme
|
||||||
else:
|
|
||||||
self.active = theme
|
|
||||||
|
|
||||||
if sys.platform == 'darwin':
|
if sys.platform == 'darwin':
|
||||||
from AppKit import NSAppearance, NSApplication, NSMiniaturizableWindowMask, NSResizableWindowMask
|
from AppKit import NSAppearance, NSApplication, NSMiniaturizableWindowMask, NSResizableWindowMask
|
||||||
|
@ -1,7 +1,15 @@
|
|||||||
"""A requests.session with a TimeoutAdapter."""
|
"""
|
||||||
import requests
|
timeout_session.py - requests session with timeout adapter.
|
||||||
from requests.adapters import HTTPAdapter
|
|
||||||
|
|
||||||
|
Copyright (c) EDCD, All Rights Reserved
|
||||||
|
Licensed under the GNU General Public License.
|
||||||
|
See LICENSE file.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from requests import Session
|
||||||
|
from requests.adapters import HTTPAdapter
|
||||||
from config import user_agent
|
from config import user_agent
|
||||||
|
|
||||||
REQUEST_TIMEOUT = 10 # reasonable timeout that all HTTP requests should use
|
REQUEST_TIMEOUT = 10 # reasonable timeout that all HTTP requests should use
|
||||||
@ -35,11 +43,9 @@ def new_session(
|
|||||||
:param session: the Session object to attach the Adapter to, defaults to a new session
|
:param session: the Session object to attach the Adapter to, defaults to a new session
|
||||||
:return: The created Session
|
:return: The created Session
|
||||||
"""
|
"""
|
||||||
if session is None:
|
with Session() as session:
|
||||||
session = requests.Session()
|
|
||||||
session.headers['User-Agent'] = user_agent
|
session.headers['User-Agent'] = user_agent
|
||||||
|
adapter = TimeoutAdapter(timeout)
|
||||||
adapter = TimeoutAdapter(timeout)
|
session.mount("http://", adapter)
|
||||||
session.mount("http://", adapter)
|
session.mount("https://", adapter)
|
||||||
session.mount("https://", adapter)
|
return session
|
||||||
return session
|
|
||||||
|
41
update.py
41
update.py
@ -1,25 +1,32 @@
|
|||||||
"""Checking for updates to this application."""
|
"""
|
||||||
|
update.py - Checking for Program Updates.
|
||||||
|
|
||||||
|
Copyright (c) EDCD, All Rights Reserved
|
||||||
|
Licensed under the GNU General Public License.
|
||||||
|
See LICENSE file.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
from os.path import dirname, join
|
from os.path import dirname, join
|
||||||
from traceback import print_exc
|
from traceback import print_exc
|
||||||
from typing import TYPE_CHECKING, Optional
|
from typing import TYPE_CHECKING
|
||||||
from xml.etree import ElementTree
|
from xml.etree import ElementTree
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import semantic_version
|
import semantic_version
|
||||||
|
from config import appname, appversion_nobuild, config, update_feed
|
||||||
|
from EDMCLogging import get_main_logger
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
|
|
||||||
from config import appname, appversion_nobuild, config, update_feed
|
|
||||||
from EDMCLogging import get_main_logger
|
|
||||||
|
|
||||||
logger = get_main_logger()
|
logger = get_main_logger()
|
||||||
|
|
||||||
|
|
||||||
class EDMCVersion(object):
|
class EDMCVersion:
|
||||||
"""
|
"""
|
||||||
Hold all the information about an EDMC version.
|
Hold all the information about an EDMC version.
|
||||||
|
|
||||||
@ -39,7 +46,7 @@ class EDMCVersion(object):
|
|||||||
self.sv: semantic_version.base.Version = sv
|
self.sv: semantic_version.base.Version = sv
|
||||||
|
|
||||||
|
|
||||||
class Updater(object):
|
class Updater:
|
||||||
"""
|
"""
|
||||||
Handle checking for updates.
|
Handle checking for updates.
|
||||||
|
|
||||||
@ -63,16 +70,16 @@ class Updater(object):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def __init__(self, tkroot: Optional['tk.Tk'] = None, provider: str = 'internal'):
|
def __init__(self, tkroot: tk.Tk | None = None, provider: str = 'internal'):
|
||||||
"""
|
"""
|
||||||
Initialise an Updater instance.
|
Initialise an Updater instance.
|
||||||
|
|
||||||
:param tkroot: reference to the root window of the GUI
|
:param tkroot: reference to the root window of the GUI
|
||||||
:param provider: 'internal' or other string if not
|
:param provider: 'internal' or other string if not
|
||||||
"""
|
"""
|
||||||
self.root: Optional['tk.Tk'] = tkroot
|
self.root: tk.Tk | None = tkroot
|
||||||
self.provider: str = provider
|
self.provider: str = provider
|
||||||
self.thread: Optional[threading.Thread] = None
|
self.thread: threading.Thread | None = None
|
||||||
|
|
||||||
if self.use_internal():
|
if self.use_internal():
|
||||||
return
|
return
|
||||||
@ -81,7 +88,7 @@ class Updater(object):
|
|||||||
import ctypes
|
import ctypes
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.updater: Optional[ctypes.CDLL] = ctypes.cdll.WinSparkle
|
self.updater: ctypes.CDLL | None = ctypes.cdll.WinSparkle
|
||||||
|
|
||||||
# Set the appcast URL
|
# Set the appcast URL
|
||||||
self.updater.win_sparkle_set_appcast_url(update_feed.encode())
|
self.updater.win_sparkle_set_appcast_url(update_feed.encode())
|
||||||
@ -150,7 +157,7 @@ class Updater(object):
|
|||||||
elif sys.platform == 'darwin' and self.updater:
|
elif sys.platform == 'darwin' and self.updater:
|
||||||
self.updater.checkForUpdates_(None)
|
self.updater.checkForUpdates_(None)
|
||||||
|
|
||||||
def check_appcast(self) -> Optional[EDMCVersion]:
|
def check_appcast(self) -> EDMCVersion | None:
|
||||||
"""
|
"""
|
||||||
Manually (no Sparkle or WinSparkle) check the update_feed appcast file.
|
Manually (no Sparkle or WinSparkle) check the update_feed appcast file.
|
||||||
|
|
||||||
@ -161,7 +168,7 @@ class Updater(object):
|
|||||||
newversion = None
|
newversion = None
|
||||||
items = {}
|
items = {}
|
||||||
try:
|
try:
|
||||||
r = requests.get(update_feed, timeout=10)
|
request = requests.get(update_feed, timeout=10)
|
||||||
|
|
||||||
except requests.RequestException as ex:
|
except requests.RequestException as ex:
|
||||||
logger.exception(f'Error retrieving update_feed file: {ex}')
|
logger.exception(f'Error retrieving update_feed file: {ex}')
|
||||||
@ -169,7 +176,7 @@ class Updater(object):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
feed = ElementTree.fromstring(r.text)
|
feed = ElementTree.fromstring(request.text)
|
||||||
|
|
||||||
except SyntaxError as ex:
|
except SyntaxError as ex:
|
||||||
logger.exception(f'Syntax error in update_feed file: {ex}')
|
logger.exception(f'Syntax error in update_feed file: {ex}')
|
||||||
@ -196,12 +203,12 @@ class Updater(object):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# This will change A.B.C.D to A.B.C+D
|
# This will change A.B.C.D to A.B.C+D
|
||||||
sv = semantic_version.Version.coerce(ver)
|
semver = semantic_version.Version.coerce(ver)
|
||||||
|
|
||||||
items[sv] = EDMCVersion(
|
items[semver] = EDMCVersion(
|
||||||
version=str(ver), # sv might have mangled version
|
version=str(ver), # sv might have mangled version
|
||||||
title=item.find('title').text, # type: ignore
|
title=item.find('title').text, # type: ignore
|
||||||
sv=sv
|
sv=semver
|
||||||
)
|
)
|
||||||
|
|
||||||
# Look for any remaining version greater than appversion
|
# Look for any remaining version greater than appversion
|
||||||
|
Loading…
x
Reference in New Issue
Block a user