From b8bd4c6b5c5986aefcec806d0674df2b1d20d24e Mon Sep 17 00:00:00 2001 From: Christian Heller Date: Tue, 10 Sep 2024 01:58:00 +0200 Subject: [PATCH] To browser, add filtering by file attributes. --- browser.py | 117 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 105 insertions(+), 12 deletions(-) diff --git a/browser.py b/browser.py index ae77c9a..41b33d5 100755 --- a/browser.py +++ b/browser.py @@ -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: -- 2.30.2