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:
parent
cc8e0bfd27
commit
8728234625
73
theme.py
73
theme.py
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user