From 791a0c80c2c0b202d27a636c36302229af88c328 Mon Sep 17 00:00:00 2001 From: David Sangrey <rixxan@hullseals.space> Date: Wed, 27 Mar 2024 18:36:03 -0400 Subject: [PATCH 01/22] [Minor] Remove Deprecated Files and Functions --- build.py | 2 -- modules.p | Bin 44802 -> 0 bytes ships.p | Bin 920 -> 0 bytes ttkHyperlinkLabel.py | 41 ----------------------------------------- 4 files changed, 43 deletions(-) delete mode 100644 modules.p delete mode 100644 ships.p diff --git a/build.py b/build.py index 1bc96765..904c4c5b 100644 --- a/build.py +++ b/build.py @@ -76,10 +76,8 @@ def generate_data_files( "ChangeLog.md", "snd_good.wav", "snd_bad.wav", - "modules.p", # TODO: Remove in 6.0 "modules.json", "ships.json", - "ships.p", # TODO: Remove in 6.0 f"{app_name}.ico", f"resources/{appcmdname}.ico", "EDMarketConnector - TRACE.bat", diff --git a/modules.p b/modules.p deleted file mode 100644 index c48f76c4894fdb061e0a1967f388afe819e679c4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44802 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 diff --git a/ships.p b/ships.p deleted file mode 100644 index d289497c9540f529b38d01a580f36eaafcd68c26..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 920 zcmZXSzmC&D5XON^LP$am4hrZjXrKrJ3J@J8zT+aty3^&95@}=a#jD%wT08556i5`5 zi-d%s;SH!L5G4hVz!OmM1XNUvy|$ABSJwRI&o?{YtH%2`8)u5obVJEG4K<SjGSl5t z5mhwuSg5mi+3kbumT9^X#<G{0X~!w&0RhPTn_#ZEoHHUq>I`FoJ;c1oid(_=vtYK? zD5FQ3Fo7))zD0O>1yu^fwnz3GS=$vPlp-Rl^qvcev;89BQdJ|aVdjNAJPBp0i&Xyu znBEi0(>Za*OjMa)H^6j<a;AtA9FImLoBIV!rw&eS><5^u9wQ4JJ|p4rKNB0d?sdF2 z`4gD)p5#efF6kRE&22L0)ONm)op&g%Nx0(zkd$j@60@YB!B6abslr34PN>$lFnEpP zYvd`JKwagX1G8CGJx#@F(%YeG5YfRnKadsi2TW@hN0$%d_p$Y8kx<1@)sV9Uf32YW zL~BT<id98;_svyNibBhpaRW@hCZ~6A`;_whNv)9l?R`a<E)HTxKE{z3ZE!y(Ld%7X z_c7iK$YN&g;NudFJr$PbxPi2_ztpiT&Gifeoe7KRb0#WR`PXrq!(~DT_wG6#J)$)U zKBMTZ0zGgJ6%mj~Rc+*dtk}c<Sdh=@6@`zST}n=tR_#IVwJF1gT=yDYzf%WGuYE(u POwk6ZDzEbk(mQ_vKH60e diff --git a/ttkHyperlinkLabel.py b/ttkHyperlinkLabel.py index 0cc19b4e..0ca87fc7 100644 --- a/ttkHyperlinkLabel.py +++ b/ttkHyperlinkLabel.py @@ -146,44 +146,3 @@ class HyperlinkLabel(sys.platform == 'darwin' and tk.Label or ttk.Label): # typ """Copy the current text to the clipboard.""" self.clipboard_clear() self.clipboard_append(self['text']) - - -def openurl(url: str) -> None: - r""" - Open the given URL in appropriate browser. - - 2022-12-06: - Firefox itself will gladly attempt to use very long URLs in its URL - input. Up to 16384 was attempted, but the Apache instance this was - tested against only allowed up to 8207 total URL length to pass, that - being 8190 octets of REQUEST_URI (path + GET params). - - Testing from Windows 10 Home 21H2 cmd.exe with: - - "<path to>\firefox.exe" -osint -url "<test url>" - - only allowed 8115 octest of REQUEST_URI to pass through. - - Microsoft Edge yielded 8092 octets. Google Chrome yielded 8093 octets. - - However, this is actually the limit of how long a CMD.EXE command-line - can be. The URL was being cut off *there*. - - The 8207 octet URL makes it through `webbrowser.open(<url>)` to: - - Firefox 107.0.1 - Microsoft Edge 108.0.1462.42 - Google Chrome 108.0.5359.95 - - This was also tested as working *with* the old winreg/subprocess code, - so it wasn't even suffering from the same limit as CMD.EXE. - - Conclusion: No reason to not just use `webbrowser.open()`, as prior - to e280d6c2833c25867b8139490e68ddf056477917 there was a bug, introduced - in 5989acd0d3263e54429ff99769ff73a20476d863, which meant the code always - ended up using `webbrowser.open()` *anyway*. - :param url: URL to open. - """ - warnings.warn("This function is deprecated. " - "Please use `webbrowser.open() instead.", DeprecationWarning, stacklevel=2) - webbrowser.open(url) From 57b6ecd88ea9bd989518ddc676db13753ba74973 Mon Sep 17 00:00:00 2001 From: David Sangrey <rixxan@hullseals.space> Date: Wed, 27 Mar 2024 19:16:45 -0400 Subject: [PATCH 02/22] [2186] Remove Config and Hotkey --- config/__init__.py | 3 - config/darwin.py | 191 ------------------------------- hotkey/__init__.py | 4 - hotkey/darwin.py | 276 --------------------------------------------- 4 files changed, 474 deletions(-) delete mode 100644 config/darwin.py delete mode 100644 hotkey/darwin.py diff --git a/config/__init__.py b/config/__init__.py index 3acb2edd..992710a0 100644 --- a/config/__init__.py +++ b/config/__init__.py @@ -468,9 +468,6 @@ def get_config(*args, **kwargs) -> AbstractConfig: :param kwargs: Args to be passed through to implementation. :return: Instance of the implementation. """ - if sys.platform == "darwin": # pragma: sys-platform-darwin - from .darwin import MacConfig - return MacConfig(*args, **kwargs) if sys.platform == "win32": # pragma: sys-platform-win32 from .windows import WinConfig diff --git a/config/darwin.py b/config/darwin.py deleted file mode 100644 index 9c15ec32..00000000 --- a/config/darwin.py +++ /dev/null @@ -1,191 +0,0 @@ -""" -darwin.py - Darwin/macOS implementation of AbstractConfig. - -Copyright (c) EDCD, All Rights Reserved -Licensed under the GNU General Public License. -See LICENSE file. -""" -from __future__ import annotations - -import pathlib -import sys -from typing import Any -from Foundation import ( # type: ignore - NSApplicationSupportDirectory, NSBundle, NSDocumentDirectory, NSSearchPathForDirectoriesInDomains, NSUserDefaults, - NSUserDomainMask -) -from config import AbstractConfig, appname, logger - -assert sys.platform == 'darwin' - - -class MacConfig(AbstractConfig): - """MacConfig is the implementation of AbstractConfig for Darwin based OSes.""" - - def __init__(self) -> None: - super().__init__() - support_path = pathlib.Path( - NSSearchPathForDirectoriesInDomains( - NSApplicationSupportDirectory, NSUserDomainMask, True - )[0] - ) - - self.app_dir_path = support_path / appname - self.app_dir_path.mkdir(exist_ok=True) - - self.plugin_dir_path = self.app_dir_path / 'plugins' - self.plugin_dir_path.mkdir(exist_ok=True) - - # Bundle IDs identify a singled app though out a system - - if getattr(sys, 'frozen', False): - exe_dir = pathlib.Path(sys.executable).parent - self.internal_plugin_dir_path = exe_dir.parent / 'Library' / 'plugins' - self.respath_path = exe_dir.parent / 'Resources' - self.identifier = NSBundle.mainBundle().bundleIdentifier() - - else: - file_dir = pathlib.Path(__file__).parent.parent - self.internal_plugin_dir_path = file_dir / 'plugins' - self.respath_path = file_dir - - self.identifier = f'uk.org.marginal.{appname.lower()}' - NSBundle.mainBundle().infoDictionary()['CFBundleIdentifier'] = self.identifier - - self.default_journal_dir_path = support_path / 'Frontier Developments' / 'Elite Dangerous' - self._defaults: Any = NSUserDefaults.standardUserDefaults() - self._settings: dict[str, int | str | list] = dict( - self._defaults.persistentDomainForName_(self.identifier) or {} - ) # make writeable - - if (out_dir := self.get_str('out_dir')) is None or not pathlib.Path(out_dir).exists(): - self.set('outdir', NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, True)[0]) - - def __raw_get(self, key: str) -> None | list | str | int: - """ - Retrieve the raw data for the given key. - - :param str: str - The key data is being requested for. - :return: The requested data. - """ - res = self._settings.get(key) - # On MacOS Catalina, with python.org python 3.9.2 any 'list' - # has type __NSCFArray so a simple `isinstance(res, list)` is - # False. So, check it's not-None, and not the other types. - # - # If we can find where to import the definition of NSCFArray - # then we could possibly test against that. - if res is not None and not isinstance(res, str) and not isinstance(res, int): - return list(res) - - return res - - def get_str(self, key: str, *, default: str = None) -> str: - """ - Return the string referred to by the given key if it exists, or the default. - - Implements :meth:`AbstractConfig.get_str`. - """ - res = self.__raw_get(key) - if res is None: - return default # Yes it could be None, but we're _assuming_ that people gave us a default - - if not isinstance(res, str): - raise ValueError(f'unexpected data returned from __raw_get: {type(res)=} {res}') - - return res - - def get_list(self, key: str, *, default: list = None) -> list: - """ - Return the list referred to by the given key if it exists, or the default. - - Implements :meth:`AbstractConfig.get_list`. - """ - res = self.__raw_get(key) - if res is None: - return default # Yes it could be None, but we're _assuming_ that people gave us a default - - if not isinstance(res, list): - raise ValueError(f'__raw_get returned unexpected type {type(res)=} {res!r}') - - return res - - def get_int(self, key: str, *, default: int = 0) -> int: - """ - Return the int referred to by key if it exists in the config. - - Implements :meth:`AbstractConfig.get_int`. - """ - res = self.__raw_get(key) - if res is None: - return default - - if not isinstance(res, (str, int)): - raise ValueError(f'__raw_get returned unexpected type {type(res)=} {res!r}') - - try: - return int(res) - - except ValueError as e: - logger.error(f'__raw_get returned {res!r} which cannot be parsed to an int: {e}') - return default # Yes it could be None, but we're _assuming_ that people gave us a default - - def get_bool(self, key: str, *, default: bool = None) -> bool: - """ - Return the bool referred to by the given key if it exists, or the default. - - Implements :meth:`AbstractConfig.get_bool`. - """ - res = self.__raw_get(key) - if res is None: - return default # Yes it could be None, but we're _assuming_ that people gave us a default - - if not isinstance(res, bool): - raise ValueError(f'__raw_get returned unexpected type {type(res)=} {res!r}') - - return res - - def set(self, key: str, val: int | str | list[str] | bool) -> None: - """ - Set the given key's data to the given value. - - Implements :meth:`AbstractConfig.set`. - """ - if self._settings is None: - raise ValueError('attempt to use a closed _settings') - - if not isinstance(val, (bool, str, int, list)): - raise ValueError(f'Unexpected type for value {type(val)=}') - - self._settings[key] = val - - def delete(self, key: str, *, suppress=False) -> None: - """ - Delete the given key from the config. - - Implements :meth:`AbstractConfig.delete`. - """ - try: - del self._settings[key] - - except Exception: - if suppress: - pass - - def save(self) -> None: - """ - Save the current configuration. - - Implements :meth:`AbstractConfig.save`. - """ - self._defaults.setPersistentDomain_forName_(self._settings, self.identifier) - self._defaults.synchronize() - - def close(self) -> None: - """ - Close this config and release any associated resources. - - Implements :meth:`AbstractConfig.close`. - """ - self.save() - self._defaults = None diff --git a/hotkey/__init__.py b/hotkey/__init__.py index e7515807..313415f9 100644 --- a/hotkey/__init__.py +++ b/hotkey/__init__.py @@ -76,10 +76,6 @@ def get_hotkeymgr() -> AbstractHotkeyMgr: :return: Appropriate class instance. :raises ValueError: If unsupported platform. """ - if sys.platform == 'darwin': - from hotkey.darwin import MacHotkeyMgr - return MacHotkeyMgr() - if sys.platform == 'win32': from hotkey.windows import WindowsHotkeyMgr return WindowsHotkeyMgr() diff --git a/hotkey/darwin.py b/hotkey/darwin.py deleted file mode 100644 index 6afd0239..00000000 --- a/hotkey/darwin.py +++ /dev/null @@ -1,276 +0,0 @@ -"""darwin/macOS implementation of hotkey.AbstractHotkeyMgr.""" -from __future__ import annotations - -import pathlib -import sys -import tkinter as tk -from typing import Callable -assert sys.platform == 'darwin' - -import objc -from AppKit import ( - NSAlternateKeyMask, NSApplication, NSBeep, NSClearLineFunctionKey, NSCommandKeyMask, NSControlKeyMask, - NSDeleteFunctionKey, NSDeviceIndependentModifierFlagsMask, NSEvent, NSF1FunctionKey, NSF35FunctionKey, - NSFlagsChanged, NSKeyDown, NSKeyDownMask, NSKeyUp, NSNumericPadKeyMask, NSShiftKeyMask, NSSound, NSWorkspace -) - -from config import config -from EDMCLogging import get_main_logger -from hotkey import AbstractHotkeyMgr - -logger = get_main_logger() - - -class MacHotkeyMgr(AbstractHotkeyMgr): - """Hot key management.""" - - POLL = 250 - # https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSEvent_Class/#//apple_ref/doc/constant_group/Function_Key_Unicodes - DISPLAY = { - 0x03: u'⌅', 0x09: u'⇥', 0xd: u'↩', 0x19: u'⇤', 0x1b: u'esc', 0x20: u'⏘', 0x7f: u'⌫', - 0xf700: u'↑', 0xf701: u'↓', 0xf702: u'←', 0xf703: u'→', - 0xf727: u'Ins', - 0xf728: u'⌦', 0xf729: u'↖', 0xf72a: u'Fn', 0xf72b: u'↘', - 0xf72c: u'⇞', 0xf72d: u'⇟', 0xf72e: u'PrtScr', 0xf72f: u'ScrollLock', - 0xf730: u'Pause', 0xf731: u'SysReq', 0xf732: u'Break', 0xf733: u'Reset', - 0xf739: u'⌧', - } - (ACQUIRE_INACTIVE, ACQUIRE_ACTIVE, ACQUIRE_NEW) = range(3) - - def __init__(self): - self.MODIFIERMASK = NSShiftKeyMask | NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask \ - | NSNumericPadKeyMask - self.root: tk.Tk - - self.keycode = 0 - self.modifiers = 0 - self.activated = False - self.observer = None - - self.acquire_key = 0 - self.acquire_state = MacHotkeyMgr.ACQUIRE_INACTIVE - - self.tkProcessKeyEvent_old: Callable - - self.snd_good = NSSound.alloc().initWithContentsOfFile_byReference_( - pathlib.Path(config.respath_path) / 'snd_good.wav', False - ) - self.snd_bad = NSSound.alloc().initWithContentsOfFile_byReference_( - pathlib.Path(config.respath_path) / 'snd_bad.wav', False - ) - - def register(self, root: tk.Tk, keycode: int, modifiers: int) -> None: - """ - Register current hotkey for monitoring. - - :param root: parent window. - :param keycode: Key to monitor. - :param modifiers: Any modifiers to take into account. - """ - self.root = root - self.keycode = keycode - self.modifiers = modifiers - self.activated = False - - if keycode: - if not self.observer: - self.root.after_idle(self._observe) - self.root.after(MacHotkeyMgr.POLL, self._poll) - - # Monkey-patch tk (tkMacOSXKeyEvent.c) - if not callable(self.tkProcessKeyEvent_old): - sel = b'tkProcessKeyEvent:' - cls = NSApplication.sharedApplication().class__() # type: ignore - self.tkProcessKeyEvent_old = NSApplication.sharedApplication().methodForSelector_(sel) # type: ignore - newmethod = objc.selector( # type: ignore - self.tkProcessKeyEvent, - selector=self.tkProcessKeyEvent_old.selector, - signature=self.tkProcessKeyEvent_old.signature - ) - objc.classAddMethod(cls, sel, newmethod) # type: ignore - - def tkProcessKeyEvent(self, cls, the_event): # noqa: N802 - """ - Monkey-patch tk (tkMacOSXKeyEvent.c). - - - workaround crash on OSX 10.9 & 10.10 on seeing a composing character - - notice when modifier key state changes - - keep a copy of NSEvent.charactersIgnoringModifiers, which is what we need for the hotkey - - (Would like to use a decorator but need to ensure the application is created before this is installed) - :param cls: ??? - :param the_event: tk event - :return: ??? - """ - if self.acquire_state: - if the_event.type() == NSFlagsChanged: - self.acquire_key = the_event.modifierFlags() & NSDeviceIndependentModifierFlagsMask - self.acquire_state = MacHotkeyMgr.ACQUIRE_NEW - # suppress the event by not chaining the old function - return the_event - - if the_event.type() in (NSKeyDown, NSKeyUp): - c = the_event.charactersIgnoringModifiers() - self.acquire_key = (c and ord(c[0]) or 0) | \ - (the_event.modifierFlags() & NSDeviceIndependentModifierFlagsMask) - self.acquire_state = MacHotkeyMgr.ACQUIRE_NEW - # suppress the event by not chaining the old function - return the_event - - # replace empty characters with charactersIgnoringModifiers to avoid crash - elif the_event.type() in (NSKeyDown, NSKeyUp) and not the_event.characters(): - the_event = NSEvent.keyEventWithType_location_modifierFlags_timestamp_windowNumber_context_characters_charactersIgnoringModifiers_isARepeat_keyCode_( # noqa: E501 - # noqa: E501 - the_event.type(), - the_event.locationInWindow(), - the_event.modifierFlags(), - the_event.timestamp(), - the_event.windowNumber(), - the_event.context(), - the_event.charactersIgnoringModifiers(), - the_event.charactersIgnoringModifiers(), - the_event.isARepeat(), - the_event.keyCode() - ) - return self.tkProcessKeyEvent_old(cls, the_event) - - def _observe(self): - # Must be called after root.mainloop() so that the app's message loop has been created - self.observer = NSEvent.addGlobalMonitorForEventsMatchingMask_handler_(NSKeyDownMask, self._handler) - - def _poll(self): - if config.shutting_down: - return - - # No way of signalling to Tkinter from within the callback handler block that doesn't - # cause Python to crash, so poll. - if self.activated: - self.activated = False - self.root.event_generate('<<Invoke>>', when="tail") - - if self.keycode or self.modifiers: - self.root.after(MacHotkeyMgr.POLL, self._poll) - - def unregister(self) -> None: - """Remove hotkey registration.""" - self.keycode = 0 - self.modifiers = 0 - - @objc.callbackFor(NSEvent.addGlobalMonitorForEventsMatchingMask_handler_) - def _handler(self, event) -> None: - # use event.charactersIgnoringModifiers to handle composing characters like Alt-e - if ( - (event.modifierFlags() & self.MODIFIERMASK) == self.modifiers - and ord(event.charactersIgnoringModifiers()[0]) == self.keycode - ): - if config.get_int('hotkey_always'): - self.activated = True - - else: # Only trigger if game client is front process - front = NSWorkspace.sharedWorkspace().frontmostApplication() - if front and front.bundleIdentifier() == 'uk.co.frontier.EliteDangerous': - self.activated = True - - def acquire_start(self) -> None: - """Start acquiring hotkey state via polling.""" - self.acquire_state = MacHotkeyMgr.ACQUIRE_ACTIVE - self.root.after_idle(self._acquire_poll) - - def acquire_stop(self) -> None: - """Stop acquiring hotkey state.""" - self.acquire_state = MacHotkeyMgr.ACQUIRE_INACTIVE - - def _acquire_poll(self) -> None: - """Perform a poll of current hotkey state.""" - if config.shutting_down: - return - - # No way of signalling to Tkinter from within the monkey-patched event handler that doesn't - # cause Python to crash, so poll. - if self.acquire_state: - if self.acquire_state == MacHotkeyMgr.ACQUIRE_NEW: - # Abuse tkEvent's keycode field to hold our acquired key & modifier - self.root.event_generate('<KeyPress>', keycode=self.acquire_key) - self.acquire_state = MacHotkeyMgr.ACQUIRE_ACTIVE - self.root.after(50, self._acquire_poll) - - def fromevent(self, event) -> bool | tuple | None: - """ - Return configuration (keycode, modifiers) or None=clear or False=retain previous. - - :param event: tk event ? - :return: False to retain previous, None to not use, else (keycode, modifiers) - """ - (keycode, modifiers) = (event.keycode & 0xffff, event.keycode & 0xffff0000) # Set by _acquire_poll() - if ( - keycode - and not (modifiers & (NSShiftKeyMask | NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask)) - ): - if keycode == 0x1b: # Esc = retain previous - self.acquire_state = MacHotkeyMgr.ACQUIRE_INACTIVE - return False - - # BkSp, Del, Clear = clear hotkey - if keycode in (0x7f, ord(NSDeleteFunctionKey), ord(NSClearLineFunctionKey)): - self.acquire_state = MacHotkeyMgr.ACQUIRE_INACTIVE - return None - - # don't allow keys needed for typing in System Map - if keycode in (0x13, 0x20, 0x2d) or 0x61 <= keycode <= 0x7a: - NSBeep() - self.acquire_state = MacHotkeyMgr.ACQUIRE_INACTIVE - return None - - return keycode, modifiers - - def display(self, keycode, modifiers) -> str: - """ - Return displayable form of given hotkey + modifiers. - - :param keycode: - :param modifiers: - :return: string form - """ - text = '' - if modifiers & NSControlKeyMask: - text += u'⌃' - - if modifiers & NSAlternateKeyMask: - text += u'⌥' - - if modifiers & NSShiftKeyMask: - text += u'⇧' - - if modifiers & NSCommandKeyMask: - text += u'⌘' - - if (modifiers & NSNumericPadKeyMask) and keycode <= 0x7f: - text += u'№' - - if not keycode: - pass - - elif ord(NSF1FunctionKey) <= keycode <= ord(NSF35FunctionKey): - text += f'F{keycode + 1 - ord(NSF1FunctionKey)}' - - elif keycode in MacHotkeyMgr.DISPLAY: # specials - text += MacHotkeyMgr.DISPLAY[keycode] - - elif keycode < 0x20: # control keys - text += chr(keycode + 0x40) - - elif keycode < 0xf700: # key char - text += chr(keycode).upper() - - else: - text += u'⁈' - - return text - - def play_good(self): - """Play the 'good' sound.""" - self.snd_good.play() - - def play_bad(self): - """Play the 'bad' sound.""" - self.snd_bad.play() From 93d26e07e057d1b70f3144898ecf34e35c4cdae2 Mon Sep 17 00:00:00 2001 From: David Sangrey <rixxan@hullseals.space> Date: Wed, 27 Mar 2024 19:19:15 -0400 Subject: [PATCH 03/22] [2186] Remove Monitor, L10n References --- l10n.py | 50 +++++--------------------------------------------- monitor.py | 23 ++--------------------- 2 files changed, 7 insertions(+), 66 deletions(-) diff --git a/l10n.py b/l10n.py index a2b5185a..183d902a 100755 --- a/l10n.py +++ b/l10n.py @@ -17,8 +17,8 @@ import re import sys import warnings from contextlib import suppress -from os import pardir, listdir, sep, makedirs -from os.path import basename, dirname, isdir, isfile, join, abspath, exists +from os import listdir, sep, makedirs +from os.path import basename, dirname, isdir, join, abspath, exists from typing import TYPE_CHECKING, Iterable, TextIO, cast from config import config from EDMCLogging import get_main_logger @@ -39,12 +39,7 @@ logger = get_main_logger() LANGUAGE_ID = '!Language' LOCALISATION_DIR = 'L10n' -if sys.platform == 'darwin': - from Foundation import ( # type: ignore # exists on Darwin - NSLocale, NSNumberFormatter, NSNumberFormatterDecimalStyle - ) - -elif sys.platform == 'win32': +if sys.platform == 'win32': import ctypes from ctypes.wintypes import BOOL, DWORD, LPCVOID, LPCWSTR, LPWSTR if TYPE_CHECKING: @@ -178,14 +173,8 @@ class _Translations: def available(self) -> set[str]: """Return a list of available language codes.""" path = self.respath() - if getattr(sys, 'frozen', False) and sys.platform == 'darwin': - available = { - x[:-len('.lproj')] for x in listdir(path) - if x.endswith('.lproj') and isfile(join(x, 'Localizable.strings')) - } - else: - available = {x[:-len('.strings')] for x in listdir(path) if x.endswith('.strings')} + available = {x[:-len('.strings')] for x in listdir(path) if x.endswith('.strings')} return available @@ -206,9 +195,6 @@ class _Translations: def respath(self) -> str: """Path to localisation files.""" if getattr(sys, 'frozen', False): - if sys.platform == 'darwin': - return abspath(join(dirname(sys.executable), pardir, 'Resources')) - return abspath(join(dirname(sys.executable), LOCALISATION_DIR)) if __file__: @@ -234,10 +220,6 @@ class _Translations: except OSError: logger.exception(f'could not open {file_path}') - elif getattr(sys, 'frozen', False) and sys.platform == 'darwin': - res_path = join(self.respath(), f'{lang}.lproj', 'Localizable.strings') - return open(res_path, encoding='utf-16') - res_path = join(self.respath(), f'{lang}.strings') return open(res_path, encoding='utf-8') @@ -245,15 +227,6 @@ class _Translations: class _Locale: """Locale holds a few utility methods to convert data to and from localized versions.""" - def __init__(self) -> None: - if sys.platform == 'darwin': - self.int_formatter = NSNumberFormatter.alloc().init() - self.int_formatter.setNumberStyle_(NSNumberFormatterDecimalStyle) - self.float_formatter = NSNumberFormatter.alloc().init() - self.float_formatter.setNumberStyle_(NSNumberFormatterDecimalStyle) - self.float_formatter.setMinimumFractionDigits_(5) - self.float_formatter.setMaximumFractionDigits_(5) - def stringFromNumber(self, number: float | int, decimals: int | None = None) -> str: # noqa: N802 warnings.warn(DeprecationWarning('use _Locale.string_from_number instead.')) return self.string_from_number(number, decimals) # type: ignore @@ -279,14 +252,6 @@ class _Locale: if decimals == 0 and not isinstance(number, numbers.Integral): number = int(round(number)) - if sys.platform == 'darwin': - if not decimals and isinstance(number, numbers.Integral): - return self.int_formatter.stringFromNumber_(number) - - self.float_formatter.setMinimumFractionDigits_(decimals) - self.float_formatter.setMaximumFractionDigits_(decimals) - return self.float_formatter.stringFromNumber_(number) - if not decimals and isinstance(number, numbers.Integral): return locale.format_string('%d', number, True) return locale.format_string('%.*f', (decimals, number), True) @@ -299,9 +264,6 @@ class _Locale: :param string: The string to convert :return: None if the string cannot be parsed, otherwise an int or float dependant on input data. """ - if sys.platform == 'darwin': - return self.float_formatter.numberFromString_(string) - with suppress(ValueError): return locale.atoi(string) @@ -332,10 +294,8 @@ class _Locale: :return: The preferred language list """ languages: Iterable[str] - if sys.platform == 'darwin': - languages = NSLocale.preferredLanguages() - elif sys.platform != 'win32': + if sys.platform != 'win32': # POSIX lang = locale.getlocale()[0] languages = [lang.replace('_', '-')] if lang else [] diff --git a/monitor.py b/monitor.py index 8364f95b..d549e533 100644 --- a/monitor.py +++ b/monitor.py @@ -38,16 +38,7 @@ if TYPE_CHECKING: def _(x: str) -> str: return x -if sys.platform == 'darwin': - from fcntl import fcntl - - from AppKit import NSWorkspace - from watchdog.events import FileSystemEventHandler - from watchdog.observers import Observer - from watchdog.observers.api import BaseObserver - F_GLOBAL_NOCACHE = 55 - -elif sys.platform == 'win32': +if sys.platform == 'win32': import ctypes from ctypes.wintypes import BOOL, HWND, LPARAM, LPWSTR @@ -382,8 +373,6 @@ class EDLogs(FileSystemEventHandler): logfile = self.logfile if logfile: loghandle: BinaryIO = open(logfile, 'rb', 0) # unbuffered - if sys.platform == 'darwin': - fcntl(loghandle, F_GLOBAL_NOCACHE, -1) # required to avoid corruption on macOS over SMB self.catching_up = True for line in loghandle: @@ -483,9 +472,6 @@ class EDLogs(FileSystemEventHandler): if logfile: loghandle = open(logfile, 'rb', 0) # unbuffered - if sys.platform == 'darwin': - fcntl(loghandle, F_GLOBAL_NOCACHE, -1) # required to avoid corruption on macOS over SMB - log_pos = 0 sleep(self._POLL) @@ -2144,12 +2130,7 @@ class EDLogs(FileSystemEventHandler): :return: bool - True if the game is running. """ - if sys.platform == 'darwin': - for app in NSWorkspace.sharedWorkspace().runningApplications(): - if app.bundleIdentifier() == 'uk.co.frontier.EliteDangerous': - return True - - elif sys.platform == 'win32': + if sys.platform == 'win32': def WindowTitle(h): # noqa: N802 if h: length = GetWindowTextLength(h) + 1 From 27093d88620ed760600b0e1df672160e25dffc7c Mon Sep 17 00:00:00 2001 From: David Sangrey <rixxan@hullseals.space> Date: Wed, 27 Mar 2024 19:26:25 -0400 Subject: [PATCH 04/22] [2186] Main, Dashboard, Prefs --- EDMarketConnector.py | 342 ++++++++++++++++--------------------------- dashboard.py | 2 +- myNotebook.py | 2 +- prefs.py | 177 ++++------------------ 4 files changed, 161 insertions(+), 362 deletions(-) diff --git a/EDMarketConnector.py b/EDMarketConnector.py index 6debf4e9..967488e0 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -496,21 +496,20 @@ class AppWindow: plug.load_plugins(master) - if sys.platform != 'darwin': - if sys.platform == 'win32': - self.w.wm_iconbitmap(default='EDMarketConnector.ico') + if sys.platform == 'win32': + self.w.wm_iconbitmap(default='EDMarketConnector.ico') - else: - self.w.tk.call('wm', 'iconphoto', self.w, '-default', - tk.PhotoImage(file=path.join(config.respath_path, 'io.edcd.EDMarketConnector.png'))) + else: + self.w.tk.call('wm', 'iconphoto', self.w, '-default', + tk.PhotoImage(file=path.join(config.respath_path, 'io.edcd.EDMarketConnector.png'))) - # TODO: Export to files and merge from them in future ? - self.theme_icon = tk.PhotoImage( - data='R0lGODlhFAAQAMZQAAoKCQoKCgsKCQwKCQsLCgwLCg4LCQ4LCg0MCg8MCRAMCRANChINCREOChIOChQPChgQChgRCxwTCyYVCSoXCS0YCTkdCTseCT0fCTsjDU0jB0EnDU8lB1ElB1MnCFIoCFMoCEkrDlkqCFwrCGEuCWIuCGQvCFs0D1w1D2wyCG0yCF82D182EHE0CHM0CHQ1CGQ5EHU2CHc3CHs4CH45CIA6CIE7CJdECIdLEolMEohQE5BQE41SFJBTE5lUE5pVE5RXFKNaFKVbFLVjFbZkFrxnFr9oFsNqFsVrF8RsFshtF89xF9NzGNh1GNl2GP+KG////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////yH5BAEKAH8ALAAAAAAUABAAAAeegAGCgiGDhoeIRDiIjIZGKzmNiAQBQxkRTU6am0tPCJSGShuSAUcLoIIbRYMFra4FAUgQAQCGJz6CDQ67vAFJJBi0hjBBD0w9PMnJOkAiJhaIKEI7HRoc19ceNAolwbWDLD8uAQnl5ga1I9CHEjEBAvDxAoMtFIYCBy+kFDKHAgM3ZtgYSLAGgwkp3pEyBOJCC2ELB31QATGioAoVAwEAOw==') # noqa: E501 - self.theme_minimize = tk.BitmapImage( - data='#define im_width 16\n#define im_height 16\nstatic unsigned char im_bits[] = {\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3f,\n 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };\n') # noqa: E501 - self.theme_close = tk.BitmapImage( - data='#define im_width 16\n#define im_height 16\nstatic unsigned char im_bits[] = {\n 0x00, 0x00, 0x00, 0x00, 0x0c, 0x30, 0x1c, 0x38, 0x38, 0x1c, 0x70, 0x0e,\n 0xe0, 0x07, 0xc0, 0x03, 0xc0, 0x03, 0xe0, 0x07, 0x70, 0x0e, 0x38, 0x1c,\n 0x1c, 0x38, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x00 };\n') # noqa: E501 + # TODO: Export to files and merge from them in future ? + self.theme_icon = tk.PhotoImage( + data='R0lGODlhFAAQAMZQAAoKCQoKCgsKCQwKCQsLCgwLCg4LCQ4LCg0MCg8MCRAMCRANChINCREOChIOChQPChgQChgRCxwTCyYVCSoXCS0YCTkdCTseCT0fCTsjDU0jB0EnDU8lB1ElB1MnCFIoCFMoCEkrDlkqCFwrCGEuCWIuCGQvCFs0D1w1D2wyCG0yCF82D182EHE0CHM0CHQ1CGQ5EHU2CHc3CHs4CH45CIA6CIE7CJdECIdLEolMEohQE5BQE41SFJBTE5lUE5pVE5RXFKNaFKVbFLVjFbZkFrxnFr9oFsNqFsVrF8RsFshtF89xF9NzGNh1GNl2GP+KG////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////yH5BAEKAH8ALAAAAAAUABAAAAeegAGCgiGDhoeIRDiIjIZGKzmNiAQBQxkRTU6am0tPCJSGShuSAUcLoIIbRYMFra4FAUgQAQCGJz6CDQ67vAFJJBi0hjBBD0w9PMnJOkAiJhaIKEI7HRoc19ceNAolwbWDLD8uAQnl5ga1I9CHEjEBAvDxAoMtFIYCBy+kFDKHAgM3ZtgYSLAGgwkp3pEyBOJCC2ELB31QATGioAoVAwEAOw==') # noqa: E501 + self.theme_minimize = tk.BitmapImage( + data='#define im_width 16\n#define im_height 16\nstatic unsigned char im_bits[] = {\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3f,\n 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };\n') # noqa: E501 + self.theme_close = tk.BitmapImage( + data='#define im_width 16\n#define im_height 16\nstatic unsigned char im_bits[] = {\n 0x00, 0x00, 0x00, 0x00, 0x0c, 0x30, 0x1c, 0x38, 0x38, 0x1c, 0x70, 0x0e,\n 0xe0, 0x07, 0xc0, 0x03, 0xc0, 0x03, 0xe0, 0x07, 0x70, 0x0e, 0x38, 0x1c,\n 0x1c, 0x38, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x00 };\n') # noqa: E501 frame = tk.Frame(self.w, name=appname.lower()) frame.grid(sticky=tk.NSEW) @@ -599,7 +598,7 @@ class AppWindow: self.theme_button = tk.Label( frame, name='themed_update_button', - width=32 if sys.platform == 'darwin' else 28, + width=28, state=tk.DISABLED ) @@ -633,148 +632,104 @@ class AppWindow: self.updater = update.Updater(tkroot=self.w) self.updater.check_for_updates() # Sparkle / WinSparkle does this automatically for packaged apps - if sys.platform == 'darwin': - # Can't handle (de)iconify if topmost is set, so suppress iconify button - # http://wiki.tcl.tk/13428 and p15 of - # https://developer.apple.com/legacy/library/documentation/Carbon/Conceptual/HandlingWindowsControls/windowscontrols.pdf - root.call('tk::unsupported::MacWindowStyle', 'style', root, 'document', 'closeBox resizable') + self.file_menu = self.view_menu = tk.Menu(self.menubar, tearoff=tk.FALSE) + self.file_menu.add_command(command=lambda: stats.StatsDialog(self.w, self.status)) + self.file_menu.add_command(command=self.save_raw) + self.file_menu.add_command(command=lambda: prefs.PreferencesDialog(self.w, self.postprefs)) + self.file_menu.add_separator() + self.file_menu.add_command(command=self.onexit) + self.menubar.add_cascade(menu=self.file_menu) + self.edit_menu = tk.Menu(self.menubar, tearoff=tk.FALSE) + self.edit_menu.add_command(accelerator='Ctrl+C', state=tk.DISABLED, command=self.copy) + self.menubar.add_cascade(menu=self.edit_menu) + self.help_menu = tk.Menu(self.menubar, tearoff=tk.FALSE) # type: ignore + self.help_menu.add_command(command=self.help_general) # Documentation + self.help_menu.add_command(command=self.help_troubleshooting) # Troubleshooting + self.help_menu.add_command(command=self.help_report_a_bug) # Report A Bug + self.help_menu.add_command(command=self.help_privacy) # Privacy Policy + self.help_menu.add_command(command=self.help_releases) # Release Notes + self.help_menu.add_command(command=lambda: self.updater.check_for_updates()) # Check for Updates... + # About E:D Market Connector + self.help_menu.add_command(command=lambda: not self.HelpAbout.showing and self.HelpAbout(self.w)) + self.help_menu.add_command(command=prefs.help_open_log_folder) # Open Log Folder - # https://www.tcl.tk/man/tcl/TkCmd/menu.htm - self.system_menu = tk.Menu(self.menubar, name='apple') - self.system_menu.add_command(command=lambda: self.w.call('tk::mac::standardAboutPanel')) - self.system_menu.add_command(command=lambda: self.updater.check_for_updates()) + self.menubar.add_cascade(menu=self.help_menu) + if sys.platform == 'win32': + # Must be added after at least one "real" menu entry + self.always_ontop = tk.BooleanVar(value=bool(config.get_int('always_ontop'))) + self.system_menu = tk.Menu(self.menubar, name='system', tearoff=tk.FALSE) + self.system_menu.add_separator() + # LANG: Appearance - Label for checkbox to select if application always on top + self.system_menu.add_checkbutton(label=_('Always on top'), + variable=self.always_ontop, + command=self.ontop_changed) # Appearance setting self.menubar.add_cascade(menu=self.system_menu) - self.file_menu = tk.Menu(self.menubar, name='file') - self.file_menu.add_command(command=self.save_raw) - self.menubar.add_cascade(menu=self.file_menu) - self.edit_menu = tk.Menu(self.menubar, name='edit') - self.edit_menu.add_command(accelerator='Command-c', state=tk.DISABLED, command=self.copy) - self.menubar.add_cascade(menu=self.edit_menu) - self.w.bind('<Command-c>', self.copy) - self.view_menu = tk.Menu(self.menubar, name='view') - self.view_menu.add_command(command=lambda: stats.StatsDialog(self.w, self.status)) - self.menubar.add_cascade(menu=self.view_menu) - window_menu = tk.Menu(self.menubar, name='window') - self.menubar.add_cascade(menu=window_menu) - self.help_menu = tk.Menu(self.menubar, name='help') - self.w.createcommand("::tk::mac::ShowHelp", self.help_general) - self.help_menu.add_command(command=self.help_troubleshooting) - self.help_menu.add_command(command=self.help_report_a_bug) - self.help_menu.add_command(command=self.help_privacy) - self.help_menu.add_command(command=self.help_releases) - self.menubar.add_cascade(menu=self.help_menu) - self.w['menu'] = self.menubar - # https://www.tcl.tk/man/tcl/TkCmd/tk_mac.htm - self.w.call('set', 'tk::mac::useCompatibilityMetrics', '0') - self.w.createcommand('tkAboutDialog', lambda: self.w.call('tk::mac::standardAboutPanel')) - self.w.createcommand("::tk::mac::Quit", self.onexit) - self.w.createcommand("::tk::mac::ShowPreferences", lambda: prefs.PreferencesDialog(self.w, self.postprefs)) - self.w.createcommand("::tk::mac::ReopenApplication", self.w.deiconify) # click on app in dock = restore - self.w.protocol("WM_DELETE_WINDOW", self.w.withdraw) # close button shouldn't quit app - self.w.resizable(tk.FALSE, tk.FALSE) # Can't be only resizable on one axis - else: - self.file_menu = self.view_menu = tk.Menu(self.menubar, tearoff=tk.FALSE) - self.file_menu.add_command(command=lambda: stats.StatsDialog(self.w, self.status)) - self.file_menu.add_command(command=self.save_raw) - self.file_menu.add_command(command=lambda: prefs.PreferencesDialog(self.w, self.postprefs)) - self.file_menu.add_separator() - self.file_menu.add_command(command=self.onexit) - self.menubar.add_cascade(menu=self.file_menu) - self.edit_menu = tk.Menu(self.menubar, tearoff=tk.FALSE) - self.edit_menu.add_command(accelerator='Ctrl+C', state=tk.DISABLED, command=self.copy) - self.menubar.add_cascade(menu=self.edit_menu) - self.help_menu = tk.Menu(self.menubar, tearoff=tk.FALSE) # type: ignore - self.help_menu.add_command(command=self.help_general) # Documentation - self.help_menu.add_command(command=self.help_troubleshooting) # Troubleshooting - self.help_menu.add_command(command=self.help_report_a_bug) # Report A Bug - self.help_menu.add_command(command=self.help_privacy) # Privacy Policy - self.help_menu.add_command(command=self.help_releases) # Release Notes - self.help_menu.add_command(command=lambda: self.updater.check_for_updates()) # Check for Updates... - # About E:D Market Connector - self.help_menu.add_command(command=lambda: not self.HelpAbout.showing and self.HelpAbout(self.w)) - self.help_menu.add_command(command=prefs.help_open_log_folder) # Open Log Folder + self.w.bind('<Control-c>', self.copy) - self.menubar.add_cascade(menu=self.help_menu) - if sys.platform == 'win32': - # Must be added after at least one "real" menu entry - self.always_ontop = tk.BooleanVar(value=bool(config.get_int('always_ontop'))) - self.system_menu = tk.Menu(self.menubar, name='system', tearoff=tk.FALSE) - self.system_menu.add_separator() - # LANG: Appearance - Label for checkbox to select if application always on top - self.system_menu.add_checkbutton(label=_('Always on top'), - variable=self.always_ontop, - command=self.ontop_changed) # Appearance setting - self.menubar.add_cascade(menu=self.system_menu) - self.w.bind('<Control-c>', self.copy) + # Bind to the Default theme minimise button + self.w.bind("<Unmap>", self.default_iconify) - # Bind to the Default theme minimise button - self.w.bind("<Unmap>", self.default_iconify) + self.w.protocol("WM_DELETE_WINDOW", self.onexit) + theme.register(self.menubar) # menus and children aren't automatically registered + theme.register(self.file_menu) + theme.register(self.edit_menu) + theme.register(self.help_menu) - self.w.protocol("WM_DELETE_WINDOW", self.onexit) - theme.register(self.menubar) # menus and children aren't automatically registered - theme.register(self.file_menu) - theme.register(self.edit_menu) - theme.register(self.help_menu) - - # Alternate title bar and menu for dark theme - self.theme_menubar = tk.Frame(frame, name="alternate_menubar") - self.theme_menubar.columnconfigure(2, weight=1) - theme_titlebar = tk.Label( - self.theme_menubar, - name="alternate_titlebar", - text=applongname, - image=self.theme_icon, cursor='fleur', - anchor=tk.W, compound=tk.LEFT - ) - theme_titlebar.grid(columnspan=3, padx=2, sticky=tk.NSEW) - self.drag_offset: tuple[int | None, int | None] = (None, None) - theme_titlebar.bind('<Button-1>', self.drag_start) - theme_titlebar.bind('<B1-Motion>', self.drag_continue) - theme_titlebar.bind('<ButtonRelease-1>', self.drag_end) - theme_minimize = tk.Label(self.theme_menubar, image=self.theme_minimize) - theme_minimize.grid(row=0, column=3, padx=2) - theme.button_bind(theme_minimize, self.oniconify, image=self.theme_minimize) - theme_close = tk.Label(self.theme_menubar, image=self.theme_close) - theme_close.grid(row=0, column=4, padx=2) - theme.button_bind(theme_close, self.onexit, image=self.theme_close) - self.theme_file_menu = tk.Label(self.theme_menubar, anchor=tk.W) - self.theme_file_menu.grid(row=1, column=0, padx=self.PADX, sticky=tk.W) - theme.button_bind(self.theme_file_menu, - lambda e: self.file_menu.tk_popup(e.widget.winfo_rootx(), - e.widget.winfo_rooty() - + e.widget.winfo_height())) - self.theme_edit_menu = tk.Label(self.theme_menubar, anchor=tk.W) - self.theme_edit_menu.grid(row=1, column=1, sticky=tk.W) - theme.button_bind(self.theme_edit_menu, - lambda e: self.edit_menu.tk_popup(e.widget.winfo_rootx(), - e.widget.winfo_rooty() - + e.widget.winfo_height())) - self.theme_help_menu = tk.Label(self.theme_menubar, anchor=tk.W) - self.theme_help_menu.grid(row=1, column=2, sticky=tk.W) - theme.button_bind(self.theme_help_menu, - lambda e: self.help_menu.tk_popup(e.widget.winfo_rootx(), - e.widget.winfo_rooty() - + e.widget.winfo_height())) - tk.Frame(self.theme_menubar, highlightthickness=1).grid(columnspan=5, padx=self.PADX, sticky=tk.EW) - theme.register(self.theme_minimize) # images aren't automatically registered - theme.register(self.theme_close) - self.blank_menubar = tk.Frame(frame, name="blank_menubar") - tk.Label(self.blank_menubar).grid() - tk.Label(self.blank_menubar).grid() - tk.Frame(self.blank_menubar, height=2).grid() - theme.register_alternate((self.menubar, self.theme_menubar, self.blank_menubar), - {'row': 0, 'columnspan': 2, 'sticky': tk.NSEW}) - self.w.resizable(tk.TRUE, tk.FALSE) + # Alternate title bar and menu for dark theme + self.theme_menubar = tk.Frame(frame, name="alternate_menubar") + self.theme_menubar.columnconfigure(2, weight=1) + theme_titlebar = tk.Label( + self.theme_menubar, + name="alternate_titlebar", + text=applongname, + image=self.theme_icon, cursor='fleur', + anchor=tk.W, compound=tk.LEFT + ) + theme_titlebar.grid(columnspan=3, padx=2, sticky=tk.NSEW) + self.drag_offset: tuple[int | None, int | None] = (None, None) + theme_titlebar.bind('<Button-1>', self.drag_start) + theme_titlebar.bind('<B1-Motion>', self.drag_continue) + theme_titlebar.bind('<ButtonRelease-1>', self.drag_end) + theme_minimize = tk.Label(self.theme_menubar, image=self.theme_minimize) + theme_minimize.grid(row=0, column=3, padx=2) + theme.button_bind(theme_minimize, self.oniconify, image=self.theme_minimize) + theme_close = tk.Label(self.theme_menubar, image=self.theme_close) + theme_close.grid(row=0, column=4, padx=2) + theme.button_bind(theme_close, self.onexit, image=self.theme_close) + self.theme_file_menu = tk.Label(self.theme_menubar, anchor=tk.W) + self.theme_file_menu.grid(row=1, column=0, padx=self.PADX, sticky=tk.W) + theme.button_bind(self.theme_file_menu, + lambda e: self.file_menu.tk_popup(e.widget.winfo_rootx(), + e.widget.winfo_rooty() + + e.widget.winfo_height())) + self.theme_edit_menu = tk.Label(self.theme_menubar, anchor=tk.W) + self.theme_edit_menu.grid(row=1, column=1, sticky=tk.W) + theme.button_bind(self.theme_edit_menu, + lambda e: self.edit_menu.tk_popup(e.widget.winfo_rootx(), + e.widget.winfo_rooty() + + e.widget.winfo_height())) + self.theme_help_menu = tk.Label(self.theme_menubar, anchor=tk.W) + self.theme_help_menu.grid(row=1, column=2, sticky=tk.W) + theme.button_bind(self.theme_help_menu, + lambda e: self.help_menu.tk_popup(e.widget.winfo_rootx(), + e.widget.winfo_rooty() + + e.widget.winfo_height())) + tk.Frame(self.theme_menubar, highlightthickness=1).grid(columnspan=5, padx=self.PADX, sticky=tk.EW) + theme.register(self.theme_minimize) # images aren't automatically registered + theme.register(self.theme_close) + self.blank_menubar = tk.Frame(frame, name="blank_menubar") + tk.Label(self.blank_menubar).grid() + tk.Label(self.blank_menubar).grid() + tk.Frame(self.blank_menubar, height=2).grid() + theme.register_alternate((self.menubar, self.theme_menubar, self.blank_menubar), + {'row': 0, 'columnspan': 2, 'sticky': tk.NSEW}) + self.w.resizable(tk.TRUE, tk.FALSE) # update geometry if config.get_str('geometry'): match = re.match(r'\+([\-\d]+)\+([\-\d]+)', config.get_str('geometry')) if match: - if sys.platform == 'darwin': - # http://core.tcl.tk/tk/tktview/c84f660833546b1b84e7 - if int(match.group(2)) >= 0: - self.w.geometry(config.get_str('geometry')) - elif sys.platform == 'win32': + if sys.platform == 'win32': # Check that the titlebar will be at least partly on screen import ctypes from ctypes.wintypes import POINT @@ -910,49 +865,28 @@ class AppWindow: self.system_label['text'] = _('System') + ':' # LANG: Label for 'System' line in main UI self.station_label['text'] = _('Station') + ':' # LANG: Label for 'Station' line in main UI self.button['text'] = self.theme_button['text'] = _('Update') # LANG: Update button in main window - if sys.platform == 'darwin': - self.menubar.entryconfigure(1, label=_('File')) # LANG: 'File' menu title on OSX - self.menubar.entryconfigure(2, label=_('Edit')) # LANG: 'Edit' menu title on OSX - self.menubar.entryconfigure(3, label=_('View')) # LANG: 'View' menu title on OSX - self.menubar.entryconfigure(4, label=_('Window')) # LANG: 'Window' menu title on OSX - self.menubar.entryconfigure(5, label=_('Help')) # LANG: Help' menu title on OSX - self.system_menu.entryconfigure( - 0, - label=_("About {APP}").format(APP=applongname) # LANG: App menu entry on OSX - ) - self.system_menu.entryconfigure(1, label=_("Check for Updates...")) # LANG: Help > Check for Updates... - self.file_menu.entryconfigure(0, label=_('Save Raw Data...')) # LANG: File > Save Raw Data... - self.view_menu.entryconfigure(0, label=_('Status')) # LANG: File > Status - self.help_menu.entryconfigure(1, label=_('Documentation')) # LANG: Help > Documentation - self.help_menu.entryconfigure(2, label=_('Troubleshooting')) # LANG: Help > Troubleshooting - self.help_menu.entryconfigure(3, label=_('Report A Bug')) # LANG: Help > Report A Bug - self.help_menu.entryconfigure(4, label=_('Privacy Policy')) # LANG: Help > Privacy Policy - self.help_menu.entryconfigure(5, label=_('Release Notes')) # LANG: Help > Release Notes - self.help_menu.entryconfigure(6, label=_('Open Log Folder')) # LANG: Help > Open Log Folder + self.menubar.entryconfigure(1, label=_('File')) # LANG: 'File' menu title + self.menubar.entryconfigure(2, label=_('Edit')) # LANG: 'Edit' menu title + self.menubar.entryconfigure(3, label=_('Help')) # LANG: 'Help' menu title + self.theme_file_menu['text'] = _('File') # LANG: 'File' menu title + self.theme_edit_menu['text'] = _('Edit') # LANG: 'Edit' menu title + self.theme_help_menu['text'] = _('Help') # LANG: 'Help' menu title - else: - self.menubar.entryconfigure(1, label=_('File')) # LANG: 'File' menu title - self.menubar.entryconfigure(2, label=_('Edit')) # LANG: 'Edit' menu title - self.menubar.entryconfigure(3, label=_('Help')) # LANG: 'Help' menu title - self.theme_file_menu['text'] = _('File') # LANG: 'File' menu title - self.theme_edit_menu['text'] = _('Edit') # LANG: 'Edit' menu title - self.theme_help_menu['text'] = _('Help') # LANG: 'Help' menu title + # File menu + self.file_menu.entryconfigure(0, label=_('Status')) # LANG: File > Status + self.file_menu.entryconfigure(1, label=_('Save Raw Data...')) # LANG: File > Save Raw Data... + self.file_menu.entryconfigure(2, label=_('Settings')) # LANG: File > Settings + self.file_menu.entryconfigure(4, label=_('Exit')) # LANG: File > Exit - # File menu - self.file_menu.entryconfigure(0, label=_('Status')) # LANG: File > Status - self.file_menu.entryconfigure(1, label=_('Save Raw Data...')) # LANG: File > Save Raw Data... - self.file_menu.entryconfigure(2, label=_('Settings')) # LANG: File > Settings - self.file_menu.entryconfigure(4, label=_('Exit')) # LANG: File > Exit - - # Help menu - self.help_menu.entryconfigure(0, label=_('Documentation')) # LANG: Help > Documentation - self.help_menu.entryconfigure(1, label=_('Troubleshooting')) # LANG: Help > Troubleshooting - self.help_menu.entryconfigure(2, label=_('Report A Bug')) # LANG: Help > Report A Bug - self.help_menu.entryconfigure(3, label=_('Privacy Policy')) # LANG: Help > Privacy Policy - self.help_menu.entryconfigure(4, label=_('Release Notes')) # LANG: Help > Release Notes - self.help_menu.entryconfigure(5, label=_('Check for Updates...')) # LANG: Help > Check for Updates... - self.help_menu.entryconfigure(6, label=_("About {APP}").format(APP=applongname)) # LANG: Help > About App - self.help_menu.entryconfigure(7, label=_('Open Log Folder')) # LANG: Help > Open Log Folder + # Help menu + self.help_menu.entryconfigure(0, label=_('Documentation')) # LANG: Help > Documentation + self.help_menu.entryconfigure(1, label=_('Troubleshooting')) # LANG: Help > Troubleshooting + self.help_menu.entryconfigure(2, label=_('Report A Bug')) # LANG: Help > Report A Bug + self.help_menu.entryconfigure(3, label=_('Privacy Policy')) # LANG: Help > Privacy Policy + self.help_menu.entryconfigure(4, label=_('Release Notes')) # LANG: Help > Release Notes + self.help_menu.entryconfigure(5, label=_('Check for Updates...')) # LANG: Help > Check for Updates... + self.help_menu.entryconfigure(6, label=_("About {APP}").format(APP=applongname)) # LANG: Help > About App + self.help_menu.entryconfigure(7, label=_('Open Log Folder')) # LANG: Help > Open Log Folder # Edit menu self.edit_menu.entryconfigure(0, label=_('Copy')) # LANG: Label for 'Copy' as in 'Copy and Paste' @@ -975,13 +909,8 @@ class AppWindow: self.button['state'] = self.theme_button['state'] = tk.DISABLED - if sys.platform == 'darwin': - self.view_menu.entryconfigure(0, state=tk.DISABLED) # Status - self.file_menu.entryconfigure(0, state=tk.DISABLED) # Save Raw Data - - else: - self.file_menu.entryconfigure(0, state=tk.DISABLED) # Status - self.file_menu.entryconfigure(1, state=tk.DISABLED) # Save Raw Data + self.file_menu.entryconfigure(0, state=tk.DISABLED) # Status + self.file_menu.entryconfigure(1, state=tk.DISABLED) # Save Raw Data self.w.update_idletasks() try: @@ -989,13 +918,8 @@ class AppWindow: # LANG: Successfully authenticated with the Frontier website self.status['text'] = _('Authentication successful') - if sys.platform == 'darwin': - self.view_menu.entryconfigure(0, state=tk.NORMAL) # Status - self.file_menu.entryconfigure(0, state=tk.NORMAL) # Save Raw Data - - else: - self.file_menu.entryconfigure(0, state=tk.NORMAL) # Status - self.file_menu.entryconfigure(1, state=tk.NORMAL) # Save Raw Data + self.file_menu.entryconfigure(0, state=tk.NORMAL) # Status + self.file_menu.entryconfigure(1, state=tk.NORMAL) # Save Raw Data except (companion.CredentialsError, companion.ServerError, companion.ServerLagging) as e: self.status['text'] = str(e) @@ -1666,13 +1590,8 @@ class AppWindow: companion.session.auth_callback() # LANG: Successfully authenticated with the Frontier website self.status['text'] = _('Authentication successful') - if sys.platform == 'darwin': - self.view_menu.entryconfigure(0, state=tk.NORMAL) # Status - self.file_menu.entryconfigure(0, state=tk.NORMAL) # Save Raw Data - - else: - self.file_menu.entryconfigure(0, state=tk.NORMAL) # Status - self.file_menu.entryconfigure(1, state=tk.NORMAL) # Save Raw Data + self.file_menu.entryconfigure(0, state=tk.NORMAL) # Status + self.file_menu.entryconfigure(1, state=tk.NORMAL) # Save Raw Data except companion.ServerError as e: self.status['text'] = str(e) @@ -1831,7 +1750,7 @@ class AppWindow: # position over parent # http://core.tcl.tk/tk/tktview/c84f660833546b1b84e7 - if sys.platform != 'darwin' or parent.winfo_rooty() > 0: + if parent.winfo_rooty() > 0: self.geometry(f'+{parent.winfo_rootx():d}+{parent.winfo_rooty():d}') # remove decoration @@ -1916,9 +1835,6 @@ class AppWindow: """ default_extension: str = '' - if sys.platform == 'darwin': - default_extension = '.json' - timestamp: str = strftime('%Y-%m-%dT%H.%M.%S', localtime()) f = tkinter.filedialog.asksaveasfilename( parent=self.w, @@ -1954,7 +1870,7 @@ class AppWindow: config.set_shutdown() # Signal we're in shutdown now. # http://core.tcl.tk/tk/tktview/c84f660833546b1b84e7 - if sys.platform != 'darwin' or self.w.winfo_rooty() > 0: + if self.w.winfo_rooty() > 0: x, y = self.w.geometry().split('+')[1:3] # e.g. '212x170+2881+1267' config.set('geometry', f'+{x}+{y}') diff --git a/dashboard.py b/dashboard.py index 1f95a943..3948c92b 100644 --- a/dashboard.py +++ b/dashboard.py @@ -20,7 +20,7 @@ from EDMCLogging import get_main_logger logger = get_main_logger() -if sys.platform in ('darwin', 'win32'): +if sys.platform == 'win32': from watchdog.events import FileSystemEventHandler from watchdog.observers import Observer else: diff --git a/myNotebook.py b/myNotebook.py index 6fa35774..dbfc9250 100644 --- a/myNotebook.py +++ b/myNotebook.py @@ -78,7 +78,7 @@ class Label(tk.Label): def __init__(self, master: ttk.Frame | None = None, **kw): # This format chosen over `sys.platform in (...)` as mypy and friends dont understand that - if sys.platform in ('darwin', 'win32'): + if sys.platform == 'win32': kw['foreground'] = kw.pop('foreground', PAGEFG) kw['background'] = kw.pop('background', PAGEBG) else: diff --git a/prefs.py b/prefs.py index 6c2722b6..862bda27 100644 --- a/prefs.py +++ b/prefs.py @@ -18,7 +18,7 @@ from typing import TYPE_CHECKING, Any, Callable, Optional, Type import myNotebook as nb # noqa: N813 import plug -from config import applongname, appversion_nobuild, config +from config import appversion_nobuild, config from EDMCLogging import edmclogger, get_main_logger from constants import appname from hotkey import hotkeymgr @@ -49,9 +49,6 @@ def help_open_log_folder() -> None: if sys.platform.startswith('win'): # On Windows, use the "start" command to open the folder system(f'start "" "{logfile_loc}"') - elif sys.platform.startswith('darwin'): - # On macOS, use the "open" command to open the folder - system(f'open "{logfile_loc}"') elif sys.platform.startswith('linux'): # On Linux, use the "xdg-open" command to open the folder system(f'xdg-open "{logfile_loc}"') @@ -172,32 +169,7 @@ class AutoInc(contextlib.AbstractContextManager): return None -if sys.platform == 'darwin': - import objc # type: ignore - from Foundation import NSFileManager # type: ignore - try: - from ApplicationServices import ( # type: ignore - AXIsProcessTrusted, AXIsProcessTrustedWithOptions, kAXTrustedCheckOptionPrompt - ) - - except ImportError: - HIServices = objc.loadBundle( - 'HIServices', - globals(), - '/System/Library/Frameworks/ApplicationServices.framework/Frameworks/HIServices.framework' - ) - - objc.loadBundleFunctions( - HIServices, - globals(), - [('AXIsProcessTrusted', 'B'), ('AXIsProcessTrustedWithOptions', 'B@')] - ) - - objc.loadBundleVariables(HIServices, globals(), [('kAXTrustedCheckOptionPrompt', '@^{__CFString=}')]) - - was_accessible_at_launch = AXIsProcessTrusted() # type: ignore - -elif sys.platform == 'win32': +if sys.platform == 'win32': import ctypes import winreg from ctypes.wintypes import HINSTANCE, HWND, LPCWSTR, LPWSTR, MAX_PATH, POINT, RECT, SIZE, UINT @@ -251,19 +223,14 @@ class PreferencesDialog(tk.Toplevel): self.parent = parent self.callback = callback - if sys.platform == 'darwin': - # LANG: File > Preferences menu entry for macOS - self.title(_('Preferences')) - - else: - # LANG: File > Settings (macOS) - self.title(_('Settings')) + # LANG: File > Settings (macOS) + self.title(_('Settings')) if parent.winfo_viewable(): self.transient(parent) # position over parent - if sys.platform != 'darwin' or parent.winfo_rooty() > 0: # http://core.tcl.tk/tk/tktview/c84f660833546b1b84e7 + if parent.winfo_rooty() > 0: # http://core.tcl.tk/tk/tktview/c84f660833546b1b84e7 # TODO this is fixed supposedly. self.geometry(f'+{parent.winfo_rootx()}+{parent.winfo_rooty()}') @@ -271,10 +238,6 @@ class PreferencesDialog(tk.Toplevel): if sys.platform == 'win32': self.attributes('-toolwindow', tk.TRUE) - elif sys.platform == 'darwin': - # http://wiki.tcl.tk/13428 - parent.call('tk::unsupported::MacWindowStyle', 'style', self, 'utility') - self.resizable(tk.FALSE, tk.FALSE) self.cmdr: str | bool | None = False # Note if Cmdr changes in the Journal @@ -302,19 +265,15 @@ class PreferencesDialog(tk.Toplevel): self.__setup_appearance_tab(notebook) self.__setup_plugin_tab(notebook) - if sys.platform == 'darwin': - self.protocol("WM_DELETE_WINDOW", self.apply) # close button applies changes - - else: - buttonframe = ttk.Frame(frame) - buttonframe.grid(padx=self.PADX, pady=self.PADX, sticky=tk.NSEW) - buttonframe.columnconfigure(0, weight=1) - ttk.Label(buttonframe).grid(row=0, column=0) # spacer - # LANG: 'OK' button on Settings/Preferences window - button = ttk.Button(buttonframe, text=_('OK'), command=self.apply) - button.grid(row=0, column=1, sticky=tk.E) - button.bind("<Return>", lambda event: self.apply()) - self.protocol("WM_DELETE_WINDOW", self._destroy) + buttonframe = ttk.Frame(frame) + buttonframe.grid(padx=self.PADX, pady=self.PADX, sticky=tk.NSEW) + buttonframe.columnconfigure(0, weight=1) + ttk.Label(buttonframe).grid(row=0, column=0) # spacer + # LANG: 'OK' button on Settings/Preferences window + button = ttk.Button(buttonframe, text=_('OK'), command=self.apply) + button.grid(row=0, column=1, sticky=tk.E) + button.bind("<Return>", lambda event: self.apply()) + self.protocol("WM_DELETE_WINDOW", self._destroy) # FIXME: Why are these being called when *creating* the Settings window? # Selectively disable buttons depending on output settings @@ -405,11 +364,7 @@ class PreferencesDialog(tk.Toplevel): self.outdir_entry = nb.Entry(output_frame, takefocus=False) self.outdir_entry.grid(columnspan=2, padx=self.PADX, pady=self.BOXY, sticky=tk.EW, row=row.get()) - if sys.platform == 'darwin': - text = (_('Change...')) # LANG: macOS Preferences - files location selection button - - else: - text = (_('Browse...')) # LANG: NOT-macOS Settings - files location selection button + text = (_('Browse...')) # LANG: NOT-macOS Settings - files location selection button self.outbutton = nb.Button( output_frame, @@ -455,11 +410,7 @@ class PreferencesDialog(tk.Toplevel): self.logdir_entry.grid(columnspan=4, padx=self.PADX, pady=self.BOXY, sticky=tk.EW, row=row.get()) - if sys.platform == 'darwin': - text = (_('Change...')) # LANG: macOS Preferences - files location selection button - - else: - text = (_('Browse...')) # LANG: NOT-macOS Setting - files location selection button + text = (_('Browse...')) # LANG: NOT-macOS Setting - files location selection button with row as cur_row: self.logbutton = nb.Button( @@ -499,7 +450,7 @@ class PreferencesDialog(tk.Toplevel): variable=self.capi_fleetcarrier ).grid(columnspan=4, padx=self.BUTTONX, pady=self.PADY, sticky=tk.W, row=row.get()) - if sys.platform in ('darwin', 'win32'): + if sys.platform == 'win32': ttk.Separator(config_frame, orient=tk.HORIZONTAL).grid( columnspan=4, padx=self.PADX, pady=self.SEPY, sticky=tk.EW, row=row.get() ) @@ -511,49 +462,21 @@ class PreferencesDialog(tk.Toplevel): with row as cur_row: nb.Label( config_frame, - text=_('Keyboard shortcut') if # LANG: Hotkey/Shortcut settings prompt on OSX - sys.platform == 'darwin' else - _('Hotkey') # LANG: Hotkey/Shortcut settings prompt on Windows + text=_('Hotkey') # LANG: Hotkey/Shortcut settings prompt on Windows ).grid(padx=self.PADX, pady=self.PADY, sticky=tk.W, row=cur_row) - if sys.platform == 'darwin' and not was_accessible_at_launch: - if AXIsProcessTrusted(): - # Shortcut settings prompt on OSX - nb.Label( - config_frame, - # LANG: macOS Preferences > Configuration - restart the app message - text=_('Re-start {APP} to use shortcuts').format(APP=applongname), - foreground='firebrick' - ).grid(padx=self.PADX, pady=self.PADY, sticky=tk.W, row=cur_row) + self.hotkey_text = nb.Entry(config_frame, width=30, justify=tk.CENTER) + self.hotkey_text.insert( + 0, + # No hotkey/shortcut currently defined + # TODO: display Only shows up on windows + # LANG: No hotkey/shortcut set + hotkeymgr.display(self.hotkey_code, self.hotkey_mods) if self.hotkey_code else _('None') + ) - else: - # Shortcut settings prompt on OSX - nb.Label( - config_frame, - # LANG: macOS - Configuration - need to grant the app permission for keyboard shortcuts - text=_('{APP} needs permission to use shortcuts').format(APP=applongname), - foreground='firebrick' - ).grid(columnspan=4, padx=self.PADX, pady=self.PADY, sticky=tk.W, row=cur_row) - - # LANG: Shortcut settings button on OSX - nb.Button(config_frame, text=_('Open System Preferences'), command=self.enableshortcuts).grid( - padx=self.PADX, pady=self.BOXY, sticky=tk.E, row=cur_row - ) - - else: - self.hotkey_text = nb.Entry(config_frame, width=( - 20 if sys.platform == 'darwin' else 30), justify=tk.CENTER) - self.hotkey_text.insert( - 0, - # No hotkey/shortcut currently defined - # TODO: display Only shows up on darwin or windows - # LANG: No hotkey/shortcut set - hotkeymgr.display(self.hotkey_code, self.hotkey_mods) if self.hotkey_code else _('None') - ) - - self.hotkey_text.bind('<FocusIn>', self.hotkeystart) - self.hotkey_text.bind('<FocusOut>', self.hotkeyend) - self.hotkey_text.grid(column=1, columnspan=2, pady=self.BOXY, sticky=tk.W, row=cur_row) + self.hotkey_text.bind('<FocusIn>', self.hotkeystart) + self.hotkey_text.bind('<FocusOut>', self.hotkeyend) + self.hotkey_text.grid(column=1, columnspan=2, pady=self.BOXY, sticky=tk.W, row=cur_row) # Hotkey/Shortcut setting self.hotkey_only_btn = nb.Checkbutton( @@ -1070,14 +993,6 @@ class PreferencesDialog(tk.Toplevel): def tabchanged(self, event: tk.Event) -> None: """Handle preferences active tab changing.""" self.outvarchanged() - if sys.platform == 'darwin': - # Hack to recompute size so that buttons show up under Mojave - notebook = event.widget - frame = self.nametowidget(notebook.winfo_parent()) - temp = nb.Label(frame) - temp.grid() - temp.update_idletasks() - temp.destroy() def outvarchanged(self, event: Optional[tk.Event] = None) -> None: """Handle Output tab variable changes.""" @@ -1139,16 +1054,6 @@ class PreferencesDialog(tk.Toplevel): entryfield.insert(0, '\\'.join(display)) # None if path doesn't exist - elif sys.platform == 'darwin' and NSFileManager.defaultManager().componentsToDisplayForPath_(pathvar.get()): - if pathvar.get().startswith(config.home): - display = ['~'] + NSFileManager.defaultManager().componentsToDisplayForPath_(pathvar.get())[ - len(NSFileManager.defaultManager().componentsToDisplayForPath_(config.home)): - ] - - else: - display = NSFileManager.defaultManager().componentsToDisplayForPath_(pathvar.get()) - - entryfield.insert(0, '/'.join(display)) else: if pathvar.get().startswith(config.home): entryfield.insert(0, '~' + pathvar.get()[len(config.home):]) @@ -1288,7 +1193,7 @@ class PreferencesDialog(tk.Toplevel): config.set('capi_fleetcarrier', self.capi_fleetcarrier.get()) - if sys.platform in ('darwin', 'win32'): + if sys.platform == 'win32': config.set('hotkey_code', self.hotkey_code) config.set('hotkey_mods', self.hotkey_mods) config.set('hotkey_always', int(not self.hotkey_only.get())) @@ -1333,25 +1238,3 @@ class PreferencesDialog(tk.Toplevel): self.parent.wm_attributes('-topmost', 1 if config.get_int('always_ontop') else 0) self.destroy() - - if sys.platform == 'darwin': - def enableshortcuts(self) -> None: - """Set up macOS preferences shortcut.""" - self.apply() - # popup System Preferences dialog - try: - # http://stackoverflow.com/questions/6652598/cocoa-button-opens-a-system-preference-page/6658201 - from ScriptingBridge import SBApplication # type: ignore - sysprefs = 'com.apple.systempreferences' - prefs = SBApplication.applicationWithBundleIdentifier_(sysprefs) - pane = [x for x in prefs.panes() if x.id() == 'com.apple.preference.security'][0] - prefs.setCurrentPane_(pane) - anchor = [x for x in pane.anchors() if x.name() == 'Privacy_Accessibility'][0] - anchor.reveal() - prefs.activate() - - except Exception: - AXIsProcessTrustedWithOptions({kAXTrustedCheckOptionPrompt: True}) - - if not config.shutting_down: - self.parent.event_generate('<<Quit>>', when="tail") From 57cd75e75ebb1a61eebac1f4b3e17b0356d6b57e Mon Sep 17 00:00:00 2001 From: David Sangrey <rixxan@hullseals.space> Date: Wed, 27 Mar 2024 19:39:51 -0400 Subject: [PATCH 05/22] [2186] Additional Files --- journal_lock.py | 4 -- plugins/eddn.py | 4 +- protocol.py | 63 +------------------------ stats.py | 13 +---- td.py | 2 +- tests/config/_old_config.py | 94 +------------------------------------ theme.py | 21 +-------- update.py | 24 ---------- 8 files changed, 9 insertions(+), 216 deletions(-) diff --git a/journal_lock.py b/journal_lock.py index 4d04992f..3a4dad52 100644 --- a/journal_lock.py +++ b/journal_lock.py @@ -218,10 +218,6 @@ class JournalLock: if sys.platform == 'win32': self.attributes('-toolwindow', tk.TRUE) - elif sys.platform == 'darwin': - # http://wiki.tcl.tk/13428 - parent.call('tk::unsupported::MacWindowStyle', 'style', self, 'utility') - self.resizable(tk.FALSE, tk.FALSE) frame = ttk.Frame(self) diff --git a/plugins/eddn.py b/plugins/eddn.py index 522ce6cb..b7133725 100644 --- a/plugins/eddn.py +++ b/plugins/eddn.py @@ -282,7 +282,7 @@ class EDDNSender: msg['header'] = { # We have to lie and say it's *this* version, but denote that # it might not actually be this version. - 'softwareName': f'{applongname} [{system() if sys.platform != "darwin" else "Mac OS"}]' + 'softwareName': f'{applongname} [{system()}]' ' (legacy replay)', 'softwareVersion': str(appversion_nobuild()), 'uploaderID': cmdr, @@ -1074,7 +1074,7 @@ class EDDN: gb = this.game_build return { - 'softwareName': f'{applongname} [{system() if sys.platform != "darwin" else "Mac OS"}]', + 'softwareName': f'{applongname} [{system()}]', 'softwareVersion': str(appversion_nobuild()), 'uploaderID': this.cmdr_name, 'gameversion': gv, diff --git a/protocol.py b/protocol.py index f3b956d2..0f1c157f 100644 --- a/protocol.py +++ b/protocol.py @@ -58,66 +58,7 @@ class GenericProtocolHandler: self.master.event_generate('<<CompanionAuthEvent>>', when="tail") -if sys.platform == 'darwin' and getattr(sys, 'frozen', False): # noqa: C901 # its guarding ALL macos stuff. - import struct - - import objc # type: ignore - from AppKit import NSAppleEventManager, NSObject # type: ignore - - kInternetEventClass = kAEGetURL = struct.unpack('>l', b'GURL')[0] # noqa: N816 # API names - keyDirectObject = struct.unpack('>l', b'----')[0] # noqa: N816 # API names - - class DarwinProtocolHandler(GenericProtocolHandler): - """ - MacOS protocol handler implementation. - - Uses macOS event stuff. - """ - - POLL = 100 # ms - - def start(self, master: 'tkinter.Tk') -> None: - """Start Protocol Handler.""" - GenericProtocolHandler.start(self, master) - self.lasturl: str | None = None - self.eventhandler = EventHandler.alloc().init() - - def poll(self) -> None: - """Poll event until URL is updated.""" - # No way of signalling to Tkinter from within the callback handler block that doesn't cause Python to crash, - # so poll. TODO: Resolved? - if self.lasturl and self.lasturl.startswith(self.redirect): - self.event(self.lasturl) - self.lasturl = None - - class EventHandler(NSObject): - """Handle NSAppleEventManager IPC stuff.""" - - def init(self) -> None: - """ - Init method for handler. - - (I'd assume this is related to the subclassing of NSObject for why its not __init__) - """ - self = objc.super(EventHandler, self).init() - NSAppleEventManager.sharedAppleEventManager().setEventHandler_andSelector_forEventClass_andEventID_( - self, - 'handleEvent:withReplyEvent:', - kInternetEventClass, - kAEGetURL - ) - return self - - def handleEvent_withReplyEvent_(self, event, replyEvent) -> None: # noqa: N802 N803 # Required to override - """Actual event handling from NSAppleEventManager.""" - protocolhandler.lasturl = parse.unquote( - event.paramDescriptorForKeyword_(keyDirectObject).stringValue() - ).strip() - - protocolhandler.master.after(DarwinProtocolHandler.POLL, protocolhandler.poll) - - -elif (config.auth_force_edmc_protocol +if (config.auth_force_edmc_protocol or ( sys.platform == 'win32' and getattr(sys, 'frozen', False) @@ -480,8 +421,6 @@ def get_handler_impl() -> Type[GenericProtocolHandler]: :return: An instantiatable GenericProtocolHandler """ - if sys.platform == 'darwin' and getattr(sys, 'frozen', False): - return DarwinProtocolHandler # pyright: reportUnboundVariable=false if ( (sys.platform == 'win32' and config.auth_force_edmc_protocol) diff --git a/stats.py b/stats.py index 4351e297..c377e5d3 100644 --- a/stats.py +++ b/stats.py @@ -374,7 +374,7 @@ class StatsResults(tk.Toplevel): self.transient(parent) # position over parent - if sys.platform != 'darwin' or parent.winfo_rooty() > 0: # http://core.tcl.tk/tk/tktview/c84f660833546b1b84e7 + if parent.winfo_rooty() > 0: # http://core.tcl.tk/tk/tktview/c84f660833546b1b84e7 self.geometry(f"+{parent.winfo_rootx()}+{parent.winfo_rooty()}") # remove decoration @@ -382,10 +382,6 @@ class StatsResults(tk.Toplevel): if sys.platform == 'win32': self.attributes('-toolwindow', tk.TRUE) - elif sys.platform == 'darwin': - # http://wiki.tcl.tk/13428 - parent.call('tk::unsupported::MacWindowStyle', 'style', self, 'utility') - frame = ttk.Frame(self) frame.grid(sticky=tk.NSEW) @@ -423,13 +419,6 @@ class StatsResults(tk.Toplevel): ttk.Frame(page).grid(pady=5) # bottom spacer notebook.add(page, text=_('Ships')) # LANG: Status dialog title - if sys.platform != 'darwin': - buttonframe = ttk.Frame(frame) - buttonframe.grid(padx=10, pady=(0, 10), sticky=tk.NSEW) # type: ignore # the tuple is supported - buttonframe.columnconfigure(0, weight=1) - ttk.Label(buttonframe).grid(row=0, column=0) # spacer - ttk.Button(buttonframe, text='OK', command=self.destroy).grid(row=0, column=1, sticky=tk.E) - # wait for window to appear on screen before calling grab_set self.wait_visibility() self.grab_set() diff --git a/td.py b/td.py index 6a588f57..d2b5dbdd 100644 --- a/td.py +++ b/td.py @@ -32,7 +32,7 @@ def export(data: CAPIData) -> None: with open(data_path / data_filename, 'wb') as h: # Format described here: https://github.com/eyeonus/Trade-Dangerous/wiki/Price-Data h.write('#! trade.py import -\n'.encode('utf-8')) - this_platform = "Mac OS" if sys.platform == 'darwin' else system() + this_platform = system() cmdr_name = data['commander']['name'].strip() h.write( f'# Created by {applongname} {appversion()} on {this_platform} for Cmdr {cmdr_name}.\n'.encode('utf-8') diff --git a/tests/config/_old_config.py b/tests/config/_old_config.py index 690c75eb..35ae19e3 100644 --- a/tests/config/_old_config.py +++ b/tests/config/_old_config.py @@ -13,13 +13,7 @@ from EDMCLogging import get_main_logger logger = get_main_logger() -if sys.platform == 'darwin': - from Foundation import ( # type: ignore - NSApplicationSupportDirectory, NSBundle, NSDocumentDirectory, NSSearchPathForDirectoriesInDomains, - NSUserDefaults, NSUserDomainMask - ) - -elif sys.platform == 'win32': +if sys.platform == 'win32': import ctypes import uuid from ctypes.wintypes import DWORD, HANDLE, HKEY, LONG, LPCVOID, LPCWSTR @@ -115,91 +109,7 @@ class OldConfig: OUT_EDDN_DELAY = 4096 OUT_STATION_ANY = OUT_EDDN_SEND_STATION_DATA | OUT_MKT_TD | OUT_MKT_CSV - if sys.platform == 'darwin': # noqa: C901 # It's gating *all* the functions - - def __init__(self): - self.app_dir = join( - NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, True)[0], appname - ) - if not isdir(self.app_dir): - mkdir(self.app_dir) - - self.plugin_dir = join(self.app_dir, 'plugins') - if not isdir(self.plugin_dir): - mkdir(self.plugin_dir) - - if getattr(sys, 'frozen', False): - self.internal_plugin_dir = normpath(join(dirname(sys.executable), pardir, 'Library', 'plugins')) - self.respath = normpath(join(dirname(sys.executable), pardir, 'Resources')) - self.identifier = NSBundle.mainBundle().bundleIdentifier() - - else: - self.internal_plugin_dir = join(dirname(__file__), 'plugins') - self.respath = dirname(__file__) - # Don't use Python's settings if interactive - self.identifier = f'uk.org.marginal.{appname.lower()}' - NSBundle.mainBundle().infoDictionary()['CFBundleIdentifier'] = self.identifier - - self.default_journal_dir: str | None = join( - NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, True)[0], - 'Frontier Developments', - 'Elite Dangerous' - ) - self.home = expanduser('~') - - self.defaults = NSUserDefaults.standardUserDefaults() - self.settings = dict(self.defaults.persistentDomainForName_(self.identifier) or {}) # make writeable - - # Check out_dir exists - if not self.get('outdir') or not isdir(str(self.get('outdir'))): - self.set('outdir', NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, True)[0]) - - def get(self, key: str, default: None | list | str = None) -> None | list | str: - """Look up a string configuration value.""" - val = self.settings.get(key) - if val is None: - return default - - if isinstance(val, str): - return str(val) - - if isinstance(val, list): - return list(val) # make writeable - - return default - - def getint(self, key: str, default: int = 0) -> int: - """Look up an integer configuration value.""" - try: - return int(self.settings.get(key, default)) # should already be int, but check by casting - - except ValueError as e: - logger.error(f"Failed to int({key=})", exc_info=e) - return default - - except Exception as e: - logger.debug('The exception type is ...', exc_info=e) - return default - - def set(self, key: str, val: int | str | list) -> None: - """Set value on the specified configuration key.""" - self.settings[key] = val - - def delete(self, key: str) -> None: - """Delete the specified configuration key.""" - self.settings.pop(key, None) - - def save(self) -> None: - """Save current configuration to disk.""" - self.defaults.setPersistentDomain_forName_(self.settings, self.identifier) - self.defaults.synchronize() - - def close(self) -> None: - """Close the configuration.""" - self.save() - self.defaults = None - - elif sys.platform == 'win32': + if sys.platform == 'win32': def __init__(self): self.app_dir = join(known_folder_path(FOLDERID_LocalAppData), appname) # type: ignore diff --git a/theme.py b/theme.py index bfb76c55..b128bc91 100644 --- a/theme.py +++ b/theme.py @@ -268,8 +268,7 @@ class _Theme: # (Mostly) system colors style = ttk.Style() self.current = { - 'background': (sys.platform == 'darwin' and 'systemMovableModalBackground' or - style.lookup('TLabel', 'background')), + 'background': (style.lookup('TLabel', 'background')), 'foreground': style.lookup('TLabel', 'foreground'), 'activebackground': (sys.platform == 'win32' and 'SystemHighlight' or style.lookup('TLabel', 'background', ['active'])), @@ -366,8 +365,6 @@ class _Theme: if 'bg' not in attribs: widget['background'] = self.current['background'] widget['activebackground'] = self.current['activebackground'] - if sys.platform == 'darwin' and isinstance(widget, tk.Button): - widget['highlightbackground'] = self.current['background'] if 'font' not in attribs: widget['font'] = self.current['font'] @@ -426,21 +423,7 @@ class _Theme: return # Don't need to mess with the window manager self.active = theme - if sys.platform == 'darwin': - from AppKit import NSAppearance, NSApplication, NSMiniaturizableWindowMask, NSResizableWindowMask - root.update_idletasks() # need main window to be created - if theme == self.THEME_DEFAULT: - appearance = NSAppearance.appearanceNamed_('NSAppearanceNameAqua') - - else: # Dark (Transparent only on win32) - appearance = NSAppearance.appearanceNamed_('NSAppearanceNameDarkAqua') - - for window in NSApplication.sharedApplication().windows(): - window.setStyleMask_(window.styleMask() & ~( - NSMiniaturizableWindowMask | NSResizableWindowMask)) # disable zoom - window.setAppearance_(appearance) - - elif sys.platform == 'win32': + if sys.platform == 'win32': GWL_STYLE = -16 # noqa: N806 # ctypes WS_MAXIMIZEBOX = 0x00010000 # noqa: N806 # ctypes # tk8.5.9/win/tkWinWm.c:342 diff --git a/update.py b/update.py index 024aeb09..e0b8f97b 100644 --- a/update.py +++ b/update.py @@ -115,21 +115,6 @@ class Updater: return - if sys.platform == 'darwin': - import objc - - try: - objc.loadBundle( - 'Sparkle', globals(), join(dirname(sys.executable), os.pardir, 'Frameworks', 'Sparkle.framework') - ) - # loadBundle presumably supplies `SUUpdater` - self.updater = SUUpdater.sharedUpdater() # noqa: F821 - - except Exception: - # can't load framework - not frozen or not included in app bundle? - print_exc() - self.updater = None - def set_automatic_updates_check(self, onoroff: bool) -> None: """ Set (Win)Sparkle to perform automatic update checks, or not. @@ -142,9 +127,6 @@ class Updater: if sys.platform == 'win32' and self.updater: self.updater.win_sparkle_set_automatic_check_for_updates(onoroff) - if sys.platform == 'darwin' and self.updater: - self.updater.SUEnableAutomaticChecks(onoroff) - def check_for_updates(self) -> None: """Trigger the requisite method to check for an update.""" if self.use_internal(): @@ -155,9 +137,6 @@ class Updater: elif sys.platform == 'win32' and self.updater: self.updater.win_sparkle_check_update_with_ui() - elif sys.platform == 'darwin' and self.updater: - self.updater.checkForUpdates_(None) - def check_appcast(self) -> EDMCVersion | None: """ Manually (no Sparkle or WinSparkle) check the update_feed appcast file. @@ -184,9 +163,6 @@ class Updater: return None - if sys.platform == 'darwin': - sparkle_platform = 'macos' - else: # For *these* purposes anything else is the same as 'windows', as # non-win32 would be running from source. From 05eaf059389a25d16c778ea6861e0836a71184f3 Mon Sep 17 00:00:00 2001 From: David Sangrey <rixxan@hullseals.space> Date: Wed, 27 Mar 2024 19:41:01 -0400 Subject: [PATCH 06/22] [Minor] Remove Unused Import For Now --- ttkHyperlinkLabel.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ttkHyperlinkLabel.py b/ttkHyperlinkLabel.py index 0ca87fc7..3b65d6b9 100644 --- a/ttkHyperlinkLabel.py +++ b/ttkHyperlinkLabel.py @@ -22,7 +22,6 @@ from __future__ import annotations import sys import tkinter as tk -import warnings import webbrowser from tkinter import font as tk_font from tkinter import ttk From 08818785d051b81a7ad88c6975b0be0df88a07c7 Mon Sep 17 00:00:00 2001 From: David Sangrey <rixxan@hullseals.space> Date: Wed, 27 Mar 2024 20:03:23 -0400 Subject: [PATCH 07/22] [2186] HyperLinkLabel first pass --- ttkHyperlinkLabel.py | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/ttkHyperlinkLabel.py b/ttkHyperlinkLabel.py index 3b65d6b9..de906510 100644 --- a/ttkHyperlinkLabel.py +++ b/ttkHyperlinkLabel.py @@ -32,7 +32,7 @@ if TYPE_CHECKING: # FIXME: Split this into multi-file module to separate the platforms -class HyperlinkLabel(sys.platform == 'darwin' and tk.Label or ttk.Label): # type: ignore +class HyperlinkLabel(tk.Label or ttk.Label): # type: ignore """Clickable label for HTTP links.""" def __init__(self, master: ttk.Frame | tk.Frame | None = None, **kw: Any) -> None: @@ -50,22 +50,14 @@ class HyperlinkLabel(sys.platform == 'darwin' and tk.Label or ttk.Label): # typ self.foreground = kw.get('foreground', 'blue') self.disabledforeground = kw.pop('disabledforeground', ttk.Style().lookup( 'TLabel', 'foreground', ('disabled',))) # ttk.Label doesn't support disabledforeground option - - if sys.platform == 'darwin': - # Use tk.Label 'cos can't set ttk.Label background - http://www.tkdocs.com/tutorial/styles.html#whydifficult - kw['background'] = kw.pop('background', 'systemDialogBackgroundActive') - kw['anchor'] = kw.pop('anchor', tk.W) # like ttk.Label - tk.Label.__init__(self, master, **kw) - - else: - ttk.Label.__init__(self, master, **kw) + ttk.Label.__init__(self, master, **kw) self.bind('<Button-1>', self._click) self.menu = tk.Menu(tearoff=tk.FALSE) # LANG: Label for 'Copy' as in 'Copy and Paste' self.menu.add_command(label=_('Copy'), command=self.copy) # As in Copy and Paste - self.bind(sys.platform == 'darwin' and '<Button-2>' or '<Button-3>', self._contextmenu) + self.bind('<Button-3>', self._contextmenu) self.bind('<Enter>', self._enter) self.bind('<Leave>', self._leave) @@ -106,10 +98,9 @@ class HyperlinkLabel(sys.platform == 'darwin' and tk.Label or ttk.Label): # typ if state == tk.DISABLED: kw['cursor'] = 'arrow' # System default elif self.url and (kw['text'] if 'text' in kw else self['text']): - kw['cursor'] = 'pointinghand' if sys.platform == 'darwin' else 'hand2' + kw['cursor'] = 'hand2' else: - kw['cursor'] = 'notallowed' if sys.platform == 'darwin' else ( - 'no' if sys.platform == 'win32' else 'circle') + kw['cursor'] = ('no' if sys.platform == 'win32' else 'circle') return super().configure(cnf, **kw) @@ -139,7 +130,7 @@ class HyperlinkLabel(sys.platform == 'darwin' and tk.Label or ttk.Label): # typ def _contextmenu(self, event: tk.Event) -> None: if self['text'] and (self.popup_copy(self['text']) if callable(self.popup_copy) else self.popup_copy): - self.menu.post(sys.platform == 'darwin' and event.x_root + 1 or event.x_root, event.y_root) + self.menu.post(event.x_root, event.y_root) def copy(self) -> None: """Copy the current text to the clipboard.""" From f7b39f8dafedf453f6f80e278f88d8e12ccced60 Mon Sep 17 00:00:00 2001 From: David Sangrey <rixxan@hullseals.space> Date: Wed, 27 Mar 2024 20:10:55 -0400 Subject: [PATCH 08/22] [2186] MyNB First Pass --- myNotebook.py | 66 +++++++-------------------------------------------- 1 file changed, 9 insertions(+), 57 deletions(-) diff --git a/myNotebook.py b/myNotebook.py index dbfc9250..efae591c 100644 --- a/myNotebook.py +++ b/myNotebook.py @@ -16,13 +16,7 @@ import sys import tkinter as tk from tkinter import ttk -# Can't do this with styles on OSX - http://www.tkdocs.com/tutorial/styles.html#whydifficult -if sys.platform == 'darwin': - from platform import mac_ver - PAGEFG = 'systemButtonText' - PAGEBG = 'systemButtonActiveDarkShadow' - -elif sys.platform == 'win32': +if sys.platform == 'win32': PAGEFG = 'SystemWindowText' PAGEBG = 'SystemWindow' # typically white @@ -35,14 +29,7 @@ class Notebook(ttk.Notebook): ttk.Notebook.__init__(self, master, **kw) style = ttk.Style() - if sys.platform == 'darwin': - if list(map(int, mac_ver()[0].split('.'))) >= [10, 10]: - # Hack for tab appearance with 8.5 on Yosemite & El Capitan. For proper fix see - # https://github.com/tcltk/tk/commit/55c4dfca9353bbd69bbcec5d63bf1c8dfb461e25 - style.configure('TNotebook.Tab', padding=(12, 10, 12, 2)) - style.map('TNotebook.Tab', foreground=[('selected', '!background', 'systemWhite')]) - self.grid(sticky=tk.NSEW) # Already padded apropriately - elif sys.platform == 'win32': + if sys.platform == 'win32': style.configure('nb.TFrame', background=PAGEBG) style.configure('nb.TButton', background=PAGEBG) style.configure('nb.TCheckbutton', foreground=PAGEFG, background=PAGEBG) @@ -60,11 +47,7 @@ class Frame(sys.platform == 'darwin' and tk.Frame or ttk.Frame): # type: ignore """Custom t(t)k.Frame class to fix some display issues.""" def __init__(self, master: ttk.Notebook | None = None, **kw): - if sys.platform == 'darwin': - kw['background'] = kw.pop('background', PAGEBG) - tk.Frame.__init__(self, master, **kw) - tk.Frame(self).grid(pady=5) - elif sys.platform == 'win32': + if sys.platform == 'win32': ttk.Frame.__init__(self, master, style='nb.TFrame', **kw) ttk.Frame(self).grid(pady=5) # top spacer else: @@ -91,21 +74,14 @@ class Entry(sys.platform == 'darwin' and tk.Entry or ttk.Entry): # type: ignore """Custom t(t)k.Entry class to fix some display issues.""" def __init__(self, master: ttk.Frame | None = None, **kw): - if sys.platform == 'darwin': - kw['highlightbackground'] = kw.pop('highlightbackground', PAGEBG) - tk.Entry.__init__(self, master, **kw) - else: - ttk.Entry.__init__(self, master, **kw) + ttk.Entry.__init__(self, master, **kw) class Button(sys.platform == 'darwin' and tk.Button or ttk.Button): # type: ignore """Custom t(t)k.Button class to fix some display issues.""" def __init__(self, master: ttk.Frame | None = None, **kw): - if sys.platform == 'darwin': - kw['highlightbackground'] = kw.pop('highlightbackground', PAGEBG) - tk.Button.__init__(self, master, **kw) - elif sys.platform == 'win32': + if sys.platform == 'win32': ttk.Button.__init__(self, master, style='nb.TButton', **kw) else: ttk.Button.__init__(self, master, **kw) @@ -115,29 +91,14 @@ class ColoredButton(sys.platform == 'darwin' and tk.Label or tk.Button): # type """Custom t(t)k.ColoredButton class to fix some display issues.""" def __init__(self, master: ttk.Frame | None = None, **kw): - if sys.platform == 'darwin': - # Can't set Button background on OSX, so use a Label instead - kw['relief'] = kw.pop('relief', tk.RAISED) - self._command = kw.pop('command', None) - tk.Label.__init__(self, master, **kw) - self.bind('<Button-1>', self._press) - else: - tk.Button.__init__(self, master, **kw) - - if sys.platform == 'darwin': - def _press(self, event): - self._command() + tk.Button.__init__(self, master, **kw) class Checkbutton(sys.platform == 'darwin' and tk.Checkbutton or ttk.Checkbutton): # type: ignore """Custom t(t)k.Checkbutton class to fix some display issues.""" def __init__(self, master: ttk.Frame | None = None, **kw): - if sys.platform == 'darwin': - kw['foreground'] = kw.pop('foreground', PAGEFG) - kw['background'] = kw.pop('background', PAGEBG) - tk.Checkbutton.__init__(self, master, **kw) - elif sys.platform == 'win32': + if sys.platform == 'win32': ttk.Checkbutton.__init__(self, master, style='nb.TCheckbutton', **kw) else: ttk.Checkbutton.__init__(self, master, **kw) @@ -147,11 +108,7 @@ class Radiobutton(sys.platform == 'darwin' and tk.Radiobutton or ttk.Radiobutton """Custom t(t)k.Radiobutton class to fix some display issues.""" def __init__(self, master: ttk.Frame | None = None, **kw): - if sys.platform == 'darwin': - kw['foreground'] = kw.pop('foreground', PAGEFG) - kw['background'] = kw.pop('background', PAGEBG) - tk.Radiobutton.__init__(self, master, **kw) - elif sys.platform == 'win32': + if sys.platform == 'win32': ttk.Radiobutton.__init__(self, master, style='nb.TRadiobutton', **kw) else: ttk.Radiobutton.__init__(self, master, **kw) @@ -161,12 +118,7 @@ class OptionMenu(sys.platform == 'darwin' and tk.OptionMenu or ttk.OptionMenu): """Custom t(t)k.OptionMenu class to fix some display issues.""" def __init__(self, master, variable, default=None, *values, **kw): - if sys.platform == 'darwin': - variable.set(default) - bg = kw.pop('background', PAGEBG) - tk.OptionMenu.__init__(self, master, variable, *values, **kw) - self['background'] = bg - elif sys.platform == 'win32': + if sys.platform == 'win32': # OptionMenu derives from Menubutton at the Python level, so uses Menubutton's style ttk.OptionMenu.__init__(self, master, variable, default, *values, style='nb.TMenubutton', **kw) self['menu'].configure(background=PAGEBG) From c1b8533cb41c2e33d63311dac120ff7b0ddd4c29 Mon Sep 17 00:00:00 2001 From: David Sangrey <rixxan@hullseals.space> Date: Wed, 27 Mar 2024 21:22:35 -0400 Subject: [PATCH 09/22] [2186] Simplify myNB Files --- docs/examples/click_counter/load.py | 4 +- myNotebook.py | 95 ++++++++++++----------------- plugins/coriolis.py | 8 +-- plugins/edsm.py | 8 +-- plugins/inara.py | 4 +- prefs.py | 22 +++---- 6 files changed, 62 insertions(+), 79 deletions(-) diff --git a/docs/examples/click_counter/load.py b/docs/examples/click_counter/load.py index ad90a084..f7f23837 100644 --- a/docs/examples/click_counter/load.py +++ b/docs/examples/click_counter/load.py @@ -7,6 +7,8 @@ from __future__ import annotations import logging import tkinter as tk +from tkinter import ttk + import myNotebook as nb # noqa: N813 from config import appname, config @@ -63,7 +65,7 @@ class ClickCounter: # setup our config in a "Click Count: number" nb.Label(frame, text='Click Count').grid(row=current_row) - nb.Entry(frame, textvariable=self.click_count).grid(row=current_row, column=1) + ttk.Entry(frame, textvariable=self.click_count).grid(row=current_row, column=1) current_row += 1 # Always increment our row counter, makes for far easier tkinter design. return frame diff --git a/myNotebook.py b/myNotebook.py index efae591c..528664ba 100644 --- a/myNotebook.py +++ b/myNotebook.py @@ -1,12 +1,9 @@ """ Custom `ttk.Notebook` to fix various display issues. -Hacks to fix various display issues with notebooks and their child widgets on -OSX and Windows. +Hacks to fix various display issues with notebooks and their child widgets on Windows. - Windows: page background should be White, not SystemButtonFace -- OSX: page background should be a darker gray than systemWindowBody - selected tab foreground should be White when the window is active Entire file may be imported by plugins. """ @@ -26,24 +23,17 @@ class Notebook(ttk.Notebook): def __init__(self, master: ttk.Frame | None = None, **kw): - ttk.Notebook.__init__(self, master, **kw) + super().__init__(master, **kw) style = ttk.Style() - - if sys.platform == 'win32': - style.configure('nb.TFrame', background=PAGEBG) - style.configure('nb.TButton', background=PAGEBG) - style.configure('nb.TCheckbutton', foreground=PAGEFG, background=PAGEBG) - style.configure('nb.TMenubutton', foreground=PAGEFG, background=PAGEBG) - style.configure('nb.TRadiobutton', foreground=PAGEFG, background=PAGEBG) - self.grid(padx=10, pady=10, sticky=tk.NSEW) - else: - self.grid(padx=10, pady=10, sticky=tk.NSEW) + style.configure('nb.TFrame', background=PAGEBG) + style.configure('nb.TButton', background=PAGEBG) + style.configure('nb.TCheckbutton', foreground=PAGEFG, background=PAGEBG) + style.configure('nb.TMenubutton', foreground=PAGEFG, background=PAGEBG) + style.configure('nb.TRadiobutton', foreground=PAGEFG, background=PAGEBG) + self.grid(padx=10, pady=10, sticky=tk.NSEW) -# FIXME: The real fix for this 'dynamic type' would be to split this whole -# thing into being a module with per-platform files, as we've done with config -# That would also make the code cleaner. -class Frame(sys.platform == 'darwin' and tk.Frame or ttk.Frame): # type: ignore +class Frame(tk.Frame or ttk.Frame): # type: ignore """Custom t(t)k.Frame class to fix some display issues.""" def __init__(self, master: ttk.Notebook | None = None, **kw): @@ -60,26 +50,25 @@ class Label(tk.Label): """Custom tk.Label class to fix some display issues.""" def __init__(self, master: ttk.Frame | None = None, **kw): - # This format chosen over `sys.platform in (...)` as mypy and friends dont understand that - if sys.platform == 'win32': - kw['foreground'] = kw.pop('foreground', PAGEFG) - kw['background'] = kw.pop('background', PAGEBG) - else: - kw['foreground'] = kw.pop('foreground', ttk.Style().lookup('TLabel', 'foreground')) - kw['background'] = kw.pop('background', ttk.Style().lookup('TLabel', 'background')) - tk.Label.__init__(self, master, **kw) # Just use tk.Label on all platforms + kw['foreground'] = kw.pop('foreground', PAGEFG if sys.platform == 'win32' + else ttk.Style().lookup('TLabel', 'foreground')) + kw['background'] = kw.pop('background', PAGEBG if sys.platform == 'win32' + else ttk.Style().lookup('TLabel', 'background')) + super().__init__(master, **kw) -class Entry(sys.platform == 'darwin' and tk.Entry or ttk.Entry): # type: ignore +class Entry(ttk.Entry): # type: ignore """Custom t(t)k.Entry class to fix some display issues.""" + # DEPRECATED: Migrate to ttk.Entry. Will remove in 5.12 or later. def __init__(self, master: ttk.Frame | None = None, **kw): - ttk.Entry.__init__(self, master, **kw) + super().__init__(master, **kw) -class Button(sys.platform == 'darwin' and tk.Button or ttk.Button): # type: ignore +class Button(tk.Button or ttk.Button): # type: ignore """Custom t(t)k.Button class to fix some display issues.""" + # DEPRECATED: Migrate to ttk.Button. Will remove in 5.12 or later. def __init__(self, master: ttk.Frame | None = None, **kw): if sys.platform == 'win32': ttk.Button.__init__(self, master, style='nb.TButton', **kw) @@ -87,47 +76,39 @@ class Button(sys.platform == 'darwin' and tk.Button or ttk.Button): # type: ign ttk.Button.__init__(self, master, **kw) -class ColoredButton(sys.platform == 'darwin' and tk.Label or tk.Button): # type: ignore +class ColoredButton(tk.Label or tk.Button): # type: ignore """Custom t(t)k.ColoredButton class to fix some display issues.""" + # DEPRECATED: Migrate to tk.Button. Will remove in 5.12 or later. def __init__(self, master: ttk.Frame | None = None, **kw): tk.Button.__init__(self, master, **kw) -class Checkbutton(sys.platform == 'darwin' and tk.Checkbutton or ttk.Checkbutton): # type: ignore +class Checkbutton(ttk.Checkbutton): """Custom t(t)k.Checkbutton class to fix some display issues.""" - def __init__(self, master: ttk.Frame | None = None, **kw): - if sys.platform == 'win32': - ttk.Checkbutton.__init__(self, master, style='nb.TCheckbutton', **kw) - else: - ttk.Checkbutton.__init__(self, master, **kw) + def __init__(self, master=None, **kw): + style = 'nb.TCheckbutton' if sys.platform == 'win32' else None + super().__init__(master, style=style, **kw) # type: ignore -class Radiobutton(sys.platform == 'darwin' and tk.Radiobutton or ttk.Radiobutton): # type: ignore +class Radiobutton(ttk.Radiobutton): """Custom t(t)k.Radiobutton class to fix some display issues.""" def __init__(self, master: ttk.Frame | None = None, **kw): - if sys.platform == 'win32': - ttk.Radiobutton.__init__(self, master, style='nb.TRadiobutton', **kw) - else: - ttk.Radiobutton.__init__(self, master, **kw) + style = 'nb.TRadiobutton' if sys.platform == 'win32' else None + super().__init__(master, style=style, **kw) # type: ignore -class OptionMenu(sys.platform == 'darwin' and tk.OptionMenu or ttk.OptionMenu): # type: ignore - """Custom t(t)k.OptionMenu class to fix some display issues.""" +class OptionMenu(ttk.OptionMenu): + """Custom ttk.OptionMenu class to fix some display issues.""" def __init__(self, master, variable, default=None, *values, **kw): - if sys.platform == 'win32': - # OptionMenu derives from Menubutton at the Python level, so uses Menubutton's style - ttk.OptionMenu.__init__(self, master, variable, default, *values, style='nb.TMenubutton', **kw) - self['menu'].configure(background=PAGEBG) - # Workaround for https://bugs.python.org/issue25684 - for i in range(0, self['menu'].index('end')+1): - self['menu'].entryconfig(i, variable=variable) - else: - ttk.OptionMenu.__init__(self, master, variable, default, *values, **kw) - self['menu'].configure(background=ttk.Style().lookup('TMenu', 'background')) - # Workaround for https://bugs.python.org/issue25684 - for i in range(0, self['menu'].index('end')+1): - self['menu'].entryconfig(i, variable=variable) + style = 'nb.TMenubutton' if sys.platform == 'win32' else ttk.Style().lookup('TMenu', 'background') + menu_background = PAGEBG if sys.platform == 'win32' else ttk.Style().lookup('TMenu', 'background') + + super().__init__(master, variable, default, *values, style=style, **kw) + self['menu'].configure(background=menu_background) + + for i in range(self['menu'].index('end') + 1): + self['menu'].entryconfig(i, variable=variable) diff --git a/plugins/coriolis.py b/plugins/coriolis.py index c142c686..20da16b1 100644 --- a/plugins/coriolis.py +++ b/plugins/coriolis.py @@ -106,12 +106,12 @@ def plugin_prefs(parent: ttk.Notebook, cmdr: str | None, is_beta: bool) -> tk.Fr # LANG: Settings>Coriolis: Label for 'NOT alpha/beta game version' URL nb.Label(conf_frame, text=_('Normal URL')).grid(sticky=tk.W, row=cur_row, column=0, padx=PADX, pady=PADY) - nb.Entry(conf_frame, + ttk.Entry(conf_frame, textvariable=coriolis_config.normal_textvar).grid( sticky=tk.EW, row=cur_row, column=1, padx=PADX, pady=BOXY ) # LANG: Generic 'Reset' button label - nb.Button(conf_frame, text=_("Reset"), + ttk.Button(conf_frame, text=_("Reset"), command=lambda: coriolis_config.normal_textvar.set(value=DEFAULT_NORMAL_URL)).grid( sticky=tk.W, row=cur_row, column=2, padx=PADX, pady=0 ) @@ -119,11 +119,11 @@ def plugin_prefs(parent: ttk.Notebook, cmdr: str | None, is_beta: bool) -> tk.Fr # LANG: Settings>Coriolis: Label for 'alpha/beta game version' URL nb.Label(conf_frame, text=_('Beta URL')).grid(sticky=tk.W, row=cur_row, column=0, padx=PADX, pady=PADY) - nb.Entry(conf_frame, textvariable=coriolis_config.beta_textvar).grid( + ttk.Entry(conf_frame, textvariable=coriolis_config.beta_textvar).grid( sticky=tk.EW, row=cur_row, column=1, padx=PADX, pady=BOXY ) # LANG: Generic 'Reset' button label - nb.Button(conf_frame, text=_('Reset'), + ttk.Button(conf_frame, text=_('Reset'), command=lambda: coriolis_config.beta_textvar.set(value=DEFAULT_BETA_URL)).grid( sticky=tk.W, row=cur_row, column=2, padx=PADX, pady=0 ) diff --git a/plugins/edsm.py b/plugins/edsm.py index 46d05f4c..146dd5ab 100644 --- a/plugins/edsm.py +++ b/plugins/edsm.py @@ -113,10 +113,10 @@ class This: self.cmdr_text: nb.Label | None = None self.user_label: nb.Label | None = None - self.user: nb.Entry | None = None + self.user: ttk.Entry | None = None self.apikey_label: nb.Label | None = None - self.apikey: nb.Entry | None = None + self.apikey: ttk.Entry | None = None this = This() @@ -345,14 +345,14 @@ def plugin_prefs(parent: ttk.Notebook, cmdr: str | None, is_beta: bool) -> tk.Fr # LANG: EDSM Commander name label in EDSM settings this.user_label = nb.Label(frame, text=_('Commander Name')) this.user_label.grid(row=cur_row, padx=PADX, pady=PADY, sticky=tk.W) - this.user = nb.Entry(frame) + this.user = ttk.Entry(frame) this.user.grid(row=cur_row, column=1, padx=PADX, pady=BOXY, sticky=tk.EW) cur_row += 1 # LANG: EDSM API key label this.apikey_label = nb.Label(frame, text=_('API Key')) this.apikey_label.grid(row=cur_row, padx=PADX, pady=PADY, sticky=tk.W) - this.apikey = nb.Entry(frame, show="*", width=50) + this.apikey = ttk.Entry(frame, show="*", width=50) this.apikey.grid(row=cur_row, column=1, padx=PADX, pady=BOXY, sticky=tk.EW) cur_row += 1 diff --git a/plugins/inara.py b/plugins/inara.py index 0e0eb7bf..d96e05ef 100644 --- a/plugins/inara.py +++ b/plugins/inara.py @@ -125,7 +125,7 @@ class This: self.log: 'tk.IntVar' self.log_button: nb.Checkbutton self.label: HyperlinkLabel - self.apikey: nb.Entry + self.apikey: ttk.Entry self.apikey_label: tk.Label self.events: dict[Credentials, Deque[Event]] = defaultdict(deque) @@ -292,7 +292,7 @@ def plugin_prefs(parent: ttk.Notebook, cmdr: str, is_beta: bool) -> tk.Frame: # LANG: Inara API key label this.apikey_label = nb.Label(frame, text=_('API Key')) # Inara setting this.apikey_label.grid(row=cur_row, padx=PADX, pady=PADY, sticky=tk.W) - this.apikey = nb.Entry(frame, show="*", width=50) + this.apikey = ttk.Entry(frame, show="*", width=50) this.apikey.grid(row=cur_row, column=1, padx=PADX, pady=BOXY, sticky=tk.EW) cur_row += 1 diff --git a/prefs.py b/prefs.py index 862bda27..ef43c5de 100644 --- a/prefs.py +++ b/prefs.py @@ -361,12 +361,12 @@ class PreferencesDialog(tk.Toplevel): # Type ignored due to incorrect type annotation. a 2 tuple does padding for each side self.outdir_label.grid(padx=self.PADX, pady=self.PADY, sticky=tk.W, row=row.get()) # type: ignore - self.outdir_entry = nb.Entry(output_frame, takefocus=False) + self.outdir_entry = ttk.Entry(output_frame, takefocus=False) self.outdir_entry.grid(columnspan=2, padx=self.PADX, pady=self.BOXY, sticky=tk.EW, row=row.get()) text = (_('Browse...')) # LANG: NOT-macOS Settings - files location selection button - self.outbutton = nb.Button( + self.outbutton = ttk.Button( output_frame, text=text, # Technically this is different from the label in Settings > Output, as *this* is used @@ -399,7 +399,7 @@ class PreferencesDialog(tk.Toplevel): logdir = default self.logdir.set(logdir) - self.logdir_entry = nb.Entry(config_frame, takefocus=False) + self.logdir_entry = ttk.Entry(config_frame, takefocus=False) # Location of the Journal files nb.Label( @@ -413,7 +413,7 @@ class PreferencesDialog(tk.Toplevel): text = (_('Browse...')) # LANG: NOT-macOS Setting - files location selection button with row as cur_row: - self.logbutton = nb.Button( + self.logbutton = ttk.Button( config_frame, text=text, # LANG: Settings > Configuration - Label for Journal files location @@ -423,7 +423,7 @@ class PreferencesDialog(tk.Toplevel): if config.default_journal_dir_path: # Appearance theme and language setting - nb.Button( + ttk.Button( config_frame, # LANG: Settings > Configuration - Label on 'reset journal files location to default' button text=_('Default'), @@ -465,7 +465,7 @@ class PreferencesDialog(tk.Toplevel): text=_('Hotkey') # LANG: Hotkey/Shortcut settings prompt on Windows ).grid(padx=self.PADX, pady=self.PADY, sticky=tk.W, row=cur_row) - self.hotkey_text = nb.Entry(config_frame, width=30, justify=tk.CENTER) + self.hotkey_text = ttk.Entry(config_frame, width=30, justify=tk.CENTER) self.hotkey_text.insert( 0, # No hotkey/shortcut currently defined @@ -623,7 +623,7 @@ class PreferencesDialog(tk.Toplevel): self.loglevel_dropdown.configure(width=15) self.loglevel_dropdown.grid(column=1, pady=self.BOXY, sticky=tk.W, row=cur_row) - nb.Button( + ttk.Button( config_frame, # LANG: Label on button used to open a filesystem folder text=_('Open Log Folder'), # Button that opens a folder in Explorer/Finder @@ -726,7 +726,7 @@ class PreferencesDialog(tk.Toplevel): self.theme_label_0.grid(padx=self.PADX, pady=self.PADY, sticky=tk.W, row=cur_row) # Main window - self.theme_button_0 = nb.ColoredButton( + self.theme_button_0 = tk.Button( appearance_frame, # LANG: Appearance - Example 'Normal' text text=_('Station'), @@ -739,7 +739,7 @@ class PreferencesDialog(tk.Toplevel): with row as cur_row: self.theme_label_1 = nb.Label(appearance_frame, text=self.theme_prompts[1]) self.theme_label_1.grid(padx=self.PADX, pady=self.PADY, sticky=tk.W, row=cur_row) - self.theme_button_1 = nb.ColoredButton( + self.theme_button_1 = tk.Button( appearance_frame, text=' Hutton Orbital ', # Do not translate background='grey4', @@ -870,7 +870,7 @@ class PreferencesDialog(tk.Toplevel): padx=self.PADX, pady=self.PADY, sticky=tk.W, row=row.get() ) - plugdirentry = nb.Entry(plugins_frame, justify=tk.LEFT) + plugdirentry = ttk.Entry(plugins_frame, justify=tk.LEFT) self.displaypath(plugdir, plugdirentry) plugdirentry.grid(columnspan=2, padx=self.PADX, pady=self.BOXY, sticky=tk.EW, row=row.get()) @@ -882,7 +882,7 @@ class PreferencesDialog(tk.Toplevel): text=_("Tip: You can disable a plugin by{CR}adding '{EXT}' to its folder name").format(EXT='.disabled') ).grid(columnspan=2, padx=self.PADX, pady=self.PADY, sticky=tk.EW, row=cur_row) - nb.Button( + ttk.Button( plugins_frame, # LANG: Label on button used to open a filesystem folder text=_('Open'), # Button that opens a folder in Explorer/Finder From ae74d949a892b919a275c2cda45e951b97c3082c Mon Sep 17 00:00:00 2001 From: David Sangrey <rixxan@hullseals.space> Date: Wed, 27 Mar 2024 21:24:11 -0400 Subject: [PATCH 10/22] [Nit] Indentation, Yay! --- plugins/coriolis.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/coriolis.py b/plugins/coriolis.py index 20da16b1..a97eb6c6 100644 --- a/plugins/coriolis.py +++ b/plugins/coriolis.py @@ -107,12 +107,12 @@ def plugin_prefs(parent: ttk.Notebook, cmdr: str | None, is_beta: bool) -> tk.Fr # LANG: Settings>Coriolis: Label for 'NOT alpha/beta game version' URL nb.Label(conf_frame, text=_('Normal URL')).grid(sticky=tk.W, row=cur_row, column=0, padx=PADX, pady=PADY) ttk.Entry(conf_frame, - textvariable=coriolis_config.normal_textvar).grid( + textvariable=coriolis_config.normal_textvar).grid( sticky=tk.EW, row=cur_row, column=1, padx=PADX, pady=BOXY ) # LANG: Generic 'Reset' button label ttk.Button(conf_frame, text=_("Reset"), - command=lambda: coriolis_config.normal_textvar.set(value=DEFAULT_NORMAL_URL)).grid( + command=lambda: coriolis_config.normal_textvar.set(value=DEFAULT_NORMAL_URL)).grid( sticky=tk.W, row=cur_row, column=2, padx=PADX, pady=0 ) cur_row += 1 @@ -124,7 +124,7 @@ def plugin_prefs(parent: ttk.Notebook, cmdr: str | None, is_beta: bool) -> tk.Fr ) # LANG: Generic 'Reset' button label ttk.Button(conf_frame, text=_('Reset'), - command=lambda: coriolis_config.beta_textvar.set(value=DEFAULT_BETA_URL)).grid( + command=lambda: coriolis_config.beta_textvar.set(value=DEFAULT_BETA_URL)).grid( sticky=tk.W, row=cur_row, column=2, padx=PADX, pady=0 ) cur_row += 1 From cfb6f729ab5888238c552aa0bb0d568b69356fbb Mon Sep 17 00:00:00 2001 From: David Sangrey <rixxan@hullseals.space> Date: Wed, 27 Mar 2024 21:27:22 -0400 Subject: [PATCH 11/22] [2186] Remove Unused Translations --- L10n/en.template | 24 ------------------------ config/__init__.py | 2 +- 2 files changed, 1 insertion(+), 25 deletions(-) diff --git a/L10n/en.template b/L10n/en.template index f5acb377..7ba67a61 100644 --- a/L10n/en.template +++ b/L10n/en.template @@ -78,12 +78,6 @@ /* EDMarketConnector.py: 'Edit' menu title on OSX; EDMarketConnector.py: 'Edit' menu title; In files: EDMarketConnector.py:922; EDMarketConnector.py:940; EDMarketConnector.py:943; */ "Edit" = "Edit"; -/* EDMarketConnector.py: 'View' menu title on OSX; In files: EDMarketConnector.py:923; */ -"View" = "View"; - -/* EDMarketConnector.py: 'Window' menu title on OSX; In files: EDMarketConnector.py:924; */ -"Window" = "Window"; - /* EDMarketConnector.py: Help' menu title on OSX; EDMarketConnector.py: 'Help' menu title; In files: EDMarketConnector.py:925; EDMarketConnector.py:941; EDMarketConnector.py:944; */ "Help" = "Help"; @@ -351,9 +345,6 @@ /* inara.py: INARA API returned some kind of error (error message will be contained in {MSG}); In files: inara.py:1650; inara.py:1663; */ "Error: Inara {MSG}" = "Error: Inara {MSG}"; -/* prefs.py: File > Preferences menu entry for macOS; In files: prefs.py:237; */ -"Preferences" = "Preferences"; - /* prefs.py: Settings > Output - choosing what data to save to files; In files: prefs.py:335; */ "Please choose what data to save" = "Please choose what data to save"; @@ -372,9 +363,6 @@ /* prefs.py: Settings > Output - Label for "where files are located"; In files: prefs.py:379; prefs.py:398; */ "File location" = "File location"; -/* prefs.py: macOS Preferences - files location selection button; In files: prefs.py:387; prefs.py:437; */ -"Change..." = "Change..."; - /* prefs.py: NOT-macOS Settings - files location selection button; prefs.py: NOT-macOS Setting - files location selection button; In files: prefs.py:390; prefs.py:440; */ "Browse..." = "Browse..."; @@ -390,21 +378,9 @@ /* prefs.py: Configuration - Enable or disable the Fleet Carrier CAPI calls; In files: prefs.py:475; */ "Enable Fleetcarrier CAPI Queries" = "Enable Fleetcarrier CAPI Queries"; -/* prefs.py: Hotkey/Shortcut settings prompt on OSX; In files: prefs.py:490; */ -"Keyboard shortcut" = "Keyboard shortcut"; - /* prefs.py: Hotkey/Shortcut settings prompt on Windows; In files: prefs.py:492; */ "Hotkey" = "Hotkey"; -/* prefs.py: macOS Preferences > Configuration - restart the app message; In files: prefs.py:501; */ -"Re-start {APP} to use shortcuts" = "Re-start {APP} to use shortcuts"; - -/* prefs.py: macOS - Configuration - need to grant the app permission for keyboard shortcuts; In files: prefs.py:510; */ -"{APP} needs permission to use shortcuts" = "{APP} needs permission to use shortcuts"; - -/* prefs.py: Shortcut settings button on OSX; In files: prefs.py:515; */ -"Open System Preferences" = "Open System Preferences"; - /* prefs.py: Configuration - Act on hotkey only when ED is in foreground; In files: prefs.py:538; */ "Only when Elite: Dangerous is the active app" = "Only when Elite: Dangerous is the active app"; diff --git a/config/__init__.py b/config/__init__.py index 992710a0..c6c404b9 100644 --- a/config/__init__.py +++ b/config/__init__.py @@ -54,7 +54,7 @@ appcmdname = 'EDMC' # <https://semver.org/#semantic-versioning-specification-semver> # Major.Minor.Patch(-prerelease)(+buildmetadata) # NB: Do *not* import this, use the functions appversion() and appversion_nobuild() -_static_appversion = '5.10.3' +_static_appversion = '5.11.0-alpha0' _cached_version: semantic_version.Version | None = None copyright = '© 2015-2019 Jonathan Harris, 2020-2024 EDCD' From 016fb96e065b45bd803587b31edb9619809f7198 Mon Sep 17 00:00:00 2001 From: David Sangrey <rixxan@hullseals.space> Date: Wed, 27 Mar 2024 22:01:49 -0400 Subject: [PATCH 12/22] [2186] General Cleanup --- prefs.py | 4 ++-- td.py | 1 - ttkHyperlinkLabel.py | 1 - update.py | 9 +++------ 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/prefs.py b/prefs.py index ef43c5de..307e3c7a 100644 --- a/prefs.py +++ b/prefs.py @@ -364,7 +364,7 @@ class PreferencesDialog(tk.Toplevel): self.outdir_entry = ttk.Entry(output_frame, takefocus=False) self.outdir_entry.grid(columnspan=2, padx=self.PADX, pady=self.BOXY, sticky=tk.EW, row=row.get()) - text = (_('Browse...')) # LANG: NOT-macOS Settings - files location selection button + text = _('Browse...') # LANG: NOT-macOS Settings - files location selection button self.outbutton = ttk.Button( output_frame, @@ -410,7 +410,7 @@ class PreferencesDialog(tk.Toplevel): self.logdir_entry.grid(columnspan=4, padx=self.PADX, pady=self.BOXY, sticky=tk.EW, row=row.get()) - text = (_('Browse...')) # LANG: NOT-macOS Setting - files location selection button + text = _('Browse...') # LANG: NOT-macOS Setting - files location selection button with row as cur_row: self.logbutton = ttk.Button( diff --git a/td.py b/td.py index d2b5dbdd..484e8d29 100644 --- a/td.py +++ b/td.py @@ -1,7 +1,6 @@ """Export data for Trade Dangerous.""" import pathlib -import sys import time from collections import defaultdict from operator import itemgetter diff --git a/ttkHyperlinkLabel.py b/ttkHyperlinkLabel.py index de906510..9bb3b9bf 100644 --- a/ttkHyperlinkLabel.py +++ b/ttkHyperlinkLabel.py @@ -31,7 +31,6 @@ if TYPE_CHECKING: def _(x: str) -> str: return x -# FIXME: Split this into multi-file module to separate the platforms class HyperlinkLabel(tk.Label or ttk.Label): # type: ignore """Clickable label for HTTP links.""" diff --git a/update.py b/update.py index e0b8f97b..346aff63 100644 --- a/update.py +++ b/update.py @@ -7,10 +7,8 @@ See LICENSE file. """ from __future__ import annotations -import os import sys import threading -from os.path import dirname, join from traceback import print_exc from typing import TYPE_CHECKING from xml.etree import ElementTree @@ -163,10 +161,9 @@ class Updater: return None - else: - # For *these* purposes anything else is the same as 'windows', as - # non-win32 would be running from source. - sparkle_platform = 'windows' + # For *these* purposes anything else is the same as 'windows', as + # non-win32 would be running from source. + sparkle_platform = 'windows' for item in feed.findall('channel/item'): # xml is a pain with types, hence these ignores From 1800f8f0b18cb8e13a7cc032d4eac61f7f67cbb1 Mon Sep 17 00:00:00 2001 From: David Sangrey <rixxan@hullseals.space> Date: Wed, 27 Mar 2024 22:17:21 -0400 Subject: [PATCH 13/22] [2186] Remove Some Comments --- config/__init__.py | 1 - dashboard.py | 4 ++-- monitor.py | 2 +- prefs.py | 2 +- theme.py | 2 +- 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/config/__init__.py b/config/__init__.py index c6c404b9..99e8fdc6 100644 --- a/config/__init__.py +++ b/config/__init__.py @@ -7,7 +7,6 @@ See LICENSE file. Windows uses the Registry to store values in a flat manner. Linux uses a file, but for commonality it's still a flat data structure. -macOS uses a 'defaults' object. """ from __future__ import annotations diff --git a/dashboard.py b/dashboard.py index 3948c92b..4776319c 100644 --- a/dashboard.py +++ b/dashboard.py @@ -26,7 +26,7 @@ if sys.platform == 'win32': else: # Linux's inotify doesn't work over CIFS or NFS, so poll class FileSystemEventHandler: # type: ignore - """Dummy class to represent a file system event handler on platforms other than macOS and Windows.""" + """Dummy class to represent a file system event handler on platforms other than Windows.""" class Dashboard(FileSystemEventHandler): @@ -160,7 +160,7 @@ class Dashboard(FileSystemEventHandler): def on_modified(self, event) -> None: """ - Watchdog callback - DirModifiedEvent on macOS, FileModifiedEvent on Windows. + Watchdog callback - FileModifiedEvent on Windows. :param event: Watchdog event. """ diff --git a/monitor.py b/monitor.py index d549e533..5acbcbc4 100644 --- a/monitor.py +++ b/monitor.py @@ -439,7 +439,7 @@ class EDLogs(FileSystemEventHandler): new_journal_file = None if logfile: - loghandle.seek(0, SEEK_END) # required to make macOS notice log change over SMB + loghandle.seek(0, SEEK_END) # required to make macOS notice log change over SMB. # TODO: Do we need this? loghandle.seek(log_pos, SEEK_SET) # reset EOF flag # TODO: log_pos reported as possibly unbound for line in loghandle: # Paranoia check to see if we're shutting down diff --git a/prefs.py b/prefs.py index 307e3c7a..5b045b1a 100644 --- a/prefs.py +++ b/prefs.py @@ -285,7 +285,7 @@ class PreferencesDialog(tk.Toplevel): # wait for window to appear on screen before calling grab_set self.parent.update_idletasks() - self.parent.wm_attributes('-topmost', 0) # needed for dialog to appear ontop of parent on OSX & Linux + self.parent.wm_attributes('-topmost', 0) # needed for dialog to appear ontop of parent on Linux self.wait_visibility() self.grab_set() diff --git a/theme.py b/theme.py index b128bc91..bbe62ef5 100644 --- a/theme.py +++ b/theme.py @@ -150,7 +150,7 @@ class _Theme: # the widget has explicit fg or bg attributes. assert isinstance(widget, (tk.BitmapImage, tk.Widget)), widget if not self.defaults: - # Can't initialise this til window is created # Windows, MacOS + # Can't initialise this til window is created # Windows self.defaults = { 'fg': tk.Label()['foreground'], # SystemButtonText, systemButtonText 'bg': tk.Label()['background'], # SystemButtonFace, White From 56a4ae25e0e598666bd0c8c6f8fa72daacdd90a2 Mon Sep 17 00:00:00 2001 From: David Sangrey <rixxan@hullseals.space> Date: Wed, 27 Mar 2024 22:21:26 -0400 Subject: [PATCH 14/22] [Nit] Clarify Comments --- monitor.py | 2 +- myNotebook.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/monitor.py b/monitor.py index 5acbcbc4..ba634c51 100644 --- a/monitor.py +++ b/monitor.py @@ -439,7 +439,7 @@ class EDLogs(FileSystemEventHandler): new_journal_file = None if logfile: - loghandle.seek(0, SEEK_END) # required to make macOS notice log change over SMB. # TODO: Do we need this? + loghandle.seek(0, SEEK_END) # required for macOS to notice log change over SMB. TODO: Do we need this? loghandle.seek(log_pos, SEEK_SET) # reset EOF flag # TODO: log_pos reported as possibly unbound for line in loghandle: # Paranoia check to see if we're shutting down diff --git a/myNotebook.py b/myNotebook.py index 528664ba..64624550 100644 --- a/myNotebook.py +++ b/myNotebook.py @@ -57,7 +57,7 @@ class Label(tk.Label): super().__init__(master, **kw) -class Entry(ttk.Entry): # type: ignore +class Entry(ttk.Entry): """Custom t(t)k.Entry class to fix some display issues.""" # DEPRECATED: Migrate to ttk.Entry. Will remove in 5.12 or later. From b5a4ee6ed2f60c68cec307e9bcd946d6440fc0b7 Mon Sep 17 00:00:00 2001 From: David Sangrey <rixxan@hullseals.space> Date: Thu, 28 Mar 2024 10:49:01 -0400 Subject: [PATCH 15/22] [Nit] Cleanup some Flake8 --- config/__init__.py | 1 - plugins/eddn.py | 1 - protocol.py | 36 ++++++++++++++++++------------------ tests/config/_old_config.py | 6 +++--- 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/config/__init__.py b/config/__init__.py index 99e8fdc6..d85956a7 100644 --- a/config/__init__.py +++ b/config/__init__.py @@ -467,7 +467,6 @@ def get_config(*args, **kwargs) -> AbstractConfig: :param kwargs: Args to be passed through to implementation. :return: Instance of the implementation. """ - if sys.platform == "win32": # pragma: sys-platform-win32 from .windows import WinConfig return WinConfig(*args, **kwargs) diff --git a/plugins/eddn.py b/plugins/eddn.py index b7133725..6345f579 100644 --- a/plugins/eddn.py +++ b/plugins/eddn.py @@ -27,7 +27,6 @@ import os import pathlib import re import sqlite3 -import sys import tkinter as tk from platform import system from textwrap import dedent diff --git a/protocol.py b/protocol.py index 0f1c157f..4c9e41ca 100644 --- a/protocol.py +++ b/protocol.py @@ -26,6 +26,7 @@ is_wine = False if sys.platform == 'win32': from ctypes import windll # type: ignore + try: if windll.ntdll.wine_get_version: is_wine = True @@ -58,13 +59,13 @@ class GenericProtocolHandler: self.master.event_generate('<<CompanionAuthEvent>>', when="tail") -if (config.auth_force_edmc_protocol - or ( - sys.platform == 'win32' - and getattr(sys, 'frozen', False) - and not is_wine - and not config.auth_force_localserver - )): +if (config.auth_force_edmc_protocol # noqa: C901 + or ( + sys.platform == 'win32' + and getattr(sys, 'frozen', False) + and not is_wine + and not config.auth_force_localserver + )): # This could be false if you use auth_force_edmc_protocol, but then you get to keep the pieces assert sys.platform == 'win32' # spell-checker: words HBRUSH HICON WPARAM wstring WNDCLASS HMENU HGLOBAL @@ -186,11 +187,11 @@ if (config.auth_force_edmc_protocol # which we can read out as shown below, and then compare. target_is_valid = lparam_low == 0 or ( - GlobalGetAtomNameW(lparam_low, service, 256) and service.value == appname + GlobalGetAtomNameW(lparam_low, service, 256) and service.value == appname ) topic_is_valid = lparam_high == 0 or ( - GlobalGetAtomNameW(lparam_high, topic, 256) and topic.value.lower() == 'system' + GlobalGetAtomNameW(lparam_high, topic, 256) and topic.value.lower() == 'system' ) if target_is_valid and topic_is_valid: @@ -251,15 +252,15 @@ if (config.auth_force_edmc_protocol # https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw hwnd = CreateWindowExW( - 0, # dwExStyle + 0, # dwExStyle wndclass.lpszClassName, # lpClassName - "DDE Server", # lpWindowName - 0, # dwStyle + "DDE Server", # lpWindowName + 0, # dwStyle CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, # X, Y, nWidth, nHeight self.master.winfo_id(), # hWndParent # Don't use HWND_MESSAGE since the window won't get DDE broadcasts - None, # hMenu - wndclass.hInstance, # hInstance - None # lpParam + None, # hMenu + wndclass.hInstance, # hInstance + None # lpParam ) msg = MSG() @@ -421,10 +422,9 @@ def get_handler_impl() -> Type[GenericProtocolHandler]: :return: An instantiatable GenericProtocolHandler """ - if ( - (sys.platform == 'win32' and config.auth_force_edmc_protocol) - or (getattr(sys, 'frozen', False) and not is_wine and not config.auth_force_localserver) + (sys.platform == 'win32' and config.auth_force_edmc_protocol) + or (getattr(sys, 'frozen', False) and not is_wine and not config.auth_force_localserver) ): return WindowsProtocolHandler diff --git a/tests/config/_old_config.py b/tests/config/_old_config.py index 35ae19e3..2b5244b6 100644 --- a/tests/config/_old_config.py +++ b/tests/config/_old_config.py @@ -5,8 +5,8 @@ import numbers import sys import warnings from configparser import NoOptionError -from os import getenv, makedirs, mkdir, pardir -from os.path import dirname, expanduser, isdir, join, normpath +from os import getenv, makedirs, mkdir +from os.path import dirname, expanduser, isdir, join from typing import TYPE_CHECKING from config import applongname, appname, update_interval from EDMCLogging import get_main_logger @@ -109,7 +109,7 @@ class OldConfig: OUT_EDDN_DELAY = 4096 OUT_STATION_ANY = OUT_EDDN_SEND_STATION_DATA | OUT_MKT_TD | OUT_MKT_CSV - if sys.platform == 'win32': + if sys.platform == 'win32': # noqa: C901 def __init__(self): self.app_dir = join(known_folder_path(FOLDERID_LocalAppData), appname) # type: ignore From d8d8540ceeea7b2c9beb57843df4ce3be136e7e2 Mon Sep 17 00:00:00 2001 From: David Sangrey <rixxan@hullseals.space> Date: Fri, 5 Apr 2024 17:16:25 -0400 Subject: [PATCH 16/22] [2186] Resolve Some Merge Issues From EntryMenu --- myNotebook.py | 63 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/myNotebook.py b/myNotebook.py index 64624550..7379aa5a 100644 --- a/myNotebook.py +++ b/myNotebook.py @@ -57,12 +57,69 @@ class Label(tk.Label): super().__init__(master, **kw) -class Entry(ttk.Entry): +class EntryMenu(ttk.Entry): + """Extended entry widget that includes a context menu with Copy, Cut-and-Paste commands.""" + + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + + self.menu = tk.Menu(self, tearoff=False) + self.menu.add_command(label="Copy", command=self.copy) + self.menu.add_command(label="Cut", command=self.cut) + self.menu.add_separator() + self.menu.add_command(label="Paste", command=self.paste) + self.menu.add_separator() + self.menu.add_command(label="Select All", command=self.select_all) + + self.bind("<Button-3>", self.display_popup) + + def display_popup(self, event: tk.Event) -> None: + """Display the menu popup.""" + self.menu.post(event.x_root, event.y_root) + + def select_all(self) -> None: + """Select all the text within the Entry.""" + self.selection_range(0, tk.END) + self.focus_set() + + def copy(self) -> None: + """Copy the selected Entry text.""" + if self.selection_present(): + self.clipboard_clear() + self.clipboard_append(self.selection_get()) + + def cut(self) -> None: + """Cut the selected Entry text.""" + if self.selection_present(): + self.copy() + self.delete(tk.SEL_FIRST, tk.SEL_LAST) + + def paste(self) -> None: + """Paste the selected Entry text.""" + try: + # Attempt to grab an image from the clipboard (apprently also works for files) + img = ImageGrab.grabclipboard() + if img: + # Hijack existing translation, yes it doesn't exactly match here. + # LANG: Generic error prefix - following text is from Frontier auth service; + messagebox.showwarning(_('Error'), + _('Cannot paste non-text content.')) # LANG: Can't Paste Images or Files in Text + return + text = self.clipboard_get() + if self.selection_present() and text: + self.delete(tk.SEL_FIRST, tk.SEL_LAST) + self.insert(tk.INSERT, text) + except tk.TclError: + # No text in clipboard or clipboard is not text + pass + + +class Entry(ttk.Entry or EntryMenu): """Custom t(t)k.Entry class to fix some display issues.""" - # DEPRECATED: Migrate to ttk.Entry. Will remove in 5.12 or later. + # DEPRECATED: Migrate to ttk.Entry or EntryMenu. Will remove in 5.12 or later. def __init__(self, master: ttk.Frame | None = None, **kw): - super().__init__(master, **kw) + EntryMenu.__init__(self, master, **kw) class Button(tk.Button or ttk.Button): # type: ignore From d00226f9e3f8dbbf211851433bb501a984334658 Mon Sep 17 00:00:00 2001 From: David Sangrey <rixxan@hullseals.space> Date: Fri, 5 Apr 2024 17:20:05 -0400 Subject: [PATCH 17/22] [2186] Additional Documentations --- .flake8 | 1 - .mypy.ini | 1 - Contributing.md | 2 +- pyproject.toml | 3 --- requirements.txt | 3 --- 5 files changed, 1 insertion(+), 9 deletions(-) diff --git a/.flake8 b/.flake8 index bdb25ca4..179e00ec 100644 --- a/.flake8 +++ b/.flake8 @@ -7,7 +7,6 @@ exclude = FDevIDs/ venv/ .venv/ - hotkey/darwin.py # FIXME: Check under macOS VM at some point # Show exactly where in a line the error happened #show-source = True diff --git a/.mypy.ini b/.mypy.ini index 75f4ccdb..9ac2f227 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -6,5 +6,4 @@ scripts_are_modules = True ; `<var> = <value>` ; i.e. no typing info. check_untyped_defs = True -; platform = darwin explicit_package_bases = True diff --git a/Contributing.md b/Contributing.md index 5f7bc158..f021f1a3 100644 --- a/Contributing.md +++ b/Contributing.md @@ -679,7 +679,7 @@ the following does not work: ```py from sys import platform -if platform == 'darwin': +if platform == 'win32': ... ``` diff --git a/pyproject.toml b/pyproject.toml index e32ad617..b98fbf4f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,6 +23,3 @@ sys-platform-not-darwin = "sys_platform == 'darwin'" sys-platform-linux = "sys_platform != 'linux'" sys-platform-not-linux = "sys_platform == 'linux'" sys-platform-not-known = "sys_platform in ('darwin', 'linux', 'win32')" - -[tool.pyright] -# pythonPlatform = 'Darwin' diff --git a/requirements.txt b/requirements.txt index e9d4f58d..c9239fdc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,6 +10,3 @@ infi.systray==0.1.12; sys_platform == 'win32' # argh==0.26.2 watchdog dep # pyyaml==5.3.1 watchdog dep semantic-version==2.10.0 - -# Base requirement for MacOS -pyobjc; sys_platform == 'darwin' From fbdc44139077db712fa39b6d4ae60fc3013e1fc2 Mon Sep 17 00:00:00 2001 From: David Sangrey <rixxan@hullseals.space> Date: Fri, 5 Apr 2024 17:28:08 -0400 Subject: [PATCH 18/22] [Minor] Return Visual Padding Just makes it nicer to read. --- protocol.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/protocol.py b/protocol.py index 4c9e41ca..b052ebae 100644 --- a/protocol.py +++ b/protocol.py @@ -254,12 +254,12 @@ if (config.auth_force_edmc_protocol # noqa: C901 hwnd = CreateWindowExW( 0, # dwExStyle wndclass.lpszClassName, # lpClassName - "DDE Server", # lpWindowName - 0, # dwStyle + "DDE Server", # lpWindowName + 0, # dwStyle CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, # X, Y, nWidth, nHeight self.master.winfo_id(), # hWndParent # Don't use HWND_MESSAGE since the window won't get DDE broadcasts - None, # hMenu - wndclass.hInstance, # hInstance + None, # hMenu + wndclass.hInstance, # hInstance None # lpParam ) From 3a8227a8741ef2856472d300490ab2e30c2c7d57 Mon Sep 17 00:00:00 2001 From: David Sangrey <rixxan@hullseals.space> Date: Fri, 5 Apr 2024 17:33:22 -0400 Subject: [PATCH 19/22] [2186] Correct Logic --- EDMarketConnector.py | 8 +++----- prefs.py | 6 +++--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/EDMarketConnector.py b/EDMarketConnector.py index 967488e0..3ab97eb5 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -1750,8 +1750,7 @@ class AppWindow: # position over parent # http://core.tcl.tk/tk/tktview/c84f660833546b1b84e7 - if parent.winfo_rooty() > 0: - self.geometry(f'+{parent.winfo_rootx():d}+{parent.winfo_rooty():d}') + self.geometry(f'+{parent.winfo_rootx():d}+{parent.winfo_rooty():d}') # remove decoration if sys.platform == 'win32': @@ -1870,9 +1869,8 @@ class AppWindow: config.set_shutdown() # Signal we're in shutdown now. # http://core.tcl.tk/tk/tktview/c84f660833546b1b84e7 - if self.w.winfo_rooty() > 0: - x, y = self.w.geometry().split('+')[1:3] # e.g. '212x170+2881+1267' - config.set('geometry', f'+{x}+{y}') + x, y = self.w.geometry().split('+')[1:3] # e.g. '212x170+2881+1267' + config.set('geometry', f'+{x}+{y}') # Let the user know we're shutting down. # LANG: The application is shutting down diff --git a/prefs.py b/prefs.py index 5b045b1a..9e0459b6 100644 --- a/prefs.py +++ b/prefs.py @@ -230,9 +230,9 @@ class PreferencesDialog(tk.Toplevel): self.transient(parent) # position over parent - if parent.winfo_rooty() > 0: # http://core.tcl.tk/tk/tktview/c84f660833546b1b84e7 - # TODO this is fixed supposedly. - self.geometry(f'+{parent.winfo_rootx()}+{parent.winfo_rooty()}') + # http://core.tcl.tk/tk/tktview/c84f660833546b1b84e7 + # TODO this is fixed supposedly. + self.geometry(f'+{parent.winfo_rootx()}+{parent.winfo_rooty()}') # remove decoration if sys.platform == 'win32': From e0ef9b52c3cdc5aedc9982c736a7169bd3a5d2f8 Mon Sep 17 00:00:00 2001 From: David Sangrey <rixxan@hullseals.space> Date: Fri, 5 Apr 2024 17:45:07 -0400 Subject: [PATCH 20/22] [2186] Additional Tweaks --- myNotebook.py | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/myNotebook.py b/myNotebook.py index 44488b8a..e8f8779b 100644 --- a/myNotebook.py +++ b/myNotebook.py @@ -30,15 +30,16 @@ class Notebook(ttk.Notebook): super().__init__(master, **kw) style = ttk.Style() - style.configure('nb.TFrame', background=PAGEBG) - style.configure('nb.TButton', background=PAGEBG) - style.configure('nb.TCheckbutton', foreground=PAGEFG, background=PAGEBG) - style.configure('nb.TMenubutton', foreground=PAGEFG, background=PAGEBG) - style.configure('nb.TRadiobutton', foreground=PAGEFG, background=PAGEBG) + if sys.platform == 'win32': + style.configure('nb.TFrame', background=PAGEBG) + style.configure('nb.TButton', background=PAGEBG) + style.configure('nb.TCheckbutton', foreground=PAGEFG, background=PAGEBG) + style.configure('nb.TMenubutton', foreground=PAGEFG, background=PAGEBG) + style.configure('nb.TRadiobutton', foreground=PAGEFG, background=PAGEBG) self.grid(padx=10, pady=10, sticky=tk.NSEW) -class Frame(tk.Frame or ttk.Frame): # type: ignore +class Frame(ttk.Frame): """Custom t(t)k.Frame class to fix some display issues.""" def __init__(self, master: ttk.Notebook | None = None, **kw): @@ -127,7 +128,7 @@ class Entry(ttk.Entry or EntryMenu): EntryMenu.__init__(self, master, **kw) -class Button(tk.Button or ttk.Button): # type: ignore +class Button(ttk.Button): # type: ignore """Custom t(t)k.Button class to fix some display issues.""" # DEPRECATED: Migrate to ttk.Button. Will remove in 5.12 or later. @@ -138,7 +139,7 @@ class Button(tk.Button or ttk.Button): # type: ignore ttk.Button.__init__(self, master, **kw) -class ColoredButton(tk.Label or tk.Button): # type: ignore +class ColoredButton(tk.Button): # type: ignore """Custom t(t)k.ColoredButton class to fix some display issues.""" # DEPRECATED: Migrate to tk.Button. Will remove in 5.12 or later. @@ -166,11 +167,14 @@ class OptionMenu(ttk.OptionMenu): """Custom ttk.OptionMenu class to fix some display issues.""" def __init__(self, master, variable, default=None, *values, **kw): - style = 'nb.TMenubutton' if sys.platform == 'win32' else ttk.Style().lookup('TMenu', 'background') - menu_background = PAGEBG if sys.platform == 'win32' else ttk.Style().lookup('TMenu', 'background') + if sys.platform == 'win32': + # OptionMenu derives from Menubutton at the Python level, so uses Menubutton's style + ttk.OptionMenu.__init__(self, master, variable, default, *values, style='nb.TMenubutton', **kw) + self['menu'].configure(background=PAGEBG) + else: + ttk.OptionMenu.__init__(self, master, variable, default, *values, **kw) + self['menu'].configure(background=ttk.Style().lookup('TMenu', 'background')) - super().__init__(master, variable, default, *values, style=style, **kw) - self['menu'].configure(background=menu_background) - - for i in range(self['menu'].index('end') + 1): + # Workaround for https://bugs.python.org/issue25684 + for i in range(0, self['menu'].index('end') + 1): self['menu'].entryconfig(i, variable=variable) From 3c6ea3c9328a95e2a82fa276a2d1e3865c235f36 Mon Sep 17 00:00:00 2001 From: David Sangrey <rixxan@hullseals.space> Date: Fri, 5 Apr 2024 17:50:30 -0400 Subject: [PATCH 21/22] [Minor] Comment Clarify --- update.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/update.py b/update.py index 346aff63..991558d6 100644 --- a/update.py +++ b/update.py @@ -161,7 +161,7 @@ class Updater: return None - # For *these* purposes anything else is the same as 'windows', as + # For *these* purposes all systems are the same as 'windows', as # non-win32 would be running from source. sparkle_platform = 'windows' From c14bd826d08daee4509cf2bf2b184245d7f54695 Mon Sep 17 00:00:00 2001 From: David Sangrey <rixxan@hullseals.space> Date: Sat, 6 Apr 2024 16:52:45 -0400 Subject: [PATCH 22/22] [Minor] Additional Visual Padding Fixes --- protocol.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/protocol.py b/protocol.py index b052ebae..1fb88595 100644 --- a/protocol.py +++ b/protocol.py @@ -252,7 +252,7 @@ if (config.auth_force_edmc_protocol # noqa: C901 # https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw hwnd = CreateWindowExW( - 0, # dwExStyle + 0, # dwExStyle wndclass.lpszClassName, # lpClassName "DDE Server", # lpWindowName 0, # dwStyle @@ -260,7 +260,7 @@ if (config.auth_force_edmc_protocol # noqa: C901 self.master.winfo_id(), # hWndParent # Don't use HWND_MESSAGE since the window won't get DDE broadcasts None, # hMenu wndclass.hInstance, # hInstance - None # lpParam + None # lpParam ) msg = MSG()