home · contact · privacy
To browser, add file selection panel.
authorChristian Heller <c.heller@plomlompom.de>
Fri, 23 Aug 2024 22:38:16 +0000 (00:38 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Fri, 23 Aug 2024 22:38:16 +0000 (00:38 +0200)
browser.py

index c2ab5b8349406c545daf9e1bf525e41449270ff9..d6925f6cddfcd542fd894b55681b51959d8b70d6 100755 (executable)
 #!/usr/bin/env python3
-from os import scandir
-from os.path import splitext
 from exiftool import ExifToolHelper  # type: ignore
 import gi  # type: ignore
 gi.require_version('Gtk', '4.0')
+gi.require_version('Gio', '2.0')
 # pylint: disable=wrong-import-position
-from gi.repository import Gtk  # type: ignore  # noqa: E402
+from gi.repository import Gtk, Gio, GObject  # type: ignore  # noqa: E402
 from stable.gen_params import GenParams  # noqa: E402
 
 
+class FileItem(GObject.GObject):
+
+    def __init__(self, name, last_mod):
+        super().__init__()
+        self.name = name
+        self.last_mod = last_mod
+        self._metadata = None
+
+    @property
+    def metadata(self):
+        if not self._metadata:
+            with ExifToolHelper() as et:
+                for d in et.get_tags([self.name], ['Comment']):
+                    for k, v in d.items():
+                        if k.endswith('Comment'):
+                            self._metadata = ''
+                            gen_params = GenParams.from_str(v)
+                            for k, v_ in gen_params.as_dict.items():
+                                self._metadata += f'\n{k}: {v_}'
+            if not self._metadata:
+                self._metadata = 'no SD comment'
+        return self._metadata
+
+
 class Window(Gtk.ApplicationWindow):
 
     def __init__(self, **kwargs):
         super().__init__(**kwargs)
 
         box_buttons = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
-        btn_close = Gtk.Button(label='close')
-        btn_close.connect('clicked', lambda _: self.close())
-        box_buttons.append(btn_close)
-        btn = Gtk.Button(label='oldest')
-        btn.connect('clicked', self.oldest)
-        box_buttons.append(btn)
-        btn = Gtk.Button(label='older')
-        btn.connect('clicked', self.older)
-        box_buttons.append(btn)
-        btn = Gtk.Button(label='newer')
-        btn.connect('clicked', self.newer)
-        box_buttons.append(btn)
-        btn = Gtk.Button(label='newest')
-        btn.connect('clicked', self.newest)
-        box_buttons.append(btn)
+        for args in (('oldest', lambda _: self.move_selection(None, 0)),
+                     ('older', lambda _: self.move_selection(-1, None)),
+                     ('newer', lambda _: self.move_selection(1, None)),
+                     ('newest', lambda _: self.move_selection(None, -1))):
+            btn = Gtk.Button(label=args[0])
+            btn.connect('clicked', args[1])
+            box_buttons.append(btn)
 
         self.metadata = Gtk.Label()
         self.label_nothing_to_show = Gtk.Label(label='nothing to show')
 
-        self.box_outer = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
-        self.box_outer.append(box_buttons)
-        self.box_outer.append(self.metadata)
-        self.box_outer.append(self.label_nothing_to_show)
-        self.props.child = self.box_outer
-
-        self.entries = []
-        self.current_index = -1
-        self.newest(None)
-
-    def _reload(self, set_max=False):
-        self.entries = [e for e in scandir('.')
-                        if e.is_file()
-                        and splitext(e)[1] in {'.png', '.jpg', '.jpeg'}]
-        self.entries.sort(key=lambda e: e.stat().st_mtime)
-        if set_max or self.current_index + 1 > len(self.entries):
-            self.current_index = len(self.entries) - 1
-        self.box_outer.remove(self.box_outer.get_last_child())
-        if self.current_index >= 0:
-            path = self.entries[self.current_index].path
-            metadata = 'no SD comment'
-            with ExifToolHelper() as et:
-                for d in et.get_tags([path], ['Comment']):
-                    for k, v in d.items():
-                        if k.endswith('Comment'):
-                            metadata = ''
-                            gen_params = GenParams.from_str(v)
-                            for k, v_ in gen_params.as_dict.items():
-                                metadata += f'\n{k}: {v_}'
-            self.metadata.props.label = f'{path}: {metadata}'
-            pic = Gtk.Picture.new_for_filename(path)
-            self.box_outer.append(pic)
+        self.viewer = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+        self.viewer.append(box_buttons)
+        self.viewer.append(self.metadata)
+        self.viewer.append(self.label_nothing_to_show)
+
+        self.dir = Gio.File.new_for_path('.')
+        self.list_store = Gio.ListStore(item_type=FileItem)
+        self.selection = Gtk.SingleSelection.new(self.list_store)
+        factory = Gtk.SignalListItemFactory()
+        self.selector = Gtk.ListView(model=self.selection, factory=factory)
+
+        box_outer = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
+        box_outer.append(self.selector)
+        box_outer.append(self.viewer)
+        self.props.child = box_outer
+
+        self.max_index = 0
+        self.item = None
+        self.selection.connect('selection-changed', self.update_selected)
+        factory.connect('setup',
+                        lambda _, i: i.set_child(Gtk.Label()))
+        factory.connect('bind',
+                        lambda _, i: i.props.child.set_text(i.props.item.name))
+        query_attrs = 'standard::name,standard::content-type,time::*'
+        enumerator = self.dir.enumerate_children(
+                query_attrs, Gio.FileQueryInfoFlags.NONE, None)
+        to_sort = []
+        for info in [info for info in enumerator
+                     if info.get_content_type().startswith('image/')]:
+            item = FileItem(info.get_name(),
+                            info.get_modification_date_time().format_iso8601())
+            to_sort += [item]
+        to_sort.sort(key=lambda i: i.last_mod)
+        for file_item in to_sort:
+            self.list_store.append(file_item)
+        self.max_index = len(self.list_store) - 1
+        self.update_selected()
+
+    def update_selected(self, *_args):
+        self.item = self.selection.props.selected_item
+        self.reload()
+        self.selector.scroll_to(self.selection.props.selected,
+                                Gtk.ListScrollFlags.NONE, None)
+
+    def reload(self):
+        self.viewer.remove(self.viewer.get_last_child())
+        if self.item:
+            metadata = f'{self.item.name}: {self.item.metadata}'
+            self.metadata.props.label = metadata
+            pic = Gtk.Picture.new_for_filename(self.item.name)
+            self.viewer.append(pic)
         else:
             self.metadata.props.label = None
-            self.box_outer.append(self.label_nothing_to_show)
-
-    def oldest(self, _widget):
-        self.current_index = 0
-        self._reload()
-
-    def older(self, _widget):
-        if self.current_index > 0:
-            self.current_index -= 1
-        self._reload()
-
-    def newer(self, _widget):
-        self.current_index += 1
-        self._reload()
-
-    def newest(self, _widget):
-        self._reload(set_max=True)
+            self.viewer.append(self.label_nothing_to_show)
+
+    def move_selection(self, increment, absolute_position):
+        cur_index = self.selection.props.selected
+        if 0 == absolute_position:
+            self.selection.props.selected = 0
+        elif -1 == absolute_position:
+            self.selection.props.selected = self.max_index
+        elif (1 == increment and cur_index < self.max_index)\
+                or (-1 == increment and cur_index > 0):
+            self.selection.props.selected = cur_index + increment
 
 
 def on_activate(app_):