feat: server threading WIP

This commit is contained in:
aclist committed 2026-01-19 08:27:10 +09:00
1 parent 2788abb755
commit fdd6cefe48
11 files changed
+140 -155

No files matched your search

+1 -1
View File
@@ -53,7 +53,7 @@ def concat_mods(mods: list[str]) -> str:
# TEST: set config to name=user, use official server and no mods, # TEST: set config to name=user, use official server and no mods,
# ensure that formatted string is identical to fixture # ensure that formatted string is identical to fixture
def connect(addr: str, appid: int, name: str, mods: list): def connect(addr: str, appid: int, name: str, mods: list) -> None:
# TODO: get name from configs # TODO: get name from configs
# TODO: concat_mods(mods): # TODO: concat_mods(mods):
# @<mod>;@<mod>; # @<mod>;@<mod>;
+101 -68
View File
@@ -5,7 +5,7 @@ import textwrap
import traceback import traceback
from pathlib import Path from pathlib import Path
from typing import Any, Callable, TYPE_CHECKING from typing import Any, Callable, Literal, TYPE_CHECKING
import dzgui.api.pefile as PeFile import dzgui.api.pefile as PeFile
import dzgui.util._json as JSON # noqa import dzgui.util._json as JSON # noqa
@@ -246,6 +246,7 @@ class Controller:
self.mediator.statusbar.set_text(text) self.mediator.statusbar.set_text(text)
def set_statusbar_placeholder(self, text: str) -> None: def set_statusbar_placeholder(self, text: str) -> None:
# TODO: use statusbar stacks instead
self.statusbar_placeholder = text self.statusbar_placeholder = text
def set_statusbar_dist(self, haversine: "Haversine") -> None: def set_statusbar_dist(self, haversine: "Haversine") -> None:
@@ -263,6 +264,7 @@ class Controller:
dist = str(separated) + " km" dist = str(separated) + " km"
text = self.statusbar_placeholder text = self.statusbar_placeholder
self.set_statusbar(f"{text} | Distance: {dist}") self.set_statusbar(f"{text} | Distance: {dist}")
self.mediator.statusbar.spinner.stop()
def delete_multiple_mods(self) -> None: def delete_multiple_mods(self) -> None:
sel = self.mediator.modtreeview.get_selection() sel = self.mediator.modtreeview.get_selection()
@@ -275,6 +277,7 @@ class Controller:
self.update_mod_statusbar() self.update_mod_statusbar()
def load_mods(self) -> None: def load_mods(self) -> None:
# TODO: threading
model = self.model_manager.get_mod_store() model = self.model_manager.get_mod_store()
model.clear() model.clear()
path = self.query_config(Preferences.DEFAULT) path = self.query_config(Preferences.DEFAULT)
@@ -310,10 +313,6 @@ class Controller:
self.mediator.grid.notebook.settings.populate_settings() self.mediator.grid.notebook.settings.populate_settings()
return return
# NOTE: disabled for now
#def present_toast(self, text: str) -> None:
# self.mediator.window.toast.set_text_and_fade(text)
def open_keybindings(self) -> None: def open_keybindings(self) -> None:
notebook = self.mediator.grid.notebook notebook = self.mediator.grid.notebook
notebook.toggle_keybindings() notebook.toggle_keybindings()
@@ -322,23 +321,6 @@ class Controller:
notebook = self.mediator.grid.notebook notebook = self.mediator.grid.notebook
notebook.focus_current() notebook.focus_current()
# TODO: deprecated
def spawn_dialog(self, msg: str, mode: Popup) -> bool:
"""
Spawns a GenericDialog transient to the OuterWindow
"""
msg = textwrap.dedent(msg)
dialog = GenericDialog(self, msg, mode)
response = dialog.run()
dialog.destroy()
match response:
case Gtk.ResponseType.OK:
return False
case Gtk.ResponseType.CANCEL | Gtk.ResponseType.DELETE_EVENT:
return True
return False
def set_statusbar_by_row(self, row: "RowType") -> None: def set_statusbar_by_row(self, row: "RowType") -> None:
self.mediator.statusbar.refresh(row) self.mediator.statusbar.refresh(row)
@@ -366,7 +348,11 @@ class Controller:
self.save_res_and_quit() self.save_res_and_quit()
return return
case ButtonType.OPTIONS: case ButtonType.OPTIONS:
try:
# TODO: where to put config file check
self.mediator.grid.notebook.settings.populate_settings() self.mediator.grid.notebook.settings.populate_settings()
except Exception:
return
case ButtonType.MODS: case ButtonType.MODS:
self.load_mods() self.load_mods()
case ButtonType.HELP: case ButtonType.HELP:
@@ -374,11 +360,10 @@ class Controller:
pass pass
case ButtonType.SERVERS: case ButtonType.SERVERS:
self.mediator.notebook.set_page_by_enum(button.opens) self.mediator.notebook.set_page_by_enum(button.opens)
# TODO: use cache
self.update_server_status()
return return
self.mediator.notebook.set_page_by_enum(button.opens) self.mediator.notebook.set_page_by_enum(button.opens)
# TODO: set crumbs by signal
self.set_crumbs(button.get_label()) self.set_crumbs(button.get_label())
def open_user_workshop(self, uid: str) -> None: def open_user_workshop(self, uid: str) -> None:
@@ -445,7 +430,9 @@ class Controller:
def update_mod_statusbar(self) -> None: def update_mod_statusbar(self) -> None:
total_mods, total_size = self.calc_mod_size() total_mods, total_size = self.calc_mod_size()
msg = format_mods(total_size, total_mods) msg = format_mods(total_size, total_mods)
# TODO: combine
self.mediator.statusbar.set_text(msg) self.mediator.statusbar.set_text(msg)
self.mediator.statusbar.spinner.stop()
def calc_mod_size(self) -> tuple[int, int]: def calc_mod_size(self) -> tuple[int, int]:
model = self.model_manager.get_mod_store() model = self.model_manager.get_mod_store()
@@ -508,13 +495,16 @@ class Controller:
log = self.prefs.paths.debug log = self.prefs.paths.debug
store = self.model_manager.get_log_store() store = self.model_manager.get_log_store()
store.clear() store.clear()
# NOTE: this model is reloaded each time as log changes
# TODO: pop dialog if log is missing try:
with open(log, "r") as f: with open(log, "r") as f:
lines = [line.split(strings.delimiter) for line in f.read().splitlines()] lines = [line.split(strings.delimiter) for line in f.read().splitlines()]
for record in lines: for record in lines:
clean = redact_log(record) clean = redact_log(record)
store.append(clean) store.append(clean)
except Exception as e:
dialog = ExceptionDialog(self, str(e))
dialog.run()
self.open_page(NotebookPage.LOG) self.open_page(NotebookPage.LOG)
def select_colorized(self) -> None: def select_colorized(self) -> None:
@@ -552,20 +542,51 @@ class Controller:
import time import time
time.sleep(1) time.sleep(1)
# TODO: use model managers, etc. # TODO: use model managers, etc.
self.data = (["BAR", "a", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False]) self.data = (
["BAR", "a", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
["BAR", "a", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
["BAR", "a", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
["BAR", "a", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
["BAR", "a", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
["BAR", "a", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
["BAR", "a", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
["BAR", "a", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
["BAR", "a", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
["BAR", "a", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
["BAR", "a", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
["BAR", "a", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
["BAR", "a", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
["BAR", "a", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
["BAR", "a", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
["BAR", "a", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
["BAR", "a", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
["BAR", "a", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
["BAR", "a", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
["BAR", "a", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
["BAR", "a", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
["BAR", "a", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
["BAR", "a", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
["BAR", "a", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
)
# TODO: refer to prior implementation--should need to load data into model while in thread # TODO: refer to prior implementation--should need to load data into model while in thread
self.get_func_data() self.get_func_data()
self.destroy_on_idle() self.destroy_on_idle()
def get_func_data(self) -> tuple: def get_func_data(self) -> tuple:
def test():
# NOTE: do not insert model until main thread is idle
# TODO: grab from model manager and insert entire model
# TODO: do not start refresh button count until load finished
for row in data:
model.append(row)
treeview.set_loaded(True)
self.update_server_status()
treeview.grab_focus()
# TODO: use model manager # TODO: use model manager
treeview = self.get_active_treeview() treeview = self.get_active_treeview()
model = treeview.get_model() model = treeview.get_model()
data = self.data data = self.data
model.append(data) GLib.idle_add(test)
treeview.set_loaded(True)
self.update_server_status()
treeview.grab_focus()
def highlight_stale(self) -> None: def highlight_stale(self) -> None:
self.call_on_thread(self.colorize_mods) self.call_on_thread(self.colorize_mods)
@@ -577,14 +598,11 @@ class Controller:
thread = threading.Thread(target=func, args=args) thread = threading.Thread(target=func, args=args)
thread.start() thread.start()
def destroy_on_idle(self) -> None: def get_callback(self) -> Callable | None:
self.wait_dialog.destroy() return self.callback["func"]
# TODO: improve upon this
func = self.callback["func"] def get_callback_args(self) -> Any:
if func is not None: return self.callback["args"]
args = self.callback["args"]
GLib.idle_add(func, *args)
self.set_callback(None, None)
def set_callback(self, callback: Callable | None, *args) -> None: def set_callback(self, callback: Callable | None, *args) -> None:
""" """
@@ -593,6 +611,14 @@ class Controller:
""" """
self.callback = { "func": callback, "args": args } self.callback = { "func": callback, "args": args }
def destroy_on_idle(self) -> None:
self.wait_dialog.destroy()
func = self.get_callback()
if func is not None:
args = self.get_callback_args()
GLib.idle_add(func, *args)
self.set_callback(None, None)
def dump_diagnostics(self) -> None: def dump_diagnostics(self) -> None:
picker = FilePicker(self.mediator.window) picker = FilePicker(self.mediator.window)
file = picker.pick_file() file = picker.pick_file()
@@ -600,7 +626,8 @@ class Controller:
try: try:
write_diagnostic(self.prefs.paths.config, file) write_diagnostic(self.prefs.paths.config, file)
except Exception as e: except Exception as e:
self.spawn_dialog(str(e), Popup.NOTIFY) dialog = ExceptionDialog(self, str(e))
dialog.run()
def test_api_response(self, text: str, key: Preferences) -> None: def test_api_response(self, text: str, key: Preferences) -> None:
if key is Preferences.STEAM: if key is Preferences.STEAM:
@@ -614,7 +641,8 @@ class Controller:
self.destroy_on_idle() self.destroy_on_idle()
else: else:
self.destroy_on_idle() self.destroy_on_idle()
self.spawn_dialog(strings.api_error, Popup.NOTIFY) dialog = ExceptionDialog(self, strings.api_error)
dialog.run()
def update_api_key(self, text: str, key: Preferences) -> None: def update_api_key(self, text: str, key: Preferences) -> None:
@@ -645,13 +673,6 @@ class Controller:
logger.info(f"Using default window size {w},{h}") logger.info(f"Using default window size {w},{h}")
window.set_default_size(w, h) window.set_default_size(w, h)
def update_server_status(self) -> None:
treeview = self.mediator.notebook.servers.get_active_treeview()
model = treeview.get_model()
status = format_player_count(model)
self.set_statusbar_placeholder(status)
self.set_statusbar(status + "| Calculating...")
def propagate_column_width(self, col: Gtk.TreeViewColumn) -> None: def propagate_column_width(self, col: Gtk.TreeViewColumn) -> None:
GLib.idle_add(self.mediator.servers.update_tab_widths, col) GLib.idle_add(self.mediator.servers.update_tab_widths, col)
@@ -662,25 +683,31 @@ class Controller:
return self.crumbs_cache return self.crumbs_cache
def refresh_tree(self) -> None: def refresh_tree(self) -> None:
treeview = self.mediator.notebook.servers.get_active_treeview() treeview = self.get_active_treeview()
treeview.set_loaded(False) treeview.set_loaded(False)
self.populate_model() self.populate_model()
def update_server_status(self) -> None:
# TODO: emit signal on statusbar only if page changed
treeview = self.get_active_treeview()
model = treeview.get_model()
status = format_player_count(model)
self.set_statusbar_placeholder(status)
self.set_statusbar(status)
self.mediator.statusbar.spinner.start()
def populate_model(self) -> None: def populate_model(self) -> None:
print("POPULATING MODEL")
# TODO: always use same server model, store in servertreeview class # TODO: always use same server model, store in servertreeview class
treeview = self.get_active_treeview() treeview = self.get_active_treeview()
if treeview.get_loaded() is False: if treeview.get_loaded() is False:
new_model = self.model_manager.new_model() new_model = self.model_manager.new_model()
# NOTE: set_query_func() # NOTE: set_query_func()
func = treeview.get_query_func() func = treeview.get_query_func()
print(func)
if func is not None: if func is not None:
model = treeview.get_model() model = treeview.get_model()
print(model)
# TODO: this may lag? # TODO: this may lag?
model.clear() model.clear()
self.set_callback(None, None)#self.get_func_data) self.set_callback(None, None)
self.call_on_thread(func) self.call_on_thread(func)
def focus_button_box(self) -> None: def focus_button_box(self) -> None:
@@ -688,7 +715,7 @@ class Controller:
def present_servers(self) -> None: def present_servers(self) -> None:
# TODO: abstract # TODO: abstract
self.grab_active_treeview() # TODO: signal for crumbs
self.update_server_status() self.update_server_status()
crumbs = self.mediator.servers.get_cached_label() crumbs = self.mediator.servers.get_cached_label()
self.set_crumbs(crumbs) self.set_crumbs(crumbs)
@@ -696,21 +723,26 @@ class Controller:
#tree = self.get_active_treeview() #tree = self.get_active_treeview()
#tree.emit("on_distcalc_started") #tree.emit("on_distcalc_started")
def toggle_check(self, event: Gdk.EventKey) -> None: def toggle_check(self, event: Gdk.EventKey) -> None | Literal[False]:
keyname = Gdk.keyval_name(event.keyval) mappings = {
if keyname.isnumeric() and int(keyname) > 0: Gdk.KEY_1: 0,
digit = int(keyname) - 1 Gdk.KEY_2: 1,
self.mediator.grid.right_panel.filters_vbox.toggle_check(digit) Gdk.KEY_3: 2,
else: Gdk.KEY_4: 3,
match event.keyval: Gdk.KEY_5: 4,
case Gdk.KEY_0: Gdk.KEY_6: 5,
self.mediator.grid.right_panel.filters_vbox.toggle_check(9) Gdk.KEY_7: 6,
case Gdk.KEY_minus: Gdk.KEY_8: 7,
self.mediator.grid.right_panel.filters_vbox.toggle_check(10) Gdk.KEY_9: 8,
case Gdk.KEY_backslash: Gdk.KEY_0: 9,
self.mediator.grid.right_panel.filters_vbox.toggle_check(11) Gdk.KEY_minus: 10,
case _: Gdk.KEY_backslash: 11,
}
if event.keyval not in mappings:
return False return False
index = mappings[event.keyval]
# TODO: register filter panel widget
self.mediator.grid.right_panel.filters_vbox.toggle_check(index)
def get_favorite(self) -> tuple[str, str] | tuple[None, None]: def get_favorite(self) -> tuple[str, str] | tuple[None, None]:
fav = str(self.query_config(Preferences.FAV_LBL)) fav = str(self.query_config(Preferences.FAV_LBL))
@@ -723,5 +755,6 @@ class Controller:
def get_dist_cache(self) -> dict[str, "Haversine"]: def get_dist_cache(self) -> dict[str, "Haversine"]:
return self.dist_cache return self.dist_cache
# TODO: deprecated in favor of map/unmap
def toggle_lan_panel(self, state: bool) -> None: def toggle_lan_panel(self, state: bool) -> None:
self.mediator.grid.conpan.set_visible(state) self.mediator.grid.conpan.set_visible(state)
+1
View File
@@ -74,6 +74,7 @@ def get_coords(ips: "Path", ip: str) -> Coords:
if split[2] == upper_digits[2]: if split[2] == upper_digits[2]:
if split[3] > upper_digits[3]: if split[3] > upper_digits[3]:
continue continue
# TODO: handle last digit equals
if split[2] <= upper_digits[2]: if split[2] <= upper_digits[2]:
return Coords(float(fields[-2]), float(fields[-1])) return Coords(float(fields[-2]), float(fields[-1]))
+5 -68
View File
@@ -1,26 +1,16 @@
import json
import locale
import logging import logging
import multiprocessing #import multiprocessing
import os
import re
import signal import signal
import subprocess #import threading
import textwrap
import threading
import typing # noqa import typing # noqa
import warnings import warnings
#from concurrent.futures import wait #from concurrent.futures import wait
#from concurrent.futures import ThreadPoolExecutor #from concurrent.futures import ThreadPoolExecutor
from dataclasses import dataclass
from enum import Enum
from pathlib import Path
from typing import TYPE_CHECKING, Literal from typing import TYPE_CHECKING, Literal
from dzgui.const.enum import NotebookPage, VAdjustment from dzgui.const.enum import NotebookPage
from dzgui.const.constants import NO_EXPAND, NO_FILL, NO_PADDING
from dzgui.const.constants import APP_NAME, APP_NAME_LOWER from dzgui.const.constants import APP_NAME, APP_NAME_LOWER
from dzgui.controllers.mc import Controller from dzgui.controllers.mc import Controller
from dzgui.util import css, strings from dzgui.util import css, strings
@@ -40,21 +30,9 @@ from dzgui.views.pages.servers import ServerNotebook
from dzgui.views.pages.thanks import Thanks from dzgui.views.pages.thanks import Thanks
from dzgui.views.components.statusbar import Statusbar from dzgui.views.components.statusbar import Statusbar
from dzgui.views.components.mod_panel import ModSelectionPanel
from dzgui.views.components.right_panel import RightPanel from dzgui.views.components.right_panel import RightPanel
from dzgui.views.components.toast import Toast
from dzgui.views.dialogs.generic import GenericDialog
from dzgui.views.mixins.scrollable_mixin import ScrollableMixin from dzgui.views.mixins.scrollable_mixin import ScrollableMixin
# TREES
from dzgui.views.trees.tree_menu import MenuTreeView
from dzgui.views.trees.tree_log import LogTreeView
from dzgui.views.trees.tree_mods import ModTreeView
from dzgui.views.trees.tree_servers import ServerTreeView
# TODO: not going to be in base anymore
import dzgui.util._json as JSON # noqa
if TYPE_CHECKING: if TYPE_CHECKING:
from dzgui.config.userprefs import UserPrefs from dzgui.config.userprefs import UserPrefs
@@ -160,41 +138,6 @@ warnings.filterwarnings("ignore", ".*g_value_get_int", Warning)
# #return # #return
# #
# #
#def process_user_input(enum: RowType) -> None:
# prompt = enum.dict["prompt"]
# link_label = enum.dict["link_label"]
# cmd_string = enum.dict["label"]
#
# if enum == RowType.CONN_BY_ID:
# key = MainController.query_config(Preferences.BM)
# if len(key) == 0:
# spawn_dialog(
# "No Battlemetrics API key is set; see Options", Popup.NOTIFY
# )
# return
#
# user_entry = EntryDialog(prompt, Popup.ENTRY, link_label, button_type=enum)
# response = user_entry.get_input()
#
# if response is None:
# logger.info("User aborted entry dialog")
# return
# logger.info(f"User entered: '{response}'")
#
# if enum == RowType.CONN_BY_IP:
# connect_by_ip(enum, response)
# return
#
# if enum == RowType.CONN_BY_ID:
# connect_by_id(enum, response, key)
# return
#
# show_wait_dialog = True
# wait_msg = "Working"
# call_on_thread(
# show_wait_dialog, cmd_string, wait_msg, response, choice=enum
# )
# return
class OuterWindow(Gtk.Window): class OuterWindow(Gtk.Window):
def __init__(self) -> None: def __init__(self) -> None:
@@ -216,6 +159,7 @@ class OuterWindow(Gtk.Window):
self.show_all() self.show_all()
css.load_css() css.load_css()
# TODO: first run # TODO: first run
MainController.open_page(NotebookPage.SERVERS) MainController.open_page(NotebookPage.SERVERS)
MainController.mediator.grid.conpan.lan.set_visible(False) MainController.mediator.grid.conpan.lan.set_visible(False)
@@ -344,7 +288,7 @@ class Notebook(ScrollableMixin, Gtk.Notebook): # type: ignore
try: try:
w.focus_first_row() w.focus_first_row()
w.grab_focus() w.grab_focus()
except Exception as e: except Exception:
w.grab_focus() w.grab_focus()
def get_page(self) -> Gtk.Widget | None: def get_page(self) -> Gtk.Widget | None:
@@ -370,12 +314,6 @@ class Notebook(ScrollableMixin, Gtk.Notebook): # type: ignore
status = enum.dict["statusbar"] status = enum.dict["statusbar"]
MainController.set_crumbs(crumbs) MainController.set_crumbs(crumbs)
#is_mods = True if enum is NotebookPage.MODS else False
#is_servers = True if enum is NotebookPage.SERVERS else False
# TODO: use signals internal to servers.py
#MainController.toggle_mod_panel(is_mods)
#MainController.toggle_server_panels(is_servers)
if status is False: if status is False:
MainController.set_statusbar("") MainController.set_statusbar("")
if enum is NotebookPage.SERVERS: if enum is NotebookPage.SERVERS:
@@ -409,7 +347,6 @@ class Grid(Gtk.Grid):
self.attach(self.notebook, 0, 0, MAX_COLS, SINGLE_ROW) self.attach(self.notebook, 0, 0, MAX_COLS, SINGLE_ROW)
els = ( els = (
(self.crumb_box, self.notebook, Gtk.PositionType.TOP, MAX_COLS, SINGLE_ROW), (self.crumb_box, self.notebook, Gtk.PositionType.TOP, MAX_COLS, SINGLE_ROW),
(self.conpan, self.notebook, Gtk.PositionType.BOTTOM, MAX_COLS, SINGLE_ROW), (self.conpan, self.notebook, Gtk.PositionType.BOTTOM, MAX_COLS, SINGLE_ROW),
+2 -1
View File
@@ -13,7 +13,8 @@ class ContextualButton(Gtk.Button):
def __init__(self, label, opens, tooltip, context): def __init__(self, label, opens, tooltip, context):
super().__init__( super().__init__(
label=label, label=label,
tooltip_text=tooltip tooltip_text=tooltip,
focus_on_click=False,
) )
self.context = context self.context = context
+2 -3
View File
@@ -1,7 +1,5 @@
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from dzgui.api.servers import validate_ip
from dzgui.util.css import add_class, remove_class
from dzgui.util.strings import connect_panel, fav_panel, lan_panel from dzgui.util.strings import connect_panel, fav_panel, lan_panel
from dzgui.views.components.buttons import AddButton, ClipboardButton, SteamConnectButton from dzgui.views.components.buttons import AddButton, ClipboardButton, SteamConnectButton
from dzgui.views.components.entry import IpEntry, PortEntry from dzgui.views.components.entry import IpEntry, PortEntry
@@ -63,7 +61,8 @@ class FavPanel(Gtk.Frame):
user_fav, self.fav_ip = self.controller.get_favorite() user_fav, self.fav_ip = self.controller.get_favorite()
server_name = f"{user_fav} ({self.fav_ip})" if user_fav is not None else connect_panel.no_fav server_name = f"{user_fav} ({self.fav_ip})" if user_fav is not None else connect_panel.no_fav
self.fav_label = Gtk.Label(label=server_name, track_visited_links=False, halign=Gtk.Align.START, hexpand=True) self.fav_label = Gtk.Label(label=server_name, track_visited_links=False, halign=Gtk.Align.START, hexpand=True)
scrollable_label = Gtk.ScrolledWindow() # NOTE: disable vscrollbar to prevent layout jumping behavior
scrollable_label = Gtk.ScrolledWindow(vscrollbar_policy=Gtk.PolicyType.NEVER)
scrollable_label.add(self.fav_label) scrollable_label.add(self.fav_label)
self.fav_button = SteamConnectButton() self.fav_button = SteamConnectButton()
+16 -8
View File
@@ -10,32 +10,40 @@ from gi.repository import Gtk # noqa E402
if TYPE_CHECKING: if TYPE_CHECKING:
from dzgui.controllers.mc import Controller from dzgui.controllers.mc import Controller
class Statusbar(Gtk.Statusbar): class Statusbar(Gtk.Grid):
def __init__(self, controller: "Controller") -> None: def __init__(self, controller: "Controller") -> None:
super().__init__() super().__init__(orientation=Gtk.Orientation.HORIZONTAL)
self.controller = controller self.controller = controller
self.controller.register_widget("statusbar", self) self.controller.register_widget("statusbar", self)
help_text = strings.statusbar_helptext help_text = strings.statusbar_helptext
self.set_text(help_text)
self.statusbar = Gtk.Statusbar()
self.spinner = Gtk.Spinner()
self.spinner.start()
version = self.controller.get_prefs().version version = self.controller.get_prefs().version
self.status_right_label = Gtk.Label(label=version) self.status_right_label = Gtk.Label(label=version, hexpand=True, halign=Gtk.Align.END)
self.add(self.status_right_label)
self.attach(self.statusbar, 0, 0, 3, 1)
self.attach_next_to(self.spinner, self.statusbar, Gtk.PositionType.RIGHT, 3, 1)
self.attach_next_to(self.status_right_label, self.spinner, Gtk.PositionType.RIGHT, 3, 1)
self.set_text(help_text)
self.players = "" self.players = ""
def get_text(self) -> str: def get_text(self) -> str:
area = self.get_message_area() area = self.statusbar.get_message_area()
label = area.get_children()[0] label = area.get_children()[0]
return label.get_text() return label.get_text()
def set_text(self, string: str) -> None: def set_text(self, string: str) -> None:
if string is None: if string is None:
return return
meta = self.get_context_id("Statusbar") meta = self.statusbar.get_context_id("Statusbar")
self.push(meta, string) self.statusbar.push(meta, string)
def refresh(self, row: "RowType") -> None: def refresh(self, row: "RowType") -> None:
if row is None: if row is None:
+6 -2
View File
@@ -9,9 +9,10 @@ from dzgui.util import strings, css, open_links
from dzgui.views.components.label import LeftLabel from dzgui.views.components.label import LeftLabel
from dzgui.views.components.eventbox import InfoEventBox from dzgui.views.components.eventbox import InfoEventBox
from dzgui.views.components.buttons import WebButton from dzgui.views.components.buttons import WebButton
from dzgui.views.dialogs.generic import ExceptionDialog
from dzgui.views.dialogs.link_dialog import WorkshopLinkDialog from dzgui.views.dialogs.link_dialog import WorkshopLinkDialog
from dzgui.const.enum import Preferences, Popup from dzgui.const.enum import Preferences
from dzgui.const.endpoints import STEAM_API_SETUP, BM_API_SETUP from dzgui.const.endpoints import STEAM_API_SETUP, BM_API_SETUP
from dzgui.const.constants import ( from dzgui.const.constants import (
APPID_DAYZ, APPID_DAYZ,
@@ -427,7 +428,10 @@ class Options(Gtk.Box):
prefs = self.controller.get_prefs() prefs = self.controller.get_prefs()
if prefs.paths.config.is_file() is False: if prefs.paths.config.is_file() is False:
# NOTE: in case file got deleted locally # NOTE: in case file got deleted locally
self.controller.spawn_dialog(strings.config_not_found, Popup.QUIT) dialog = ExceptionDialog(self.controller, strings.config_not_found)
dialog.run()
raise Exception
# FIXME: return to main menu
return return
config = query.get_config(prefs.paths.config) config = query.get_config(prefs.paths.config)
+1 -1
View File
@@ -51,7 +51,7 @@ class ServerNotebook(Gtk.ScrolledWindow):
pass pass
else: else:
pass pass
# TODO: set model on init of servertreeview # TODO: set model only on init of servertreeview
# TODO: set model manager for each tab on init # TODO: set model manager for each tab on init
# TODO: add set_ and get_model_manager() methods # TODO: add set_ and get_model_manager() methods
tree.set_model(store) tree.set_model(store)
+3 -1
View File
@@ -88,8 +88,10 @@ class TreeView(CursorMixin, Gtk.TreeView): # type: ignore
) -> None: ) -> None:
if is_navkey(event.keyval): if is_navkey(event.keyval):
# TODO: investigate this # FIXME: will fail on mod list
tv = self.controller.get_active_treeview() tv = self.controller.get_active_treeview()
if len(tv.get_model()) < 1:
return
if self.sel_blocked is False: if self.sel_blocked is False:
self.controller.suppress_signal( self.controller.suppress_signal(
tv, tv,
+2 -2
View File
@@ -1,6 +1,7 @@
import logging import logging
import multiprocessing import multiprocessing
import subprocess import subprocess
from typing import Self
import gi import gi
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
@@ -145,7 +146,6 @@ class ServerTreeView(TreeView):
self.controller.mediator.grid.conpan.lan.set_visible(True) self.controller.mediator.grid.conpan.lan.set_visible(True)
def _on_unmap(self, a) -> None: def _on_unmap(self, a) -> None:
# TODO: could be done just when changing pages?
if self.get_enum() is ServerTab.LAN: if self.get_enum() is ServerTab.LAN:
self.controller.mediator.grid.conpan.lan.set_visible(False) self.controller.mediator.grid.conpan.lan.set_visible(False)
@@ -199,7 +199,7 @@ class ServerTreeView(TreeView):
if self.current_proc and self.current_proc.is_alive(): if self.current_proc and self.current_proc.is_alive():
self.current_proc.terminate() self.current_proc.terminate()
def _on_distcalc_started(self, treeview): def _on_distcalc_started(self, treeview: Self):
record = self.get_record() record = self.get_record()
if record is None: if record is None:
return return