From 2fb52654633a713cad3880565cf73cb76ec8d3d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Hamal=20Dvo=C5=99=C3=A1k?= <mordae@anilinux.org> Date: Sun, 9 Mar 2025 13:25:14 +0100 Subject: [PATCH] Use ListModel for faster directory traversal --- lazy_player/__init__.py | 24 +++++++++----------- lazy_player/file_model.py | 47 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 55 insertions(+), 16 deletions(-) diff --git a/lazy_player/__init__.py b/lazy_player/__init__.py index 433fe3b..3097188 100644 --- a/lazy_player/__init__.py +++ b/lazy_player/__init__.py @@ -7,20 +7,20 @@ from typing import Any, cast import gi -from .file_model import FileItem, FileType +from .file_model import FileItem, FileListModel, FileType gi.require_version("Gdk", "4.0") gi.require_version("Gtk", "4.0") gi.require_version("Gst", "1.0") gi.require_version("Pango", "1.0") -from gi.repository import Gdk, Gio, Gst, Gtk, Pango # NOQA: E402 +from gi.repository import Gdk, Gst, Gtk, Pango # NOQA: E402 class MainWindow(Gtk.ApplicationWindow): file_info_label: Gtk.Label stack: Gtk.Stack list_view: Gtk.ListView - list_store: Gio.ListStore + list_model: FileListModel selection_model: Gtk.SingleSelection video_widget: Gtk.Picture pipeline: Gst.Pipeline @@ -137,10 +137,10 @@ class MainWindow(Gtk.ApplicationWindow): main_box.append(grid) - # Create list store and view - self.list_store = Gio.ListStore(item_type=FileItem) + # Create list model and view + self.list_model = FileListModel() self.list_view = Gtk.ListView() - self.selection_model = Gtk.SingleSelection.new(self.list_store) + self.selection_model = Gtk.SingleSelection.new(self.list_model) self.selection_model.connect("selection-changed", self._on_selection_changed) self.list_view.set_model(self.selection_model) self.list_view.set_vexpand(True) @@ -285,8 +285,8 @@ class MainWindow(Gtk.ApplicationWindow): self._populate_file_list() # Find and select the directory we came from - for i in range(self.list_store.get_n_items()): - item = self.list_store.get_item(i) + for i in range(self.list_model.get_n_items()): + item = self.list_model.get_item(i) if not item: continue @@ -392,7 +392,7 @@ class MainWindow(Gtk.ApplicationWindow): file_item.save_attribute("position", duration) # Force the list to update the changed item - self.list_store.items_changed(self.selection_model.get_selected(), 1, 1) + self.list_model.items_changed(self.selection_model.get_selected(), 1, 1) def _on_menu_key_pressed( self, @@ -551,11 +551,7 @@ class MainWindow(Gtk.ApplicationWindow): # Sort directories first, then files, both alphabetically items.sort(key=lambda x: (x.file_type != FileType.DIRECTORY, x.name.lower())) - while self.list_store.get_n_items(): - self.list_store.remove(0) - - for item in items: - self.list_store.append(item) + self.list_model.set_items(items) if items: self.file_info_label.set_text(items[0].name) diff --git a/lazy_player/file_model.py b/lazy_player/file_model.py index ed1ba41..8e53c56 100644 --- a/lazy_player/file_model.py +++ b/lazy_player/file_model.py @@ -3,12 +3,12 @@ from __future__ import annotations import os from enum import Enum, auto from pathlib import Path -from typing import overload +from typing import Optional, overload import gi gi.require_version("GObject", "2.0") -from gi.repository import GObject # NOQA: E402 +from gi.repository import Gio, GObject # NOQA: E402 class FileType(Enum): @@ -54,3 +54,46 @@ class FileItem(GObject.Object): pass self.notify("attrs-changed") + + +class FileListModel(GObject.Object, Gio.ListModel): + """A ListModel implementation for FileItems""" + + __gtype_name__ = "FileListModel" + + items: list[FileItem] + + def __init__(self): + super().__init__() + self.items = [] + + def do_get_item_type(self) -> GObject.GType: + return GObject.type_from_name("FileItem") + + def do_get_n_items(self) -> int: + return len(self.items) + + def do_get_item(self, position: int) -> Optional[FileItem]: + if 0 <= position < len(self.items): + return self.items[position] + return None + + def remove_all(self) -> None: + removed = len(self.items) + self.items = [] + self.items_changed(0, removed, 0) + + def set_items(self, items: list[FileItem]): + removed = len(self.items) + self.items = list(items) + self.items_changed(0, removed, len(self.items)) + + def append(self, item: FileItem) -> None: + pos = len(self.items) + self.items.append(item) + self.items_changed(pos, 0, 1) + + def remove(self, position: int) -> None: + if 0 <= position < len(self.items): + self.items.pop(position) + self.items_changed(position, 1, 0)