home · contact · privacy
To browser, add filtering by file attributes.
authorChristian Heller <c.heller@plomlompom.de>
Mon, 9 Sep 2024 23:58:00 +0000 (01:58 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Mon, 9 Sep 2024 23:58:00 +0000 (01:58 +0200)
browser.py

index ae77c9abbc52e3cdf565e7879b2d589c5ef9608e..41b33d547dfbc75fba062ef29a24be01a8cba56c 100755 (executable)
@@ -12,8 +12,9 @@ gi.require_version('Gio', '2.0')
 from gi.repository import Gdk, Gtk, Gio  # type: ignore  # noqa: E402
 from gi.repository import GObject, GLib  # type: ignore  # noqa: E402
 # pylint: disable=no-name-in-module
-from stable.gen_params import (GenParams,  # noqa: E402
-                               GEN_PARAMS, GEN_PARAMS_STR)  # noqa: E402
+from stable.gen_params import (GenParams,  GEN_PARAMS_FLOAT,  # noqa: E402
+                               GEN_PARAMS_INT, GEN_PARAMS_STR,  # noqa: E402
+                               GEN_PARAMS)  # noqa: E402
 
 IMG_DIR_DEFAULT = '.'
 SORT_DEFAULT = 'guidance,n_steps,model,scheduler,prompt,seed,height,width'
@@ -103,6 +104,7 @@ class MainWindow(Gtk.Window):
     sort_store: Gtk.ListStore
     sort_selection: Gtk.SingleSelection
     prev_key: list
+    filter_inputs = dict
 
     def __init__(self, app, **kwargs):
         super().__init__(**kwargs)
@@ -156,7 +158,7 @@ class MainWindow(Gtk.Window):
             metadata_box.append(text_view)
             return metadata_box
 
-        def init_sort_orderer():
+        def init_sorter_and_filterer():
             self.sort_order = [p.lower() for p in GEN_PARAMS]
             new_sort_order = []
             do_reverse = '-' in self.app.suggested_sort_order
@@ -171,21 +173,44 @@ class MainWindow(Gtk.Window):
             self.sort_store = Gio.ListStore(item_type=SortLabelItem)
             self.sort_selection = Gtk.SingleSelection.new(self.sort_store)
             factory = Gtk.SignalListItemFactory()
-            factory.connect('setup',
-                            lambda _, i: i.set_child(Gtk.Label(xalign=0)))
-            factory.connect(
-                    'bind',
-                    lambda _, i: i.props.child.set_text(i.props.item.name))
+
+            def setup_sort_order_item(_, item):
+                box = Gtk.Box(orientation=OR_H, halign=Gtk.Align.END)
+                box.append(Gtk.Label(hexpand=True))
+                box.append(Gtk.Entry.new())
+                box.get_last_child().props.placeholder_text = 'filter?'
+                item.set_child(box)
+
+            def bind_sort_order_item(_, item):
+
+                def on_filter_enter(entry):
+                    text = entry.get_buffer().get_text()
+                    if '' != text.rstrip():
+                        self.filter_inputs[item.props.item.name] = text
+                    else:
+                        del self.filter_inputs[item.props.item.name]
+                    self.update_gallery()
+
+                label = f'{item.props.item.name} '
+                item.props.item.filterer = item.props.child.get_last_child()
+                item.props.child.get_first_child().set_text(label)
+                filter_entry = item.props.child.get_last_child()
+                filter_text = self.filter_inputs.get(item.props.item.name, '')
+                filter_entry.get_buffer().set_text(filter_text, -1)
+                filter_entry.connect('activate', on_filter_enter)
+
+            factory.connect('setup', setup_sort_order_item)
+            factory.connect('bind', bind_sort_order_item)
             selector = Gtk.ListView(model=self.sort_selection, factory=factory)
             sort_box = Gtk.Box(orientation=OR_V)
             sort_box.append(Gtk.Label(label='** sort order **'))
             sort_box.append(selector)
+            self.filter_inputs = {}
             return sort_box
 
         def init_gallery_content():
             self.gallery_store = Gio.ListStore(item_type=FileItem)
-            list_filter = Gtk.CustomFilter.new(
-                    lambda x: self.include_dirs or isinstance(x, ImgItem))
+            list_filter = Gtk.CustomFilter.new(self.gallery_filter)
             self.gallery_store_filtered = Gtk.FilterListModel(
                     model=self.gallery_store, filter=list_filter)
             self.gallery_selection = Gtk.SingleSelection.new(
@@ -211,7 +236,7 @@ class MainWindow(Gtk.Window):
         viewer.append(self.navbar)
         viewer.append(init_gallery_widgets())
         self.side_box = Gtk.Box(orientation=OR_V)
-        self.side_box.append(init_sort_orderer())
+        self.side_box.append(init_sorter_and_filterer())
         self.side_box.append(init_metadata_box())
         box_outer = Gtk.Box(orientation=OR_H)
         box_outer.append(self.side_box)
@@ -424,6 +449,72 @@ class MainWindow(Gtk.Window):
 
     # navbar callables
 
+    def gallery_filter(self, item):
+        """Apply user-set filters to gallery."""
+
+        def number_filter(attr_name, filter_line, to_compare):
+            use_float = attr_name in GEN_PARAMS_FLOAT
+            constraint_strings = filter_line.split(',')
+            numbers_or = set()
+            unequal = set()
+            less_than = None
+            less_or_equal = None
+            more_or_equal = None
+            more_than = None
+            for constraint_string in constraint_strings:
+                toks = constraint_string.split()
+                if len(toks) == 1:
+                    value = float(toks[0]) if use_float else int(toks[0])
+                    numbers_or.add(value)
+                elif len(toks) == 2:
+                    value = float(toks[1]) if use_float else int(toks[1])
+                    if toks[0] == '!=':
+                        unequal.add(value)
+                    elif toks[0] == '<':
+                        if less_than is None or less_than >= value:
+                            less_than = value
+                    elif toks[0] == '<=':
+                        if less_or_equal is None or less_or_equal > value:
+                            less_or_equal = value
+                    elif toks[0] == '>=':
+                        if more_or_equal is None or more_or_equal < value:
+                            more_or_equal = value
+                    elif toks[0] == '>':
+                        if more_than is None or more_than <= value:
+                            more_than = value
+            if to_compare in numbers_or:
+                print("return TRUE")
+                return True
+            if len(numbers_or) > 0 and (less_than == less_or_equal ==
+                                        more_or_equal == more_than):
+                return False
+            if to_compare in unequal:
+                return False
+            if (less_than is not None
+                and to_compare >= less_than)\
+                    or (less_or_equal is not None
+                        and to_compare > less_or_equal)\
+                    or (more_or_equal is not None
+                        and to_compare < more_or_equal)\
+                    or (more_than is not None
+                        and to_compare <= more_than):
+                return False
+            return True
+
+        if not self.include_dirs and isinstance(item, DirItem):
+            return False
+        for filter_attribute, value in self.filter_inputs.items():
+            if not hasattr(item, filter_attribute):
+                return False
+            to_compare = getattr(item, filter_attribute)
+            number_attributes = set(GEN_PARAMS_INT) | set(GEN_PARAMS_FLOAT)
+            if filter_attribute.upper() in number_attributes:
+                if not number_filter(filter_attribute, value, to_compare):
+                    return False
+            elif value not in to_compare:
+                return False
+        return True
+
     def load_directory(self, update_gallery=True):
         """Load into gallery directory at .app.img_dir_absolute."""
 
@@ -546,7 +637,9 @@ class MainWindow(Gtk.Window):
         self.hit_file_selection()
 
     def handle_keypress(self, keyval):
-        """Handle keys, and if, return True to declare key handling done."""
+        """Handle keys if not in Entry, return True if key handling done."""
+        if isinstance(self.get_focus().get_parent(), Gtk.Entry):
+            return False
         if Gdk.KEY_G == keyval:
             self.move_selection_in_gallery(None, None, 1)
         elif Gdk.KEY_h == keyval: