1
0
mirror of https://github.com/EDCD/EDMarketConnector.git synced 2025-04-12 15:27:14 +03:00

Update to use sys.platform over platform

This commit is contained in:
A_D 2022-01-24 13:54:04 +02:00
parent 86292e02e2
commit bff6175ee7
No known key found for this signature in database
GPG Key ID: 4BE9EB7DF45076C4
10 changed files with 267 additions and 253 deletions

View File

@ -1,6 +1,7 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Entry point for the main GUI application."""
from __future__ import annotations
import argparse
import html
@ -14,7 +15,6 @@ import webbrowser
from builtins import object, str
from os import chdir, environ
from os.path import dirname, join
from sys import platform
from time import localtime, strftime, time
from typing import TYPE_CHECKING, Optional, Tuple, Union
@ -23,7 +23,7 @@ from typing import TYPE_CHECKING, Optional, Tuple, Union
# place for things like config.py reading .gitversion
if getattr(sys, 'frozen', False):
# Under py2exe sys.path[0] is the executable name
if platform == 'win32':
if sys.platform == 'win32':
chdir(dirname(sys.path[0]))
# Allow executable to be invoked from any cwd
environ['TCL_LIBRARY'] = join(dirname(sys.path[0]), 'lib', 'tcl')
@ -234,7 +234,7 @@ if __name__ == '__main__': # noqa: C901
"""Handle any edmc:// auth callback, else foreground existing window."""
logger.trace_if('frontier-auth.windows', 'Begin...')
if platform == 'win32':
if sys.platform == 'win32':
# If *this* instance hasn't locked, then another already has and we
# now need to do the edmc:// checks for auth callback
@ -370,8 +370,9 @@ if __name__ == '__main__': # noqa: C901
# isort: off
if TYPE_CHECKING:
from logging import TRACE # type: ignore # noqa: F401 # Needed to update mypy
import update
from infi.systray import SysTrayIcon
if sys.platform == 'win32':
from infi.systray import SysTrayIcon
# isort: on
def _(x: str) -> str:
@ -443,7 +444,7 @@ class AppWindow(object):
self.prefsdialog = None
if platform == 'win32':
if sys.platform == 'win32':
from infi.systray import SysTrayIcon
def open_window(systray: 'SysTrayIcon') -> None:
@ -456,8 +457,8 @@ class AppWindow(object):
plug.load_plugins(master)
if platform != 'darwin':
if platform == 'win32':
if sys.platform != 'darwin':
if sys.platform == 'win32':
self.w.wm_iconbitmap(default='EDMarketConnector.ico')
else:
@ -527,7 +528,7 @@ class AppWindow(object):
# LANG: Update button in main window
self.button = ttk.Button(frame, text=_('Update'), width=28, default=tk.ACTIVE, state=tk.DISABLED)
self.theme_button = tk.Label(frame, width=32 if platform == 'darwin' else 28, state=tk.DISABLED)
self.theme_button = tk.Label(frame, width=32 if sys.platform == 'darwin' else 28, state=tk.DISABLED)
self.status = tk.Label(frame, name='status', anchor=tk.W)
ui_row = frame.grid_size()[1]
@ -540,14 +541,15 @@ class AppWindow(object):
theme.button_bind(self.theme_button, self.capi_request_data)
for child in frame.winfo_children():
child.grid_configure(padx=self.PADX, pady=(platform != 'win32' or isinstance(child, tk.Frame)) and 2 or 0)
child.grid_configure(padx=self.PADX, pady=(
sys.platform != 'win32' or isinstance(child, tk.Frame)) and 2 or 0)
# The type needs defining for adding the menu entry, but won't be
# properly set until later
self.updater: update.Updater = None
self.menubar = tk.Menu()
if platform == 'darwin':
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
@ -603,7 +605,7 @@ class AppWindow(object):
self.help_menu.add_command(command=lambda: not self.HelpAbout.showing and self.HelpAbout(self.w))
self.menubar.add_cascade(menu=self.help_menu)
if platform == 'win32':
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)
@ -674,11 +676,11 @@ class AppWindow(object):
if config.get_str('geometry'):
match = re.match(r'\+([\-\d]+)\+([\-\d]+)', config.get_str('geometry'))
if match:
if platform == 'darwin':
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 platform == 'win32':
elif sys.platform == 'win32':
# Check that the titlebar will be at least partly on screen
import ctypes
from ctypes.wintypes import POINT
@ -776,7 +778,7 @@ class AppWindow(object):
self.suit_shown = True
if not self.suit_shown:
if platform != 'win32':
if sys.platform != 'win32':
pady = 2
else:
@ -826,7 +828,7 @@ class AppWindow(object):
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 platform == 'darwin':
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
@ -873,7 +875,7 @@ class AppWindow(object):
self.button['state'] = self.theme_button['state'] = tk.DISABLED
if platform == 'darwin':
if sys.platform == 'darwin':
self.view_menu.entryconfigure(0, state=tk.DISABLED) # Status
self.file_menu.entryconfigure(0, state=tk.DISABLED) # Save Raw Data
@ -887,7 +889,7 @@ class AppWindow(object):
# LANG: Successfully authenticated with the Frontier website
self.status['text'] = _('Authentication successful')
if platform == 'darwin':
if sys.platform == 'darwin':
self.view_menu.entryconfigure(0, state=tk.NORMAL) # Status
self.file_menu.entryconfigure(0, state=tk.NORMAL) # Save Raw Data
@ -1211,7 +1213,7 @@ class AppWindow(object):
companion.session.invalidate()
self.login()
except companion.ServerConnectionError as e:
except companion.ServerConnectionError as e: # TODO: unreachable (subclass of ServerLagging -- move to above)
logger.warning(f'Exception while contacting server: {e}')
err = self.status['text'] = str(e)
play_bad = True
@ -1429,7 +1431,7 @@ class AppWindow(object):
companion.session.auth_callback()
# LANG: Successfully authenticated with the Frontier website
self.status['text'] = _('Authentication successful')
if platform == 'darwin':
if sys.platform == 'darwin':
self.view_menu.entryconfigure(0, state=tk.NORMAL) # Status
self.file_menu.entryconfigure(0, state=tk.NORMAL) # Save Raw Data
@ -1570,11 +1572,11 @@ class AppWindow(object):
# position over parent
# http://core.tcl.tk/tk/tktview/c84f660833546b1b84e7
if platform != 'darwin' or parent.winfo_rooty() > 0:
if sys.platform != 'darwin' or parent.winfo_rooty() > 0:
self.geometry(f'+{parent.winfo_rootx():d}+{parent.winfo_rooty():d}')
# remove decoration
if platform == 'win32':
if sys.platform == 'win32':
self.attributes('-toolwindow', tk.TRUE)
self.resizable(tk.FALSE, tk.FALSE)
@ -1651,7 +1653,7 @@ class AppWindow(object):
"""
default_extension: str = ''
if platform == 'darwin':
if sys.platform == 'darwin':
default_extension = '.json'
timestamp: str = strftime('%Y-%m-%dT%H.%M.%S', localtime())
@ -1676,7 +1678,7 @@ class AppWindow(object):
def onexit(self, event=None) -> None:
"""Application shutdown procedure."""
if platform == 'win32':
if sys.platform == 'win32':
shutdown_thread = threading.Thread(target=self.systray.shutdown)
shutdown_thread.setDaemon(True)
shutdown_thread.start()
@ -1684,7 +1686,7 @@ class AppWindow(object):
config.set_shutdown() # Signal we're in shutdown now.
# http://core.tcl.tk/tk/tktview/c84f660833546b1b84e7
if platform != 'darwin' or self.w.winfo_rooty() > 0:
if sys.platform != 'darwin' or self.w.winfo_rooty() > 0:
x, y = self.w.geometry().split('+')[1:3] # e.g. '212x170+2881+1267'
config.set('geometry', f'+{x}+{y}')

View File

@ -2,11 +2,11 @@
import json
import pathlib
import sys
import time
import tkinter as tk
from calendar import timegm
from os.path import getsize, isdir, isfile
from sys import platform
from typing import Any, Dict
from config import config
@ -14,11 +14,11 @@ from EDMCLogging import get_main_logger
logger = get_main_logger()
if platform == 'darwin':
if sys.platform == 'darwin':
from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer
elif platform == 'win32':
elif sys.platform == 'win32':
from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer
@ -71,26 +71,25 @@ class Dashboard(FileSystemEventHandler):
# File system events are unreliable/non-existent over network drives on Linux.
# We can't easily tell whether a path points to a network drive, so assume
# any non-standard logdir might be on a network drive and poll instead.
polling = platform != 'win32'
if not polling and not self.observer:
if not (sys.platform != 'win32') and not self.observer:
logger.debug('Setting up observer...')
self.observer = Observer()
self.observer.daemon = True
self.observer.start()
logger.debug('Done')
elif polling and self.observer:
elif (sys.platform != 'win32') and self.observer:
logger.debug('Using polling, stopping observer...')
self.observer.stop()
self.observer = None # type: ignore
logger.debug('Done')
if not self.observed and not polling:
if not self.observed and not (sys.platform != 'win32'):
logger.debug('Starting observer...')
self.observed = self.observer.schedule(self, self.currentdir)
logger.debug('Done')
logger.info(f'{polling and "Polling" or "Monitoring"} Dashboard "{self.currentdir}"')
logger.info(f'{(sys.platform != "win32") and "Polling" or "Monitoring"} Dashboard "{self.currentdir}"')
# Even if we're not intending to poll, poll at least once to process pre-existing
# data and to check whether the watchdog thread has crashed due to events not

View File

@ -1,10 +1,10 @@
"""Implements locking of Journal directory."""
import pathlib
import sys
import tkinter as tk
from enum import Enum
from os import getpid as os_getpid
from sys import platform
from tkinter import ttk
from typing import TYPE_CHECKING, Callable, Optional
@ -94,7 +94,7 @@ class JournalLock:
:return: LockResult - See the class Enum definition
"""
if platform == 'win32':
if sys.platform == 'win32':
logger.trace_if('journal-lock', 'win32, using msvcrt')
# win32 doesn't have fcntl, so we have to use msvcrt
import msvcrt
@ -143,7 +143,7 @@ class JournalLock:
return True # We weren't locked, and still aren't
unlocked = False
if platform == 'win32':
if sys.platform == 'win32':
logger.trace_if('journal-lock', 'win32, using msvcrt')
# win32 doesn't have fcntl, so we have to use msvcrt
import msvcrt
@ -206,10 +206,10 @@ class JournalLock:
self.title(_('Journal directory already locked'))
# remove decoration
if platform == 'win32':
if sys.platform == 'win32':
self.attributes('-toolwindow', tk.TRUE)
elif platform == 'darwin':
elif sys.platform == 'darwin':
# http://wiki.tcl.tk/13428
parent.call('tk::unsupported::MacWindowStyle', 'style', self, 'utility')

