From 78a50c5d7400dae328d78bd8c53b0225db2ab791 Mon Sep 17 00:00:00 2001 From: Qubasa Date: Sun, 3 Mar 2024 16:01:08 +0700 Subject: [PATCH 1/2] clan_vm_manager: Added suffix task to glib task functions --- .../clan_vm_manager/models/use_join.py | 4 ++-- .../clan_vm_manager/models/use_vms.py | 17 +++++++++-------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/pkgs/clan-vm-manager/clan_vm_manager/models/use_join.py b/pkgs/clan-vm-manager/clan_vm_manager/models/use_join.py index 258bed982..38197b5f8 100644 --- a/pkgs/clan-vm-manager/clan_vm_manager/models/use_join.py +++ b/pkgs/clan-vm-manager/clan_vm_manager/models/use_join.py @@ -26,7 +26,7 @@ class JoinValue(GObject.Object): url: ClanURI entry: HistoryEntry | None - def _join_finished(self) -> bool: + def _join_finished_task(self) -> bool: self.emit("join_finished", self) return GLib.SOURCE_REMOVE @@ -38,7 +38,7 @@ class JoinValue(GObject.Object): def __join(self) -> None: new_entry = add_history(self.url) self.entry = new_entry - GLib.idle_add(self._join_finished) + GLib.idle_add(self._join_finished_task) def join(self) -> None: threading.Thread(target=self.__join).start() diff --git a/pkgs/clan-vm-manager/clan_vm_manager/models/use_vms.py b/pkgs/clan-vm-manager/clan_vm_manager/models/use_vms.py index 7088fe56e..8823de370 100644 --- a/pkgs/clan-vm-manager/clan_vm_manager/models/use_vms.py +++ b/pkgs/clan-vm-manager/clan_vm_manager/models/use_vms.py @@ -37,7 +37,7 @@ class VM(GObject.Object): "vm_status_changed": (GObject.SignalFlags.RUN_FIRST, None, [GObject.Object]) } - def vm_status_changed(self) -> bool: + def vm_status_changed_task(self) -> bool: self.emit("vm_status_changed", self) return GLib.SOURCE_REMOVE @@ -130,7 +130,7 @@ class VM(GObject.Object): yield self.machine self.machine = None - def _pulse_progress_bar(self) -> bool: + def _pulse_progress_bar_task(self) -> bool: if self.progress_bar.is_visible(): self.progress_bar.pulse() return GLib.SOURCE_CONTINUE @@ -150,7 +150,7 @@ class VM(GObject.Object): machine=machine, tmpdir=log_dir, ) - GLib.idle_add(self.vm_status_changed) + GLib.idle_add(self.vm_status_changed_task) # Start the logs watcher self._logs_id = GLib.timeout_add( @@ -162,7 +162,7 @@ class VM(GObject.Object): # Start the progress bar and show it self.progress_bar.show() - self.prog_bar_id = GLib.timeout_add(100, self._pulse_progress_bar) + self.prog_bar_id = GLib.timeout_add(100, self._pulse_progress_bar_task) if self.prog_bar_id == 0: log.error("Couldn't spawn a progess bar task") @@ -175,7 +175,7 @@ class VM(GObject.Object): # Check if the VM was built successfully if self.build_process.proc.exitcode != 0: log.error(f"Failed to build VM {self.get_id()}") - GLib.idle_add(self.vm_status_changed) + GLib.idle_add(self.vm_status_changed_task) return log.info(f"Successfully built VM {self.get_id()}") @@ -187,7 +187,7 @@ class VM(GObject.Object): vm=self.data.flake.vm, ) log.debug(f"Started VM {self.get_id()}") - GLib.idle_add(self.vm_status_changed) + GLib.idle_add(self.vm_status_changed_task) # Start the logs watcher self._logs_id = GLib.timeout_add(50, self._get_logs_task, self.vm_process) @@ -198,7 +198,7 @@ class VM(GObject.Object): # Wait for the VM to stop self.vm_process.proc.join() log.debug(f"VM {self.get_id()} has stopped") - GLib.idle_add(self.vm_status_changed) + GLib.idle_add(self.vm_status_changed_task) def start(self) -> None: if self.is_running(): @@ -274,7 +274,7 @@ class VM(GObject.Object): # Try 20 times to stop the VM time.sleep(self.KILL_TIMEOUT / 20) - GLib.idle_add(self.vm_status_changed) + GLib.idle_add(self.vm_status_changed_task) log.debug(f"VM {self.get_id()} has stopped") def shutdown(self) -> None: @@ -348,6 +348,7 @@ class VMs: return GLib.SOURCE_REMOVE def push_history_entry(self, entry: HistoryEntry) -> None: + # TODO: We shouldn't do this here but in the list view if entry.flake.icon is None: icon = assets.loc / "placeholder.jpeg" else: From 8290660f20d427579df4b49251d6fb3d7078af84 Mon Sep 17 00:00:00 2001 From: Qubasa Date: Sun, 3 Mar 2024 16:15:50 +0700 Subject: [PATCH 2/2] clan_vm_manager: Improved readability of GKVStore --- .../clan_vm_manager/models/gkvstore.py | 144 +++++++++++------- 1 file changed, 91 insertions(+), 53 deletions(-) diff --git a/pkgs/clan-vm-manager/clan_vm_manager/models/gkvstore.py b/pkgs/clan-vm-manager/clan_vm_manager/models/gkvstore.py index 6ff6112e2..c35df9cf4 100644 --- a/pkgs/clan-vm-manager/clan_vm_manager/models/gkvstore.py +++ b/pkgs/clan-vm-manager/clan_vm_manager/models/gkvstore.py @@ -32,56 +32,19 @@ class GKVStore(GObject.GObject, Gio.ListModel, Generic[K, V]): self.key_gen = key_gen self._items: "OrderedDict[K, V]" = OrderedDict() + ################################## + # # + # Gio.ListStore Interface # + # # + ################################## @classmethod def new(cls: Any, gtype: type[V]) -> "GKVStore": return cls.__new__(cls, gtype) - ######################### - # # - # READ OPERATIONS # - # # - ######################### - def keys(self) -> list[K]: - return list(self._items.keys()) + def append(self, item: V) -> None: + key = self.key_gen(item) + self[key] = item - def values(self) -> list[V]: - return list(self._items.values()) - - def items(self) -> list[tuple[K, V]]: - return list(self._items.items()) - - def get(self, key: K, default: V | None = None) -> V | None: - return self._items.get(key, default) - - def get_n_items(self) -> int: - return len(self._items) - - def do_get_n_items(self) -> int: - return self.get_n_items() - - def get_item(self, position: int) -> V | None: - if position < 0 or position >= self.get_n_items(): - return None - # Access items by index since OrderedDict does not support direct indexing - key = list(self._items.keys())[position] - return self._items[key] - - def do_get_item(self, position: int) -> V | None: - return self.get_item(position) - - def get_item_type(self) -> GObject.GType: - return self.gtype.__gtype__ - - def do_get_item_type(self) -> GObject.GType: - return self.get_item_type() - - def first(self) -> V: - return self.values()[0] - - def last(self) -> V: - return self.values()[-1] - - # O(n) operation def find(self, item: V) -> tuple[bool, int]: log.warning("Finding is O(n) in GKVStore. Better use indexing") for i, v in enumerate(self.values()): @@ -89,11 +52,24 @@ class GKVStore(GObject.GObject, Gio.ListModel, Generic[K, V]): return True, i return False, -1 - ######################### - # # - # WRITE OPERATIONS # - # # - ######################### + def find_with_equal_func( + self, item: V, equal_func: Callable[[V, V], bool] + ) -> tuple[bool, int]: + log.warning("Finding is O(n) in GKVStore. Better use indexing") + for i, v in enumerate(self.values()): + if equal_func(v, item): + return True, i + return False, -1 + + def find_with_equal_func_full( + self, item: V, equal_func: Callable[[V, V, Any], bool], user_data: Any + ) -> tuple[bool, int]: + log.warning("Finding is O(n) in GKVStore. Better use indexing") + for i, v in enumerate(self.values()): + if equal_func(v, item, user_data): + return True, i + return False, -1 + def insert(self, position: int, item: V) -> None: log.warning("Inserting is O(n) in GKVStore. Better use append") log.warning( @@ -122,9 +98,10 @@ class GKVStore(GObject.GObject, Gio.ListModel, Generic[K, V]): # Notify the model of the changes self.items_changed(position, 0, 1) - def append(self, item: V) -> None: - key = self.key_gen(item) - self[key] = item + def insert_sorted( + self, item: V, compare_func: Callable[[V, V, Any], int], user_data: Any + ) -> None: + raise NotImplementedError("insert_sorted is not implemented in GKVStore") def remove(self, position: int) -> None: if position < 0 or position >= self.get_n_items(): @@ -137,6 +114,56 @@ class GKVStore(GObject.GObject, Gio.ListModel, Generic[K, V]): self._items.clear() self.items_changed(0, len(self._items), 0) + def sort(self, compare_func: Callable[[V, V, Any], int], user_data: Any) -> None: + raise NotImplementedError("sort is not implemented in GKVStore") + + def splice(self, position: int, n_removals: int, additions: list[V]) -> None: + raise NotImplementedError("splice is not implemented in GKVStore") + + ################################## + # # + # Gio.ListModel Interface # + # # + ################################## + def get_item(self, position: int) -> V | None: + if position < 0 or position >= self.get_n_items(): + return None + # Access items by index since OrderedDict does not support direct indexing + key = list(self._items.keys())[position] + return self._items[key] + + def do_get_item(self, position: int) -> V | None: + return self.get_item(position) + + def get_item_type(self) -> GObject.GType: + return self.gtype.__gtype__ + + def do_get_item_type(self) -> GObject.GType: + return self.get_item_type() + + def get_n_items(self) -> int: + return len(self._items) + + def do_get_n_items(self) -> int: + return self.get_n_items() + + ################################## + # # + # Dict Interface # + # # + ################################## + def keys(self) -> list[K]: + return list(self._items.keys()) + + def values(self) -> list[V]: + return list(self._items.values()) + + def items(self) -> list[tuple[K, V]]: + return list(self._items.items()) + + def get(self, key: K, default: V | None = None) -> V | None: + return self._items.get(key, default) + # O(1) operation if the key does not exist, O(n) if it does def __setitem__(self, key: K, value: V) -> None: # If the key already exists, remove it O(n) @@ -178,3 +205,14 @@ class GKVStore(GObject.GObject, Gio.ListModel, Generic[K, V]): def __repr__(self) -> str: return self._items.__str__() + + ################################## + # # + # Custom Methods # + # # + ################################## + def first(self) -> V: + return self.values()[0] + + def last(self) -> V: + return self.values()[-1]