mirror of
https://github.com/aclist/dztui.git
synced 2026-08-26 17:57:06 +02:00
fix: statusbar race condition WIP
This commit is contained in:
parent
2f9000ee80
commit
9cc1ece0d7
@ -1,7 +1,6 @@
|
||||
import logging
|
||||
import shutil
|
||||
import threading
|
||||
import textwrap
|
||||
import traceback
|
||||
|
||||
from pathlib import Path
|
||||
@ -17,23 +16,16 @@ from dzgui.api.mods import (
|
||||
get_local_mod_path,
|
||||
find_stale_mods,
|
||||
_hash,
|
||||
remove_stale_signatures
|
||||
)
|
||||
from dzgui.const.enum import (
|
||||
Preferences,
|
||||
Popup,
|
||||
NotebookPage,
|
||||
ButtonType,
|
||||
ContextMenu,
|
||||
RowType
|
||||
remove_stale_signatures,
|
||||
)
|
||||
from dzgui.const.enum import Preferences, NotebookPage, ButtonType, ContextMenu, RowType
|
||||
|
||||
from dzgui.const.constants import (
|
||||
APPID_DAYZ,
|
||||
APPID_DAYZ_EXP,
|
||||
HEX_RED,
|
||||
WINDOW_DEFAULT_X,
|
||||
WINDOW_DEFAULT_Y
|
||||
WINDOW_DEFAULT_Y,
|
||||
)
|
||||
|
||||
from dzgui.config import update
|
||||
@ -45,19 +37,20 @@ from dzgui.util.diag import write_diagnostic
|
||||
from dzgui.util._json import read_json, write_json
|
||||
from dzgui.util.localize import number
|
||||
from dzgui.util.open_links import open_workshop_page, open_user_workshop
|
||||
from dzgui.util.format import format_mods, format_player_count, pluralize
|
||||
from dzgui.util.format import format_mods, format_player_count
|
||||
from dzgui.util.redact import redact_log
|
||||
|
||||
from dzgui.views.dialogs.filepicker import FilePicker
|
||||
from dzgui.views.dialogs.generic import GenericDialog
|
||||
|
||||
import gi
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
from gi.repository import Gtk, Gdk, GLib, GObject # noqa E402
|
||||
from gi.repository import Gtk, Gdk, GLib, GObject # noqa E402
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from dzgui.const.enum import ServerTab
|
||||
from dzgui.util.dist import Haversine
|
||||
from dzgui.views.base import Notebook, Grid, OuterWindow
|
||||
from dzgui.views.components.buttonbox import ContextualButton
|
||||
@ -65,12 +58,12 @@ if TYPE_CHECKING:
|
||||
from dzgui.views.components.right_panel import RightPanel
|
||||
from dzgui.views.components.statusbar import Statusbar
|
||||
from dzgui.views.pages.servers import ServerNotebook
|
||||
from dzgui.views.trees.tree_base import TreeView
|
||||
from dzgui.views.trees.tree_log import LogTreeView
|
||||
from dzgui.views.trees.tree_menu import MenuTreeView
|
||||
from dzgui.views.trees.tree_mods import ModTreeView
|
||||
from dzgui.views.trees.tree_servers import ServerTreeView
|
||||
|
||||
|
||||
class AppNavigation:
|
||||
window: "OuterWindow"
|
||||
grid: "Grid"
|
||||
@ -80,16 +73,16 @@ class AppNavigation:
|
||||
modtreeview: "ModTreeView"
|
||||
menu: "MenuTreeView"
|
||||
servers: "ServerNotebook"
|
||||
browser: "ServerTreeView"
|
||||
saved: "ServerTreeView"
|
||||
recent: "ServerTreeView"
|
||||
lan: "ServerTreeView"
|
||||
logtreeview: "LogTreeView"
|
||||
filters: "FilterPanel"
|
||||
|
||||
|
||||
class Controller:
|
||||
def __init__(self) -> None:
|
||||
self.dist_cache: dict[str, "Haversine"] = {}
|
||||
self.dist_cache: dict[str, "Haversine", "ServerTab"] = {}
|
||||
self.crumbs_cache = ""
|
||||
self.mediator = AppNavigation()
|
||||
self.prefs: UserPrefs
|
||||
@ -128,7 +121,8 @@ class Controller:
|
||||
|
||||
def terminate_process(self) -> None:
|
||||
# TODO: only used by server table multiprocessing queue
|
||||
self.mediator.notebook.servers.browser.terminate_process()
|
||||
tv = self.get_active_treeview()
|
||||
tv.terminate_process()
|
||||
|
||||
def get_prefs(self) -> UserPrefs:
|
||||
return self.prefs
|
||||
@ -165,7 +159,9 @@ class Controller:
|
||||
"_on_tree_selection_changed",
|
||||
state,
|
||||
)
|
||||
self.suppress_signal(self.mediator.menu, self.mediator.menu, "_on_keypress", state)
|
||||
self.suppress_signal(
|
||||
self.mediator.menu, self.mediator.menu, "_on_keypress", state
|
||||
)
|
||||
for check in self.mediator.filters.checks:
|
||||
self.suppress_signal(
|
||||
self.mediator.filters,
|
||||
@ -184,7 +180,7 @@ class Controller:
|
||||
else:
|
||||
widget.handler_unblock_by_func(func)
|
||||
# TODO: deprecated?
|
||||
#self.mediator.menu.sel_blocked = state
|
||||
# self.mediator.menu.sel_blocked = state
|
||||
|
||||
def toggle_debug_mode(self) -> None:
|
||||
self.toggle_config(Preferences.DEBUG)
|
||||
@ -234,14 +230,22 @@ class Controller:
|
||||
def get_statusbar(self) -> str:
|
||||
return self.mediator.statusbar.get_text()
|
||||
|
||||
def set_statusbar(self, text: str) -> None:
|
||||
self.mediator.statusbar.set_text(text)
|
||||
def remove_statusbar(self, context: str) -> None:
|
||||
c = self.mediator.statusbar.statusbar.get_context_id(context)
|
||||
self.mediator.statusbar.statusbar.remove_all(c)
|
||||
|
||||
def set_statusbar(self, text: str, context: str) -> None:
|
||||
self.mediator.statusbar.set_text(text, context)
|
||||
|
||||
def set_statusbar_placeholder(self, text: str) -> None:
|
||||
# TODO: use statusbar stacks instead
|
||||
self.statusbar_placeholder = text
|
||||
|
||||
def set_statusbar_dist(self, haversine: "Haversine") -> None:
|
||||
def set_statusbar_dist(self, haversine: "Haversine", enum: "ServerTab") -> None:
|
||||
tv = self.get_active_treeview()
|
||||
if tv.get_enum() != enum:
|
||||
self.mediator.statusbar.spinner.stop()
|
||||
return
|
||||
dist: str
|
||||
if haversine is None:
|
||||
dist = "Unknown"
|
||||
@ -255,7 +259,8 @@ class Controller:
|
||||
separated = number(raw)
|
||||
dist = str(separated) + " km"
|
||||
text = self.statusbar_placeholder
|
||||
self.set_statusbar(f"{text} | Distance: {dist}")
|
||||
# TODO: abstract
|
||||
self.set_statusbar(f"{text} | Distance: {dist}", "Servers")
|
||||
self.mediator.statusbar.spinner.stop()
|
||||
|
||||
def delete_multiple_mods(self) -> None:
|
||||
@ -328,7 +333,7 @@ class Controller:
|
||||
def show_developers_page(self) -> None:
|
||||
self.open_page(NotebookPage.DEVELOPERS)
|
||||
# TODO: put cursor on first row
|
||||
#self.mediator.developers.focus_first_row()
|
||||
# self.mediator.developers.focus_first_row()
|
||||
|
||||
def open_page(self, page: NotebookPage) -> None:
|
||||
self.mediator.grid.notebook.set_page_by_enum(page)
|
||||
@ -386,7 +391,9 @@ class Controller:
|
||||
open_workshop_page(mod, cmd)
|
||||
|
||||
# TODO: put in model manager (dedicated manager for mod store)
|
||||
def get_mod_from_tree_path(self, tree_path: Gtk.TreePath) -> tuple[str, Gtk.TreeIter]:
|
||||
def get_mod_from_tree_path(
|
||||
self, tree_path: Gtk.TreePath
|
||||
) -> tuple[str, Gtk.TreeIter]:
|
||||
model = self.model_man.get_mod_store()
|
||||
tree_iter = model.get_iter(tree_path)
|
||||
mod = model.get(tree_iter, 2)[0]
|
||||
@ -403,7 +410,7 @@ class Controller:
|
||||
app_path = PeFile.get_nested_app_path(steam_path, APPID_DAYZ)
|
||||
|
||||
md5 = _hash(mod)
|
||||
symlink = app_path / md5
|
||||
symlink = app_path / md5
|
||||
symlink.unlink()
|
||||
shutil.rmtree(mods_path / mod)
|
||||
|
||||
@ -419,15 +426,13 @@ class Controller:
|
||||
model = self.model_man.get_mod_store()
|
||||
model.remove(it)
|
||||
|
||||
|
||||
def update_mod_statusbar(self) -> None:
|
||||
total_mods, total_size = self.calc_mod_size()
|
||||
msg = format_mods(total_size, total_mods)
|
||||
# TODO: combine
|
||||
meta = self.mediator.statusbar.statusbar.get_context_id("Mods")
|
||||
self.mediator.statusbar.statusbar.push(meta, msg)
|
||||
#meta = self.mediator.statusbar.set_text(msg)
|
||||
|
||||
# meta = self.mediator.statusbar.statusbar.get_context_id("Mods")
|
||||
# self.mediator.statusbar.statusbar.push(meta, msg)
|
||||
self.mediator.statusbar.set_text(msg, "Mods")
|
||||
|
||||
def calc_mod_size(self) -> tuple[int, int]:
|
||||
model = self.model_man.get_mod_store()
|
||||
@ -437,7 +442,6 @@ class Controller:
|
||||
total_size += mod[3]
|
||||
return total_mods, total_size
|
||||
|
||||
|
||||
def menu_action(self, action: ContextMenu, path: Gtk.TreePath) -> None:
|
||||
match action:
|
||||
# NOTE: manipulates server stores
|
||||
@ -470,14 +474,12 @@ class Controller:
|
||||
# Gtk.TreeModel, row-inserted/row-deleted
|
||||
self.update_mod_statusbar()
|
||||
remove_stale_signatures(
|
||||
self.prefs.paths.config,
|
||||
self.prefs.paths.version
|
||||
self.prefs.paths.config, self.prefs.paths.version
|
||||
)
|
||||
|
||||
case ContextMenu.OPEN_WORKSHOP:
|
||||
self.open_mod_page(path)
|
||||
|
||||
|
||||
def toggle_mod_selection(self, state: bool) -> None:
|
||||
sel = self.mediator.modtreeview.get_selection()
|
||||
if state:
|
||||
@ -493,7 +495,9 @@ class Controller:
|
||||
# NOTE: this model is reloaded each time as log changes
|
||||
try:
|
||||
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:
|
||||
clean = redact_log(record)
|
||||
store.append(clean)
|
||||
@ -535,6 +539,7 @@ class Controller:
|
||||
|
||||
def dump_test_1(self) -> None:
|
||||
import time
|
||||
|
||||
time.sleep(1)
|
||||
# TODO: use model managers, etc.
|
||||
self.data = (
|
||||
@ -561,6 +566,7 @@ class Controller:
|
||||
|
||||
def dump_test_2(self) -> None:
|
||||
import time
|
||||
|
||||
time.sleep(1)
|
||||
# TODO: use model managers, etc.
|
||||
self.data = (
|
||||
@ -570,7 +576,7 @@ class Controller:
|
||||
self.get_func_data()
|
||||
self.destroy_on_idle()
|
||||
|
||||
def get_func_data(self):
|
||||
def get_func_data(self) -> None:
|
||||
def test():
|
||||
# NOTE: do not insert model until main thread is idle
|
||||
# TODO: grab from model manager and insert entire model
|
||||
@ -580,6 +586,7 @@ class Controller:
|
||||
treeview.set_loaded(True)
|
||||
self.update_server_status()
|
||||
treeview.grab_focus()
|
||||
|
||||
treeview = self.get_active_treeview()
|
||||
|
||||
manager = treeview.get_filter_man()
|
||||
@ -591,6 +598,7 @@ class Controller:
|
||||
|
||||
def call_on_thread(self, func: Callable, *args) -> None:
|
||||
from dzgui.views.dialogs.generic import WaitDialog
|
||||
|
||||
self.wait_dialog = WaitDialog(self, strings.dialog.fetching)
|
||||
self.wait_dialog.show_all()
|
||||
thread = threading.Thread(target=func, args=args)
|
||||
@ -607,7 +615,7 @@ class Controller:
|
||||
Lets calling widgets register a callback function
|
||||
when spawning a threaded process
|
||||
"""
|
||||
self.callback = { "func": callback, "args": args }
|
||||
self.callback = {"func": callback, "args": args}
|
||||
|
||||
def destroy_on_idle(self) -> None:
|
||||
self.wait_dialog.destroy()
|
||||
@ -642,7 +650,6 @@ class Controller:
|
||||
dialog = ExceptionDialog(self, strings.api_error)
|
||||
dialog.run()
|
||||
|
||||
|
||||
def update_api_key(self, text: str, key: Preferences) -> None:
|
||||
self.call_on_thread(self.test_api_response, text, key)
|
||||
|
||||
@ -688,13 +695,15 @@ class Controller:
|
||||
def update_server_status(self) -> None:
|
||||
# TODO: emit signal on statusbar only if page changed
|
||||
treeview = self.get_active_treeview()
|
||||
context = str(treeview.enum)
|
||||
model = treeview.get_model()
|
||||
status = format_player_count(model)
|
||||
self.set_statusbar_placeholder(status)
|
||||
self.set_statusbar(status)
|
||||
self.mediator.statusbar.spinner.start()
|
||||
if len(model) < 1:
|
||||
self.mediator.statusbar.spinner.stop()
|
||||
|
||||
self.set_statusbar(status, context)
|
||||
if len(model) >= 1:
|
||||
self.mediator.statusbar.spinner.start()
|
||||
# self.mediator.statusbar.spinner.stop()
|
||||
|
||||
def populate_model(self) -> None:
|
||||
treeview = self.get_active_treeview()
|
||||
@ -718,8 +727,8 @@ class Controller:
|
||||
crumbs = self.mediator.servers.get_cached_label()
|
||||
self.set_crumbs(crumbs)
|
||||
# TODO: could emit this when treeview gains keyboard focus
|
||||
#tree = self.get_active_treeview()
|
||||
#tree.emit("on_distcalc_started")
|
||||
# tree = self.get_active_treeview()
|
||||
# tree.emit("on_distcalc_started")
|
||||
|
||||
def toggle_check(self, event: Gdk.EventKey) -> None | Literal[False]:
|
||||
mappings = {
|
||||
@ -739,7 +748,6 @@ class Controller:
|
||||
if event.keyval not in mappings:
|
||||
return False
|
||||
index = mappings[event.keyval]
|
||||
# TODO: register filter panel widget
|
||||
self.mediator.filters.toggle_check(index)
|
||||
|
||||
def get_favorite(self) -> tuple[str, str] | tuple[None, None]:
|
||||
|
||||
@ -44,10 +44,12 @@ args = parser.parse_args()
|
||||
if TYPE_CHECKING:
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
# TODO: profile load time
|
||||
def make_parents(path: "Path") -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
def uninstall() -> None:
|
||||
# TODO: uninstall data files (-u)
|
||||
# -u removes state, log, freedesktop
|
||||
@ -56,6 +58,7 @@ def uninstall() -> None:
|
||||
# XDG_DATA_HOME/dzgui
|
||||
pass
|
||||
|
||||
|
||||
def main() -> None:
|
||||
lock = lock_acquire() # noqa
|
||||
|
||||
@ -85,7 +88,9 @@ def main() -> None:
|
||||
migrate_cols_file(XDG.columns)
|
||||
copy_state_files(xdg_paths["XDG_STATE_HOME"])
|
||||
|
||||
_format = "%(asctime)s␞%(levelname)s␞%(filename)s::%(funcName)s::%(lineno)s␞%(message)s"
|
||||
_format = (
|
||||
"%(asctime)s␞%(levelname)s␞%(filename)s::%(funcName)s::%(lineno)s␞%(message)s"
|
||||
)
|
||||
logging.basicConfig(filename=XDG.debug, format=_format, level=logging.DEBUG)
|
||||
with open(XDG.debug, "w") as f:
|
||||
f.truncate(0)
|
||||
@ -126,7 +131,7 @@ def main() -> None:
|
||||
local_coords,
|
||||
version,
|
||||
allow,
|
||||
XDG
|
||||
XDG,
|
||||
)
|
||||
print("All OK. Loading UI...")
|
||||
App(prefs)
|
||||
|
||||
@ -4,19 +4,20 @@ from math import radians, cos, sin, asin, sqrt
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from dzgui.util.ip import GeolocationError, get_coords
|
||||
from dzgui.util.localize import number
|
||||
|
||||
import gi
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
from gi.repository import Gtk # noqa E402
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from dzgui.util.ip import Coords
|
||||
from dzgui.const.enum import ServerTab
|
||||
from dzgui.controllers.mc import Controller
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class Haversine():
|
||||
|
||||
class Haversine:
|
||||
def __init__(self, lat1: float, lon1: float, lat2: float, lon2: float) -> None:
|
||||
|
||||
R = 6371 * 1000
|
||||
@ -26,7 +27,7 @@ class Haversine():
|
||||
lat1 = radians(lat1)
|
||||
lat2 = radians(lat2)
|
||||
|
||||
a = sin(dLat/2) ** 2 + cos(lat1) * cos(lat2) * sin(dLon/2) ** 2
|
||||
a = sin(dLat / 2) ** 2 + cos(lat1) * cos(lat2) * sin(dLon / 2) ** 2
|
||||
c = 2 * asin(sqrt(a))
|
||||
|
||||
self.dist = R * c
|
||||
@ -37,15 +38,18 @@ class Haversine():
|
||||
def as_miles(self) -> float:
|
||||
return self.dist / 1609.344
|
||||
|
||||
|
||||
class CalcDist(multiprocessing.Process):
|
||||
def __init__(
|
||||
self,
|
||||
addr: str,
|
||||
enum: "ServerTab",
|
||||
result_queue: multiprocessing.Queue,
|
||||
controller: "Controller",
|
||||
) -> None:
|
||||
super().__init__()
|
||||
|
||||
self.enum = enum
|
||||
self.controller = controller
|
||||
self.result_queue = result_queue
|
||||
self.addr = addr
|
||||
@ -55,11 +59,11 @@ class CalcDist(multiprocessing.Process):
|
||||
cache = self.controller.get_dist_cache()
|
||||
if self.addr in cache:
|
||||
logger.info(f"Address '{self.addr}' already in cache")
|
||||
self.result_queue.put([self.addr, cache[self.addr]])
|
||||
self.result_queue.put([self.addr, cache[self.addr], self.enum])
|
||||
return
|
||||
|
||||
dist = self.compare(self.ip)
|
||||
self.result_queue.put([self.addr, dist])
|
||||
self.result_queue.put([self.addr, dist, self.enum])
|
||||
|
||||
def compare(self, remote: str) -> int | None:
|
||||
prefs = self.controller.get_prefs()
|
||||
|
||||
@ -1,13 +1,7 @@
|
||||
import logging
|
||||
#import multiprocessing
|
||||
import signal
|
||||
#import threading
|
||||
import typing # noqa
|
||||
import warnings
|
||||
|
||||
#from concurrent.futures import wait
|
||||
#from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
from typing import TYPE_CHECKING, Literal
|
||||
|
||||
from dzgui.const.enum import NotebookPage
|
||||
@ -37,6 +31,7 @@ if TYPE_CHECKING:
|
||||
from dzgui.config.userprefs import UserPrefs
|
||||
|
||||
import gi
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
from gi.repository import Gtk, GLib, Gdk, GObject, Pango # noqa E402
|
||||
|
||||
@ -46,10 +41,10 @@ warnings.filterwarnings("ignore", ".*g_value_get_int", Warning)
|
||||
|
||||
# TODO: move to controller
|
||||
# kilometer cache; note, user may change measurement it partway through, flush cache
|
||||
#cache: dict[str, int] = {}
|
||||
# cache: dict[str, int] = {}
|
||||
|
||||
## TODO: move to configs/servers
|
||||
#def query_history() -> list | None:
|
||||
# def query_history() -> list | None:
|
||||
# history_file = MainController.get_prefs().paths.history
|
||||
# try:
|
||||
# with open(history_file, "r") as f:
|
||||
@ -58,7 +53,7 @@ warnings.filterwarnings("ignore", ".*g_value_get_int", Warning)
|
||||
# rows = None
|
||||
# return rows
|
||||
#
|
||||
#def process_tree_option(choice: RowType) -> None:
|
||||
# def process_tree_option(choice: RowType) -> None:
|
||||
# # server tables
|
||||
# if command == RowType.RESOLVE_IP:
|
||||
# record = treeview.get_record()
|
||||
@ -91,18 +86,18 @@ warnings.filterwarnings("ignore", ".*g_value_get_int", Warning)
|
||||
# return
|
||||
#
|
||||
## TODO: belongs in model
|
||||
#def str_to_record(record: str) -> Record | None:
|
||||
# def str_to_record(record: str) -> Record | None:
|
||||
# r = record.split(":")
|
||||
# if len(r) != 3:
|
||||
# return None
|
||||
# return Record(r[0], int(r[1]), int(r[2]))
|
||||
#
|
||||
## TODO: ibid
|
||||
#def record_to_str(record: Record) -> str:
|
||||
# def record_to_str(record: Record) -> str:
|
||||
# return f"{record.ip}:{record.gameport}:{record.qport}"
|
||||
#
|
||||
#
|
||||
#def connect_by_ip(enum: RowType, response: str) -> None:
|
||||
# def connect_by_ip(enum: RowType, response: str) -> None:
|
||||
# def _prep(response: str) -> None:
|
||||
# record = Servers.validate_ip(response)
|
||||
# proc = treeview.prepare_connection(record)
|
||||
@ -114,7 +109,7 @@ warnings.filterwarnings("ignore", ".*g_value_get_int", Warning)
|
||||
# return
|
||||
#
|
||||
#
|
||||
#def connect_by_id(enum: RowType, uid: str, key: str) -> None:
|
||||
# def connect_by_id(enum: RowType, uid: str, key: str) -> None:
|
||||
# def _prep(key: str, response: str) -> None:
|
||||
# # TODO: if response is non numeric, raise dialog
|
||||
# if response.isnumeric() is False:
|
||||
@ -139,6 +134,7 @@ warnings.filterwarnings("ignore", ".*g_value_get_int", Warning)
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
class OuterWindow(Gtk.Window):
|
||||
def __init__(self) -> None:
|
||||
super().__init__(title=APP_NAME, border_width=10, icon_name=APP_NAME_LOWER)
|
||||
@ -168,9 +164,7 @@ class OuterWindow(Gtk.Window):
|
||||
MainController.loaded = True
|
||||
MainController.populate_model()
|
||||
|
||||
def _on_delete_event(
|
||||
self, window: "OuterWindow", event: Gdk.EventKey
|
||||
) -> None:
|
||||
def _on_delete_event(self, window: "OuterWindow", event: Gdk.EventKey) -> None:
|
||||
self.halt_proc_and_quit()
|
||||
|
||||
def halt_proc_and_quit(self) -> None:
|
||||
@ -316,7 +310,7 @@ class Notebook(ScrollableMixin, Gtk.Notebook): # type: ignore
|
||||
MainController.set_crumbs(crumbs)
|
||||
|
||||
if status is False:
|
||||
MainController.set_statusbar("")
|
||||
MainController.set_statusbar("", "")
|
||||
if enum is NotebookPage.SERVERS:
|
||||
MainController.present_servers()
|
||||
|
||||
@ -351,8 +345,20 @@ class Grid(Gtk.Grid):
|
||||
els = (
|
||||
(self.crumb_box, self.notebook, Gtk.PositionType.TOP, MAX_COLS, SINGLE_ROW),
|
||||
(self.conpan, self.notebook, Gtk.PositionType.BOTTOM, MAX_COLS, SINGLE_ROW),
|
||||
(self.statusbar, self.conpan, Gtk.PositionType.BOTTOM, MAX_COLS, SINGLE_ROW),
|
||||
(self.right_panel, self.notebook, Gtk.PositionType.RIGHT, SINGLE_COL, MAX_ROWS),
|
||||
(
|
||||
self.statusbar,
|
||||
self.conpan,
|
||||
Gtk.PositionType.BOTTOM,
|
||||
MAX_COLS,
|
||||
SINGLE_ROW,
|
||||
),
|
||||
(
|
||||
self.right_panel,
|
||||
self.notebook,
|
||||
Gtk.PositionType.RIGHT,
|
||||
SINGLE_COL,
|
||||
MAX_ROWS,
|
||||
),
|
||||
)
|
||||
for el, sibling, pos, h_span, v_span in els:
|
||||
self.attach_next_to(el, sibling, pos, h_span, v_span)
|
||||
@ -372,7 +378,7 @@ class Grid(Gtk.Grid):
|
||||
def toggle_refresh_button(self, state: bool) -> None:
|
||||
self.right_panel.refresh_button.set_visible(state)
|
||||
|
||||
# TODO make this method internal to Statusbar
|
||||
# TODO: make this method internal to Statusbar
|
||||
def get_breadcrumbs(self) -> str:
|
||||
return self.breadcrumbs.get_text()
|
||||
|
||||
@ -397,9 +403,7 @@ class App(Gtk.Application):
|
||||
)
|
||||
self.win.add_accel_group(accel)
|
||||
|
||||
GLib.unix_signal_add(
|
||||
GLib.PRIORITY_DEFAULT, signal.SIGINT, self._catch_sigint
|
||||
)
|
||||
GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, self._catch_sigint)
|
||||
Gtk.main()
|
||||
|
||||
def _catch_sigint(self) -> Literal[True]:
|
||||
@ -415,9 +419,11 @@ class App(Gtk.Application):
|
||||
) -> None:
|
||||
self.win.halt_proc_and_quit()
|
||||
|
||||
|
||||
MainController = Controller()
|
||||
|
||||
# TODO: icon sanity check, log result
|
||||
#theme = Gtk.IconTheme.get_default()
|
||||
#icons = theme.list_icons(None)
|
||||
#print("steam_tray_mono" in icons)
|
||||
# theme = Gtk.IconTheme.get_default()
|
||||
# icons = theme.list_icons(None)
|
||||
# print("steam_tray_mono" in icons)
|
||||
# FIXME:
|
||||
|
||||
@ -93,6 +93,9 @@ class RefreshButton(IconTextButton):
|
||||
self.connect("clicked", self._on_refresh_clicked)
|
||||
|
||||
def _on_refresh_clicked(self, button: Self) -> None:
|
||||
# TODO: get page enum
|
||||
print(self.controller.mediator.notebook.get_page_by_enum())
|
||||
# if .MODS, .LOG, .OPTIONS, reload in place
|
||||
self.set_sensitive(False)
|
||||
self.show_time(True)
|
||||
GLib.timeout_add_seconds(1, self.decrement)
|
||||
|
||||
@ -26,13 +26,14 @@ class InfoEventBox(Gtk.EventBox):
|
||||
self, eventbox: Gtk.EventBox, eventcrossing: Gdk.EventCrossing
|
||||
) -> None:
|
||||
self.icon.set_opacity(1)
|
||||
self.parent.controller.set_statusbar(self.text)
|
||||
self.parent.controller.set_statusbar(self.text, "Options")
|
||||
|
||||
def _on_leave_tooltip(
|
||||
self, eventbox: Gtk.EventBox, eventcrossing: Gdk.EventCrossing
|
||||
) -> None:
|
||||
self.icon.set_opacity(0.8)
|
||||
self.parent.controller.set_statusbar("")
|
||||
#self.parent.controller.set_statusbar("", "Options")
|
||||
self.parent.controller.remove_statusbar("Options")
|
||||
|
||||
def set_text(self, text: str) -> None:
|
||||
self.text = text
|
||||
|
||||
@ -19,6 +19,7 @@ class Statusbar(Gtk.Grid):
|
||||
|
||||
help_text = strings.statusbar_helptext
|
||||
|
||||
self.context: int
|
||||
self.statusbar = Gtk.Statusbar()
|
||||
|
||||
self.spinner = Gtk.Spinner()
|
||||
@ -31,7 +32,7 @@ class Statusbar(Gtk.Grid):
|
||||
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.set_text(help_text, "Help")
|
||||
self.players = ""
|
||||
|
||||
def get_text(self) -> str:
|
||||
@ -39,20 +40,41 @@ class Statusbar(Gtk.Grid):
|
||||
label = area.get_children()[0]
|
||||
return label.get_text()
|
||||
|
||||
def set_text(self, string: str) -> None:
|
||||
if string is None:
|
||||
return
|
||||
meta = self.statusbar.get_context_id("Statusbar")
|
||||
def set_text(self, string: str, context: str) -> None:
|
||||
# if string is None:
|
||||
# return
|
||||
meta = self.statusbar.get_context_id(context)
|
||||
#tv = self.controller.get_active_treeview()
|
||||
#cur_context = tv.get_enum()
|
||||
#cid = self.get_context_by_enum(cur_context)
|
||||
#if cid != meta:
|
||||
# print("requested: ", meta)
|
||||
# print("current: ", cid)
|
||||
# return
|
||||
# TODO: substacks
|
||||
# get_context_id(ServerTab)
|
||||
self.statusbar.push(meta, string)
|
||||
#self.set_context(meta)
|
||||
|
||||
|
||||
# TODO: type checking
|
||||
# def get_context_by_enum(self, context: "ServerTab") -> int:
|
||||
# cid = self.statusbar.get_context_id(str(context))
|
||||
# # TODO: substacks
|
||||
# return cid
|
||||
#
|
||||
# def get_context(self) -> int:
|
||||
# return self.context
|
||||
#
|
||||
# def set_context(self, context: int) -> None:
|
||||
# self.context = context
|
||||
|
||||
def refresh(self, row: "RowType") -> None:
|
||||
if row is None:
|
||||
formatted = ""
|
||||
else:
|
||||
formatted = self.format_metadata(row)
|
||||
self.set_text(formatted)
|
||||
self.set_text(formatted, "Help")
|
||||
|
||||
def append_distance(self, dist: str) -> None:
|
||||
# TODO: process strings in controller
|
||||
|
||||
@ -78,6 +78,8 @@ class ServerNotebook(Gtk.ScrolledWindow):
|
||||
def _on_page_changed(self, notebook: Gtk.Notebook, child: Gtk.Widget, index: int) -> None:
|
||||
if self.controller.loaded is False:
|
||||
return
|
||||
|
||||
# self.controller.remove_statusbar("Servers")
|
||||
# TODO: abstract
|
||||
label = self.notebook.get_tab_label_text(child)
|
||||
|
||||
|
||||
@ -1,29 +1,24 @@
|
||||
import logging
|
||||
import multiprocessing
|
||||
import subprocess
|
||||
from typing import Self
|
||||
|
||||
import gi
|
||||
gi.require_version("Gtk", "3.0")
|
||||
from gi.repository import Gtk, GLib, Gdk, GObject, Pango # noqa
|
||||
|
||||
from dzgui.const.enum import (
|
||||
ContextMenu,
|
||||
ContextMenuGroup,
|
||||
RowType,
|
||||
)
|
||||
from dzgui.const.constants import UDP_PORT
|
||||
from dzgui.const.enum import FilterMode, ServerTab
|
||||
from dzgui.const.enum import ContextMenu, ContextMenuGroup, FilterMode, ServerTab
|
||||
from dzgui.api.servers import Record
|
||||
from dzgui.model.filtered_model import FilteredModelManager
|
||||
from dzgui.util.dist import CalcDist
|
||||
from dzgui.util.keys import is_navkey
|
||||
from dzgui.util import strings
|
||||
from typing import Any, Callable, Literal, TYPE_CHECKING
|
||||
from typing import Callable, Literal, TYPE_CHECKING
|
||||
|
||||
from dzgui.views.trees.tree_base import TreeView
|
||||
import dzgui.util._json as JSON # noqa
|
||||
|
||||
import gi
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
from gi.repository import Gtk, GLib, Gdk, GObject, Pango # noqa
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -31,6 +26,7 @@ if TYPE_CHECKING:
|
||||
|
||||
# TODO: restore missing methods
|
||||
|
||||
|
||||
class EnumeratedMenuItem(Gtk.MenuItem):
|
||||
def __init__(self, enum: ContextMenu):
|
||||
super().__init__(label=enum.dict["label"])
|
||||
@ -38,10 +34,11 @@ class EnumeratedMenuItem(Gtk.MenuItem):
|
||||
|
||||
|
||||
class ServerTreeView(TreeView):
|
||||
__gsignals__ = {
|
||||
"on_distcalc_started": (GObject.SignalFlags.RUN_FIRST, None, ())
|
||||
}
|
||||
def __init__(self, controller: "Controller", enum: ServerTab, menu: ContextMenuGroup) -> None:
|
||||
__gsignals__ = {"on_distcalc_started": (GObject.SignalFlags.RUN_FIRST, None, ())}
|
||||
|
||||
def __init__(
|
||||
self, controller: "Controller", enum: ServerTab, menu: ContextMenuGroup
|
||||
) -> None:
|
||||
super().__init__(controller)
|
||||
|
||||
QUEUE_CHECK_DELAY = 200
|
||||
@ -181,11 +178,11 @@ class ServerTreeView(TreeView):
|
||||
case Gdk.KEY_j:
|
||||
if ind == len(children) - 1:
|
||||
return True
|
||||
menu.select_item(children[ind+1])
|
||||
menu.select_item(children[ind + 1])
|
||||
case Gdk.KEY_k:
|
||||
if ind - 1 < 0:
|
||||
return True
|
||||
menu.select_item(children[ind-1])
|
||||
menu.select_item(children[ind - 1])
|
||||
case Gdk.KEY_g:
|
||||
menu.select_item(children[0])
|
||||
case Gdk.KEY_G:
|
||||
@ -207,15 +204,12 @@ class ServerTreeView(TreeView):
|
||||
"""
|
||||
Propagate width change to other tabs
|
||||
"""
|
||||
title = col.get_title()
|
||||
size = col.get_width()
|
||||
|
||||
# NOTE: get final width after drag action completes
|
||||
GLib.idle_add(self.controller.propagate_column_width, col)
|
||||
|
||||
def terminate_process(self) -> None:
|
||||
if self.current_proc and self.current_proc.is_alive():
|
||||
self.current_proc.terminate()
|
||||
if self.current_proc and self.current_proc.is_alive():
|
||||
self.current_proc.terminate()
|
||||
|
||||
def _on_distcalc_started(self, treeview: Self):
|
||||
record = self.get_record()
|
||||
@ -223,8 +217,9 @@ class ServerTreeView(TreeView):
|
||||
return
|
||||
# TODO:
|
||||
self.controller.mediator.statusbar.spinner.start()
|
||||
ip = record.ip
|
||||
self.current_proc = CalcDist(record.ip, self.queue, self.controller)
|
||||
self.current_proc = CalcDist(
|
||||
record.ip, self.get_enum(), self.queue, self.controller
|
||||
)
|
||||
self.current_proc.start()
|
||||
|
||||
def _check_result_queue(self) -> Literal[True]:
|
||||
@ -235,13 +230,15 @@ class ServerTreeView(TreeView):
|
||||
latest_result = self.queue.get()
|
||||
|
||||
cache = self.controller.get_dist_cache()
|
||||
|
||||
if latest_result:
|
||||
# NOTE: if CalcDist was spawned on other tab
|
||||
addr = latest_result[0]
|
||||
haversine = latest_result[1]
|
||||
if addr not in cache:
|
||||
cache[addr] = haversine
|
||||
# FIXME
|
||||
self.controller.set_statusbar_dist(haversine)
|
||||
self.controller.set_statusbar_dist(haversine, self.get_enum())
|
||||
return True
|
||||
|
||||
def _on_server_keypress(
|
||||
@ -308,7 +305,7 @@ class ServerTreeView(TreeView):
|
||||
mod_context_items = [ContextMenu.OPEN_WORKSHOP, ContextMenu.DELETE_MOD]
|
||||
|
||||
# TODO: dynamic menu entries
|
||||
#for row in items:
|
||||
# for row in items:
|
||||
# if row == ContextMenu.ADD_SERVER:
|
||||
# if self.is_in_favs():
|
||||
# row = ContextMenu.REMOVE_SERVER
|
||||
@ -326,18 +323,14 @@ class ServerTreeView(TreeView):
|
||||
if event.type is Gdk.EventType.KEY_PRESS and event.keyval is Gdk.KEY_l:
|
||||
if self.is_selection_empty():
|
||||
return
|
||||
self.menu.popup_at_widget(
|
||||
widget, Gdk.Gravity.CENTER, Gdk.Gravity.WEST
|
||||
)
|
||||
self.menu.popup_at_widget(widget, Gdk.Gravity.CENTER, Gdk.Gravity.WEST)
|
||||
else:
|
||||
self.menu.popup_at_pointer(event)
|
||||
self.menu.select_first(False)
|
||||
|
||||
def _parent_row_activated(self,
|
||||
tree: TreeView,
|
||||
path: Gtk.TreePath,
|
||||
column: Gtk.TreeViewColumn
|
||||
) -> None:
|
||||
def _parent_row_activated(
|
||||
self, tree: TreeView, path: Gtk.TreePath, column: Gtk.TreeViewColumn
|
||||
) -> None:
|
||||
# TODO: process server connection
|
||||
# TODO: get record
|
||||
print(self.get_value_at_index(0))
|
||||
|
||||
Loading…
Reference in New Issue
Block a user