mirror of
https://github.com/aclist/dztui.git
synced 2026-08-26 17:57:06 +02:00
chore: move signal emission into ServerTreeView
This commit is contained in:
parent
99ede36a32
commit
68cd567249
@ -112,6 +112,8 @@ class Controller(GObject.GObject):
|
||||
self.prefs: UserPrefs
|
||||
self.cleanup_func: StoredFunc = None
|
||||
|
||||
#self.config_man = ConfigManager()
|
||||
|
||||
self.emitter = Emitter()
|
||||
self.emitter.connect("map_selection_changed", self._on_map_selection_changed)
|
||||
self.emitter.connect("check_toggled", self._on_check_toggled)
|
||||
@ -150,12 +152,15 @@ class Controller(GObject.GObject):
|
||||
logger.critical(f"{attr} is not a valid AppNavigation attribute.")
|
||||
|
||||
def get_prefs(self) -> UserPrefs:
|
||||
# return self.config_man.get_prefs()
|
||||
return self.prefs
|
||||
|
||||
def set_prefs(self, prefs: UserPrefs) -> None:
|
||||
# self.config_man.set_prefs()
|
||||
self.prefs = prefs
|
||||
|
||||
def query_config(self, key: Preferences) -> str | bool | list:
|
||||
# return self.config_man.lookup(key)
|
||||
config = self.prefs.paths.config
|
||||
return lookup(config, key)
|
||||
|
||||
@ -201,6 +206,7 @@ class Controller(GObject.GObject):
|
||||
widget.handler_unblock_by_func(func)
|
||||
|
||||
def toggle_debug_mode(self) -> None:
|
||||
# self.config_man().toggle_config(Preferences.DEBUG)
|
||||
self.toggle_config(Preferences.DEBUG)
|
||||
|
||||
def get_active_context(self) -> NotebookPage:
|
||||
@ -217,6 +223,7 @@ class Controller(GObject.GObject):
|
||||
treeview = self.get_active_treeview()
|
||||
columns = treeview.get_columns()
|
||||
|
||||
#columns_file = self.config_man.get_columns()
|
||||
columns_file = self.prefs.paths.columns
|
||||
try:
|
||||
data = JSON.read_json(columns_file)
|
||||
@ -242,6 +249,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.prefs.paths.resolution
|
||||
try:
|
||||
write_json(data, res_path)
|
||||
@ -279,6 +287,7 @@ class Controller(GObject.GObject):
|
||||
if haversine is None:
|
||||
dist = "Unknown"
|
||||
else:
|
||||
# self.config_man.use_miles()
|
||||
if self.prefs.use_miles:
|
||||
raw = round(haversine.as_miles())
|
||||
separated = number(raw)
|
||||
@ -651,25 +660,6 @@ class Controller(GObject.GObject):
|
||||
model[path][4] = None
|
||||
self.mediator.modtreeview.set_cursor(0)
|
||||
|
||||
def dump_test_2(self) -> None:
|
||||
data = (
|
||||
[
|
||||
"BAR",
|
||||
"chernarusplus",
|
||||
"a",
|
||||
"a",
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
"185.207.214.16:2302",
|
||||
1,
|
||||
1,
|
||||
"a",
|
||||
False,
|
||||
],
|
||||
)
|
||||
self.push_data_success(data, FilterMode.INITIAL)
|
||||
|
||||
def get_map_man(self) -> "MapManager":
|
||||
return self.get_active_treeview().get_map_man()
|
||||
|
||||
@ -928,6 +918,7 @@ class Controller(GObject.GObject):
|
||||
self.emitter.emit("load_maps", store)
|
||||
|
||||
def _on_servers_loaded(self, emitter: "Emitter", tab: "ServerTab") -> None:
|
||||
return
|
||||
# NOTE: workaround for GTK bug where fullscreen causes headers to vanish when model is None
|
||||
# TODO: this should be internal to servers page
|
||||
state = self.has_server_model()
|
||||
|
||||
@ -3,7 +3,7 @@ import logging
|
||||
import threading
|
||||
|
||||
from functools import wraps
|
||||
from typing import Callable
|
||||
from typing import Callable, Optional
|
||||
from dzgui.views.dialogs.generic import WaitDialog
|
||||
|
||||
|
||||
@ -49,6 +49,7 @@ class ThreadingManager:
|
||||
self.parent = parent
|
||||
self.jobs = 1
|
||||
self.cleanup_func = None
|
||||
self.alternate_statusbar = None
|
||||
|
||||
def call_on_thread(self, dialog_str: str, func: StoredFunc) -> None:
|
||||
def callback() -> None:
|
||||
@ -69,6 +70,13 @@ class ThreadingManager:
|
||||
def increment_dialog_with_str(self, text: str) -> None:
|
||||
GLib.idle_add(lambda: self.wait_dialog.increment(text))
|
||||
|
||||
# TODO: this should not be delegated here
|
||||
def set_alternate_statusbar(self, msg: str) -> None:
|
||||
self.alternate_statusbar = msg
|
||||
|
||||
def get_alternate_statusbar(self) -> Optional[str]:
|
||||
return self.alternate_statusbar
|
||||
|
||||
def set_cleanup_func(self, func: StoredFunc) -> None:
|
||||
if type(func) not in (StoredFunc, type(None)):
|
||||
msg = f"Callback function '{func}' is not of type StoredFunc or None"
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
from datetime import datetime
|
||||
import logging
|
||||
import threading
|
||||
|
||||
from concurrent.futures import wait, as_completed
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
@ -21,6 +22,8 @@ import gi
|
||||
gi.require_version("Gtk", "3.0")
|
||||
from gi.repository import Gtk, GLib # noqa E402
|
||||
|
||||
LAN_TIMEOUT = 0.5
|
||||
API_TIMEOUT = 3
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from dzgui.controllers.mc import Controller
|
||||
@ -29,6 +32,10 @@ if TYPE_CHECKING:
|
||||
|
||||
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:
|
||||
|
||||
@ -37,6 +44,7 @@ class ServerModelManager:
|
||||
self.emitter = controller.get_emitter()
|
||||
if tv.is_loaded():
|
||||
self.emitter.emit("servers_loaded", enum)
|
||||
return
|
||||
|
||||
self.tv = tv
|
||||
self.jobs = 1
|
||||
@ -61,7 +69,8 @@ class ServerModelManager:
|
||||
# TODO: get row count
|
||||
self._dump_history()
|
||||
case ServerTab.LAN:
|
||||
self._dump_lan()
|
||||
return
|
||||
#self._dump_lan()
|
||||
|
||||
@call_on_thread(dialog.fetching)
|
||||
def _dump_api(self) -> None:
|
||||
@ -74,7 +83,7 @@ class ServerModelManager:
|
||||
for future in as_completed(futures):
|
||||
try:
|
||||
self.thread_man.increment_dialog()
|
||||
res = future.result(timeout=3)
|
||||
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))
|
||||
return
|
||||
@ -92,15 +101,12 @@ class ServerModelManager:
|
||||
j = res.json
|
||||
servers += j["response"]["servers"]
|
||||
|
||||
# TODO: strings
|
||||
#self.thread_man.increment_dialog_with_str("Unpacking servers")
|
||||
# TODO: try/except
|
||||
# TODO: try/except when parsing
|
||||
parsed = Servers.parse_json(servers)
|
||||
self._push_data_success(parsed, FilterMode.INITIAL)
|
||||
self._push_data(parsed, FilterMode.INITIAL)
|
||||
|
||||
# TODO: strings
|
||||
@call_on_thread("scanning LAN ports")
|
||||
def _dump_lan(self, port: int, early_abort: bool) -> None:
|
||||
@call_on_thread(dialog.scanning)
|
||||
def dump_lan(self, port: int, early_abort: bool) -> None:
|
||||
servers = []
|
||||
ports = range(1, 256)
|
||||
|
||||
@ -111,24 +117,24 @@ class ServerModelManager:
|
||||
]
|
||||
for future in as_completed(futures):
|
||||
try:
|
||||
res = future.result(timeout=0.5)
|
||||
res = future.result(timeout=LAN_TIMEOUT)
|
||||
if res is not None and early_abort is True:
|
||||
# NOTE: first non-empty hit, flag pending threads to close
|
||||
event.set()
|
||||
servers.append(res)
|
||||
self.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.cleanup_func = StoredFunc(self._cleanup_on_failure)
|
||||
self.thread_man.set_cleanup_func(StoredFunc(self._cleanup_on_failure))
|
||||
if len(servers) == 0:
|
||||
self.cleanup_func = StoredFunc(self._cleanup_on_failure)
|
||||
self.thread_man.set_cleanup_func(StoredFunc(self._cleanup_on_failure))
|
||||
return
|
||||
parsed = Servers.parse_json(servers)
|
||||
self._push_data_success(parsed, FilterMode.INITIAL)
|
||||
self._push_data(parsed, FilterMode.INITIAL)
|
||||
|
||||
# TODO: strings
|
||||
@call_on_thread("dumping ips")
|
||||
@ -136,10 +142,11 @@ class ServerModelManager:
|
||||
# NOTE: block malformed records (TODO: add github issue no.)
|
||||
# TODO: sanitize ip list at config time, drop this
|
||||
ips = [ip for ip in ips if len(ip.split(":")) == 3 and ip.split(":")[2] != ""]
|
||||
job = Servers.query_direct
|
||||
with ThreadPoolExecutor() as executor:
|
||||
futures = [
|
||||
executor.submit(
|
||||
Servers.query_direct,
|
||||
job,
|
||||
ip.split(":")[0],
|
||||
int(ip.split(":")[2]),
|
||||
)
|
||||
@ -154,12 +161,11 @@ class ServerModelManager:
|
||||
continue
|
||||
servers.append(res)
|
||||
if len(servers) == 0:
|
||||
self.cleanup_func = StoredFunc(self._cleanup_on_failure)
|
||||
self.thread_man.set_cleanup_func(StoredFunc(self._cleanup_on_failure))
|
||||
return
|
||||
|
||||
# NOTE: 1 extra progress bar pass for parsing
|
||||
parsed = Servers.parse_json(servers)
|
||||
self._push_data_success(parsed, FilterMode.INITIAL)
|
||||
self._push_data(parsed, FilterMode.INITIAL)
|
||||
|
||||
def _query_ip_id(self, addr: str) -> None:
|
||||
# NOTE: Battlemetrics
|
||||
@ -177,7 +183,8 @@ class ServerModelManager:
|
||||
# TODO: strings
|
||||
@call_on_thread("querying address")
|
||||
def _connect_by_id_or_ip(self, addr: str) -> None:
|
||||
res = self.query_ip_id(addr)
|
||||
res = self._query_ip_id(addr)
|
||||
# TODO: unimplemented
|
||||
|
||||
@call_on_thread("querying address")
|
||||
def _add_by_id_or_ip(self, addr: str) -> None:
|
||||
@ -193,7 +200,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
|
||||
# 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:
|
||||
@ -203,10 +211,10 @@ class ServerModelManager:
|
||||
with open(history, "r") as f:
|
||||
rows = [row.rstrip("\n") for row in f]
|
||||
except OSError:
|
||||
self.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.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)
|
||||
@ -218,7 +226,7 @@ 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.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)
|
||||
|
||||
@ -243,17 +251,10 @@ class ServerModelManager:
|
||||
self.emitter.emit("servers_loaded", context)
|
||||
|
||||
def _cleanup_on_success(self) -> None:
|
||||
self.pending_jobs = 1
|
||||
self.tv.set_loaded(True)
|
||||
self.tv.set_model(self.to_insert)
|
||||
|
||||
# TODO: this will allow history and saved tab to emit signals to statusbar
|
||||
# CHORE: test if treeview's sort method inserts row at the correct index
|
||||
# inserting a row serializes file on disk, updates control model for that tab, and
|
||||
# reapplies filters to ephemeral model; since filters are applied, in-situ insertion might not be necessary
|
||||
# TODO: will be inserted out of order
|
||||
#self.to_insert.connect("row-inserted", lambda *args: print("row inserted into model"))
|
||||
|
||||
# inserting a row serializes file on disk, updates control model for that tab, and updates model
|
||||
# NOTE: when inserting new rows, the entire control model is wiped and rebuilt, then proxy model is swapped in
|
||||
# TODO: signals or other approach to deferring map
|
||||
# model insertion after thread closes
|
||||
# cf. servers_loaded signal
|
||||
@ -262,7 +263,8 @@ class ServerModelManager:
|
||||
context = self.tv.get_enum()
|
||||
self.emitter.emit("servers_loaded", context)
|
||||
|
||||
# CHORE: this is placeholder logic
|
||||
# CHORE: this is placeholder logic,
|
||||
# refills map combo with new maps
|
||||
if self.first_iteration:
|
||||
map_man = self.tv.get_map_man()
|
||||
map_man.set_unique_maps(self.new_maps)
|
||||
@ -270,14 +272,11 @@ class ServerModelManager:
|
||||
self.first_iteration = False
|
||||
self.new_maps = None
|
||||
|
||||
self.tv.grab_focus()
|
||||
|
||||
def _cleanup_on_failure(self, show_dialog=True) -> None:
|
||||
self.treeview.set_loaded(True)
|
||||
map_man = self.treeview.get_map_man()
|
||||
|
||||
# TODO: disable map, keyword, and filter widgets if model is None
|
||||
# -> signal driven (servers_empty)
|
||||
# -> 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
|
||||
@ -286,8 +285,6 @@ class ServerModelManager:
|
||||
# wipe refresh state to False
|
||||
|
||||
self.treeview.set_model(None)
|
||||
self.treeview.grab_focus()
|
||||
|
||||
map_man.set_unique_maps(None)
|
||||
context = self.treeview.get_enum()
|
||||
|
||||
@ -300,7 +297,7 @@ class ServerModelManager:
|
||||
|
||||
# TODO: break into initial dump and refilter modes, can drop filtermode kwarg
|
||||
# and stop pushing empty data
|
||||
def _push_data_success(self, data: tuple, mode: Optional[FilterMode]) -> None:
|
||||
def _push_data(self, data: tuple, mode: Optional[FilterMode]) -> None:
|
||||
# FIXME: calls treeview read methods in thread
|
||||
# treeview = self.get_active_treeview()
|
||||
# manager = treeview.get_filter_man()
|
||||
@ -313,7 +310,7 @@ class ServerModelManager:
|
||||
manager.set_control(data)
|
||||
manager.filter(mode)
|
||||
self.to_insert = manager.get_proxy_model()
|
||||
# TODO: pre parse maps
|
||||
|
||||
u_maps = set([row[1] for row in data])
|
||||
self.new_maps = sorted(u_maps)
|
||||
|
||||
|
||||
@ -234,6 +234,7 @@ class Dialog:
|
||||
modlist: str
|
||||
working: str
|
||||
updating_mods: str
|
||||
scanning: str
|
||||
|
||||
|
||||
@dataclass(slots=True, frozen=True)
|
||||
@ -429,6 +430,7 @@ dialog = Dialog(
|
||||
modlist="Fetching modlist",
|
||||
working="Working",
|
||||
updating_mods="Updating mods",
|
||||
scanning="Scanning LAN ports",
|
||||
)
|
||||
|
||||
buttons = Button(
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from dzgui.model.servers import ServerModelManager
|
||||
from dzgui.util.strings import connect_panel, fav_panel, lan_panel
|
||||
from dzgui.views.components.buttons import (
|
||||
AddButton,
|
||||
@ -75,7 +76,9 @@ class LanPanel(Gtk.Frame):
|
||||
else:
|
||||
port = self.entry.get_text()
|
||||
abort = self.early_abort.get_active()
|
||||
self.controller.dump_lan(int(port), abort)
|
||||
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()
|
||||
|
||||
@ -55,6 +55,18 @@ class ServerNotebook(Gtk.ScrolledWindow):
|
||||
self.connect("map", self._on_map)
|
||||
self.connect("unmap", self._on_unmap)
|
||||
|
||||
self.emitter.connect("servers_loaded", self._on_servers_loaded)
|
||||
|
||||
def _on_servers_loaded(self, emitter: "Emitter", tab: "ServerTab") -> None:
|
||||
# NOTE: workaround for GTK bug where fullscreen causes headers to vanish when model is None
|
||||
# TODO: this should be internal to servers page
|
||||
tv = self.get_active_treeview()
|
||||
state = False if tv.get_model() is None else True
|
||||
tv.set_headers_visible(state)
|
||||
tv.set_headers_clickable(state)
|
||||
tv.set_loaded(True)
|
||||
tv.grab_focus()
|
||||
|
||||
def _on_map(self, widget: Self) -> None:
|
||||
self.emitter.emit("server_page_toggled", True)
|
||||
# FIXME: only applies to server notebook, not atomic page
|
||||
@ -137,5 +149,17 @@ class ServerNotebook(Gtk.ScrolledWindow):
|
||||
tab, col, "_on_col_width_changed", False
|
||||
)
|
||||
|
||||
def get_tabs(self) -> tuple:
|
||||
return (self.browser, self.saved, self.recent, self.lan)
|
||||
# def get_tabs(self) -> tuple:
|
||||
# return (self.browser, self.saved, self.recent, self.lan)
|
||||
|
||||
def get_browser(self) -> ServerTreeView:
|
||||
return self.browser
|
||||
|
||||
def get_saved(self) -> ServerTreeView:
|
||||
return self.saved
|
||||
|
||||
def get_recent(self) -> ServerTreeView:
|
||||
return self.recent
|
||||
|
||||
def get_lan(self) -> ServerTreeView:
|
||||
return self.lan
|
||||
|
||||
@ -45,7 +45,6 @@ class ServerTreeView(ContextMixin, TreeView):
|
||||
self.enum = enum
|
||||
|
||||
self.loaded = False
|
||||
self.query_func: Callable = None
|
||||
|
||||
self.filter_man = FilteredModelManager(controller)
|
||||
model = self.filter_man.get_proxy_model()
|
||||
@ -59,12 +58,7 @@ class ServerTreeView(ContextMixin, TreeView):
|
||||
|
||||
self.queue_id: int
|
||||
self.handler_id: int
|
||||
self.query_func_jobs = 1
|
||||
|
||||
self.current_proc = None
|
||||
|
||||
self.queue = Queue()
|
||||
# self.queue = multiprocessing.Queue()
|
||||
|
||||
prefs = self.controller.get_prefs()
|
||||
columns = prefs.paths.columns
|
||||
@ -129,6 +123,7 @@ class ServerTreeView(ContextMixin, TreeView):
|
||||
self.connect("map", self._on_map)
|
||||
self.connect("unmap", self._on_unmap)
|
||||
|
||||
# TODO: why is this being saved?
|
||||
self.thread = None
|
||||
|
||||
def start_timeout(self) -> None:
|
||||
@ -184,14 +179,6 @@ class ServerTreeView(ContextMixin, TreeView):
|
||||
if self.get_enum() is ServerTab.LAN:
|
||||
self.emitter.emit("lan_tab_toggled", False)
|
||||
|
||||
def set_query_func(self, func: "StoredFunc", jobs: int = 1) -> None:
|
||||
self.query_func = func
|
||||
self.query_func_jobs = jobs
|
||||
|
||||
# TODO: possibly split this up
|
||||
def get_query_func(self) -> tuple["StoredFunc", int]:
|
||||
return self.query_func, self.query_func_jobs
|
||||
|
||||
def _on_col_width_changed(
|
||||
self, col: Gtk.TreeViewColumn, width: GObject.ParamSpecInt
|
||||
) -> None:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user