diff --git a/dzgui/api/acf.py b/dzgui/api/acf.py index ce243a4..4b1dc71 100644 --- a/dzgui/api/acf.py +++ b/dzgui/api/acf.py @@ -111,7 +111,8 @@ class WorkshopACF(ACF): def __init__(self, file: Path) -> None: super().__init__(file) - def unpack(self, d: dict, lines: list[Any] = []) -> str: + def unpack(self, d: dict[str, Any], lines: list[Any] = []) -> str: + self.indent: int t1 = "AppWorkshop" t2 = ("WorkshopItemsInstalled", "WorkshopItemDetails") for k, v in d.items(): diff --git a/dzgui/api/mods.py b/dzgui/api/mods.py index cbb55ad..722e3dc 100644 --- a/dzgui/api/mods.py +++ b/dzgui/api/mods.py @@ -106,10 +106,6 @@ def get_delimited_mods(steam_path: Path) -> list[Any]: return parse_mods(mods) -def get_missing_mods(local: list, remote: list) -> list: - return [mod for mod in remote if mod not in local] - - def _hash(uid: str, use_custom: bool = False) -> str: md5 = hashlib.md5() md5.update(uid.encode("ascii")) @@ -144,7 +140,7 @@ def remove_stale_signatures( def find_stale_mods(config: Path) -> list[int]: - def push_record(rec: str) -> list: + def push_record(rec: str) -> list[int]: record = fqip_to_record(rec) if record is None: return [] diff --git a/dzgui/api/servers.py b/dzgui/api/servers.py index 8c331e4..6ac446b 100644 --- a/dzgui/api/servers.py +++ b/dzgui/api/servers.py @@ -111,7 +111,7 @@ def get_netmask() -> str: # TODO: rename -def test_ip(suffix: int, port: int, event: threading.Event) -> dict | None: +def test_ip(suffix: int, port: int, event: threading.Event) -> dict[str, Any] | None: if event.is_set(): return None netmask = get_netmask() @@ -139,7 +139,9 @@ def sanitize(name: str) -> str: return name -def parse_json(json: list) -> list: +def parse_json( + json: list[dict[str, Any]], +) -> list[tuple[str, str, str, str, int, int, int, str, int, int, str, bool]]: """ Server metadata is underspecified and server operators tend to insert random garbage in the headers. In case @@ -507,14 +509,14 @@ def record_to_fqip(record: Record) -> str: return f"{record.ip}:{record.gameport}:{record.qport}" -def response_to_fqip(res: dict) -> str: +def response_to_fqip(res: dict[str, Any]) -> str: ip = res["addr"].split(":")[0] gameport = res["gameport"] qport = res["addr"].split(":")[1] return f"{ip}:{gameport}:{qport}" -def response_to_record(res: dict) -> Record: +def response_to_record(res: dict[str, Any]) -> Record: ip = res["addr"].split(":")[0] gameport = res["gameport"] qport = res["addr"].split(":")[1] diff --git a/dzgui/config/xdg.py b/dzgui/config/xdg.py index b72ea7b..532c73f 100644 --- a/dzgui/config/xdg.py +++ b/dzgui/config/xdg.py @@ -51,7 +51,7 @@ def is_writeable(path_str: str) -> bool: return True -def get_xdg_paths() -> dict: +def get_xdg_paths() -> dict[str, Path]: home = Path.home() xdg_paths = { "XDG_CONFIG_HOME": home / ".config", @@ -73,7 +73,7 @@ def get_xdg_paths() -> dict: return resolved_paths -def parse_filepaths(xdg: dict) -> Xdg: +def parse_filepaths(xdg: dict[str, Path]) -> Xdg: config = xdg["XDG_CONFIG_HOME"] state = xdg["XDG_STATE_HOME"] share = xdg["XDG_DATA_HOME"] diff --git a/dzgui/controllers/mc.py b/dzgui/controllers/mc.py index 5c5a532..c6b2280 100644 --- a/dzgui/controllers/mc.py +++ b/dzgui/controllers/mc.py @@ -77,7 +77,7 @@ class AppNavigation: class Controller(GObject.GObject): def __init__(self) -> None: - self.dist_cache: dict[str, "Haversine"] = {} + self.dist_cache: dict[str, "Haversine" | None] = {} self.mediator = AppNavigation() self.prefs: UserPrefs @@ -274,7 +274,7 @@ class Controller(GObject.GObject): except Exception as e: dialog = ExceptionDialog(self, str(e)) dialog.show_all() - dialog.run() + dialog.run() # type: ignore[no-untyped-call] def select_colorized(self) -> None: mod_man = self.mediator.modtreeview.get_mod_man() @@ -308,7 +308,7 @@ class Controller(GObject.GObject): except Exception as e: dialog = ExceptionDialog(self, str(e)) dialog.show_all() - dialog.run() + dialog.run() # type: ignore[no-untyped-call] def update_steam_api_key(self, text: str) -> None: self.config_man.update_steam_api_key(text) @@ -355,17 +355,17 @@ class Controller(GObject.GObject): filter_man.set_filter(filt, state) self.mediator.filters.set_filters(filters) - def get_dist_cache(self) -> dict[str, "Haversine"]: + def get_dist_cache(self) -> dict[str, Union["Haversine", None]]: return self.dist_cache - def set_dist_cache(self, addr: str, haversine: "Haversine") -> None: + def set_dist_cache(self, addr: str, haversine: Union["Haversine", None]) -> None: self.dist_cache[addr] = haversine def get_map_store(self) -> Gtk.ListStore: filter_man = self.get_filter_man() return filter_man.get_map_store() - def get_enabled_filters(self) -> dict: + def get_enabled_filters(self) -> dict[str, bool]: filter_man = self.get_filter_man() return filter_man.get_filters() diff --git a/dzgui/managers/config.py b/dzgui/managers/config.py index 7490b15..de5d12b 100644 --- a/dzgui/managers/config.py +++ b/dzgui/managers/config.py @@ -162,7 +162,7 @@ class ConfigManager: trace = traceback.format_exc() dialog = ExceptionDialog(self.controller, trace) dialog.show_all() - dialog.run() + dialog.run() # type: ignore[no-untyped-call] def save_res_and_quit(self, tv: "ServerTreeView", window: "OuterWindow") -> None: columns = tv.get_columns() diff --git a/dzgui/managers/connection.py b/dzgui/managers/connection.py index d543ce3..a57a421 100644 --- a/dzgui/managers/connection.py +++ b/dzgui/managers/connection.py @@ -291,22 +291,22 @@ class ConnectionManager: def _present_modlist_dialog(self, mods: list[list[str]]) -> None: dialog = ServerModDialog(self.controller, mods) - dialog.run() + dialog.run() # type: ignore[no-untyped-call] def _present_details_dialog(self, details: Servers.Details) -> None: dialog = ServerDetailsDialog(self.controller, details) - dialog.run() + dialog.run() # type: ignore[no-untyped-call] def _workshop_timeout(self, msg: str) -> None: dialog = ExceptionDialog(self.controller, msg) dialog.show_all() - dialog.run() + dialog.run() # type: ignore[no-untyped-call] def _server_timeout(self) -> None: dialog = ExceptionDialog(self.controller, server_timeout) dialog.set_details_buffer(kb.DZG_006) dialog.show_all() - dialog.run() + dialog.run() # type: ignore[no-untyped-call] def get_debug_args(self) -> str: addr = f"{self.record.ip}:{self.record.gameport}" diff --git a/dzgui/managers/contextmenu.py b/dzgui/managers/contextmenu.py index 016ce7b..03e6350 100644 --- a/dzgui/managers/contextmenu.py +++ b/dzgui/managers/contextmenu.py @@ -44,7 +44,7 @@ class ContextMenuManager: # UNTHREADED case ContextMenu.ADD_NOTE: dialog = NoteDialog(self.controller) - dialog.run() + dialog.run() # type: ignore[no-untyped-call] case ContextMenu.COPY_SERVER_IP: self.copy_server_ip() case ContextMenu.COPY_SERVER_NAME: diff --git a/dzgui/managers/filter.py b/dzgui/managers/filter.py index ffb41ef..5204d35 100644 --- a/dzgui/managers/filter.py +++ b/dzgui/managers/filter.py @@ -51,7 +51,6 @@ class FilterManager: self.active_map = (0, all_maps) self.prior_map = all_maps - self.filters: list self.enabled_filters = dict(self.default_filters) def get_tooltips(self) -> dict[str, str]: @@ -75,11 +74,11 @@ class FilterManager: def set_active_keyword(self, word: str) -> None: self.active_keyword = word - def get_default_filters(self) -> dict: + def get_default_filters(self) -> dict[str, bool]: """Deep copy of defaults""" return dict(self.default_filters) - def get_filters(self) -> dict: + def get_filters(self) -> dict[str, bool]: return self.enabled_filters def set_filter(self, label: str, state: bool) -> None: diff --git a/dzgui/managers/mods.py b/dzgui/managers/mods.py index 421a95f..3bb2ace 100644 --- a/dzgui/managers/mods.py +++ b/dzgui/managers/mods.py @@ -221,7 +221,7 @@ class ModManager: total_size += mod[3] return total_mods, total_size - def _on_stale_mods_found(self, stale_mods: list) -> None: + def _on_stale_mods_found(self, stale_mods: list[int]) -> None: """Manipulates attached ListStore in the main event loop""" model = self.treeview.get_model() if model is None: diff --git a/dzgui/managers/offline.py b/dzgui/managers/offline.py index 645a682..d16acaa 100644 --- a/dzgui/managers/offline.py +++ b/dzgui/managers/offline.py @@ -52,7 +52,7 @@ class OfflineManager: is_valid = is_mission(folder) self.emitter.emit("custom_mission_loaded", str(folder), is_valid) - def find_custom_mods(self, callback: Callable) -> None: + def find_custom_mods(self, callback: Callable[[], None]) -> None: folder = self.open_folderpicker(dialogs.custom_mod_dialog) window = self.controller.get_window() window.set_sensitive(False) diff --git a/dzgui/managers/threading.py b/dzgui/managers/threading.py index 43b89aa..0344103 100644 --- a/dzgui/managers/threading.py +++ b/dzgui/managers/threading.py @@ -22,8 +22,8 @@ logger = logging.getLogger(APP_NAME) def call_on_thread( dialog_str: str, show_dialog: bool = True, show_cancel: bool = False -) -> Callable: - def decorator(func: Callable) -> Callable: +) -> Callable[..., Any]: + def decorator(func: Callable[..., Any]) -> Callable[..., Any]: @wraps(func) def wrapper(*args: Any, **kwargs: Any) -> None: self = args[0] @@ -42,7 +42,7 @@ def call_on_thread( class StoredFunc: - def __init__(self, func: Callable, *args: Any, **kwargs: Any) -> None: + def __init__(self, func: Callable[..., Any], *args: Any, **kwargs: Any) -> None: sig = inspect.signature(func) self.func = func self.bindings = sig.bind(*args, **kwargs) diff --git a/dzgui/managers/update.py b/dzgui/managers/update.py index f519c4f..f08b279 100644 --- a/dzgui/managers/update.py +++ b/dzgui/managers/update.py @@ -55,9 +55,9 @@ class UpdateManager: def _on_update_success(self) -> None: msg = dialogs.update_success dialog = QuitDialog(self.controller, msg) - dialog.run() + dialog.run() # type: ignore[no-untyped-call] def _on_update_failure(self, msg: str) -> None: dialog = ExceptionDialog(self.controller, msg) dialog.show_all() - dialog.run() + dialog.run() # type: ignore[no-untyped-call] diff --git a/dzgui/model/model_factory.py b/dzgui/model/model_factory.py index dc90f86..d2fb0a5 100644 --- a/dzgui/model/model_factory.py +++ b/dzgui/model/model_factory.py @@ -11,7 +11,7 @@ from gi.repository.Gtk import ListStore, TreeIter from gi.repository import GObject GTYPE_TO_PYTHON = { - GObject.type_from_name(GObject.type_name(ptype)): ptype + GObject.type_from_name(GObject.type_name(ptype)): ptype # type: ignore[no-untyped-call] for ptype in (int, float, str, bool, object) } diff --git a/dzgui/model/proxy_model.py b/dzgui/model/proxy_model.py index 0ec9a12..5cb3d97 100644 --- a/dzgui/model/proxy_model.py +++ b/dzgui/model/proxy_model.py @@ -1,5 +1,6 @@ import re +from dataclasses import dataclass from typing import Any, TYPE_CHECKING, Union from dzgui.const.enum import FilterMode @@ -17,6 +18,22 @@ DAY_REG = r"([0][6-9]|[1][0-7])" NIGHT_REG = r"([0][0-5]|[1][8-9]|[2][0-3])" +@dataclass(slots=True, frozen=True) +class ServerRow: + name: str + mapname: str + view: str + time: str + players: int + max_players: int + queue: int + ip: str + qport: int + ping: str + provider: str + modded: bool + + class ProxyModelManager: """ Manages access to cached FastInsertListStore resources and @@ -31,7 +48,7 @@ class ProxyModelManager: def __init__(self, filter_man: "FilterManager") -> None: self.filter_cache: dict[ - tuple[str], + tuple[str, ...], list[tuple[str, str, str, str, int, int, int, str, int, int, str, bool]], ] = {} @@ -52,9 +69,6 @@ class ProxyModelManager: return False return True - def append_row(self, row: list) -> None: - self.proxy_model.append(row) - def append_row_to_control( self, row: tuple[str, str, str, str, int, int, int, str, int, int, str, bool] ) -> None: @@ -68,7 +82,6 @@ class ProxyModelManager: self.proxy_model[treeiter][4] = playercount.players self.proxy_model[treeiter][6] = playercount.queue - # TODO: use dataclass for record rows def append_row_to_history( self, history: tuple[str, str, str, str, int, int, int, str, int, int, str, bool], @@ -248,7 +261,7 @@ class ProxyModelManager: rows = [row for row in rows if row[11]] return rows - def filter_toggle_on(self, filters: tuple[str]) -> list: + def filter_toggle_on(self, filters: tuple[str, ...]) -> list: """Effectively applies all filters""" self.set_filtered(self.control_model) self.set_filtered(self.filter_map(filters)) @@ -259,7 +272,7 @@ class ProxyModelManager: self.set_filtered(self.filter_toggle_off(filters, f)) return self.filtered - def set_cache(self, filters: tuple[str], filtered_rows: list[tuple]) -> None: + def set_cache(self, filters: tuple[str, ...], filtered_rows: list[tuple]) -> None: self.filter_cache[filters] = filtered_rows def set_filtered(self, rows: list | None) -> None: diff --git a/dzgui/model/servers.py b/dzgui/model/servers.py index 93f2fce..dd4cf9b 100644 --- a/dzgui/model/servers.py +++ b/dzgui/model/servers.py @@ -432,7 +432,7 @@ class ServerModelManager: if show_dialog: dialog = ExceptionDialog(self.controller, dialogs.load_error_lan) dialog.show_all() - dialog.run() + dialog.run() # type: ignore[no-untyped-call] def _cleanup_on_failure(self, show_dialog: bool = True) -> None: # TODO: disable map, keyword, and filter widgets if model is None @@ -451,14 +451,14 @@ class ServerModelManager: dialog = ExceptionDialog(self.controller, server_timeout) dialog.set_details_buffer(kb.DZG_006) dialog.show_all() - dialog.run() + dialog.run() # type: ignore[no-untyped-call] def _push_data(self, data: list[Any]) -> None: self._get_proxy_man().push(data) self._sort_unique_maps(data) self.thread_man.set_cleanup_func(StoredFunc(self._cleanup_on_success)) - def _sort_unique_maps(self, data: list) -> None: + def _sort_unique_maps(self, data: list[Any]) -> None: u_maps = set([row[1] for row in data]) self._set_new_maps(sorted(u_maps)) diff --git a/dzgui/util/dist.py b/dzgui/util/dist.py index 86f9aa1..94ec811 100644 --- a/dzgui/util/dist.py +++ b/dzgui/util/dist.py @@ -54,7 +54,7 @@ class CalcDist: self, addr: str, enum: "ServerTab", - result_queue: "Queue", + result_queue: "Queue[tuple[str, Haversine | None, ServerTab]]", controller: "Controller", cache: dict[str, Haversine], ) -> None: @@ -67,7 +67,7 @@ class CalcDist: self.ip = self.addr dist = self.compare(self.ip) - self.result_queue.put([self.addr, dist, self.enum]) + self.result_queue.put((self.addr, dist, self.enum)) def compare(self, remote_ip: str) -> Haversine | None: prefs = self.controller.get_prefs() diff --git a/dzgui/util/flatpak.py b/dzgui/util/flatpak.py index 7372fd6..d86663d 100644 --- a/dzgui/util/flatpak.py +++ b/dzgui/util/flatpak.py @@ -1,12 +1,16 @@ import logging import subprocess +from typing import TYPE_CHECKING from dzgui.const.constants import APP_NAME, FLATPAK_APPID, GTK_APP_ID, TMP_PATH logger = logging.getLogger(APP_NAME) +if TYPE_CHECKING: + from pathlib import Path -def apply_overrides(xdg: dict[str, str]) -> None: + +def apply_overrides(xdg: dict[str, "Path"]) -> None: try: # NOTE: D-Bus registration required for sandbox args = [ @@ -19,7 +23,7 @@ def apply_overrides(xdg: dict[str, str]) -> None: subprocess.run([*args], capture_output=True) paths: list[str] = [] - paths.extend([*xdg.values()]) + paths.extend([str(path) for path in xdg.values()]) paths.append(TMP_PATH) for path in paths: diff --git a/dzgui/util/format.py b/dzgui/util/format.py index 3d75e30..38894f8 100644 --- a/dzgui/util/format.py +++ b/dzgui/util/format.py @@ -1,4 +1,5 @@ import re +from typing import Any from dzgui.util.localize import number from dzgui.strings.generic import selectable_tree @@ -60,7 +61,7 @@ def format_server_mods(mods: int) -> str: return f"Found {mods:n} {plural}. {workshop}" -def format_player_count(model: Gtk.TreeModel | None, control: list) -> str: +def format_player_count(model: Gtk.TreeModel | None, control: list[Any]) -> str: players = 0 hits: int status: str diff --git a/dzgui/util/ip.py b/dzgui/util/ip.py index d9a2c12..168e036 100644 --- a/dzgui/util/ip.py +++ b/dzgui/util/ip.py @@ -31,7 +31,7 @@ class ReservedIPError(Exception): pass -def ip2list(ip: str) -> list: +def ip2list(ip: str) -> list[int]: return [int(digit) for digit in ip.split(".")] diff --git a/dzgui/util/strings.py b/dzgui/util/strings.py index 095d49a..1043f01 100644 --- a/dzgui/util/strings.py +++ b/dzgui/util/strings.py @@ -118,6 +118,7 @@ servers = { "0": "toggle filter 10", "Minus": "toggle filter 11", "Backslash": "toggle filter 12", + "Apostrophe": "toggle filter 13", } vim = { "j": "Move down a row/scroll up", diff --git a/dzgui/views/components/buttons.py b/dzgui/views/components/buttons.py index cbd7c7e..2fb62eb 100644 --- a/dzgui/views/components/buttons.py +++ b/dzgui/views/components/buttons.py @@ -1,10 +1,8 @@ -from typing import Callable, Literal, Self, TYPE_CHECKING, Union +from typing import Callable, Literal, Self, TYPE_CHECKING from dzgui.util.clip import copy_clipboard -from dzgui.util.format import pluralize from dzgui.strings import buttons from dzgui.util.strings import ( - alert_button_tooltip, atomic_buttons, ) from dzgui.const.icons import ( @@ -64,7 +62,7 @@ class IconButton(Gtk.Button): self.icon = Icon(icon, margin_start=margin_start, margin_end=margin_end) self.set_image(self.icon) self.set_image_position(position) - self.set_focus_on_click(False) + self.set_focus_on_click(False) # type: ignore[no-untyped-call] self.set_always_show_image(True) def swap_icon(self, icon: str) -> None: @@ -108,7 +106,7 @@ class ClipboardButton(IconTextButton): self.set_tooltip_text(buttons.clipboard_button) - def _on_button_clicked(self, button: Self, func: Callable) -> None: + def _on_button_clicked(self, button: Self, func: Callable[[], str]) -> None: def revert() -> Literal[False]: self.revert_icon() return False @@ -119,10 +117,9 @@ class ClipboardButton(IconTextButton): GLib.timeout_add(600, revert) -# TODO: determine when controller would be passed to this button or drop class CopyIpButton(ClipboardButton): - def __init__(self, controller: Union["Controller", None], func: Callable) -> None: - super().__init__(controller, func) + def __init__(self, func: Callable[[], str]) -> None: + super().__init__(func) self.set_tooltip_text(buttons.clipboard_ip_button) @@ -249,30 +246,6 @@ class AddButton(IconTextButton): self.set_tooltip_text(connect_panel.add_tooltip) -# TODO: drop -class LoggerAlertsButton(IconTextButton): - def __init__(self, warnings: int, errors: int) -> None: - warnings_text = "" - errors_text = "" - separator = "" - if warnings > 0: - warnings_plural = pluralize("warnings", warnings) - warnings_text = f"{warnings} {warnings_plural}" - if errors > 0: - errors_plural = pluralize("errors", errors) - errors_text = f" {errors} {errors_plural}" - if warnings + errors > 1: - separator = "," - concat = warnings_text + separator + errors_text - super().__init__( - icon=ICON_WARNING, - label=f"Loaded with{concat}", - ) - self.set_halign(Gtk.Align.END) - self.set_hexpand(True) - self.set_tooltip_text(alert_button_tooltip) - - class SpinnerButton(Gtk.Button): def __init__(self, label: str) -> None: super().__init__() diff --git a/dzgui/views/components/connect_panel.py b/dzgui/views/components/connect_panel.py index dce831a..7ea9a34 100644 --- a/dzgui/views/components/connect_panel.py +++ b/dzgui/views/components/connect_panel.py @@ -189,7 +189,7 @@ class FavPanel(Gtk.Frame): self.fav_button = SteamConnectButton() self.fav_button.connect("clicked", self._on_connect_clicked) - self.copy_button = CopyIpButton(self.controller, self.get_fav_ip) + self.copy_button = CopyIpButton(self.get_fav_ip) self.unset_button = CloseButton(unset_button_label) self.unset_button.connect("clicked", self._on_unset_clicked) diff --git a/dzgui/views/components/entry.py b/dzgui/views/components/entry.py index 5a69e10..db3fb94 100644 --- a/dzgui/views/components/entry.py +++ b/dzgui/views/components/entry.py @@ -39,7 +39,7 @@ class ValidatedEntry(Gtk.Entry): def __init__( self, controller: "Controller", - func: Callable, + func: Callable[[str], bool], placeholder_text: str = "", tooltip_text: str = "", ) -> None: @@ -47,7 +47,6 @@ class ValidatedEntry(Gtk.Entry): hexpand=True, placeholder_text=placeholder_text, tooltip_text=tooltip_text ) - self.func: Callable self.set_validation_func(func) self.controller = controller @@ -73,7 +72,7 @@ class ValidatedEntry(Gtk.Entry): self.emit("string_validated", False) remove_class(self, self.classname) - def set_validation_func(self, func: Callable) -> None: + def set_validation_func(self, func: Callable[[str], bool]) -> None: self.func = func def insert_icon(self) -> None: @@ -142,7 +141,7 @@ class APIEntry(Gtk.Box): def __init__(self) -> None: super().__init__(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) - self.func: Callable | None = None + self.func: Callable[[str], None] | None = None self.entry = Gtk.Entry( width_chars=60, hexpand=True, placeholder_text=api_placeholder @@ -221,7 +220,7 @@ class APIEntry(Gtk.Box): res = self.func(text) return res - def set_validation_func(self, func: Callable | None) -> None: + def set_validation_func(self, func: Callable[[str], None] | None) -> None: self.func = func def disable_button(self) -> None: diff --git a/dzgui/views/components/filter_panel.py b/dzgui/views/components/filter_panel.py index 952defe..b3bc584 100644 --- a/dzgui/views/components/filter_panel.py +++ b/dzgui/views/components/filter_panel.py @@ -31,7 +31,7 @@ class ButtonGrid(Gtk.Grid): def __init__( self, controller: "Controller", - defaults: dict[str, str], + defaults: dict[str, bool], tooltips: dict[str, str], ) -> None: super().__init__( @@ -97,7 +97,7 @@ class ButtonGrid(Gtk.Grid): logger.info(f"User toggled button '{label}' to {state}") self.emitter.emit("check_toggled", label, state) - def get_checkboxes(self) -> list: + def get_checkboxes(self) -> list[Gtk.CheckButton]: return self.checks @@ -306,6 +306,7 @@ class FilterPanel(Gtk.Box): Gdk.KEY_0: 9, Gdk.KEY_minus: 10, Gdk.KEY_backslash: 11, + Gdk.KEY_apostrophe: 12, } if keyval not in mappings: return False diff --git a/dzgui/views/components/statusbar.py b/dzgui/views/components/statusbar.py index 6726021..6f9489c 100644 --- a/dzgui/views/components/statusbar.py +++ b/dzgui/views/components/statusbar.py @@ -2,7 +2,6 @@ from typing import Self, Union, TYPE_CHECKING from dzgui.const.enum import NotebookPage, ServerTab from dzgui.util.strings import esc_to_return, question_to_return -from dzgui.views.components.buttons import LoggerAlertsButton import gi @@ -47,9 +46,6 @@ class Statusbar(Gtk.Grid): self.emitter.connect("servers_loaded", self._on_servers_loaded) self.emitter.connect("mods_updated", self._on_mods_updated) - def _on_alerts_clicked(self, button: LoggerAlertsButton) -> None: - self.controller.populate_log() - def _on_mods_updated(self, emitter: "Emitter", msg: str, mods: int) -> None: self.set_by_context(NotebookPage.MODS, msg) diff --git a/dzgui/views/dialogs/boot.py b/dzgui/views/dialogs/boot.py index 471e2ce..bfc0b1e 100644 --- a/dzgui/views/dialogs/boot.py +++ b/dzgui/views/dialogs/boot.py @@ -104,7 +104,7 @@ class BootDialog(ColorAwareApp, Gtk.Dialog): # type: ignore self.error_label = Gtk.Label(margin_start=15, margin_end=15) # TODO: abstract class - self.copy_button = ClipboardButton(None, lambda: self.error_label.get_text()) + self.copy_button = ClipboardButton(self.error_label.get_text) self.button_hbox = Gtk.Box( orientation=Gtk.Orientation.HORIZONTAL, halign=Gtk.Align.CENTER, spacing=10 ) diff --git a/dzgui/views/dialogs/early_alert.py b/dzgui/views/dialogs/early_alert.py index 702560b..c861725 100644 --- a/dzgui/views/dialogs/early_alert.py +++ b/dzgui/views/dialogs/early_alert.py @@ -46,7 +46,7 @@ class AbortDialog(ColorAwareApp, Gtk.MessageDialog): # type: ignore self.connect("response", self._on_response) - self.run() + self.run() # type: ignore[no-untyped-call] self.destroy() def _on_response(self, dialog: Self, response: Gtk.ResponseType) -> None: diff --git a/dzgui/views/dialogs/filepicker.py b/dzgui/views/dialogs/filepicker.py index 15371c3..7951ae7 100644 --- a/dzgui/views/dialogs/filepicker.py +++ b/dzgui/views/dialogs/filepicker.py @@ -16,14 +16,14 @@ class FilePicker(Gtk.FileChooserDialog): transient_for=parent, resizable=True, ) - self.add_buttons("_Cancel", Gtk.ResponseType.CANCEL) - self.add_buttons("_Save", Gtk.ResponseType.OK) + self.add_buttons("_Cancel", Gtk.ResponseType.CANCEL) # type: ignore[no-untyped-call] + self.add_buttons("_Save", Gtk.ResponseType.OK) # type: ignore[no-untyped-call] self.set_default_response(Gtk.ResponseType.OK) self.set_current_name(picker.placeholder) def pick_file(self) -> Path | None: - res = self.run() + res = self.run() # type: ignore[no-untyped-call] if res in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT): self.destroy() return None @@ -43,12 +43,12 @@ class FolderPicker(Gtk.FileChooserDialog): parent=parent, resizable=True, ) - self.add_buttons("_Cancel", Gtk.ResponseType.CANCEL) - self.add_buttons("_OK", Gtk.ResponseType.OK) + self.add_buttons("_Cancel", Gtk.ResponseType.CANCEL) # type: ignore[no-untyped-call] + self.add_buttons("_OK", Gtk.ResponseType.OK) # type: ignore[no-untyped-call] self.set_default_response(Gtk.ResponseType.OK) def pick_folder(self) -> Path | None: - res = self.run() + res = self.run() # type: ignore[no-untyped-call] if res in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT): self.destroy() return None diff --git a/dzgui/views/dialogs/generic.py b/dzgui/views/dialogs/generic.py index 06e23ef..da46346 100644 --- a/dzgui/views/dialogs/generic.py +++ b/dzgui/views/dialogs/generic.py @@ -185,7 +185,7 @@ class TextBufferDialog(GenericDialog): secondary=secondary, ) self.text = text - copy_button = ClipboardButton(controller, self.get_text) + copy_button = ClipboardButton(self.get_text) self.add_action_widget(copy_button, Gtk.ResponseType.NONE) self.add_button("OK", Gtk.ResponseType.OK) self.connect("response", self._on_response) diff --git a/dzgui/views/dialogs/uninstall.py b/dzgui/views/dialogs/uninstall.py index c649c8a..8b25510 100644 --- a/dzgui/views/dialogs/uninstall.py +++ b/dzgui/views/dialogs/uninstall.py @@ -22,7 +22,7 @@ from gi.repository import Gtk, GLib, Pango class Assistant(Gtk.Assistant): - def __init__(self, is_deck: bool, paths: dict[str, str]): + def __init__(self, is_deck: bool, paths: dict[str, Path]): super().__init__() if is_deck: # NOTE: deemed to be "safe" dimensions that exclude taskbar size @@ -63,7 +63,7 @@ class Assistant(Gtk.Assistant): class Window(Gtk.Window): - def __init__(self, is_deck: bool, paths: dict[str, str]) -> None: + def __init__(self, is_deck: bool, paths: dict[str, Path]) -> None: super().__init__(title=APP_NAME, icon_name=APP_NAME) self.assistant = Assistant(is_deck, paths) @@ -121,7 +121,7 @@ class CheckboxWithPath(CheckboxWithLabel): class UninstallPage(ScrolledWizardPage): - def __init__(self, paths: dict[str, str], pyapp: str | None): + def __init__(self, paths: dict[str, Path], pyapp: str | None): super().__init__(heading=uninstall.pg1_heading, description=uninstall.pg1_blurb) self.pyapp = pyapp @@ -219,7 +219,7 @@ class UninstallPage(ScrolledWizardPage): class UninstallWizard(ColorAwareApp, Gtk.Application): # type: ignore - def __init__(self, is_deck: bool, paths: dict[str, str]) -> None: + def __init__(self, is_deck: bool, paths: dict[str, Path]) -> None: super().__init__() config = Path(paths["XDG_CONFIG_HOME"]) if not config.is_dir() or not config.joinpath("config.json").is_file(): diff --git a/dzgui/views/dialogs/wizard.py b/dzgui/views/dialogs/wizard.py index b3aa618..82d84f0 100644 --- a/dzgui/views/dialogs/wizard.py +++ b/dzgui/views/dialogs/wizard.py @@ -168,7 +168,12 @@ class NotificationFrame(Gtk.Frame): class APIValidationPage(EnumeratedWizardPage): def __init__( - self, enum: PageNum, heading: str, description: str, link: str, func: Callable + self, + enum: PageNum, + heading: str, + description: str, + link: str, + func: Callable[[str], None], ) -> None: super().__init__( enum=enum, @@ -421,7 +426,7 @@ class PreferencesPage(EnumeratedWizardPage): class FlatpakOverridePage(EnumeratedWizardPage): - def __init__(self, xdg_paths: dict[str, str]) -> None: + def __init__(self, xdg_paths: dict[str, Path]) -> None: super().__init__( enum=PageNum.FLATPAK, heading=wizard.heading_flatpak, @@ -471,7 +476,7 @@ class CompletionPage(EnumeratedWizardPage): class Assistant(Gtk.Assistant): - def __init__(self, is_deck: bool, XDG: "Xdg", xdg_paths: dict[str, str]): + def __init__(self, is_deck: bool, XDG: "Xdg", xdg_paths: dict[str, Path]): super().__init__() if is_deck: # NOTE: deemed to be "safe" dimensions that exclude taskbar size @@ -776,7 +781,7 @@ class SteamPathPage(EnumeratedWizardPage): class SetupWizard(ColorAwareApp, Gtk.Application): # type: ignore - def __init__(self, is_deck: bool, XDG: "Xdg", xdg_paths: dict[str, str]) -> None: + def __init__(self, is_deck: bool, XDG: "Xdg", xdg_paths: dict[str, Path]) -> None: super().__init__() GLib.set_prgname(APP_NAME) self.win = Window(is_deck, XDG, xdg_paths) @@ -787,7 +792,7 @@ class SetupWizard(ColorAwareApp, Gtk.Application): # type: ignore class Window(Gtk.Window): - def __init__(self, is_deck: bool, XDG: "Xdg", xdg_paths: dict[str, str]) -> None: + def __init__(self, is_deck: bool, XDG: "Xdg", xdg_paths: dict[str, Path]) -> None: super().__init__(title=APP_NAME, icon_name=APP_NAME) self.assistant = Assistant(is_deck, XDG, xdg_paths) diff --git a/dzgui/views/mixins/context_mixin.py b/dzgui/views/mixins/context_mixin.py index 9a92739..825deac 100644 --- a/dzgui/views/mixins/context_mixin.py +++ b/dzgui/views/mixins/context_mixin.py @@ -15,9 +15,7 @@ logger = logging.getLogger(APP_NAME) class ContextMixin(TreeView): - def present_menu( - self, widget: Gtk.Widget, event: Gdk.Event - ) -> bool: + def present_menu(self, widget: Gtk.Widget, event: Gdk.Event) -> bool: if self.menu is None: return False @@ -26,7 +24,7 @@ class ContextMixin(TreeView): match event.type: case Gdk.EventType.BUTTON_PRESS: - if event.button != 3: + if event.button != Gdk.BUTTON_SECONDARY: # type: ignore[comparison-overlap] return False self._process_button_event(event) # type: ignore case Gdk.EventType.KEY_PRESS: diff --git a/dzgui/views/pages/keys.py b/dzgui/views/pages/keys.py index 92bea81..ddd630e 100644 --- a/dzgui/views/pages/keys.py +++ b/dzgui/views/pages/keys.py @@ -13,6 +13,7 @@ if TYPE_CHECKING: from dzgui.controllers.mc import Controller +# TODO: refactor into autopopulated ListStores class Keybindings(ScrollableMixin, Gtk.ScrolledWindow): # type: ignore """ Notebook page holding a prearranged grid @@ -35,7 +36,7 @@ class Keybindings(ScrollableMixin, Gtk.ScrolledWindow): # type: ignore self.connect("key-press-event", self._on_keypress) - def build_keys(self, items: list) -> Gtk.Grid: + def build_keys(self, items: list[dict[str, str]]) -> Gtk.Grid: grid = Gtk.Grid(row_spacing=10, column_homogeneous=True) grid.set_halign(Gtk.Align.START) row, col = 1, 0 @@ -72,7 +73,7 @@ class Keybindings(ScrollableMixin, Gtk.ScrolledWindow): # type: ignore row += 1 return grid - def build_sidebar(self, categories: list) -> Gtk.Grid: + def build_sidebar(self, categories: list[str]) -> Gtk.Grid: row, col = 0, 0 w, h = 1, 1 sidebar = Gtk.Grid(row_homogeneous=True, orientation=Gtk.Orientation.VERTICAL) @@ -83,7 +84,7 @@ class Keybindings(ScrollableMixin, Gtk.ScrolledWindow): # type: ignore sidebar.attach(label, col, row, w, h) return sidebar - def build_grid(self, items: list) -> Gtk.Grid: + def build_grid(self, items: list[dict[str, str]]) -> Gtk.Grid: grid = Gtk.Grid( row_spacing=20, halign=Gtk.Align.CENTER, diff --git a/dzgui/views/pages/options.py b/dzgui/views/pages/options.py index 990dc58..1fa3fbb 100644 --- a/dzgui/views/pages/options.py +++ b/dzgui/views/pages/options.py @@ -1,5 +1,5 @@ from pathlib import Path -from typing import Callable, TYPE_CHECKING +from typing import Any, Callable, TYPE_CHECKING from dzgui.api import pefile as PeFile from dzgui.api.steam import get_last_updated @@ -51,7 +51,7 @@ class SubmitField(Gtk.Box): ) -> None: super().__init__(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) - self.callback: Callable + self.callback: Callable[[], None] self.controller = controller self.context = context @@ -139,7 +139,7 @@ class SubmitField(Gtk.Box): return self.save_option() - def set_callback(self, callback: Callable) -> None: + def set_callback(self, callback: Callable[[], None]) -> None: self.callback = callback def save_option(self) -> None: @@ -206,7 +206,7 @@ class NameSubmitField(SubmitField): Preferences.NAME, private=False, ) - self.entry.set_width_chars(30) # type: ignore + self.entry.set_width_chars(30) self.set_callback(self.save_player_name) def save_player_name(self) -> None: @@ -402,7 +402,8 @@ class Options(ScrollableMixin, Gtk.ScrolledWindow): # type: ignore def _on_link_button_clicked(self, button: Gtk.Button, url: str) -> None: open_links.open_link_by_url(url) - def _make_grid(self, rows: list) -> Gtk.Grid: + # TODO: use FlowBox + def _make_grid(self, rows: list[list[Any]]) -> Gtk.Grid: grid = Gtk.Grid( orientation=Gtk.Orientation.VERTICAL, column_spacing=10, @@ -435,7 +436,7 @@ class Options(ScrollableMixin, Gtk.ScrolledWindow): # type: ignore if prefs.paths.config.is_file() is False: dialog = ExceptionDialog(self.controller, strings.config_not_found) dialog.show_all() - dialog.run() + dialog.run() # type: ignore[no-untyped-call] raise OSError(f"Config file '{prefs.paths.config}' not found") config = query.get_config(prefs.paths.config) diff --git a/dzgui/views/pages/preconnect.py b/dzgui/views/pages/preconnect.py index 9a340ef..3f54b31 100644 --- a/dzgui/views/pages/preconnect.py +++ b/dzgui/views/pages/preconnect.py @@ -231,7 +231,7 @@ class PreConnectionAssistant(Gtk.Box): args = self.controller.get_debug_args() d = DebugDialog(self.controller, args) d.show_all() - d.run() + d.run() # type: ignore[no-untyped-call] def _on_ok_clicked(self, button: Gtk.Button) -> None: self.controller.update_and_connect() diff --git a/dzgui/views/trees/tree_base.py b/dzgui/views/trees/tree_base.py index 5d7b6a2..0919b08 100644 --- a/dzgui/views/trees/tree_base.py +++ b/dzgui/views/trees/tree_base.py @@ -79,11 +79,6 @@ class TreeView(CursorMixin, Gtk.TreeView): # type: ignore index = path.get_indices()[0] return index - def get_selected_records(self) -> list: - sel = self.get_selection() - model, rows = sel.get_selected_rows() - return [model[row] for row in rows] - def _on_keypress(self, treeview: Gtk.TreeView, event: Gdk.EventKey) -> bool: from dzgui.views.trees.tree_servers import ServerTreeView @@ -145,9 +140,11 @@ class TreeView(CursorMixin, Gtk.TreeView): # type: ignore self.set_cursor(path) def get_value_at_index(self, index: int) -> Any: - (model, pathlist) = self.get_model_and_pathlist() + model, pathlist = self.get_model_and_pathlist() if len(pathlist) < 1: return "" + if model is None: + return "" path = pathlist[0] tree_iter = model.get_iter(path) value = model.get_value(tree_iter, index) @@ -161,20 +158,16 @@ class TreeView(CursorMixin, Gtk.TreeView): # type: ignore sel = self.get_selection() self._on_tree_selection_changed(sel) - def get_model_and_pathlist(self) -> tuple: + def get_model_and_pathlist( + self, + ) -> tuple[Gtk.TreeModel | None, list[Gtk.TreePath]]: select = self.get_selection() sels = select.get_selected_rows() - (model, pathlist) = sels + model: Gtk.TreeModel | None + pathlist: list[Gtk.TreePath] + model, pathlist = sels return (model, pathlist) - # @deprecated("Currently unused") - # def get_mpath(self) -> Optional[Gtk.TreePath]: - # (model, pathlist) = self.get_model_and_pathlist() - # if len(pathlist) < 1: - # return None - # path = pathlist[0] - # return path - def _on_row_activated( self, treeview: Gtk.TreeView, @@ -189,15 +182,6 @@ class TreeView(CursorMixin, Gtk.TreeView): # type: ignore return True return False - # @deprecated("unused") - # def get_selected_row(self) -> Optional[Gtk.TreeModelRow]: - # ind = self.get_selected_row_index() - # model = self.get_model() - # if model is None: - # return None - # row = model[ind] - # return row - def get_col_value_by_path_index(self, path: Gtk.TreePath, index: int) -> Any: model = self.get_model() if model is None: diff --git a/dzgui/views/trees/tree_servers.py b/dzgui/views/trees/tree_servers.py index 3143c6d..6cfac6e 100644 --- a/dzgui/views/trees/tree_servers.py +++ b/dzgui/views/trees/tree_servers.py @@ -32,6 +32,7 @@ if TYPE_CHECKING: from dzgui.controllers.mc import Controller from dzgui.controllers.emitter import Emitter from dzgui.model.model_factory import FastInsertListStore + from dzgui.util.dist import Haversine QUEUE_CHECK_DELAY = 200 @@ -64,7 +65,7 @@ class ServerTreeView(ContextMixin, TreeView): # type: ignore self.queue_id: int self.handler_id: int - self.queue: Queue = Queue() + self.queue: Queue[tuple[str, "Haversine" | None, ServerTab]] = Queue() self.seen_cache: list[Record] = [] @@ -406,7 +407,7 @@ class ServerTreeView(ContextMixin, TreeView): # type: ignore path = model.get_path(it) row_index = path.get_indices()[0] try: - start, end = self.get_visible_range() + start, end = self.get_visible_range() # type: ignore[no-untyped-call] except Exception: return if row_index >= start[0] <= end[0]: diff --git a/pyproject.toml b/pyproject.toml index 9605b7b..4283cec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -105,7 +105,7 @@ python-a2s = { path = "./dzgui/lib/a2s", editable = true } dayzquery = { path = "./dzgui/lib/dayzquery", editable = true } [tool.uv] -config-settings-package = { pygobject-stubs = { config = "Gtk3,Gdk3,Soup2" } } +config-settings-package = { pygobject-stubs = { config = "Gtk3,Gdk3" } } [tool.pytest.ini_options] markers = [