From e56b89fd64ee8cf77e10f0e64795fa05b56f3fea Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Sun, 22 Feb 2026 22:57:44 +0900 Subject: [PATCH] fix: set cell renderer properties --- dzgui/controllers/emitter.py | 9 ++- dzgui/controllers/mc.py | 22 +++---- dzgui/managers/thread_man.py | 1 + dzgui/model/model_factory.py | 23 +++++-- dzgui/model/servers.py | 80 ++++++++++++++++--------- dzgui/util/strings.py | 3 +- dzgui/views/components/connect_panel.py | 49 +++++++++++---- dzgui/views/pages/servers.py | 8 +-- dzgui/views/trees/tree_mods.py | 27 +++++++-- dzgui/views/trees/tree_servers.py | 36 ++++------- 10 files changed, 160 insertions(+), 98 deletions(-) diff --git a/dzgui/controllers/emitter.py b/dzgui/controllers/emitter.py index c055e6c..b0c2284 100644 --- a/dzgui/controllers/emitter.py +++ b/dzgui/controllers/emitter.py @@ -33,8 +33,13 @@ class Emitter(GObject.GObject): pass @GObject.Signal(flags=GObject.SignalFlags.RUN_LAST, arg_types=()) - def request_lan_entry_focus(self) -> None: - """User invoked Ctrl-p keybinding from ServerTreeView""" + def request_custom_port_focus(self) -> None: + """User invoked Ctrl-n keybinding from ServerTreeView""" + pass + + @GObject.Signal(flags=GObject.SignalFlags.RUN_LAST, arg_types=()) + def request_default_port_focus(self) -> None: + """user invoked ctrl-d keybinding from servertreeview""" pass @GObject.Signal(flags=GObject.SignalFlags.RUN_LAST, arg_types=()) diff --git a/dzgui/controllers/mc.py b/dzgui/controllers/mc.py index ebdcdc3..379dae5 100644 --- a/dzgui/controllers/mc.py +++ b/dzgui/controllers/mc.py @@ -4,18 +4,14 @@ import shutil import threading import traceback -from concurrent.futures import wait, as_completed -from concurrent.futures import ThreadPoolExecutor from functools import wraps from pathlib import Path from typing import Any, Callable, Optional, TYPE_CHECKING from warnings import deprecated import dzgui.api.pefile as PeFile -import dzgui.api.servers as Servers import dzgui.util._json as JSON # noqa -from dzgui.api.bm import map_id_to_record from dzgui.api.mods import ( get_delimited_mods, get_local_mod_path, @@ -112,7 +108,7 @@ class Controller(GObject.GObject): self.prefs: UserPrefs self.cleanup_func: StoredFunc = None - #self.config_man = ConfigManager() + # self.config_man = ConfigManager() self.emitter = Emitter() self.emitter.connect("map_selection_changed", self._on_map_selection_changed) @@ -223,7 +219,7 @@ class Controller(GObject.GObject): treeview = self.get_active_treeview() columns = treeview.get_columns() - #columns_file = self.config_man.get_columns() + # columns_file = self.config_man.get_columns() columns_file = self.prefs.paths.columns try: data = JSON.read_json(columns_file) @@ -249,7 +245,7 @@ class Controller(GObject.GObject): w, h = self.mediator.window.get_size() data = {"res": {"width": w, "height": h}} - #res_path = self.config_man.get_resolution() + # res_path = self.config_man.get_resolution() res_path = self.prefs.paths.resolution try: write_json(data, res_path) @@ -316,13 +312,13 @@ class Controller(GObject.GObject): # TODO: delegate to threadmanager @call_on_thread(strings.dialog.modlist) def load_mods(self) -> None: - model = ModelFactory().make_mod_store() #self.model_man.new_mod_store() + model = ModelFactory().make_mod_store() # self.model_man.new_mod_store() path = self.query_config(Preferences.DEFAULT) mods = get_delimited_mods(Path(path)) for mod in mods: - # NOTE: holds color column - mod.append(None) + # NOTE: show highlight color bool + mod.append(False) model.append(mod) self.cleanup_func = StoredFunc(self.load_mods_cleanup, model) @@ -749,7 +745,7 @@ class Controller(GObject.GObject): it = mod.iter path = model.get_path(it) if int(mod[2]) in stale_mods: - model[path][4] = HEX_RED + model[path][4] = True self.emitter.emit("mods_highlighted") @call_on_thread(strings.dialog.working) @@ -946,10 +942,6 @@ class Controller(GObject.GObject): map_man.set_selected_map(selection) self.refilter_model(FilterMode.MAP) - def hide_widgets_on_init(self) -> None: - self.mediator.grid.conpan.set_lan_visible(False) - self.mediator.grid.right_panel.sel_panel.hide() - def get_notebook(self) -> "Notebook": return self.mediator.notebook diff --git a/dzgui/managers/thread_man.py b/dzgui/managers/thread_man.py index 7eafcf6..036416d 100644 --- a/dzgui/managers/thread_man.py +++ b/dzgui/managers/thread_man.py @@ -21,6 +21,7 @@ def call_on_thread(dialog_str: str) -> Callable: def wrapper(*args, **kwargs): self = args[0] stored = StoredFunc(func, *args, **kwargs) + print("SELF", self) if not hasattr(self, "thread_man"): raise AttributeError if type(self.thread_man) is not ThreadingManager: diff --git a/dzgui/model/model_factory.py b/dzgui/model/model_factory.py index afeb3c1..5e9d687 100644 --- a/dzgui/model/model_factory.py +++ b/dzgui/model/model_factory.py @@ -18,7 +18,7 @@ class ModCols: symlink: str directory: str size: float - color: str + color: bool @dataclass(slots=True, frozen=True) @@ -42,6 +42,17 @@ class MenuCols: hidden: GObject.TYPE_PYOBJECT +class FastInsertListStore(ListStore): + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + + def append(self, row) -> None: + total = len(row) + i = len(self) + tree_iter = self.insert_with_values(i, tuple(range(0, total)), row) + return tree_iter + + class ModelFactory: def __init__(self) -> None: pass @@ -49,17 +60,17 @@ class ModelFactory: def new_model_from_logfile(self, path: str) -> None: store = self.make_log_store() with open(path, "r") as f: - lines = [ - line.split(delimiter) for line in f.read().splitlines() - ] + lines = [line.split(delimiter) for line in f.read().splitlines()] for record in lines: # NOTE: strip PII and API keys clean = redact_log(record) store.append(clean) return store - def new_model_from_class(self, cls: type) -> ListStore: - store = ListStore(*[ftype for field, ftype in cls.__annotations__.items()]) + def new_model_from_class(self, cls: type) -> FastInsertListStore: + store = FastInsertListStore( + *[ftype for field, ftype in cls.__annotations__.items()] + ) return store def make_map_store(self) -> ListStore: diff --git a/dzgui/model/servers.py b/dzgui/model/servers.py index bfceaca..8fe14f7 100644 --- a/dzgui/model/servers.py +++ b/dzgui/model/servers.py @@ -12,7 +12,7 @@ from dzgui.const.constants import ( ) from dzgui.const.enum import FilterMode, Preferences, ServerTab from dzgui.managers.thread_man import call_on_thread, StoredFunc, ThreadingManager -from dzgui.util.strings import dialog +from dzgui.util.strings import api_warn_msg, dialog from dzgui.views.dialogs.generic import ExceptionDialog from typing import Optional, TYPE_CHECKING @@ -36,8 +36,11 @@ logger = logging.getLogger(__name__) # TODO: failure: spawns error dialog # TODO: non failure with empty model: updates statusbar + class ServerModelManager: - def __init__(self, controller: "Controller", tv: Gtk.TreeView, first_iteration=False) -> None: + def __init__( + self, controller: "Controller", tv: Gtk.TreeView, first_iteration=False + ) -> None: enum = tv.get_enum() self.controller = controller @@ -58,6 +61,8 @@ class ServerModelManager: # filter: iteration N+1 self.first_iteration = first_iteration + # TODO: pass servers.saved treeview when using conpan regardless of current context + match enum: case ServerTab.BROWSER: # NOTE: extra DAYZ_EXP param @@ -69,8 +74,11 @@ class ServerModelManager: # TODO: get row count self._dump_history() case ServerTab.LAN: - return - #self._dump_lan() + # NOTE: LAN tab is only loaded on demand + pass + case _: + pass + # self._dump_lan() @call_on_thread(dialog.fetching) def _dump_api(self) -> None: @@ -85,14 +93,18 @@ class ServerModelManager: self.thread_man.increment_dialog() res = future.result(timeout=API_TIMEOUT) if res.status != 200 or not res.parsed: - self.thread_man.set_cleanup_func(StoredFunc(self._cleanup_on_failure)) + self.thread_man.set_cleanup_func( + StoredFunc(self._cleanup_on_failure) + ) return j = res.json servers += j["response"]["servers"] except Exception as e: # TODO: could store exception in cleanup func logger.critical(e) - self.thread_man.set_cleanup_func(StoredFunc(self._cleanup_on_failure)) + self.thread_man.set_cleanup_func( + StoredFunc(self._cleanup_on_failure) + ) return # NOTE: This step is allowed to fail, since this metadata is incidental @@ -112,9 +124,7 @@ class ServerModelManager: event = threading.Event() with ThreadPoolExecutor() as executor: - futures = [ - executor.submit(Servers.test_ip, i, port, event) for i in ports - ] + futures = [executor.submit(Servers.test_ip, i, port, event) for i in ports] for future in as_completed(futures): try: res = future.result(timeout=LAN_TIMEOUT) @@ -122,14 +132,18 @@ class ServerModelManager: # NOTE: first non-empty hit, flag pending threads to close event.set() servers.append(res) - self.thread_man.set_cleanup_func(StoredFunc(self._cleanup_on_success)) + self.thread_man.set_cleanup_func( + StoredFunc(self._cleanup_on_success) + ) return if res is None: continue servers.append(res) except Exception as e: logger.critical(e) - self.thread_man.set_cleanup_func(StoredFunc(self._cleanup_on_failure)) + self.thread_man.set_cleanup_func( + StoredFunc(self._cleanup_on_failure) + ) if len(servers) == 0: self.thread_man.set_cleanup_func(StoredFunc(self._cleanup_on_failure)) return @@ -161,7 +175,9 @@ class ServerModelManager: continue servers.append(res) if len(servers) == 0: - self.thread_man.set_cleanup_func(StoredFunc(self._cleanup_on_failure)) + self.thread_man.set_cleanup_func( + StoredFunc(self._cleanup_on_failure) + ) return parsed = Servers.parse_json(servers) @@ -200,8 +216,8 @@ class ServerModelManager: # NOTE: this can be called from other tabs--if current focus is not ServerTab.SAVED, update label only # TODO: saved servers might not be loaded yet, in which case should just update local file only # TODO: perform simple equality comparison of self.tv.get_enum() == ServerTab.SAVED - # FIXME: filter man is saved on a per tab basis, so this will mismatch - # check if servers.get_active_treeview() is same as self.tv + # FIXME: filter man is saved on a per tab basis, so this will mismatch + # check if servers.get_active_treeview() is same as self.tv self.set_cleanup_func(StoredFunc(self._cleanup_on_insert)) def _dump_history(self) -> None: @@ -211,10 +227,14 @@ class ServerModelManager: with open(history, "r") as f: rows = [row.rstrip("\n") for row in f] except OSError: - self.thread_man.set_cleanup_func(StoredFunc(self._cleanup_on_failure, False)) + self.thread_man.set_cleanup_func( + StoredFunc(self._cleanup_on_failure, False) + ) return if len(rows) == 0: - self.thread_man.set_cleanup_func(StoredFunc(self._cleanup_on_failure, False)) + self.thread_man.set_cleanup_func( + StoredFunc(self._cleanup_on_failure, False) + ) return self.thread_man.set_job_count(len(rows)) self._dump_ips(rows) @@ -226,7 +246,9 @@ class ServerModelManager: if len(ips) == 0: # FIXME: this is not a failure, just a quiet exit with custom statusbar # TODO: add custom statusbar parameters - self.thread_man.set_cleanup_func(StoredFunc(self._cleanup_on_failure, False)) + self.thread_man.set_cleanup_func( + StoredFunc(self._cleanup_on_failure, False) + ) return self._dump_ips(ips) @@ -273,10 +295,10 @@ class ServerModelManager: self.new_maps = None def _cleanup_on_failure(self, show_dialog=True) -> None: - map_man = self.treeview.get_map_man() + map_man = self.tv.get_map_man() # TODO: disable map, keyword, and filter widgets if model is None - # -> signal driven (servers_empty, servers_failed_to_load) + # -> signal driven (servers_empty, servers_failed_to_load) # TODO: what if refresh action occurred and failed, and the old model is still valid? # skip the step below if refresh action failed # do not wipe control model in this case @@ -284,15 +306,15 @@ class ServerModelManager: # revert old model # wipe refresh state to False - self.treeview.set_model(None) + self.tv.set_model(None) map_man.set_unique_maps(None) - context = self.treeview.get_enum() + context = self.tv.get_enum() # TODO: distinguish signals, e.g. "servers_failed_to_load", "servers_loaded_empty" - # customize statusbar accordingly + # customize statusbar and dialog accordingly self.emitter.emit("servers_loaded", context) if show_dialog: - dialog = ExceptionDialog(self, strings.api_warn_msg) + dialog = ExceptionDialog(self.controller, api_warn_msg) dialog.run() # TODO: break into initial dump and refilter modes, can drop filtermode kwarg @@ -330,9 +352,9 @@ class ServerModelManager: # self.cleanup_func = StoredFunc(self.cleanup_on_success) # TODO: call filter_man methods directly - #def refilter_model(self, mode: FilterMode, label: Optional[str] = None) -> None: - # tv = self.get_active_treeview() - # filter_man = tv.get_filter_man() - # if filter_man.get_control() is None: - # return - # self.filter_threaded(filter_man, mode, label) + def refilter_model(self, mode: FilterMode, label: Optional[str] = None) -> None: + tv = self.get_active_treeview() + self.filter_man = tv.get_filter_man() + if self.filter_man.get_control() is None: + return + self.filter_threaded(self.filter_man, mode, label) diff --git a/dzgui/util/strings.py b/dzgui/util/strings.py index cba5d78..77959c2 100644 --- a/dzgui/util/strings.py +++ b/dzgui/util/strings.py @@ -132,7 +132,8 @@ servers = { "Ctrl-p": "refresh ping", "Ctrl-f": "jump to keyword search field", "Ctrl-m": "jump to maps field", - "Ctrl-d": "toggle dry run (debug) mode", + "Ctrl-d": "select default LAN port", + "Ctrl-n": "select custom LAN port", "Ctrl-i": "jump to IP insert field", "ESC": "return to table", "1-9": "toggle filter 1-9 on/off", diff --git a/dzgui/views/components/connect_panel.py b/dzgui/views/components/connect_panel.py index 2e38462..7dbde37 100644 --- a/dzgui/views/components/connect_panel.py +++ b/dzgui/views/components/connect_panel.py @@ -32,9 +32,11 @@ class LanPanel(Gtk.Frame): label = BoldLabel(lan_panel.heading) self.set_label_widget(label) - self.radio1 = Gtk.RadioButton.new_with_label(None, lan_panel.default_button) - radio2 = Gtk.RadioButton.new_with_label_from_widget( - self.radio1, lan_panel.custom_button + self.default_radio = Gtk.RadioButton.new_with_label( + None, lan_panel.default_button + ) + self.custom_radio = Gtk.RadioButton.new_with_label_from_widget( + self.default_radio, lan_panel.custom_button ) self.entry = PortEntry(controller) @@ -47,18 +49,22 @@ class LanPanel(Gtk.Frame): "Unless you have multiple DayZ servers on your LAN,\nleave this checked to get results faster" ) + self.entry.connect("activate", self._on_entry_activated) self.entry.connect("string_validated", self._on_port_validated) + self.emitter.connect("request_custom_port_focus", self._on_custom_port_binding) self.emitter.connect( - "request_lan_entry_focus", lambda _: self.entry.grab_focus() + "request_default_port_focus", lambda _: self.default_radio.set_active(True) ) - self.radio1.connect("toggled", self._on_radio_toggled) + self.default_radio.connect("toggled", self._on_radio_toggled) self.grid = Gtk.Grid(margin=10, vexpand=False, column_spacing=15, row_spacing=5) - self.grid.attach(self.radio1, 0, 0, COLS, ROWS) + self.grid.attach(self.default_radio, 0, 0, COLS, ROWS) self.grid.attach_next_to( - radio2, self.radio1, Gtk.PositionType.RIGHT, COLS, ROWS + self.custom_radio, self.default_radio, Gtk.PositionType.RIGHT, COLS, ROWS + ) + self.grid.attach_next_to( + self.entry, self.custom_radio, Gtk.PositionType.RIGHT, COLS, ROWS ) - self.grid.attach_next_to(self.entry, radio2, Gtk.PositionType.RIGHT, COLS, ROWS) self.grid.attach_next_to( self.scan, self.entry, Gtk.PositionType.RIGHT, COLS, ROWS ) @@ -70,23 +76,36 @@ class LanPanel(Gtk.Frame): self.entry.set_sensitive(False) + def _on_custom_port_binding(self, emitter: "Emitter") -> None: + self.custom_radio.set_active(True) + self.entry.grab_focus() + + def _on_entry_activated(self, entry: Gtk.Entry) -> None: + if self.entry.get_text() == "": + return + self.scan_ports() + def _on_scan_clicked(self, button: Gtk.Button) -> None: - if self.radio1.get_active(): + self.scan_ports() + + def scan_ports(self) -> None: + if self.default_radio.get_active(): port = 27016 else: port = self.entry.get_text() abort = self.early_abort.get_active() smm = ServerModelManager(self.controller, self.controller.get_active_treeview()) smm.dump_lan(int(port), abort) - #self.controller.dump_lan(int(port), abort) def _on_radio_toggled(self, button: Gtk.RadioButton) -> None: state = button.get_active() self.entry.set_sensitive(not state) if state: self.scan.set_sensitive(True) - elif len(self.entry.get_text()) < 1: - self.scan.set_sensitive(False) + else: + self.entry.grab_focus() + if len(self.entry.get_text()) < 1: + self.scan.set_sensitive(False) def _on_port_validated(self, entry: Gtk.Entry, state: bool) -> None: self.scan.set_sensitive(state) @@ -182,9 +201,13 @@ class AddPanel(Gtk.Frame): def _submit_query(self) -> None: text = self.entry.get_text() - self.controller.add_by_id_or_ip(text) + # TODO: call servermodelmanager + ServerModelManager().add_by_id_or_ip(text) + # self.controller.add_by_id_or_ip(text) def _on_activate(self, entry: Gtk.Entry) -> None: + if not self.add_server.is_sensitive(): + return self._submit_query() def _on_add_clicked(self, button: Gtk.Button) -> None: diff --git a/dzgui/views/pages/servers.py b/dzgui/views/pages/servers.py index 907473f..4fce415 100644 --- a/dzgui/views/pages/servers.py +++ b/dzgui/views/pages/servers.py @@ -1,6 +1,6 @@ import logging -from typing import Self, TYPE_CHECKING +from typing import Optional, Self, TYPE_CHECKING from dzgui.controllers.mc import StoredFunc from dzgui.const.enum import ContextMenuGroup, ServerTab @@ -76,13 +76,13 @@ class ServerNotebook(Gtk.ScrolledWindow): def _on_unmap(self, widget: Self) -> None: self.emitter.emit("server_page_toggled", False) - def _on_keypress(self, widget: Self, event: Gdk.EventKey) -> None: + def _on_keypress(self, widget: Self, event: Gdk.EventKey) -> Optional[False]: + if event.state is Gdk.ModifierType.CONTROL_MASK: + return False match event.keyval: case Gdk.KEY_n: self.notebook.next_page() case Gdk.KEY_p: - if event.state is Gdk.ModifierType.CONTROL_MASK: - return self.notebook.prev_page() case _: return diff --git a/dzgui/views/trees/tree_mods.py b/dzgui/views/trees/tree_mods.py index f86a8ee..766fae5 100644 --- a/dzgui/views/trees/tree_mods.py +++ b/dzgui/views/trees/tree_mods.py @@ -1,11 +1,12 @@ import logging from typing import Any, TYPE_CHECKING +from dzgui.const.constants import HEX_RED +from dzgui.const.enum import ContextMenuGroup from dzgui.util import strings, localize -from dzgui.views.trees.tree_base import TreeView from dzgui.views.mixins.context_mixin import ContextMixin from dzgui.views.mixins.mods_mixin import ModsMixin -from dzgui.const.enum import ContextMenuGroup +from dzgui.views.trees.tree_base import TreeView import gi @@ -35,14 +36,15 @@ class ModTreeView(ModsMixin, ContextMixin, TreeView): for i, column_title in enumerate(strings.mod_cols): renderer = Gtk.CellRendererText() - column = Gtk.TreeViewColumn(column_title, renderer, text=i, foreground=4) - if i == 3: + column = Gtk.TreeViewColumn(column_title, renderer, text=i) + column.set_cell_data_func(renderer, self._format_color, func_data=None) + if i == 4: column.set_cell_data_func(renderer, self._format_float, func_data=None) + if column_title == "Mod": column.set_fixed_width(500) else: column.set_fixed_width(150) - # NOTE: hidden color property column column.set_sizing(Gtk.TreeViewColumnSizing.FIXED) column.set_sort_column_id(i) if i != 4: @@ -88,6 +90,21 @@ class ModTreeView(ModsMixin, ContextMixin, TreeView): def _parent_selection_changed(self, base_class: TreeView, sel: Gtk.TreeSelection): pass + def _format_color( + self, + column: Gtk.TreeViewColumn, + cell: Gtk.CellRendererText, + model: Gtk.TreeModel, + it: Gtk.TreeIter, + data: Any, + ) -> Any: + state = model[it][4] + if state is True: + cell.set_property("foreground", HEX_RED) + else: + cell.set_property("foreground", None) + return + def _format_float( self, column: Gtk.TreeViewColumn, diff --git a/dzgui/views/trees/tree_servers.py b/dzgui/views/trees/tree_servers.py index f05d542..228acff 100644 --- a/dzgui/views/trees/tree_servers.py +++ b/dzgui/views/trees/tree_servers.py @@ -8,12 +8,11 @@ from warnings import deprecated from dzgui.views.mixins.context_mixin import ContextMixin from dzgui.const.enum import ContextMenu, ContextMenuGroup, ServerTab from dzgui.api.servers import Record -from dzgui.api.servers import ping as Ping from dzgui.model.map_model import MapManager from dzgui.model.filtered_model import FilteredModelManager from dzgui.util.dist import CalcDist from dzgui.util import strings -from typing import Callable, Literal, TYPE_CHECKING +from typing import Literal, TYPE_CHECKING from dzgui.views.trees.tree_base import TreeView @@ -28,7 +27,7 @@ from gi.repository import Gtk, GLib, Gdk, GObject, Pango # noqa logger = logging.getLogger(__name__) if TYPE_CHECKING: - from dzgui.controllers.mc import Controller, StoredFunc + from dzgui.controllers.mc import Controller from dzgui.controllers.emitter import Emitter QUEUE_CHECK_DELAY = 200 @@ -216,7 +215,7 @@ class ServerTreeView(ContextMixin, TreeView): while not self.queue.empty(): latest_result = self.queue.get() - # FIXME: cache is being checked at two intervals + # FIXME: cache is being checked in two passes cache = self.controller.get_dist_cache() if latest_result: @@ -239,9 +238,11 @@ class ServerTreeView(ContextMixin, TreeView): self.emitter.emit("request_maps_focus") case Gdk.KEY_i: self.emitter.emit("request_ip_entry_focus") - case Gdk.KEY_p: + case Gdk.KEY_d: + self.emitter.emit("request_default_port_focus") + case Gdk.KEY_n: if self.enum is ServerTab.LAN: - self.emitter.emit("request_lan_entry_focus") + self.emitter.emit("request_custom_port_focus") case Gdk.KEY_c: self.controller.menu_action(ContextMenu.COPY_SERVER_IP, self) case Gdk.KEY_r: @@ -294,23 +295,12 @@ class ServerTreeView(ContextMixin, TreeView): return f"{addr}:{qport}" def get_record(self) -> Record | None: - # TODO: clean up, use tree_base.get_selected_row() - select = self.get_selection() - sels = select.get_selected_rows() - (model, pathlist) = sels - if len(pathlist) < 1: + r = self.get_record_string() + try: + ip, gameport, qport = r.split(":") + return Record(ip, int(gameport), int(qport)) + except ValueError: return None - path = pathlist[0] - model = self.get_model() - if not model: - return None - addr = model[path][7] - if addr is None: - return - qport = model[path][8] - ip = addr.split(":")[0] - gameport = int(addr.split(":")[1]) - return Record(ip, gameport, qport) def is_loaded(self) -> bool: return self.loaded @@ -348,7 +338,7 @@ class ServerTreeView(ContextMixin, TreeView): # thread = threading.Thread( # daemon=True, # target=ping_server, - # args=(model, it, ip, qport, ping), + # args=(model, it, addr, qport, ping), # ) # thread.start()