1
0
mirror of https://github.com/EDCD/EDMarketConnector.git synced 2025-06-04 01:21:03 +03:00

theme.py: mypy pass

mypy/typeshed still doesn't like 'generic' tk arguments on things like
.configure(), so lots of `# type: ignore` used for those.
This commit is contained in:
Athanasius 2022-12-03 17:36:10 +00:00
parent cc8e0bfd27
commit 8728234625
No known key found for this signature in database
GPG Key ID: 772697E181BB2767

View File

@ -11,6 +11,7 @@ import tkinter as tk
from os.path import join from os.path import join
from tkinter import font as tk_font from tkinter import font as tk_font
from tkinter import ttk from tkinter import ttk
from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Set, Tuple
from config import config from config import config
from EDMCLogging import get_main_logger from EDMCLogging import get_main_logger
@ -18,6 +19,9 @@ from ttkHyperlinkLabel import HyperlinkLabel
logger = get_main_logger() logger = get_main_logger()
if TYPE_CHECKING:
def _(x: str) -> str: ...
if __debug__: if __debug__:
from traceback import print_exc from traceback import print_exc
@ -29,7 +33,7 @@ if sys.platform == 'win32':
import ctypes import ctypes
from ctypes.wintypes import DWORD, LPCVOID, LPCWSTR from ctypes.wintypes import DWORD, LPCVOID, LPCWSTR
AddFontResourceEx = ctypes.windll.gdi32.AddFontResourceExW AddFontResourceEx = ctypes.windll.gdi32.AddFontResourceExW
AddFontResourceEx.restypes = [LPCWSTR, DWORD, LPCVOID] AddFontResourceEx.restypes = [LPCWSTR, DWORD, LPCVOID] # type: ignore
FR_PRIVATE = 0x10 FR_PRIVATE = 0x10
FR_NOT_ENUM = 0x20 FR_NOT_ENUM = 0x20
AddFontResourceEx(join(config.respath, u'EUROCAPS.TTF'), FR_PRIVATE, 0) AddFontResourceEx(join(config.respath, u'EUROCAPS.TTF'), FR_PRIVATE, 0)
@ -119,17 +123,17 @@ elif sys.platform == 'linux':
class _Theme(object): class _Theme(object):
def __init__(self): def __init__(self) -> None:
self.active = None # Starts out with no theme self.active = None # Starts out with no theme
self.minwidth = None self.minwidth: Optional[int] = None
self.widgets = {} self.widgets: Dict[tk.Widget, Set] = {}
self.widgets_pair = [] self.widgets_pair: List = []
self.defaults = {} self.defaults: Dict = {}
self.current = {} self.current: Dict = {}
self.default_ui_scale = None # None == not yet known self.default_ui_scale = None # None == not yet known
self.startup_ui_scale = None self.startup_ui_scale = None
def register(self, widget): # noqa: CCR001, C901 def register(self, widget: tk.Widget) -> None: # noqa: CCR001, C901
# Note widget and children for later application of a theme. Note if # Note widget and children for later application of a theme. Note if
# the widget has explicit fg or bg attributes. # the widget has explicit fg or bg attributes.
assert isinstance(widget, tk.Widget) or isinstance(widget, tk.BitmapImage), widget assert isinstance(widget, tk.Widget) or isinstance(widget, tk.BitmapImage), widget
@ -193,15 +197,17 @@ class _Theme(object):
for child in widget.winfo_children(): for child in widget.winfo_children():
self.register(child) self.register(child)
def register_alternate(self, pair, gridopts): def register_alternate(self, pair: Tuple, gridopts: Dict) -> None:
self.widgets_pair.append((pair, gridopts)) self.widgets_pair.append((pair, gridopts))
def button_bind(self, widget, command, image=None): def button_bind(
self, widget: tk.Widget, command: Callable, image: Optional[tk.BitmapImage] = None
) -> None:
widget.bind('<Button-1>', command) widget.bind('<Button-1>', command)
widget.bind('<Enter>', lambda e: self._enter(e, image)) widget.bind('<Enter>', lambda e: self._enter(e, image))
widget.bind('<Leave>', lambda e: self._leave(e, image)) widget.bind('<Leave>', lambda e: self._leave(e, image))
def _enter(self, event, image): def _enter(self, event: tk.Event, image: Optional[tk.BitmapImage]) -> None:
widget = event.widget widget = event.widget
if widget and widget['state'] != tk.DISABLED: if widget and widget['state'] != tk.DISABLED:
try: try:
@ -218,7 +224,7 @@ class _Theme(object):
except Exception: except Exception:
logger.exception(f'Failure configuring image: {image=}') logger.exception(f'Failure configuring image: {image=}')
def _leave(self, event, image): def _leave(self, event: tk.Event, image: Optional[tk.BitmapImage]) -> None:
widget = event.widget widget = event.widget
if widget and widget['state'] != tk.DISABLED: if widget and widget['state'] != tk.DISABLED:
try: try:
@ -235,7 +241,7 @@ class _Theme(object):
logger.exception(f'Failure configuring image: {image=}') logger.exception(f'Failure configuring image: {image=}')
# Set up colors # Set up colors
def _colors(self, root, theme): def _colors(self, root: tk.Tk, theme: int) -> None:
style = ttk.Style() style = ttk.Style()
if sys.platform == 'linux': if sys.platform == 'linux':
style.theme_use('clam') style.theme_use('clam')
@ -281,10 +287,11 @@ class _Theme(object):
# Apply current theme to a widget and its children, and register it for future updates # Apply current theme to a widget and its children, and register it for future updates
def update(self, widget): def update(self, widget: tk.Widget) -> None:
assert isinstance(widget, tk.Widget) or isinstance(widget, tk.BitmapImage), widget assert isinstance(widget, tk.Widget) or isinstance(widget, tk.BitmapImage), widget
if not self.current: if not self.current:
return # No need to call this for widgets created in plugin_app() return # No need to call this for widgets created in plugin_app()
self.register(widget) self.register(widget)
self._update_widget(widget) self._update_widget(widget)
if isinstance(widget, tk.Frame) or isinstance(widget, ttk.Frame): if isinstance(widget, tk.Frame) or isinstance(widget, ttk.Frame):
@ -292,67 +299,67 @@ class _Theme(object):
self._update_widget(child) self._update_widget(child)
# Apply current theme to a single widget # Apply current theme to a single widget
def _update_widget(self, widget): # noqa: CCR001, C901 def _update_widget(self, widget: tk.Widget) -> None: # noqa: CCR001, C901
if widget not in self.widgets: if widget not in self.widgets:
assert_str = f'{widget.winfo_class()} {widget} "{"text" in widget.keys() and widget["text"]}"' assert_str = f'{widget.winfo_class()} {widget} "{"text" in widget.keys() and widget["text"]}"'
raise AssertionError(assert_str) raise AssertionError(assert_str)
attribs = self.widgets.get(widget, []) attribs: Set = self.widgets.get(widget, set())
try: try:
if isinstance(widget, tk.BitmapImage): if isinstance(widget, tk.BitmapImage):
# not a widget # not a widget
if 'fg' not in attribs: if 'fg' not in attribs:
widget.configure(foreground=self.current['foreground']), widget.configure(foreground=self.current['foreground']), # type: ignore
if 'bg' not in attribs: if 'bg' not in attribs:
widget.configure(background=self.current['background']) widget.configure(background=self.current['background']) # type: ignore
elif 'cursor' in widget.keys() and str(widget['cursor']) not in ['', 'arrow']: elif 'cursor' in widget.keys() and str(widget['cursor']) not in ['', 'arrow']:
# Hack - highlight widgets like HyperlinkLabel with a non-default cursor # Hack - highlight widgets like HyperlinkLabel with a non-default cursor
if 'fg' not in attribs: if 'fg' not in attribs:
widget.configure(foreground=self.current['highlight']), widget.configure(foreground=self.current['highlight']), # type: ignore
if 'insertbackground' in widget.keys(): # tk.Entry if 'insertbackground' in widget.keys(): # tk.Entry
widget.configure(insertbackground=self.current['foreground']), widget.configure(insertbackground=self.current['foreground']), # type: ignore
if 'bg' not in attribs: if 'bg' not in attribs:
widget.configure(background=self.current['background']) widget.configure(background=self.current['background']) # type: ignore
if 'highlightbackground' in widget.keys(): # tk.Entry if 'highlightbackground' in widget.keys(): # tk.Entry
widget.configure(highlightbackground=self.current['background']) widget.configure(highlightbackground=self.current['background']) # type: ignore
if 'font' not in attribs: if 'font' not in attribs:
widget.configure(font=self.current['font']) widget.configure(font=self.current['font']) # type: ignore
elif 'activeforeground' in widget.keys(): elif 'activeforeground' in widget.keys():
# e.g. tk.Button, tk.Label, tk.Menu # e.g. tk.Button, tk.Label, tk.Menu
if 'fg' not in attribs: if 'fg' not in attribs:
widget.configure(foreground=self.current['foreground'], widget.configure(foreground=self.current['foreground'], # type: ignore
activeforeground=self.current['activeforeground'], activeforeground=self.current['activeforeground'],
disabledforeground=self.current['disabledforeground']) disabledforeground=self.current['disabledforeground'])
if 'bg' not in attribs: if 'bg' not in attribs:
widget.configure(background=self.current['background'], widget.configure(background=self.current['background'], # type: ignore
activebackground=self.current['activebackground']) activebackground=self.current['activebackground'])
if sys.platform == 'darwin' and isinstance(widget, tk.Button): if sys.platform == 'darwin' and isinstance(widget, tk.Button):
widget.configure(highlightbackground=self.current['background']) widget.configure(highlightbackground=self.current['background']) # type: ignore
if 'font' not in attribs: if 'font' not in attribs:
widget.configure(font=self.current['font']) widget.configure(font=self.current['font']) # type: ignore
elif 'foreground' in widget.keys(): elif 'foreground' in widget.keys():
# e.g. ttk.Label # e.g. ttk.Label
if 'fg' not in attribs: if 'fg' not in attribs:
widget.configure(foreground=self.current['foreground']), widget.configure(foreground=self.current['foreground']), # type: ignore
if 'bg' not in attribs: if 'bg' not in attribs:
widget.configure(background=self.current['background']) widget.configure(background=self.current['background']) # type: ignore
if 'font' not in attribs: if 'font' not in attribs:
widget.configure(font=self.current['font']) widget.configure(font=self.current['font']) # type: ignore
elif 'background' in widget.keys() or isinstance(widget, tk.Canvas): elif 'background' in widget.keys() or isinstance(widget, tk.Canvas):
# e.g. Frame, Canvas # e.g. Frame, Canvas
if 'bg' not in attribs: if 'bg' not in attribs:
widget.configure(background=self.current['background'], widget.configure(background=self.current['background'], # type: ignore
highlightbackground=self.current['disabledforeground']) highlightbackground=self.current['disabledforeground'])
except Exception: except Exception:
@ -360,7 +367,7 @@ class _Theme(object):
# Apply configured theme # Apply configured theme
def apply(self, root): # noqa: CCR001 def apply(self, root: tk.Tk) -> None: # noqa: CCR001
theme = config.get_int('theme') theme = config.get_int('theme')
self._colors(root, theme) self._colors(root, theme)
@ -411,7 +418,7 @@ class _Theme(object):
GetWindowLongW = ctypes.windll.user32.GetWindowLongW # noqa: N806 # ctypes GetWindowLongW = ctypes.windll.user32.GetWindowLongW # noqa: N806 # ctypes
SetWindowLongW = ctypes.windll.user32.SetWindowLongW # noqa: N806 # ctypes SetWindowLongW = ctypes.windll.user32.SetWindowLongW # noqa: N806 # ctypes
root.overrideredirect(theme and 1 or 0) root.overrideredirect(theme and True or False)
root.attributes("-transparentcolor", theme > 1 and 'grey4' or '') root.attributes("-transparentcolor", theme > 1 and 'grey4' or '')
root.withdraw() root.withdraw()
root.update_idletasks() # Size and windows styles get recalculated here root.update_idletasks() # Size and windows styles get recalculated here