Compare commits

..
16 Commits
Author SHA1 Message Date
aclist 20dab38fff Merge pull request #351 from aclist/fix/enqueue-mods
Mirror to Codeberg / mirror-to-codeberg (push) Has been cancelled
fix: enqueue mods
2026-06-07 21:59:49 +09:00
aclist 1ddc4a5ff1 chore: bump version 2026-06-07 21:58:44 +09:00
aclist d605447fca chore: clear typehinting errors 2026-06-07 21:51:55 +09:00
aclist 94fe3f9dff fix: suppress search popup (#353) 2026-06-07 21:49:41 +09:00
aclist a966993999 fix: invoke steam client directly (#348) 2026-06-07 21:21:49 +09:00
aclist 8ee3b36816 fix: restore mod statusbar when returning from other pages 2026-06-07 21:20:56 +09:00
aclist 3af53775cd fix: grab focus on tree 2026-06-07 21:20:32 +09:00
aclist e2d6a462db fix: suppress headers if no model is loaded 2026-06-07 20:23:13 +09:00
aclist c4cc9f04c4 Merge pull request #347 from aclist/chore/clean-statusbar
Mirror to Codeberg / mirror-to-codeberg (push) Waiting to run
chore: drop unused code from statusbar
2026-06-06 17:56:03 +09:00
aclist bcd59162b1 chore: drop unused code from statusbar 2026-06-06 17:55:05 +09:00
aclist 2f0bac75ac Merge pull request #346 from aclist/fix/permit-filters
fix: permit legacy filters
2026-06-06 17:53:19 +09:00
aclist 9191f43c8e chore: drop comment 2026-06-06 17:50:53 +09:00
aclist 2419fedd19 chore: format exceptions with type 2026-06-06 17:50:32 +09:00
aclist 83a7996189 The filter panel previously did not pack filter labels homogeneously, so
the width of the "Unofficial" label was being manually constrained by
abbreviating it. Labels auto ellipsize and adjust now, so the full name
is used. The filter file check is also allowed to fail if the user's
filter preferences file contains old key names.
2026-06-06 17:39:49 +09:00
aclist 9d3aa8d9f5 Merge pull request #344 from aclist/fix/stringify-exceptions
Mirror to Codeberg / mirror-to-codeberg (push) Waiting to run
fix: stringify Exception when passing to dialog
2026-06-06 02:31:09 +09:00
aclist 0de0dd4a03 fix: stringify Exception when passing to dialog 2026-06-06 02:29:09 +09:00
13 changed files with 57 additions and 58 deletions

No files matched your search

+4 -2
View File
@@ -14,6 +14,7 @@ from typing import Any, Optional, TYPE_CHECKING, Union
from dzgui.api.bm import map_id_to_record
from dzgui.const.constants import APP_NAME, REQUEST_TIMEOUT
from dzgui.const.endpoints import STEAM_SERVERS
from dzgui.util.format import format_exception
from dzgui.util import strings
import a2s
@@ -272,8 +273,9 @@ def source_info_to_dict(ip: str, qport: int, info: "SourceInfo") -> dict[str, An
res["ping"] = ping
return res
except Exception as e:
# TODO: generalized function
logger.critical(f"{type(e).__name__}: {e} ({ip}:{qport})")
msg = format_exception(e)
msg += f" ({ip}:{qport})"
logger.critical(msg)
raise e
+4 -9
View File
@@ -73,14 +73,9 @@ def get_local_signatures(version_file: Path) -> dict[str, int]:
return hashes
def enqueue_mod(mod: str, appid: int) -> None:
args = [
"steam",
f"steam://url/CommunityFilePage/{mod}+workshop_download_item",
str(appid),
mod,
]
subprocess.Popen(["/usr/bin/env", "bash", *args])
def enqueue_mod(client: str, mod: str, appid: int) -> None:
client_args = concat_bash_args(client)
subprocess.Popen([*client_args, "+workshop_download_item", str(appid), mod])
def get_needs_update(
@@ -146,7 +141,7 @@ def connect(client: str, addr: str, appid: int, name: str, mods: list[str]) -> i
return proc.returncode
def load_to_menu(client: str, addr: str, appid: int, name: str, mods: list[str]) -> int:
def load_to_menu(client: str, appid: int, name: str, mods: list[str]) -> int:
"""Loads to the menu screen with the selected mods; used for AFK/pre-joining"""
concat = concat_mods(mods)
client_args = concat_bash_args(client)
+7 -6
View File
@@ -88,6 +88,7 @@ class ConnectionManager:
self.record: Record
self.workshop: Path
self.client: str
self.remote_mod_ids: list[str] = []
self.missing_mods: list[tuple[str, str, int, int]] = []
@@ -171,6 +172,7 @@ class ConnectionManager:
client = self.controller.query_config(Preferences.CLIENT)
running = is_steam_running(client)
steam_proc = SteamProcess(client_name, running)
self.client = client
game_mode = prefs.is_game_mode
@@ -238,8 +240,7 @@ class ConnectionManager:
def query_modlist_and_present(self, record: Servers.Record) -> None:
try:
mods = self._query_modlist(record)
except Exception as e:
print(f"{type(e).__name__}: {e}")
except Exception:
self.thread_man.set_cleanup_func(
StoredFunc(self._server_timeout), destroy_first=True
)
@@ -264,11 +265,10 @@ class ConnectionManager:
def _connect_steam(self, menu_only: bool) -> None:
addr = f"{self.record.ip}:{self.record.gameport}"
playername = self.controller.query_config(Preferences.NAME)
client = self.controller.query_config(Preferences.CLIENT)
if menu_only:
rc = load_to_menu(client, addr, self.appid, playername, self.remote_mod_ids)
rc = load_to_menu(self.client, self.appid, playername, self.remote_mod_ids)
else:
rc = connect(client, addr, self.appid, playername, self.remote_mod_ids)
rc = connect(self.client, addr, self.appid, playername, self.remote_mod_ids)
if rc != 0:
# TODO: log/pop the error
func = StoredFunc(self.controller.update_status)
@@ -299,7 +299,8 @@ class ConnectionManager:
for title, mod, stamp, size in self.missing_mods:
if self.controller.is_cancel_pending():
return
enqueue_mod(mod, self.appid)
enqueue_mod(self.client, mod, self.appid)
# NOTE: prevents rate limiting
time.sleep(3)
if raise_window is True:
+1 -1
View File
@@ -53,7 +53,7 @@ class UpdateManager:
func = StoredFunc(self._on_update_failure, msg)
self.thread_man.set_cleanup_func(func, destroy_first=True)
except Exception as e:
func = StoredFunc(self._on_update_failure, e)
func = StoredFunc(self._on_update_failure, str(e))
self.thread_man.set_cleanup_func(func, destroy_first=True)
logger.warning(e)
+4
View File
@@ -83,3 +83,7 @@ def format_player_count(model: Gtk.TreeModel | None, control: list) -> str:
def embolden(text: str) -> str:
return f"<b>{text}</b>"
def format_exception(e: Exception) -> str:
return f"{type(e).__name__}: {e}"
+1 -1
View File
@@ -181,7 +181,7 @@ filter_lowpop = "Low pop"
filter_nonascii = "Non-ASCII"
filter_duplicate = "Duplicate"
filter_official = "Official"
filter_unofficial = "Unoffic."
filter_unofficial = "Unofficial"
filter_modded = "Modded"
# maps
+8 -3
View File
@@ -11,6 +11,7 @@ from dzgui.const.constants import (
)
from dzgui.const.enum import FilterMode
from dzgui.model.servers import ServerModelManager
from dzgui.util.format import format_exception
from dzgui.util.strings import all_maps
from dzgui.views.components.maps_combo import MapsCombo
from dzgui.views.components.labels import BoldLabel
@@ -333,6 +334,10 @@ class FilterPanel(Gtk.Box):
self.emitter.emit("map_selection_changed", name)
def set_filters(self, filters: dict[str, bool]) -> None:
for check in self.button_grid.checks:
label = check.get_label()
check.set_active(filters[label])
try:
for check in self.button_grid.checks:
label = check.get_label()
check.set_active(filters[label])
except Exception as e:
msg = format_exception(e)
logger.warning(msg)
+12 -23
View File
@@ -1,5 +1,4 @@
from typing import Self, Union, TYPE_CHECKING
from warnings import deprecated
from dzgui.const.enum import NotebookPage, ServerTab
from dzgui.util.strings import esc_to_return, question_to_return
@@ -8,7 +7,7 @@ from dzgui.views.components.buttons import LoggerAlertsButton
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GObject # noqa E402
from gi.repository import Gtk # noqa E402
if TYPE_CHECKING:
from dzgui.const.enum import ServerTab
@@ -26,6 +25,8 @@ class Statusbar(Gtk.Grid):
self.controller.register_widget("statusbar", self)
self.emitter = controller.get_emitter()
self.prior_enum: NotebookPage
self.prior_status: str
self.playercount = ""
self.statusbar = Gtk.Statusbar()
@@ -34,14 +35,6 @@ class Statusbar(Gtk.Grid):
self.attach(self.statusbar, 0, 0, 3, 1)
self.attach_next_to(self.spinner, self.statusbar, Gtk.PositionType.RIGHT, 3, 1)
# TODO: pack version event box in right panel into hbox with update button
# TODO: spawns a modal or just jumps right to install page
# from dzgui.views.components.buttons import IconTextButton
# b = IconTextButton("dialog-information-symbolic", label="Updates available")
# b.set_halign(Gtk.Align.END)
# b.set_hexpand(True)
# self.attach_next_to(b, self.spinner, Gtk.PositionType.RIGHT, 3, 1)
self.players = ""
controller.get_menu().connect("generic_treesel_changed", self._help_row_changed)
@@ -79,6 +72,7 @@ class Statusbar(Gtk.Grid):
NotebookPage.LOG,
NotebookPage.CONNECTION,
):
self.prior_enum = enum
self.set_by_context(enum, esc_to_return)
return
@@ -88,16 +82,11 @@ class Statusbar(Gtk.Grid):
case NotebookPage.KEYS:
bar = question_to_return
case _:
return
bar = self.prior_status
self.prior_enum = enum
self.set_by_context(enum, bar)
# TODO: unused
def _on_notebook_page_returned(
self, statusbar: Self, prior_context: NotebookPage
) -> None:
self.pop(prior_context)
def _on_server_row_changed(self, statusbar: Self) -> None:
self.spinner.start()
@@ -111,6 +100,10 @@ class Statusbar(Gtk.Grid):
context: Union["ServerTab", NotebookPage],
) -> None:
self.spinner.stop()
if type(context) is NotebookPage:
self.prior_enum = context
self.prior_status = ""
# FIXME: CalcDist is being called when table is not loaded
if dist is None:
return
@@ -132,15 +125,11 @@ class Statusbar(Gtk.Grid):
cid = self.statusbar.get_context_id(str(context))
self.statusbar.pop(cid)
@deprecated("currently unused")
# def get_text(self) -> str:
# area = self.statusbar.get_message_area()
# label = area.get_children()[0]
# return str(label.get_text())
def set_by_context(
self, context: Union[NotebookPage, "ServerTab"], string: str
) -> None:
if context != NotebookPage.KEYS:
self.prior_status = string
meta = self.statusbar.get_context_id(str(context))
self.statusbar.push(meta, string)
self.set_cache(string)
+2 -2
View File
@@ -14,6 +14,7 @@ from dzgui.init.update import check_updates
from dzgui.const.constants import EXPAND, FILL
from dzgui.managers.threading import call_on_thread, StoredFunc, ThreadingManager
from dzgui.strings import preboot
from dzgui.util.format import format_exception
from dzgui.util.strings import dialog_header
from dzgui.util.symlink import rebuild_symlinks
from dzgui.views.components.buttons import ClipboardButton
@@ -175,8 +176,7 @@ class BootDialog(Gtk.Dialog):
self.thread_man.set_cleanup_func(callback)
except Exception as e:
self.failed = True
# TODO: generic exception wrapper
self.exception = f"{type(e).__name__}: {e}"
self.exception = format_exception(e)
callback = StoredFunc(self.update_status, Success.FAIL)
self.thread_man.set_cleanup_func(callback)
+1 -4
View File
@@ -327,10 +327,7 @@ class PreConnectionAssistant(Gtk.Box):
self.connect_last.hide()
self._process_warnings(prereqs)
if self.tree.is_visible():
self.tree.grab_focus()
else:
self.grab_focus()
self.tree.grab_focus()
def mark_finished(self) -> None:
self.mod_count.set_label(preconnect.all_updated)
+11 -4
View File
@@ -4,7 +4,7 @@ from typing import Any, Optional, TYPE_CHECKING
from warnings import deprecated
from dzgui.const.constants import APP_NAME, SEPARATOR
from dzgui.util.keys import is_navkey
from dzgui.util.keys import is_ctrl_mask, is_navkey
from dzgui.views.mixins.cursor_mixin import CursorMixin
import gi
@@ -85,10 +85,17 @@ class TreeView(CursorMixin, Gtk.TreeView): # type: ignore
model, rows = sel.get_selected_rows()
return [model[row] for row in rows]
def _on_keypress(self, treeview: Gtk.TreeView, event: Gdk.EventKey) -> None:
def _on_keypress(self, treeview: Gtk.TreeView, event: Gdk.EventKey) -> bool:
from dzgui.views.trees.tree_servers import ServerTreeView
# NOTE: suppress popup search and delegate binding to server view (cf. request_keyword_focus)
if not isinstance(treeview, ServerTreeView):
if is_ctrl_mask(event) and event.keyval == Gdk.KEY_f:
return True
if is_navkey(event.keyval):
if self.get_model() is None:
return
return True
if self.sel_blocked is False:
self.sel_blocked = True
self.controller.suppress_signal(
@@ -98,7 +105,7 @@ class TreeView(CursorMixin, Gtk.TreeView): # type: ignore
True,
)
self._vim_nav(event)
return
return False
def _on_key_release(self, treeview: Gtk.TreeView, event: Gdk.EventKey) -> None:
"""
+1 -2
View File
@@ -30,8 +30,6 @@ class ModTreeView(ModsMixin, ContextMixin, TreeView): # type: ignore
emitter.connect("mods_highlighted", self._on_mods_highlighted)
self.set_fixed_height_mode(True)
self.set_headers_visible(True)
self.set_model(None)
for i, column_title in enumerate(strings.mod_cols):
@@ -64,6 +62,7 @@ class ModTreeView(ModsMixin, ContextMixin, TreeView): # type: ignore
def _on_mods_updated(self, emitter: "Emitter", msg: str, mods: int) -> None:
if mods < 1:
return
self.set_headers_visible(True)
path = Gtk.TreePath.new_from_indices([0])
self.set_cursor(path)
+1 -1
View File
@@ -4,7 +4,7 @@ description = "DayZ server browser and mod manager for Linux"
authors = [
{name = "aclist"}
]
version = "7.0.0b10"
version = "7.0.0b11"
license = "GPL-3.0-or-later"
license-files = ["LICENSE"]
readme = "README.md"