diff --git a/README.md b/README.md index 8c0abe0..c46dc9e 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ GUI wallpaper setter for both Wayland and X11 window managers that works as a fr - GUI wallpaper selection - Works on both Wayland (with `swaybg` or `swww`) and X11 (with `feh`) - Restores wallpaper at launch of your WM +- Caching for fast loading ## Installation @@ -19,17 +20,15 @@ You need to install at least one of the backends and Waypaper, which works as a Install a preferred backend from your package manager: [swaybg](https://github.com/swaywm/swaybg) or [swww](https://github.com/Horus645/swww) on Wayland or [feh](https://github.com/derf/feh) on x11. You can also install and test all of them. -- [swaybg](https://github.com/swaywm/swaybg) - the wayland backend that supports only static images. -- [swww](https://github.com/Horus645/swww) - the wayland backend that also supports animated GIFs. -- [feh](https://github.com/derf/feh) - the x11 backend that supports static images. +### 2. Install Waypaper -### 2. Install Waypaper (from PyPi) +#### From PyPi `pipx install waypaper` If `pipx` is not found, you first need to install `pipx` from your package manager, it's sometimes called `python-pipx`. -### 2. Install Waypaper (from AUR) +#### From AUR [waypaper-git](https://aur.archlinux.org/packages/waypaper-git) package is available in AUR, thanks to *metak*. So, on arch-based system, you can install it as: diff --git a/temp.jpg b/temp.jpg new file mode 100644 index 0000000..e69de29 diff --git a/waypaper/__main__.py b/waypaper/__main__.py index f9a6f9a..70d1bae 100644 --- a/waypaper/__main__.py +++ b/waypaper/__main__.py @@ -6,7 +6,7 @@ from waypaper.arguments import args -__version__ = "1.5.1" +__version__ = "1.6" def run(): diff --git a/waypaper/app.py b/waypaper/app.py index 37de10c..6504530 100644 --- a/waypaper/app.py +++ b/waypaper/app.py @@ -5,7 +5,6 @@ import os import subprocess import configparser - import distutils.spawn gi.require_version("Gtk", "3.0") @@ -16,6 +15,33 @@ from waypaper.options import FILL_OPTIONS, BACKEND_OPTIONS +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 = [] + for root, directories, files in os.walk(root_folder): + if not include_subfolders and root != root_folder: + continue + if depth is not None and root != root_folder: + current_depth = root.count(os.path.sep) - root_folder.count(os.path.sep) + if current_depth > depth: + continue + for filename in files: + if filename.endswith(".jpg") or filename.endswith(".png") or filename.endswith(".gif"): + image_paths.append(os.path.join(root, filename)) + return image_paths + + +def cache_image(image_path): + """Resize and cache images using gtk library""" + pixbuf = GdkPixbuf.Pixbuf.new_from_file(image_path) + aspect_ratio = pixbuf.get_width() / pixbuf.get_height() + scaled_width = 240 + scaled_height = int(scaled_width / aspect_ratio) + scaled_pixbuf = pixbuf.scale_simple(scaled_width, scaled_height, GdkPixbuf.InterpType.BILINEAR) + output_file = f"{cf.config_folder}/.cache/{os.path.basename(image_path)}" + scaled_pixbuf.savev(output_file, "jpeg", [], []) + + class App(Gtk.Window): """Main application class that controls GUI""" @@ -154,28 +180,13 @@ def show_no_backend_message(self, message): dialog.destroy() - def get_image_paths(self, root_folder, include_subfolders=False, depth=None): - """Get a list of file paths depending of weather we include subfolders and how deep we scan""" - self.image_paths = [] - for root, directories, files in os.walk(root_folder): - if not include_subfolders and root != root_folder: - continue - if depth is not None and root != root_folder: - current_depth = root.count(os.path.sep) - root_folder.count(os.path.sep) - if current_depth > depth: - continue - for filename in files: - if filename.endswith(".jpg") or filename.endswith(".png") or filename.endswith(".gif"): - self.image_paths.append(os.path.join(root, filename)) - - def process_images(self): """Load images from the selected folder, resize them, and arrange into a grid""" - self.get_image_paths(cf.image_folder, cf.include_subfolders, depth=1) + self.image_paths = get_image_paths(cf.image_folder, cf.include_subfolders, depth=1) - # Show loading label: - self.loading_label = Gtk.Label(label=f"Loading {len(self.image_paths)} wallpapers...") + # Show caching label: + self.loading_label = Gtk.Label(label=f"Caching {len(self.image_paths)} wallpapers...") self.bottom_loading_box.add(self.loading_label) self.show_all() @@ -184,16 +195,17 @@ def process_images(self): for image_path in self.image_paths: - # Load and scale the image: - pixbuf = GdkPixbuf.Pixbuf.new_from_file(image_path) - aspect_ratio = pixbuf.get_width() / pixbuf.get_height() - scaled_width = 240 - scaled_height = int(scaled_width / aspect_ratio) - scaled_pixbuf = pixbuf.scale_simple(scaled_width, scaled_height, GdkPixbuf.InterpType.BILINEAR) - self.thumbnails.append(scaled_pixbuf) + # If this image is not cached yet, resize and cache it: + if not os.path.exists(f"{cf.config_folder}/.cache/{os.path.basename(image_path)}"): + cache_image(image_path) + + # Load cached thumbnail: + cached_image_path = f"{cf.config_folder}/.cache/{os.path.basename(image_path)}" + thumbnail = GdkPixbuf.Pixbuf.new_from_file(cached_image_path) + self.thumbnails.append(thumbnail) self.image_names.append(os.path.basename(image_path)) - # When image processing is done, remove loading label and display the images: + # When image processing is done, remove caching label and display the images: self.bottom_loading_box.remove(self.loading_label) GLib.idle_add(self.load_image_grid) diff --git a/waypaper/config.py b/waypaper/config.py index 80d24f9..42e1450 100644 --- a/waypaper/config.py +++ b/waypaper/config.py @@ -84,6 +84,10 @@ def read_parameters_from_user_arguments(self): if not os.path.exists(cf.config_folder): os.makedirs(cf.config_folder) +# Create cache folder: +if not os.path.exists(f"{cf.config_folder}/.cache"): + os.makedirs(f"{cf.config_folder}/.cache") + # Create config file: if not os.path.exists(cf.config_file): cf.create()