From b5b383cef87d65ce8378efc7595e007b1bb1e45c Mon Sep 17 00:00:00 2001 From: Roman Date: Sun, 27 Aug 2023 12:52:55 +0900 Subject: [PATCH] Implementing sorting --- README.md | 3 +- waypaper/__main__.py | 2 +- waypaper/app.py | 86 ++++++++++++++++++++++++++++++++++++++------ waypaper/config.py | 9 +++++ waypaper/options.py | 6 ++++ 5 files changed, 94 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 3037bbd..43619e6 100644 --- a/README.md +++ b/README.md @@ -74,9 +74,10 @@ To restore your wallpaper at launch, add `waypaper --restore` to your startup co ## Roadmap - ~Improve loading of folders with many images~. -- Additional options for ~search in subfolders~, ~background color~, and setting a uniform color. +- Additional options for ~search in subfolders~, ~background color~, ~sorting~, and setting a uniform color. - Support for other backends like ~swww~, ~feh~, ~wallutils~, wbg, and hyprpaper. - Better keyboard-driven experience and hjkl support. +- Support for multiple monitors ## Contributions diff --git a/waypaper/__main__.py b/waypaper/__main__.py index a66ac18..283638c 100644 --- a/waypaper/__main__.py +++ b/waypaper/__main__.py @@ -4,7 +4,7 @@ from waypaper.config import cf from waypaper.arguments import args -__version__ = "1.6" +__version__ = "1.7" def run(): diff --git a/waypaper/app.py b/waypaper/app.py index 01f498c..aa9988e 100644 --- a/waypaper/app.py +++ b/waypaper/app.py @@ -1,17 +1,26 @@ """Module that runs GUI app""" -from waypaper.changer import change_wallpaper -from waypaper.config import cf -from waypaper.options import FILL_OPTIONS, BACKEND_OPTIONS import threading import os +import shutil import distutils.spawn import gi +from waypaper.changer import change_wallpaper +from waypaper.config import cf +from waypaper.options import FILL_OPTIONS, BACKEND_OPTIONS, SORT_OPTIONS, SORT_DISPLAYS + gi.require_version("Gtk", "3.0") from gi.repository import Gtk, GdkPixbuf, Gdk, GLib +def has_image_extension(file_path): + """Check if the file has image extension""" + image_extensions = ['.gif', '.jpg', '.jpeg', '.png'] + ext = os.path.splitext(file_path)[1].lower() + return ext in image_extensions + + def get_image_paths(root_folder, include_subfolders=False, depth=None): """Get a list of file paths depending of weather we include subfolders and how deep we scan""" image_paths = [] @@ -23,7 +32,7 @@ def get_image_paths(root_folder, include_subfolders=False, depth=None): if current_depth > depth: continue for filename in files: - if filename.endswith(".jpg") or filename.endswith(".png") or filename.endswith(".gif"): + if has_image_extension(filename): image_paths.append(os.path.join(root, filename)) return image_paths @@ -93,8 +102,8 @@ class App(Gtk.Window): # Set as active line the backend from config, if it is installed: try: - filtered_backends = [value for value, miss in zip(BACKEND_OPTIONS, self.missing_backends) if not miss] - active_num = filtered_backends.index(cf.backend) + installed_backends = [value for value, miss in zip(BACKEND_OPTIONS, self.missing_backends) if not miss] + active_num = installed_backends.index(cf.backend) except: active_num = 0 self.backend_option_combo.set_active(active_num) @@ -116,10 +125,22 @@ class App(Gtk.Window): self.color_picker_button.set_rgba(rgba_color) self.color_picker_button.connect("color-set", self.on_color_set) + # Create a sort option dropdown menu: + self.sort_option_combo = Gtk.ComboBoxText() + for option in SORT_OPTIONS: + self.sort_option_combo.append_text(SORT_DISPLAYS[option]) + active_num = SORT_OPTIONS.index(cf.sort_option) + self.sort_option_combo.set_active(active_num) + self.sort_option_combo.connect("changed", self.on_sort_option_changed) + # Create exit button: self.exit_button = Gtk.Button(label=" Exit ") self.exit_button.connect("clicked", self.on_exit_clicked) + # Create refresh button: + self.refresh_button = Gtk.Button(label=" Refresh ") + self.refresh_button.connect("clicked", self.on_refresh_clicked) + # Create a box to contain the bottom row of buttons with margin: self.bottom_button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=20) self.bottom_button_box.set_margin_bottom(10) @@ -137,7 +158,9 @@ class App(Gtk.Window): # Create a horizontal box for display option and exit button: self.options_box = Gtk.HBox(spacing=10) self.options_box.pack_end(self.exit_button, False, False, 0) + self.options_box.pack_end(self.refresh_button, False, False, 0) self.options_box.pack_end(self.include_subfolders_checkbox, False, False, 0) + self.options_box.pack_end(self.sort_option_combo, False, False, 0) self.options_box.pack_end(self.color_picker_button, False, False, 0) self.options_box.pack_end(self.fill_option_combo, False, False, 0) self.options_box.pack_end(self.backend_option_combo, False, False, 0) @@ -179,10 +202,23 @@ class App(Gtk.Window): dialog.destroy() + def sort_images(self): + """Sort images depending on the sorting option""" + if cf.sort_option == "name": + self.image_paths.sort(key=lambda x: os.path.basename(x)) + elif cf.sort_option == "namerev": + self.image_paths.sort(key=lambda x: os.path.basename(x), reverse=True) + elif cf.sort_option == "date": + self.image_paths.sort(key=lambda x: os.path.getmtime(x)) + elif cf.sort_option == "daterev": + self.image_paths.sort(key=lambda x: os.path.getmtime(x), reverse=True) + + def process_images(self): """Load images from the selected folder, resize them, and arrange into a grid""" self.image_paths = get_image_paths(cf.image_folder, cf.include_subfolders, depth=1) + self.sort_images() # Show caching label: self.loading_label = Gtk.Label(label=f"Caching {len(self.image_paths)} wallpapers...") @@ -266,6 +302,14 @@ class App(Gtk.Window): cf.fill_option = combo.get_active_text() + def on_sort_option_changed(self, combo): + """Save sort parameter whet it is changed""" + selected_option = combo.get_active_text() + selected_option_num = list(SORT_DISPLAYS.values()).index(selected_option) + cf.sort_option = list(SORT_DISPLAYS.keys())[selected_option_num] + threading.Thread(target=self.process_images).start() + + def on_backend_option_changed(self, combo): """Save backend parameter whet it is changed""" cf.backend = combo.get_active_text() @@ -289,17 +333,39 @@ class App(Gtk.Window): cf.save() + def on_refresh_clicked(self, widget): + """On clicking refresh button, clear cache""" + self.clear_cache() + + def on_exit_clicked(self, widget): - """On clicking exit button, save the data and quit""" + """On clicking exit button, exit""" + self.exit_app() + + + def exit_app(self): + """Save the data and quit""" cf.save() Gtk.main_quit() + def clear_cache(self): + """Delete cache folder and reprocess the images""" + cache_folder = f"{cf.config_folder}/.cache" + try: + shutil.rmtree(cache_folder) + os.makedirs(cache_folder) + except OSError as e: + print(f"Error deleting cache '{cache_folder}': {e}") + threading.Thread(target=self.process_images).start() + + def on_key_pressed(self, widget, event): - """On clicking q, save the data and quit""" + """Process various key bindigns""" if event.keyval == Gdk.KEY_q: - cf.save() - Gtk.main_quit() + self.exit_app() + if event.keyval == Gdk.KEY_r: + self.clear_cache() def run(self): diff --git a/waypaper/config.py b/waypaper/config.py index 2dee6dc..cc6dc2d 100644 --- a/waypaper/config.py +++ b/waypaper/config.py @@ -5,6 +5,7 @@ import pathlib import os from waypaper.arguments import args +from waypaper.options import FILL_OPTIONS, SORT_OPTIONS class Config: @@ -15,6 +16,7 @@ class Config: self.image_folder = str(pathlib.Path.home()) + "/Pictures" self.wallpaper = None self.fill_option = "fill" + self.sort_option = "name" self.backend = "swaybg" self.color = "#ffffff" self.include_subfolders = False @@ -28,6 +30,7 @@ class Config: config["Settings"] = { "folder": str(self.image_folder), "fill": str(self.fill_option), + "sort": str(self.sort_option), "backend": str(self.backend), "color": str(self.color), "subfolders": str(self.include_subfolders), @@ -45,6 +48,11 @@ class Config: self.image_folder = config.get("Settings", "folder", fallback=self.image_folder) self.wallpaper = config.get("Settings", "wallpaper", fallback=self.wallpaper) self.fill_option = config.get("Settings", "fill", fallback=self.fill_option) + if self.fill_option not in FILL_OPTIONS: + self.sort_option = FILL_OPTIONS[0] + self.sort_option = config.get("Settings", "sort", fallback=self.sort_option) + if self.sort_option not in SORT_OPTIONS: + self.sort_option = SORT_OPTIONS[0] self.backend = config.get("Settings", "backend", fallback=self.backend) self.color = config.get("Settings", "color", fallback=self.color) self.include_subfolders = config.getboolean("Settings", "subfolders", fallback=self.include_subfolders) @@ -60,6 +68,7 @@ class Config: config.set("Settings", "folder", cf.image_folder) config.set("Settings", "wallpaper", cf.wallpaper) config.set("Settings", "fill", cf.fill_option) + config.set("Settings", "sort", cf.sort_option) config.set("Settings", "backend", cf.backend) config.set("Settings", "color", cf.color) config.set("Settings", "subfolders", str(cf.include_subfolders)) diff --git a/waypaper/options.py b/waypaper/options.py index d163cf6..cc6dc16 100644 --- a/waypaper/options.py +++ b/waypaper/options.py @@ -1,2 +1,8 @@ BACKEND_OPTIONS = ["swaybg", "swww", "feh", "wallutils"] FILL_OPTIONS = ["fill", "stretch", "fit", "center", "tile"] +SORT_OPTIONS = ["name", "namerev", "date", "daterev"] +SORT_DISPLAYS = { + "name": "Sort by name ↓", + "namerev": "Sort by name ↑", + "date": "Sort by date ↓", + "daterev": "Sort by date ↑"}