21
l10n.py
View File

@ -12,7 +12,6 @@ import warnings
from collections import OrderedDict
from contextlib import suppress
from os.path import basename, dirname, isdir, isfile, join
from sys import platform
from typing import TYPE_CHECKING, Dict, Iterable, Optional, Set, TextIO, Union, cast
if TYPE_CHECKING:
@ -37,12 +36,12 @@ LANGUAGE_ID = '!Language'
LOCALISATION_DIR = 'L10n'
if platform == 'darwin':
if sys.platform == 'darwin':
from Foundation import ( # type: ignore # exists on Darwin
NSLocale, NSNumberFormatter, NSNumberFormatterDecimalStyle
)
elif platform == 'win32':
elif sys.platform == 'win32':
import ctypes
from ctypes.wintypes import BOOL, DWORD, LPCVOID, LPCWSTR, LPWSTR
if TYPE_CHECKING:
@ -176,7 +175,7 @@ class _Translations:
def available(self) -> Set[str]:
"""Return a list of available language codes."""
path = self.respath()
if getattr(sys, 'frozen', False) and platform == 'darwin':
if getattr(sys, 'frozen', False) and sys.platform == 'darwin':
available = {
x[:-len('.lproj')] for x in os.listdir(path)
if x.endswith('.lproj') and isfile(join(x, 'Localizable.strings'))
@ -204,7 +203,7 @@ class _Translations:
def respath(self) -> pathlib.Path:
"""Path to localisation files."""
if getattr(sys, 'frozen', False):
if platform == 'darwin':
if sys.platform == 'darwin':
return (pathlib.Path(sys.executable).parents[0] / os.pardir / 'Resources').resolve()
return pathlib.Path(dirname(sys.executable)) / LOCALISATION_DIR
@ -233,7 +232,7 @@ class _Translations:
except OSError:
logger.exception(f'could not open {f}')
elif getattr(sys, 'frozen', False) and platform == 'darwin':
elif getattr(sys, 'frozen', False) and sys.platform == 'darwin':
return (self.respath() / f'{lang}.lproj' / 'Localizable.strings').open('r', encoding='utf-16')
return (self.respath() / f'{lang}.strings').open('r', encoding='utf-8')
@ -243,7 +242,7 @@ class _Locale:
"""Locale holds a few utility methods to convert data to and from localized versions."""
def __init__(self) -> None:
if platform == 'darwin':
if sys.platform == 'darwin':
self.int_formatter = NSNumberFormatter.alloc().init()
self.int_formatter.setNumberStyle_(NSNumberFormatterDecimalStyle)
self.float_formatter = NSNumberFormatter.alloc().init()
@ -276,7 +275,7 @@ class _Locale:
if decimals == 0 and not isinstance(number, numbers.Integral):
number = int(round(number))
if platform == 'darwin':
if sys.platform == 'darwin':
if not decimals and isinstance(number, numbers.Integral):
return self.int_formatter.stringFromNumber_(number)
@ -298,7 +297,7 @@ 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 platform == 'darwin':
if sys.platform == 'darwin':
return self.float_formatter.numberFromString_(string)
with suppress(ValueError):
@ -321,10 +320,10 @@ class _Locale:
:return: The preferred language list
"""
languages: Iterable[str]
if platform == 'darwin':
if sys.platform == 'darwin':
languages = NSLocale.preferredLanguages()
elif platform != 'win32':
elif sys.platform != 'win32':
# POSIX
lang = locale.getlocale()[0]
languages = lang and [lang.replace('_', '-')] or []

View File

@ -4,22 +4,21 @@
# - OSX: page background should be a darker gray than systemWindowBody
# selected tab foreground should be White when the window is active
#
from sys import platform
import sys
import tkinter as tk
from tkinter import ttk
# Entire file may be imported by plugins
# Can't do this with styles on OSX - http://www.tkdocs.com/tutorial/styles.html#whydifficult
if platform == 'darwin':
if sys.platform == 'darwin':
from platform import mac_ver
PAGEFG = 'systemButtonText'
PAGEBG = 'systemButtonActiveDarkShadow'
elif platform == 'win32':
elif sys.platform == 'win32':
PAGEFG = 'SystemWindowText'
PAGEBG = 'SystemWindow' # typically white
PAGEBG = 'SystemWindow' # typically white
class Notebook(ttk.Notebook):
@ -29,14 +28,14 @@ class Notebook(ttk.Notebook):
ttk.Notebook.__init__(self, master, **kw)
style = ttk.Style()
if platform=='darwin':
if list(map(int, mac_ver()[0].split('.'))) >= [10,10]:
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.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 platform == 'win32':
self.grid(sticky=tk.NSEW) # Already padded apropriately
elif sys.platform == 'win32':
style.configure('nb.TFrame', background=PAGEBG)
style.configure('nb.TButton', background=PAGEBG)
style.configure('nb.TCheckbutton', foreground=PAGEFG, background=PAGEBG)
@ -47,56 +46,60 @@ class Notebook(ttk.Notebook):
self.grid(padx=10, pady=10, sticky=tk.NSEW)
class Frame(platform == 'darwin' and tk.Frame or ttk.Frame):
class Frame(sys.platform == 'darwin' and tk.Frame or ttk.Frame):
def __init__(self, master=None, **kw):
if platform == 'darwin':
if sys.platform == 'darwin':
kw['background'] = kw.pop('background', PAGEBG)
tk.Frame.__init__(self, master, **kw)
tk.Frame(self).grid(pady=5)
elif platform == 'win32':
elif sys.platform == 'win32':
ttk.Frame.__init__(self, master, style='nb.TFrame', **kw)
ttk.Frame(self).grid(pady=5) # top spacer
ttk.Frame(self).grid(pady=5) # top spacer
else:
ttk.Frame.__init__(self, master, **kw)
ttk.Frame(self).grid(pady=5) # top spacer
self.configure(takefocus = 1) # let the frame take focus so that no particular child is focused
ttk.Frame(self).grid(pady=5) # top spacer
self.configure(takefocus=1) # let the frame take focus so that no particular child is focused
class Label(tk.Label):
def __init__(self, master=None, **kw):
if platform in ['darwin', 'win32']:
if sys.platform in ['darwin', '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
tk.Label.__init__(self, master, **kw) # Just use tk.Label on all platforms
class Entry(platform == 'darwin' and tk.Entry or ttk.Entry):
class Entry(sys.platform == 'darwin' and tk.Entry or ttk.Entry):
def __init__(self, master=None, **kw):
if platform == 'darwin':
if sys.platform == 'darwin':
kw['highlightbackground'] = kw.pop('highlightbackground', PAGEBG)
tk.Entry.__init__(self, master, **kw)
else:
ttk.Entry.__init__(self, master, **kw)
class Button(platform == 'darwin' and tk.Button or ttk.Button):
class Button(sys.platform == 'darwin' and tk.Button or ttk.Button):
def __init__(self, master=None, **kw):
if platform == 'darwin':
if sys.platform == 'darwin':
kw['highlightbackground'] = kw.pop('highlightbackground', PAGEBG)
tk.Button.__init__(self, master, **kw)
elif platform == 'win32':
elif sys.platform == 'win32':
ttk.Button.__init__(self, master, style='nb.TButton', **kw)
else:
ttk.Button.__init__(self, master, **kw)
class ColoredButton(platform == 'darwin' and tk.Label or tk.Button):
class ColoredButton(sys.platform == 'darwin' and tk.Label or tk.Button):
def __init__(self, master=None, **kw):
if platform == 'darwin':
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)
@ -105,52 +108,55 @@ class ColoredButton(platform == 'darwin' and tk.Label or tk.Button):
else:
tk.Button.__init__(self, master, **kw)
if platform == 'darwin':
if sys.platform == 'darwin':
def _press(self, event):
self._command()
class Checkbutton(platform == 'darwin' and tk.Checkbutton or ttk.Checkbutton):
class Checkbutton(sys.platform == 'darwin' and tk.Checkbutton or ttk.Checkbutton):
def __init__(self, master=None, **kw):
if platform == 'darwin':
if sys.platform == 'darwin':
kw['foreground'] = kw.pop('foreground', PAGEFG)
kw['background'] = kw.pop('background', PAGEBG)
tk.Checkbutton.__init__(self, master, **kw)
elif platform == 'win32':
elif sys.platform == 'win32':
ttk.Checkbutton.__init__(self, master, style='nb.TCheckbutton', **kw)
else:
ttk.Checkbutton.__init__(self, master, **kw)
class Radiobutton(platform == 'darwin' and tk.Radiobutton or ttk.Radiobutton):
class Radiobutton(sys.platform == 'darwin' and tk.Radiobutton or ttk.Radiobutton):
def __init__(self, master=None, **kw):
if platform == 'darwin':
if sys.platform == 'darwin':
kw['foreground'] = kw.pop('foreground', PAGEFG)
kw['background'] = kw.pop('background', PAGEBG)
tk.Radiobutton.__init__(self, master, **kw)
elif platform == 'win32':
elif sys.platform == 'win32':
ttk.Radiobutton.__init__(self, master, style='nb.TRadiobutton', **kw)
else:
ttk.Radiobutton.__init__(self, master, **kw)
class OptionMenu(platform == 'darwin' and tk.OptionMenu or ttk.OptionMenu):
class OptionMenu(sys.platform == 'darwin' and tk.OptionMenu or ttk.OptionMenu):
def __init__(self, master, variable, default=None, *values, **kw):
if platform == 'darwin':
if sys.platform == 'darwin':
variable.set(default)
bg = kw.pop('background', PAGEBG)
tk.OptionMenu.__init__(self, master, variable, *values, **kw)
self['background'] = bg
elif platform == 'win32':
elif 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)
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'))
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)

View File

@ -3,10 +3,10 @@
import contextlib
import logging
import sys
import tkinter as tk
import webbrowser
from os.path import expanduser, expandvars, join, normpath
from sys import platform
from tkinter import colorchooser as tkColorChooser # type: ignore # noqa: N812
from tkinter import ttk
from types import TracebackType
@ -154,7 +154,7 @@ class AutoInc(contextlib.AbstractContextManager):
return None
if platform == 'darwin':
if sys.platform == 'darwin':
import objc # type: ignore
from Foundation import NSFileManager # type: ignore
try:
@ -179,7 +179,7 @@ if platform == 'darwin':
was_accessible_at_launch = AXIsProcessTrusted() # type: ignore
elif platform == 'win32':
elif sys.platform == 'win32':
import ctypes
import winreg
from ctypes.wintypes import HINSTANCE, HWND, LPARAM, LPCWSTR, LPVOID, LPWSTR, MAX_PATH, POINT, RECT, SIZE, UINT
@ -246,7 +246,7 @@ class PreferencesDialog(tk.Toplevel):
self.parent = parent
self.callback = callback
if platform == 'darwin':
if sys.platform == 'darwin':
# LANG: File > Preferences menu entry for macOS
self.title(_('Preferences'))
@ -258,15 +258,15 @@ class PreferencesDialog(tk.Toplevel):
self.transient(parent)
# position over parent
if platform != 'darwin' or parent.winfo_rooty() > 0: # http://core.tcl.tk/tk/tktview/c84f660833546b1b84e7
if sys.platform != 'darwin' or 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()}')
# remove decoration
if platform == 'win32':
if sys.platform == 'win32':
self.attributes('-toolwindow', tk.TRUE)
elif platform == 'darwin':
elif sys.platform == 'darwin':
# http://wiki.tcl.tk/13428
parent.call('tk::unsupported::MacWindowStyle', 'style', self, 'utility')
@ -294,7 +294,7 @@ class PreferencesDialog(tk.Toplevel):
self.__setup_appearance_tab(notebook)
self.__setup_plugin_tab(notebook)
if platform == 'darwin':
if sys.platform == 'darwin':
self.protocol("WM_DELETE_WINDOW", self.apply) # close button applies changes
else:
@ -322,7 +322,7 @@ class PreferencesDialog(tk.Toplevel):
self.grab_set()
# Ensure fully on-screen
if platform == 'win32' and CalculatePopupWindowPosition:
if sys.platform == 'win32' and CalculatePopupWindowPosition:
position = RECT()
GetWindowRect(GetParent(self.winfo_id()), position)
if CalculatePopupWindowPosition(
@ -396,7 +396,7 @@ class PreferencesDialog(tk.Toplevel):
self.outdir_entry = nb.Entry(output_frame, takefocus=False)
self.outdir_entry.grid(columnspan=2, padx=self.PADX, pady=(0, self.PADY), sticky=tk.EW, row=row.get())
if platform == 'darwin':
if sys.platform == 'darwin':
text = (_('Change...')) # LANG: macOS Preferences - files location selection button
else:
@ -446,7 +446,7 @@ class PreferencesDialog(tk.Toplevel):
self.logdir_entry.grid(columnspan=4, padx=self.PADX, pady=(0, self.PADY), sticky=tk.EW, row=row.get())
if platform == 'darwin':
if sys.platform == 'darwin':
text = (_('Change...')) # LANG: macOS Preferences - files location selection button
else:
@ -470,7 +470,7 @@ class PreferencesDialog(tk.Toplevel):
state=tk.NORMAL if config.get_str('journaldir') else tk.DISABLED
).grid(column=2, pady=self.PADY, sticky=tk.EW, row=row.get())
if platform in ('darwin', 'win32'):
if sys.platform in ('darwin', 'win32'):
ttk.Separator(config_frame, orient=tk.HORIZONTAL).grid(
columnspan=4, padx=self.PADX, pady=self.PADY*4, sticky=tk.EW, row=row.get()
)
@ -482,11 +482,11 @@ class PreferencesDialog(tk.Toplevel):
nb.Label(
config_frame,
text=_('Keyboard shortcut') if # LANG: Hotkey/Shortcut settings prompt on OSX
platform == 'darwin' else
sys.platform == 'darwin' else
_('Hotkey') # LANG: Hotkey/Shortcut settings prompt on Windows
).grid(padx=self.PADX, sticky=tk.W, row=row.get())
if platform == 'darwin' and not was_accessible_at_launch:
if sys.platform == 'darwin' and not was_accessible_at_launch:
if AXIsProcessTrusted():
# Shortcut settings prompt on OSX
nb.Label(
@ -511,7 +511,8 @@ class PreferencesDialog(tk.Toplevel):
)
else:
self.hotkey_text = nb.Entry(config_frame, width=(20 if platform == 'darwin' else 30), justify=tk.CENTER)
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
@ -741,7 +742,7 @@ class PreferencesDialog(tk.Toplevel):
appearance_frame, text=_('Dark'), variable=self.theme, value=1, command=self.themevarchanged
).grid(columnspan=3, padx=self.BUTTONX, sticky=tk.W, row=row.get())
if platform == 'win32':
if sys.platform == 'win32':
nb.Radiobutton(
appearance_frame,
# LANG: Label for 'Transparent' theme radio button
@ -870,7 +871,7 @@ class PreferencesDialog(tk.Toplevel):
)
self.ontop_button.grid(columnspan=3, padx=self.BUTTONX, sticky=tk.W, row=row.get()) # Appearance setting
if platform == 'win32':
if sys.platform == 'win32':
nb.Checkbutton(
appearance_frame,
# LANG: Appearance option for Windows "minimize to system tray"
@ -997,7 +998,7 @@ class PreferencesDialog(tk.Toplevel):
def tabchanged(self, event: tk.Event) -> None:
"""Handle preferences active tab changing."""
self.outvarchanged()
if platform == 'darwin':
if sys.platform == 'darwin':
# Hack to recompute size so that buttons show up under Mojave
notebook = event.widget
frame = self.nametowidget(notebook.winfo_parent())
@ -1027,9 +1028,8 @@ class PreferencesDialog(tk.Toplevel):
# If encoding isn't UTF-8 we can't use the tkinter dialog
current_locale = locale.getlocale(locale.LC_CTYPE)
from sys import platform as sys_platform
directory = None
if sys_platform == 'win32' and current_locale[1] not in ('utf8', 'UTF8', 'utf-8', 'UTF-8'):
if sys.platform == 'win32' and current_locale[1] not in ('utf8', 'UTF8', 'utf-8', 'UTF-8'):
def browsecallback(hwnd, uMsg, lParam, lpData): # noqa: N803 # Windows API convention
# set initial folder
if uMsg == BFFM_INITIALIZED and lpData:
@ -1075,7 +1075,7 @@ class PreferencesDialog(tk.Toplevel):
# TODO: This is awful.
entryfield['state'] = tk.NORMAL # must be writable to update
entryfield.delete(0, tk.END)
if platform == 'win32':
if sys.platform == 'win32':
start = len(config.home.split('\\')) if pathvar.get().lower().startswith(config.home.lower()) else 0
display = []
components = normpath(pathvar.get()).split('\\')
@ -1096,7 +1096,7 @@ class PreferencesDialog(tk.Toplevel):
entryfield.insert(0, '\\'.join(display))
# None if path doesn't exist
elif platform == 'darwin' and NSFileManager.defaultManager().componentsToDisplayForPath_(pathvar.get()):
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)):
@ -1236,7 +1236,7 @@ class PreferencesDialog(tk.Toplevel):
else:
config.set('journaldir', logdir)
if platform in ('darwin', 'win32'):
if sys.platform in ('darwin', '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()))
@ -1282,7 +1282,7 @@ class PreferencesDialog(tk.Toplevel):
self.parent.wm_attributes('-topmost', 1 if config.get_int('always_ontop') else 0)
self.destroy()
if platform == 'darwin':
if sys.platform == 'darwin':
def enableshortcuts(self) -> None:
"""Set up macOS preferences shortcut."""
self.apply()

View File

@ -1,9 +1,9 @@
"""CMDR Status information."""
import csv
import json
import sys
import tkinter
import tkinter as tk
from sys import platform
from tkinter import ttk
from typing import TYPE_CHECKING, Any, AnyStr, Callable, Dict, List, NamedTuple, Optional, Sequence, cast
@ -20,11 +20,9 @@ logger = EDMCLogging.get_main_logger()
if TYPE_CHECKING:
def _(x: str) -> str: ...
if platform == 'win32':
if sys.platform == 'win32':
import ctypes
from ctypes.wintypes import HWND, POINT, RECT, SIZE, UINT
if TYPE_CHECKING:
import ctypes.windll # type: ignore # Fake this into existing, its really a magic dll thing
try:
CalculatePopupWindowPosition = ctypes.windll.user32.CalculatePopupWindowPosition
@ -372,15 +370,15 @@ class StatsResults(tk.Toplevel):
self.transient(parent)
# position over parent
if platform != 'darwin' or parent.winfo_rooty() > 0: # http://core.tcl.tk/tk/tktview/c84f660833546b1b84e7
if sys.platform != 'darwin' or parent.winfo_rooty() > 0: # http://core.tcl.tk/tk/tktview/c84f660833546b1b84e7
self.geometry(f"+{parent.winfo_rootx()}+{parent.winfo_rooty()}")
# remove decoration
self.resizable(tk.FALSE, tk.FALSE)
if platform == 'win32':
if sys.platform == 'win32':
self.attributes('-toolwindow', tk.TRUE)
elif platform == 'darwin':
elif sys.platform == 'darwin':
# http://wiki.tcl.tk/13428
parent.call('tk::unsupported::MacWindowStyle', 'style', self, 'utility')
@ -421,7 +419,7 @@ class StatsResults(tk.Toplevel):
ttk.Frame(page).grid(pady=5) # bottom spacer
notebook.add(page, text=_('Ships')) # LANG: Status dialog title
if platform != 'darwin':
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)
@ -433,7 +431,7 @@ class StatsResults(tk.Toplevel):
self.grab_set()
# Ensure fully on-screen
if platform == 'win32' and CalculatePopupWindowPosition:
if sys.platform == 'win32' and CalculatePopupWindowPosition:
position = RECT()
GetWindowRect(GetParent(self.winfo_id()), position)
if CalculatePopupWindowPosition(

View File

@ -4,7 +4,6 @@ import warnings
from configparser import NoOptionError
from os import getenv, makedirs, mkdir, pardir
from os.path import dirname, expanduser, isdir, join, normpath
from sys import platform
from typing import TYPE_CHECKING, Optional, Union
from config import applongname, appname, update_interval
@ -12,13 +11,13 @@ from EDMCLogging import get_main_logger
logger = get_main_logger()
if platform == 'darwin':
if sys.platform == 'darwin':
from Foundation import ( # type: ignore
NSApplicationSupportDirectory, NSBundle, NSDocumentDirectory, NSSearchPathForDirectoriesInDomains,
NSUserDefaults, NSUserDomainMask
)
elif platform == 'win32':
elif sys.platform == 'win32':
import ctypes
import uuid
from ctypes.wintypes import DWORD, HANDLE, HKEY, LONG, LPCVOID, LPCWSTR
@ -90,7 +89,7 @@ elif platform == 'win32':
CoTaskMemFree(buf) # and free original
return retval
elif platform == 'linux':
elif sys.platform == 'linux':
import codecs
from configparser import RawConfigParser
@ -114,7 +113,7 @@ class OldConfig():
OUT_SYS_EDDN = 2048
OUT_SYS_DELAY = 4096
if platform == 'darwin': # noqa: C901 # It's gating *all* the functions
if sys.platform == 'darwin': # noqa: C901 # It's gating *all* the functions
def __init__(self):
self.app_dir = join(
@ -199,7 +198,7 @@ class OldConfig():
self.save()
self.defaults = None
elif platform == 'win32':
elif sys.platform == 'win32':
def __init__(self):
self.app_dir = join(known_folder_path(FOLDERID_LocalAppData), appname) # type: ignore # Not going to change
@ -362,7 +361,7 @@ class OldConfig():
RegCloseKey(self.hkey)
self.hkey = None
elif platform == 'linux':
elif sys.platform == 'linux':
SECTION = 'config'
def __init__(self):

206
theme.py
View File

@ -6,9 +6,9 @@
#
import os
import sys
import tkinter as tk
from os.path import join
from sys import platform
from tkinter import font as tkFont
from tkinter import ttk
@ -18,20 +18,20 @@ from ttkHyperlinkLabel import HyperlinkLabel
if __debug__:
from traceback import print_exc
if platform == "linux":
if sys.platform == "linux":
from ctypes import POINTER, Structure, byref, c_char_p, c_int, c_long, c_uint, c_ulong, c_void_p, cdll
if platform == 'win32':
if sys.platform == 'win32':
import ctypes
from ctypes.wintypes import DWORD, LPCVOID, LPCWSTR
AddFontResourceEx = ctypes.windll.gdi32.AddFontResourceExW
AddFontResourceEx.restypes = [LPCWSTR, DWORD, LPCVOID]
FR_PRIVATE = 0x10
FR_PRIVATE = 0x10
FR_NOT_ENUM = 0x20
AddFontResourceEx(join(config.respath, u'EUROCAPS.TTF'), FR_PRIVATE, 0)
elif platform == 'linux':
elif sys.platform == 'linux':
# pyright: reportUnboundVariable=false
XID = c_ulong # from X.h: typedef unsigned long XID
Window = XID
@ -40,7 +40,7 @@ elif platform == 'linux':
PropModeReplace = 0
PropModePrepend = 1
PropModeAppend = 2
PropModeAppend = 2
# From xprops.h
MWM_HINTS_FUNCTIONS = 1 << 0
@ -69,16 +69,17 @@ elif platform == 'linux':
('input_mode', c_long),
('status', c_ulong),
]
# workaround for https://github.com/EDCD/EDMarketConnector/issues/568
if not os.getenv("EDMC_NO_UI") :
if not os.getenv("EDMC_NO_UI"):
try:
xlib = cdll.LoadLibrary('libX11.so.6')
XInternAtom = xlib.XInternAtom
XInternAtom.argtypes = [POINTER(Display), c_char_p, c_int]
XInternAtom.restype = Atom
XChangeProperty = xlib.XChangeProperty
XChangeProperty.argtypes = [POINTER(Display), Window, Atom, Atom, c_int, c_int, POINTER(MotifWmHints), c_int]
XChangeProperty.argtypes = [POINTER(Display), Window, Atom, Atom, c_int,
c_int, POINTER(MotifWmHints), c_int]
XChangeProperty.restype = c_int
XFlush = xlib.XFlush
XFlush.argtypes = [POINTER(Display)]
@ -87,29 +88,31 @@ elif platform == 'linux':
XOpenDisplay.argtypes = [c_char_p]
XOpenDisplay.restype = POINTER(Display)
XQueryTree = xlib.XQueryTree
XQueryTree.argtypes = [POINTER(Display), Window, POINTER(Window), POINTER(Window), POINTER(Window), POINTER(c_uint)]
XQueryTree.argtypes = [POINTER(Display), Window, POINTER(
Window), POINTER(Window), POINTER(Window), POINTER(c_uint)]
XQueryTree.restype = c_int
dpy = xlib.XOpenDisplay(None)
if not dpy:
raise Exception("Can't find your display, can't continue")
motif_wm_hints_property = XInternAtom(dpy, b'_MOTIF_WM_HINTS', False)
motif_wm_hints_normal = MotifWmHints(MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS,
MWM_FUNC_RESIZE | MWM_FUNC_MOVE | MWM_FUNC_MINIMIZE | MWM_FUNC_CLOSE,
MWM_DECOR_BORDER | MWM_DECOR_RESIZEH | MWM_DECOR_TITLE | MWM_DECOR_MENU | MWM_DECOR_MINIMIZE,
0, 0)
motif_wm_hints_dark = MotifWmHints(MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS,
MWM_FUNC_RESIZE | MWM_FUNC_MOVE | MWM_FUNC_MINIMIZE | MWM_FUNC_CLOSE,
0, 0, 0)
MWM_FUNC_RESIZE | MWM_FUNC_MOVE | MWM_FUNC_MINIMIZE | MWM_FUNC_CLOSE,
MWM_DECOR_BORDER | MWM_DECOR_RESIZEH | MWM_DECOR_TITLE | MWM_DECOR_MENU | MWM_DECOR_MINIMIZE,
0, 0)
motif_wm_hints_dark = MotifWmHints(MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS,
MWM_FUNC_RESIZE | MWM_FUNC_MOVE | MWM_FUNC_MINIMIZE | MWM_FUNC_CLOSE,
0, 0, 0)
except:
if __debug__: print_exc()
if __debug__:
print_exc()
dpy = None
class _Theme(object):
def __init__(self):
self.active = None # Starts out with no theme
self.active = None # Starts out with no theme
self.minwidth = None
self.widgets = {}
self.widgets_pair = []
@ -124,18 +127,18 @@ class _Theme(object):
if not self.defaults:
# Can't initialise this til window is created # Windows, MacOS
self.defaults = {
'fg' : tk.Label()['foreground'], # SystemButtonText, systemButtonText
'bg' : tk.Label()['background'], # SystemButtonFace, White
'font' : tk.Label()['font'], # TkDefaultFont
'bitmapfg' : tk.BitmapImage()['foreground'], # '-foreground {} {} #000000 #000000'
'bitmapbg' : tk.BitmapImage()['background'], # '-background {} {} {} {}'
'entryfg' : tk.Entry()['foreground'], # SystemWindowText, Black
'entrybg' : tk.Entry()['background'], # SystemWindow, systemWindowBody
'entryfont' : tk.Entry()['font'], # TkTextFont
'frame' : tk.Frame()['background'], # SystemButtonFace, systemWindowBody
'menufg' : tk.Menu()['foreground'], # SystemMenuText,
'menubg' : tk.Menu()['background'], # SystemMenu,
'menufont' : tk.Menu()['font'], # TkTextFont
'fg': tk.Label()['foreground'], # SystemButtonText, systemButtonText
'bg': tk.Label()['background'], # SystemButtonFace, White
'font': tk.Label()['font'], # TkDefaultFont
'bitmapfg': tk.BitmapImage()['foreground'], # '-foreground {} {} #000000 #000000'
'bitmapbg': tk.BitmapImage()['background'], # '-background {} {} {} {}'
'entryfg': tk.Entry()['foreground'], # SystemWindowText, Black
'entrybg': tk.Entry()['background'], # SystemWindow, systemWindowBody
'entryfont': tk.Entry()['font'], # TkTextFont
'frame': tk.Frame()['background'], # SystemButtonFace, systemWindowBody
'menufg': tk.Menu()['foreground'], # SystemMenuText,
'menubg': tk.Menu()['background'], # SystemMenu,
'menufont': tk.Menu()['font'], # TkTextFont
}
if widget not in self.widgets:
@ -189,26 +192,27 @@ class _Theme(object):
def _enter(self, event, image):
widget = event.widget
if widget and widget['state'] != tk.DISABLED:
widget.configure(state = tk.ACTIVE)
widget.configure(state=tk.ACTIVE)
if image:
image.configure(foreground = self.current['activeforeground'], background = self.current['activebackground'])
image.configure(foreground=self.current['activeforeground'],
background=self.current['activebackground'])
def _leave(self, event, image):
widget = event.widget
if widget and widget['state'] != tk.DISABLED:
widget.configure(state = tk.NORMAL)
widget.configure(state=tk.NORMAL)
if image:
image.configure(foreground = self.current['foreground'], background = self.current['background'])
image.configure(foreground=self.current['foreground'], background=self.current['background'])
# Set up colors
def _colors(self, root, theme):
style = ttk.Style()
if platform == 'linux':
if sys.platform == 'linux':
style.theme_use('clam')
# Default dark theme colors
if not config.get_str('dark_text'):
config.set('dark_text', '#ff8000') # "Tangerine" in OSX color picker
config.set('dark_text', '#ff8000') # "Tangerine" in OSX color picker
if not config.get_str('dark_highlight'):
config.set('dark_highlight', 'white')
@ -216,40 +220,40 @@ class _Theme(object):
# Dark
(r, g, b) = root.winfo_rgb(config.get_str('dark_text'))
self.current = {
'background' : 'grey4', # OSX inactive dark titlebar color
'foreground' : config.get_str('dark_text'),
'activebackground' : config.get_str('dark_text'),
'activeforeground' : 'grey4',
'disabledforeground' : '#%02x%02x%02x' % (int(r/384), int(g/384), int(b/384)),
'highlight' : config.get_str('dark_highlight'),
'background': 'grey4', # OSX inactive dark titlebar color
'foreground': config.get_str('dark_text'),
'activebackground': config.get_str('dark_text'),
'activeforeground': 'grey4',
'disabledforeground': '#%02x%02x%02x' % (int(r/384), int(g/384), int(b/384)),
'highlight': config.get_str('dark_highlight'),
# Font only supports Latin 1 / Supplement / Extended, and a few General Punctuation and Mathematical Operators
# LANG: Label for commander name in main window
'font' : (theme > 1 and not 0x250 < ord(_('Cmdr')[0]) < 0x3000 and
tkFont.Font(family='Euro Caps', size=10, weight=tkFont.NORMAL) or
'TkDefaultFont'),
'font': (theme > 1 and not 0x250 < ord(_('Cmdr')[0]) < 0x3000 and
tkFont.Font(family='Euro Caps', size=10, weight=tkFont.NORMAL) or
'TkDefaultFont'),
}
else:
# (Mostly) system colors
style = ttk.Style()
self.current = {
'background' : (platform == 'darwin' and 'systemMovableModalBackground' or
style.lookup('TLabel', 'background')),
'foreground' : style.lookup('TLabel', 'foreground'),
'activebackground' : (platform == 'win32' and 'SystemHighlight' or
style.lookup('TLabel', 'background', ['active'])),
'activeforeground' : (platform == 'win32' and 'SystemHighlightText' or
style.lookup('TLabel', 'foreground', ['active'])),
'disabledforeground' : style.lookup('TLabel', 'foreground', ['disabled']),
'highlight' : 'blue',
'font' : 'TkDefaultFont',
'background': (sys.platform == 'darwin' and 'systemMovableModalBackground' or
style.lookup('TLabel', 'background')),
'foreground': style.lookup('TLabel', 'foreground'),
'activebackground': (sys.platform == 'win32' and 'SystemHighlight' or
style.lookup('TLabel', 'background', ['active'])),
'activeforeground': (sys.platform == 'win32' and 'SystemHighlightText' or
style.lookup('TLabel', 'foreground', ['active'])),
'disabledforeground': style.lookup('TLabel', 'foreground', ['disabled']),
'highlight': 'blue',
'font': 'TkDefaultFont',
}
# Apply current theme to a widget and its children, and register it for future updates
def update(self, widget):
assert isinstance(widget, tk.Widget) or isinstance(widget, tk.BitmapImage), widget
if not self.current:
return # No need to call this for widgets created in plugin_app()
return # No need to call this for widgets created in plugin_app()
self.register(widget)
self._update_widget(widget)
if isinstance(widget, tk.Frame) or isinstance(widget, ttk.Frame):
@ -258,56 +262,57 @@ class _Theme(object):
# Apply current theme to a single widget
def _update_widget(self, widget):
assert widget in self.widgets, '%s %s "%s"' %(widget.winfo_class(), widget, 'text' in widget.keys() and widget['text'])
assert widget in self.widgets, '%s %s "%s"' % (
widget.winfo_class(), widget, 'text' in widget.keys() and widget['text'])
attribs = self.widgets.get(widget, [])
if isinstance(widget, tk.BitmapImage):
# not a widget
if 'fg' not in attribs:
widget.configure(foreground = self.current['foreground']),
widget.configure(foreground=self.current['foreground']),
if 'bg' not in attribs:
widget.configure(background = self.current['background'])
widget.configure(background=self.current['background'])
elif 'cursor' in widget.keys() and str(widget['cursor']) not in ['', 'arrow']:
# Hack - highlight widgets like HyperlinkLabel with a non-default cursor
if 'fg' not in attribs:
widget.configure(foreground = self.current['highlight']),
if 'insertbackground' in widget.keys(): # tk.Entry
widget.configure(insertbackground = self.current['foreground']),
widget.configure(foreground=self.current['highlight']),
if 'insertbackground' in widget.keys(): # tk.Entry
widget.configure(insertbackground=self.current['foreground']),
if 'bg' not in attribs:
widget.configure(background = self.current['background'])
if 'highlightbackground' in widget.keys(): # tk.Entry
widget.configure(highlightbackground = self.current['background'])
widget.configure(background=self.current['background'])
if 'highlightbackground' in widget.keys(): # tk.Entry
widget.configure(highlightbackground=self.current['background'])
if 'font' not in attribs:
widget.configure(font = self.current['font'])
widget.configure(font=self.current['font'])
elif 'activeforeground' in widget.keys():
# e.g. tk.Button, tk.Label, tk.Menu
if 'fg' not in attribs:
widget.configure(foreground = self.current['foreground'],
activeforeground = self.current['activeforeground'],
disabledforeground = self.current['disabledforeground'])
widget.configure(foreground=self.current['foreground'],
activeforeground=self.current['activeforeground'],
disabledforeground=self.current['disabledforeground'])
if 'bg' not in attribs:
widget.configure(background = self.current['background'],
activebackground = self.current['activebackground'])
if platform == 'darwin' and isinstance(widget, tk.Button):
widget.configure(highlightbackground = self.current['background'])
widget.configure(background=self.current['background'],
activebackground=self.current['activebackground'])
if sys.platform == 'darwin' and isinstance(widget, tk.Button):
widget.configure(highlightbackground=self.current['background'])
if 'font' not in attribs:
widget.configure(font = self.current['font'])
widget.configure(font=self.current['font'])
elif 'foreground' in widget.keys():
# e.g. ttk.Label
if 'fg' not in attribs:
widget.configure(foreground = self.current['foreground']),
widget.configure(foreground=self.current['foreground']),
if 'bg' not in attribs:
widget.configure(background = self.current['background'])
widget.configure(background=self.current['background'])
if 'font' not in attribs:
widget.configure(font = self.current['font'])
widget.configure(font=self.current['font'])
elif 'background' in widget.keys() or isinstance(widget, tk.Canvas):
# e.g. Frame, Canvas
if 'bg' not in attribs:
widget.configure(background = self.current['background'],
highlightbackground = self.current['disabledforeground'])
widget.configure(background=self.current['background'],
highlightbackground=self.current['disabledforeground'])
# Apply configured theme
def apply(self, root):
theme = config.get_int('theme')
@ -316,7 +321,7 @@ class _Theme(object):
# Apply colors
for widget in set(self.widgets):
if isinstance(widget, tk.Widget) and not widget.winfo_exists():
self.widgets.pop(widget) # has been destroyed
self.widgets.pop(widget) # has been destroyed
else:
self._update_widget(widget)
@ -334,58 +339,61 @@ class _Theme(object):
pair[theme].grid(**gridopts)
if self.active == theme:
return # Don't need to mess with the window manager
return # Don't need to mess with the window manager
else:
self.active = theme
if platform == 'darwin':
if sys.platform == 'darwin':
from AppKit import NSAppearance, NSApplication, NSMiniaturizableWindowMask, NSResizableWindowMask
root.update_idletasks() # need main window to be created
root.update_idletasks() # need main window to be created
appearance = NSAppearance.appearanceNamed_(theme and
'NSAppearanceNameDarkAqua' or
'NSAppearanceNameAqua')
for window in NSApplication.sharedApplication().windows():
window.setStyleMask_(window.styleMask() & ~(NSMiniaturizableWindowMask | NSResizableWindowMask)) # disable zoom
window.setStyleMask_(window.styleMask() & ~(
NSMiniaturizableWindowMask | NSResizableWindowMask)) # disable zoom
window.setAppearance_(appearance)
elif platform == 'win32':
elif sys.platform == 'win32':
GWL_STYLE = -16
WS_MAXIMIZEBOX = 0x00010000
WS_MAXIMIZEBOX = 0x00010000
# tk8.5.9/win/tkWinWm.c:342
GWL_EXSTYLE = -20
WS_EX_APPWINDOW = 0x00040000
WS_EX_LAYERED = 0x00080000
WS_EX_APPWINDOW = 0x00040000
WS_EX_LAYERED = 0x00080000
GetWindowLongW = ctypes.windll.user32.GetWindowLongW
SetWindowLongW = ctypes.windll.user32.SetWindowLongW
root.overrideredirect(theme and 1 or 0)
root.attributes("-transparentcolor", theme > 1 and 'grey4' or '')
root.withdraw()
root.update_idletasks() # Size and windows styles get recalculated here
root.update_idletasks() # Size and windows styles get recalculated here
hwnd = ctypes.windll.user32.GetParent(root.winfo_id())
SetWindowLongW(hwnd, GWL_STYLE, GetWindowLongW(hwnd, GWL_STYLE) & ~WS_MAXIMIZEBOX) # disable maximize
SetWindowLongW(hwnd, GWL_EXSTYLE, theme > 1 and WS_EX_APPWINDOW|WS_EX_LAYERED or WS_EX_APPWINDOW) # Add to taskbar
SetWindowLongW(hwnd, GWL_STYLE, GetWindowLongW(hwnd, GWL_STYLE) & ~WS_MAXIMIZEBOX) # disable maximize
SetWindowLongW(hwnd, GWL_EXSTYLE, theme > 1 and WS_EX_APPWINDOW |
WS_EX_LAYERED or WS_EX_APPWINDOW) # Add to taskbar
root.deiconify()
root.wait_visibility() # need main window to be displayed before returning
root.wait_visibility() # need main window to be displayed before returning
else:
root.withdraw()
root.update_idletasks() # Size gets recalculated here
root.update_idletasks() # Size gets recalculated here
if dpy:
xroot = Window()
parent = Window()
children = Window()
nchildren = c_uint()
XQueryTree(dpy, root.winfo_id(), byref(xroot), byref(parent), byref(children), byref(nchildren))
XChangeProperty(dpy, parent, motif_wm_hints_property, motif_wm_hints_property, 32, PropModeReplace, theme and motif_wm_hints_dark or motif_wm_hints_normal, 5)
XChangeProperty(dpy, parent, motif_wm_hints_property, motif_wm_hints_property, 32,
PropModeReplace, theme and motif_wm_hints_dark or motif_wm_hints_normal, 5)
XFlush(dpy)
else:
root.overrideredirect(theme and 1 or 0)
root.deiconify()
root.wait_visibility() # need main window to be displayed before returning
root.wait_visibility() # need main window to be displayed before returning
if not self.minwidth:
self.minwidth = root.winfo_width() # Minimum width = width on first creation
self.minwidth = root.winfo_width() # Minimum width = width on first creation
root.minsize(self.minwidth, -1)

View File

@ -1,14 +1,12 @@
import sys
import tkinter as tk
import webbrowser
from sys import platform
from tkinter import font as tkFont
from tkinter import ttk
if platform == 'win32':
if sys.platform == 'win32':
import subprocess
from winreg import (
HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, CloseKey, OpenKeyEx, QueryValueEx
)
from winreg import HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, CloseKey, OpenKeyEx, QueryValueEx
# A clickable ttk Label
#
@ -18,19 +16,22 @@ if platform == 'win32':
# popup_copy: Whether right-click on non-empty label text pops up a context menu with a 'Copy' option. Defaults to no context menu. If popup_copy is a function it will be called with the current label text and should return a boolean.
#
# May be imported by plugins
class HyperlinkLabel(platform == 'darwin' and tk.Label or ttk.Label, object):
class HyperlinkLabel(sys.platform == 'darwin' and tk.Label or ttk.Label, object):
def __init__(self, master=None, **kw):
self.url = 'url' in kw and kw.pop('url') or None
self.popup_copy = kw.pop('popup_copy', False)
self.underline = kw.pop('underline', None) # override ttk.Label's underline
self.underline = kw.pop('underline', None) # override ttk.Label's underline
self.foreground = kw.get('foreground') or 'blue'
self.disabledforeground = kw.pop('disabledforeground', ttk.Style().lookup('TLabel', 'foreground', ('disabled',))) # ttk.Label doesn't support disabledforeground option
self.disabledforeground = kw.pop('disabledforeground', ttk.Style().lookup(
'TLabel', 'foreground', ('disabled',))) # ttk.Label doesn't support disabledforeground option
if platform == 'darwin':
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
kw['anchor'] = kw.pop('anchor', tk.W) # like ttk.Label
tk.Label.__init__(self, master, **kw)
else:
ttk.Label.__init__(self, master, **kw)
@ -39,16 +40,16 @@ class HyperlinkLabel(platform == 'darwin' and tk.Label or ttk.Label, object):
self.menu = tk.Menu(None, 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(platform == 'darwin' and '<Button-2>' or '<Button-3>', self._contextmenu)
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('<Enter>', self._enter)
self.bind('<Leave>', self._leave)
# set up initial appearance
self.configure(state = kw.get('state', tk.NORMAL),
text = kw.get('text'),
font = kw.get('font', ttk.Style().lookup('TLabel', 'font')))
self.configure(state=kw.get('state', tk.NORMAL),
text=kw.get('text'),
font=kw.get('font', ttk.Style().lookup('TLabel', 'font')))
# Change cursor and appearance depending on state and text
def configure(self, cnf=None, **kw):
@ -70,17 +71,18 @@ class HyperlinkLabel(platform == 'darwin' and tk.Label or ttk.Label, object):
if 'font' in kw:
self.font_n = kw['font']
self.font_u = tkFont.Font(font = self.font_n)
self.font_u.configure(underline = True)
self.font_u = tkFont.Font(font=self.font_n)
self.font_u.configure(underline=True)
kw['font'] = self.underline is True and self.font_u or self.font_n
if 'cursor' not in kw:
if (kw['state'] if 'state' in kw else str(self['state'])) == tk.DISABLED:
kw['cursor'] = 'arrow' # System default
kw['cursor'] = 'arrow' # System default
elif self.url and (kw['text'] if 'text' in kw else self['text']):
kw['cursor'] = platform=='darwin' and 'pointinghand' or 'hand2'
kw['cursor'] = sys.platform == 'darwin' and 'pointinghand' or 'hand2'
else:
kw['cursor'] = (platform=='darwin' and 'notallowed') or (platform=='win32' and 'no') or 'circle'
kw['cursor'] = (sys.platform == 'darwin' and 'notallowed') or (
sys.platform == 'win32' and 'no') or 'circle'
super(HyperlinkLabel, self).configure(cnf, **kw)
@ -89,22 +91,22 @@ class HyperlinkLabel(platform == 'darwin' and tk.Label or ttk.Label, object):
def _enter(self, event):
if self.url and self.underline is not False and str(self['state']) != tk.DISABLED:
super(HyperlinkLabel, self).configure(font = self.font_u)
super(HyperlinkLabel, self).configure(font=self.font_u)
def _leave(self, event):
if not self.underline:
super(HyperlinkLabel, self).configure(font = self.font_n)
super(HyperlinkLabel, self).configure(font=self.font_n)
def _click(self, event):
if self.url and self['text'] and str(self['state']) != tk.DISABLED:
url = self.url(self['text']) if callable(self.url) else self.url
if url:
self._leave(event) # Remove underline before we change window to browser
self._leave(event) # Remove underline before we change window to browser
openurl(url)
def _contextmenu(self, event):
if self['text'] and (self.popup_copy(self['text']) if callable(self.popup_copy) else self.popup_copy):
self.menu.post(platform == 'darwin' and event.x_root + 1 or event.x_root, event.y_root)
self.menu.post(sys.platform == 'darwin' and event.x_root + 1 or event.x_root, event.y_root)
def copy(self):
self.clipboard_clear()
@ -112,13 +114,14 @@ class HyperlinkLabel(platform == 'darwin' and tk.Label or ttk.Label, object):
def openurl(url):
if platform == 'win32':
if sys.platform == 'win32':
# On Windows webbrowser.open calls os.startfile which calls ShellExecute which can't handle long arguments,
# so discover and launch the browser directly.
# https://blogs.msdn.microsoft.com/oldnewthing/20031210-00/?p=41553
try:
hkey = OpenKeyEx(HKEY_CURRENT_USER, r'Software\Microsoft\Windows\Shell\Associations\UrlAssociations\https\UserChoice')
hkey = OpenKeyEx(HKEY_CURRENT_USER,
r'Software\Microsoft\Windows\Shell\Associations\UrlAssociations\https\UserChoice')
(value, typ) = QueryValueEx(hkey, 'ProgId')
CloseKey(hkey)
if value in ['IE.HTTP', 'AppXq0fevzme2pys62n3e0fbqa7peapykr8v']:
@ -128,7 +131,7 @@ def openurl(url):
else:
cls = value
except:
cls = 'https'
cls = 'https'
if cls:
try: