mirror of
https://github.com/aclist/dztui.git
synced 2026-08-26 17:57:06 +02:00
feat: add extend() method to ListStore
This commit is contained in:
parent
3859188ade
commit
d6fd7b2494
@ -315,10 +315,10 @@ class Controller(GObject.GObject):
|
||||
path = self.query_config(Preferences.DEFAULT)
|
||||
mods = get_delimited_mods(Path(path))
|
||||
|
||||
# NOTE: cell renderer highlight toggle
|
||||
for mod in mods:
|
||||
# NOTE: show highlight color bool
|
||||
mod.append(False)
|
||||
model.append(mod)
|
||||
model.extend(mods)
|
||||
|
||||
self.cleanup_func = StoredFunc(self.load_mods_cleanup, model)
|
||||
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
import datetime
|
||||
import re
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
from warnings import deprecated
|
||||
|
||||
from dzgui.const.enum import FilterMode
|
||||
from dzgui.model.model_factory import ModelFactory
|
||||
from dzgui.util import strings
|
||||
|
||||
import gi
|
||||
@ -19,22 +18,6 @@ if TYPE_CHECKING:
|
||||
from dzgui.controllers.emitter import Emitter
|
||||
|
||||
|
||||
@dataclass(slots=True, frozen=True)
|
||||
class ServerColumns:
|
||||
name: str
|
||||
_map: str
|
||||
perspective: str
|
||||
gametime: str
|
||||
players: int
|
||||
_max: int
|
||||
queue: int
|
||||
ip: str
|
||||
qport: int
|
||||
ping: int
|
||||
provider: str
|
||||
modded: bool
|
||||
|
||||
|
||||
class FilteredModelManager:
|
||||
"""
|
||||
Manages access to cached ListStore resources and
|
||||
@ -42,7 +25,9 @@ class FilteredModelManager:
|
||||
which share the same column structure.
|
||||
|
||||
A FilteredModelManager is attached to each ServerTreeView.
|
||||
Filter methods are not thread-safe in themselves.
|
||||
|
||||
Raw data is cached before being packed into a ListStore, see get_control()
|
||||
Filtration creates a proxy of the TreeView's model, see get_proxy_model()
|
||||
"""
|
||||
|
||||
def __init__(self, controller: "Controller") -> None:
|
||||
@ -117,21 +102,17 @@ class FilteredModelManager:
|
||||
rows = self.filter_toggle_on(filters, *args)
|
||||
|
||||
# TODO: unimplemented
|
||||
# just write pings into control model instead
|
||||
# if mode is not FilterMode.INITIAL:
|
||||
# for row in rows:
|
||||
# if row[7] in self.ping_cache:
|
||||
# row[9] = self.ping_cache[row[7]]
|
||||
|
||||
# NOTE: this ListStore manipulation must remain local to the thread
|
||||
clone = self.new_model_from_class(ServerColumns)
|
||||
n_cols = clone.get_n_columns()
|
||||
clone = ModelFactory().make_server_store()
|
||||
if len(rows) > 0:
|
||||
rows = self.sort_rows(rows)
|
||||
for i, row in enumerate(rows):
|
||||
# TODO: consider overriding append() method of Gtk.ListStore
|
||||
# check Gtk source code
|
||||
clone.insert_with_values(i, tuple(range(0, n_cols)), row)
|
||||
# clone.append(row)
|
||||
clone.extend(rows)
|
||||
|
||||
self.set_cache(filters, clone, rows)
|
||||
self.set_proxy_model(clone)
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from dzgui.const.enum import HELP_MENU_ROWS
|
||||
from dzgui.util.redact import redact_log
|
||||
from dzgui.util.strings import delimiter
|
||||
from dzgui.views.dialogs.generic import ExceptionDialog
|
||||
|
||||
import gi
|
||||
|
||||
@ -11,6 +11,27 @@ gi.require_version("Gtk", "3.0")
|
||||
from gi.repository.Gtk import ListStore # noqa E402
|
||||
from gi.repository import GObject # noqa E402
|
||||
|
||||
GTYPE_TO_PYTHON = {
|
||||
GObject.type_from_name(GObject.type_name(ptype)): ptype
|
||||
for ptype in (int, float, str, bool, object)
|
||||
}
|
||||
|
||||
|
||||
@dataclass(slots=True, frozen=True)
|
||||
class ServerCols:
|
||||
name: str
|
||||
_map: str
|
||||
perspective: str
|
||||
gametime: str
|
||||
players: int
|
||||
_max: int
|
||||
queue: int
|
||||
ip: str
|
||||
qport: int
|
||||
ping: int
|
||||
provider: str
|
||||
modded: bool
|
||||
|
||||
|
||||
@dataclass(slots=True, frozen=True)
|
||||
class ModCols:
|
||||
@ -38,7 +59,7 @@ class ServerModCols:
|
||||
|
||||
@dataclass(slots=True, frozen=True)
|
||||
class MenuCols:
|
||||
name: str
|
||||
name: GObject.TYPE_STRING # str
|
||||
hidden: GObject.TYPE_PYOBJECT
|
||||
|
||||
|
||||
@ -46,10 +67,52 @@ class FastInsertListStore(ListStore):
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def _is_same_length(self, lists: list[list[Any]]) -> bool:
|
||||
first_len = len(lists[0])
|
||||
if not all(len(sublist) == first_len for sublist in lists):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _is_type_homogeneous(self, lists: list[list[Any]]) -> bool:
|
||||
transposed = zip(*lists)
|
||||
|
||||
for column in transposed:
|
||||
first_type = type(column[0])
|
||||
if not all(type(item) is first_type for item in column):
|
||||
return False
|
||||
return True
|
||||
|
||||
def extend(self, rows: list[list[Any]]) -> None:
|
||||
"""
|
||||
Compared to calling append() directly, introduces negligible overhead,
|
||||
but guarantees type and length equivalence prior to insertion
|
||||
"""
|
||||
n_cols = self.get_n_columns()
|
||||
expected_types = [
|
||||
GTYPE_TO_PYTHON[self.get_column_type(i)] for i in range(n_cols)
|
||||
]
|
||||
if not self._is_same_length(rows):
|
||||
raise ValueError("Sublists are not of uniform length")
|
||||
if not self._is_type_homogeneous(rows):
|
||||
raise TypeError("Sublists are not type homogeneous")
|
||||
if not all(isinstance(a, b) for a, b in zip(rows[0], expected_types)):
|
||||
raise TypeError("Sublist types are not same as ListStore")
|
||||
if len(rows[0]) != n_cols:
|
||||
raise ValueError("Sublist column length is not same as ListStore")
|
||||
|
||||
for row in rows:
|
||||
self.append(row)
|
||||
|
||||
def append(self, row) -> None:
|
||||
"""
|
||||
Optimized for speed, but makes no assurances about row homogeneity
|
||||
and may segfault if types and length are not identical to ListStore.
|
||||
For this reason, it is recommended to use the extend() method to insert
|
||||
an entire list of lists
|
||||
"""
|
||||
total = len(row)
|
||||
i = len(self)
|
||||
tree_iter = self.insert_with_values(i, tuple(range(0, total)), row)
|
||||
tree_iter = self.insert_with_values(i, tuple(range(total)), row)
|
||||
return tree_iter
|
||||
|
||||
|
||||
@ -62,7 +125,7 @@ class ModelFactory:
|
||||
with open(path, "r") as f:
|
||||
lines = [line.split(delimiter) for line in f.read().splitlines()]
|
||||
for record in lines:
|
||||
# NOTE: strip PII and API keys
|
||||
# NOTE: strips PII and API keys
|
||||
clean = redact_log(record)
|
||||
store.append(clean)
|
||||
return store
|
||||
@ -73,21 +136,28 @@ class ModelFactory:
|
||||
)
|
||||
return store
|
||||
|
||||
def make_map_store(self) -> ListStore:
|
||||
def make_map_store(self) -> FastInsertListStore:
|
||||
return ListStore(str)
|
||||
|
||||
def make_help_store(self) -> ListStore:
|
||||
def make_help_store(self) -> FastInsertListStore:
|
||||
store = self.new_model_from_class(MenuCols)
|
||||
for row in HELP_MENU_ROWS:
|
||||
label = row.dict["label"]
|
||||
store.append([label, row])
|
||||
rows = [[row.dict["label"], row] for row in HELP_MENU_ROWS]
|
||||
# for row in rows:HELP_MENU_ROWS:
|
||||
# print(type(row) is object)
|
||||
store.extend(rows)
|
||||
# for row in HELP_MENU_ROWS:
|
||||
# label = row.dict["label"]
|
||||
# store.append([label, row])
|
||||
return store
|
||||
|
||||
def make_mod_store(self) -> ListStore:
|
||||
def make_mod_store(self) -> FastInsertListStore:
|
||||
return self.new_model_from_class(ModCols)
|
||||
|
||||
def make_log_store(self) -> ListStore:
|
||||
def make_log_store(self) -> FastInsertListStore:
|
||||
return self.new_model_from_class(LogCols)
|
||||
|
||||
def make_server_mod_store(self) -> ListStore:
|
||||
def make_server_mod_store(self) -> FastInsertListStore:
|
||||
return self.new_model_from_class(ServerModCols)
|
||||
|
||||
def make_server_store(self) -> FastInsertListStore:
|
||||
return self.new_model_from_class(ServerCols)
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import os
|
||||
import locale
|
||||
|
||||
|
||||
def number(num: int | float) -> str:
|
||||
spec = ""
|
||||
if type(num) is int:
|
||||
|
||||
@ -36,7 +36,6 @@ class ServerNotebook(Gtk.ScrolledWindow):
|
||||
controller, ServerTab.RECENT, ContextMenuGroup.RECENT
|
||||
)
|
||||
self.lan = ServerTreeView(controller, ServerTab.LAN, ContextMenuGroup.SCAN_LAN)
|
||||
self.lan.set_headers_visible(False)
|
||||
|
||||
tabs = [
|
||||
(self.browser, server_labels.browser),
|
||||
|
||||
@ -53,7 +53,8 @@ class ServerTreeView(ContextMixin, TreeView):
|
||||
self.map_man = MapManager()
|
||||
|
||||
self.set_fixed_height_mode(True)
|
||||
self.set_headers_visible(True)
|
||||
# NOTE: headers become visible on model load
|
||||
self.set_headers_visible(False)
|
||||
|
||||
self.queue_id: int
|
||||
self.handler_id: int
|
||||
|
||||
Loading…
Reference in New Issue
Block a user