From b233f30268928b09de5bc5b1723272993162d5aa Mon Sep 17 00:00:00 2001 From: Christian Heller Date: Mon, 26 Aug 2024 03:39:49 +0200 Subject: [PATCH] To browser, add directory navigation. --- browser.py | 121 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 89 insertions(+), 32 deletions(-) diff --git a/browser.py b/browser.py index a3e0979..9fbe217 100755 --- a/browser.py +++ b/browser.py @@ -21,11 +21,26 @@ SORT_KEY_RANDOM = 'randomize' class FileItem(GObject.GObject): - def __init__(self, path, info, cache): + def __init__(self, path, info): super().__init__() self.name = info.get_name() self.last_mod_time = info.get_modification_date_time().format_iso8601() self.full_path = path_join(path, self.name) + + +class DirItem(FileItem): + + def __init__(self, path, info, is_parent=False): + super().__init__(path, info) + self.name = ' ..' if is_parent else f' {self.name}/' + if is_parent: + self.full_path = path + + +class ImgItem(FileItem): + + def __init__(self, path, info, cache): + super().__init__(path, info) for param_name in GEN_PARAMS: if param_name in GEN_PARAMS_STR: setattr(self, param_name.lower(), '') @@ -38,7 +53,7 @@ class FileItem(GObject.GObject): setattr(self, k, cached[k]) def set_metadata(self, et, cache): - for d in et.get_tags([self.name], ['Comment']): + for d in et.get_tags([self.full_path], ['Comment']): for k, v in d.items(): if k.endswith('Comment'): gen_params = GenParams.from_str(v) @@ -97,7 +112,6 @@ class Window(Gtk.ApplicationWindow): self.dir_box.append(self.sorter) self.img_dir_absolute = abspath(IMG_DIR) - self.dir = Gio.File.new_for_path(self.img_dir_absolute) self.list_store = Gio.ListStore(item_type=FileItem) self.selection = Gtk.SingleSelection.new(self.list_store) factory = Gtk.SignalListItemFactory() @@ -105,6 +119,7 @@ class Window(Gtk.ApplicationWindow): factory.connect('bind', lambda _, i: i.props.child.set_text(i.props.item.name)) self.selector = Gtk.ListView(model=self.selection, factory=factory) + self.selector.connect('activate', self.on_selector_activate) scrolled = Gtk.ScrolledWindow(child=self.selector, vexpand=True, propagate_natural_width=True) self.dir_box.append(scrolled) @@ -115,9 +130,19 @@ class Window(Gtk.ApplicationWindow): box_outer.append(self.viewer) self.props.child = box_outer - self.item = None + self.item_img, self.item_dir = None, None + self.unsorted_dirs, self.unsorted_files = [], [] self.sort_order = ['last_mod_time'] self.reload_dir() + self.selection.props.selected = self.max_index + + def on_selector_activate(self, _, __): + if isinstance(self.selection.props.selected_item, DirItem): + self.item_dir = self.selection.props.selected_item + self.img_dir_absolute = self.item_dir.full_path + self.item_img, self.item_dir = None, None + self.unsorted_dirs, self.unsorted_files = [], [] + self.reload_dir() def toggle_folder_view(self, _): self.dir_box.props.visible = not self.dir_box.props.visible @@ -139,32 +164,41 @@ class Window(Gtk.ApplicationWindow): self.list_store.remove_all() for key in self.sort_order: if SORT_KEY_RANDOM == key: - shuffle(self.unsorted) + shuffle(self.unsorted_files) + shuffle(self.unsorted_dirs) continue - self.unsorted.sort(key=attrgetter(key)) - for file_item in self.unsorted: + self.unsorted_files.sort(key=attrgetter(key)) + if key in {'name', 'last_mod_time'}: + self.unsorted_dirs.sort(key=attrgetter(key)) + for file_item in [self.parent_dir_item]\ + + self.unsorted_dirs + self.unsorted_files: self.list_store.append(file_item) - if self.item: - for pos, item in enumerate(self.list_store): - if item.full_path == self.item.full_path: - self.selection.set_selected(pos) - else: - self.update_selected() + for self_item in (self.item_dir, self.item_img): + if self_item: + for pos, item in enumerate(self.list_store): + if item.full_path == self_item.full_path: + self.selection.set_selected(pos) + return + self.update_selected() def update_selected(self, *_args): - self.item = self.selection.props.selected_item - self.reload_image() + if isinstance(self.selection.props.selected_item, ImgItem): + self.item_img = self.selection.props.selected_item + self.item_dir = None + self.reload_image() + else: + self.item_dir = self.selection.props.selected_item self.selector.scroll_to(self.selection.props.selected, Gtk.ListScrollFlags.NONE, None) def reload_image(self): self.viewer.remove(self.viewer.get_last_child()) - if self.item: - params_strs = [f'{k}: ' + str(getattr(self.item, k.lower())) + if self.item_img: + params_strs = [f'{k}: ' + str(getattr(self.item_img, k.lower())) for k in GEN_PARAMS] - self.metadata.props.label = '\n'.join([self.item.full_path] + self.metadata.props.label = '\n'.join([self.item_img.full_path] + params_strs) - pic = Gtk.Picture.new_for_filename(self.item.name) + pic = Gtk.Picture.new_for_filename(self.item_img.full_path) pic.props.halign = Gtk.Align.START self.viewer.append(pic) else: @@ -173,38 +207,61 @@ class Window(Gtk.ApplicationWindow): def move_selection(self, increment, absolute_position): cur_index = self.selection.props.selected + if len(self.unsorted_files) > 2: + self.min_index = len(self.unsorted_dirs) + 1 + else: + self.min_index = 0 if 0 == absolute_position: - self.selection.props.selected = 0 + self.selection.props.selected = self.min_index 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): + or (-1 == increment and cur_index > self.min_index): self.selection.props.selected = cur_index + increment def reload_dir(self): - old_item_path = self.item.full_path if self.item else '' + self.dir = Gio.File.new_for_path(self.img_dir_absolute) + old_dir_path = self.item_dir.full_path if self.item_dir else '' + old_img_path = self.item_img.full_path if self.item_img else '' if not path_exists(CACHE_PATH): with open(CACHE_PATH, 'w', encoding='utf8') as f: json_dump({}, f) with open(CACHE_PATH, 'r', encoding='utf8') as f: cache = json_load(f) self.max_index = 0 - self.item = None + query_attrs = 'standard::name,standard::type,time::*' + self.item_img, self.item_dir = None, None self.selection.connect('selection-changed', self.update_selected) - query_attrs = 'standard::name,standard::content-type,time::*' + parent_path = abspath(path_join(self.img_dir_absolute, '..')) + parent_dir = self.dir.get_parent() + parent_dir_info = parent_dir.query_info( + query_attrs, Gio.FileQueryInfoFlags.NONE, None) + self.parent_dir_item = DirItem( + parent_path, parent_dir_info, is_parent=True) + self.unsorted_dirs, self.unsorted_files = [], [] enumerator = self.dir.enumerate_children( query_attrs, Gio.FileQueryInfoFlags.NONE, None) - self.unsorted = [] for info in [info for info in enumerator - if info.get_content_type().startswith('image/')]: - item = FileItem(self.img_dir_absolute, info, cache) - if old_item_path == item.full_path: - self.item = item - self.unsorted += [item] + if info.get_file_type() == Gio.FileType.DIRECTORY]: + item = DirItem(self.img_dir_absolute, info) + if old_dir_path == item.full_path: + self.item_dir = item + self.unsorted_dirs += [item] + enumerator = self.dir.enumerate_children( + query_attrs + ',standard::content-type', + Gio.FileQueryInfoFlags.NONE, None) + for info in [info for info in enumerator + if info.get_file_type() == Gio.FileType.REGULAR + and info.get_content_type().startswith('image/')]: + item = ImgItem(self.img_dir_absolute, info, cache) + if old_img_path == item.full_path: + self.item_img = item + self.unsorted_files += [item] with ExifToolHelper() as et: - for item in [item for item in self.unsorted if '' == item.model]: + for item in [item for item + in self.unsorted_files if '' == item.model]: item.set_metadata(et, cache) - self.max_index = len(self.unsorted) - 1 + self.max_index = len(self.unsorted_files + self.unsorted_dirs) self.sort() with open(CACHE_PATH, 'w', encoding='utf8') as f: json_dump(cache, f) -- 2.30.2