Compare commits

...

3 Commits

Author SHA1 Message Date
Roman
07ee2da7c1 Fixing setup.py 2024-01-01 20:40:48 +01:00
Roman
e78289d878 Adding manpage 2024-01-01 20:39:15 +01:00
Roman
f7235f551f Improvements of initial backend handling
This improved how the installed backnends are checked and makes sure that only installed backends are reconded into config.
This addresses the issue #23 and generally improved the code.
2024-01-01 18:59:30 +01:00
9 changed files with 103 additions and 67 deletions

View File

@ -12,7 +12,7 @@ GUI wallpaper setter for Wayland and Xorg window managers. It works as a fronten
- Works on both Wayland (with `swww` or `swaybg` or `wallutils`) and Xorg (with `feh` or `wallutils`)
- Restores wallpaper at launch of your WM
- Caching for fast loading
- Translated in en, fr, de, ru, pl
- Translated in en, fr, de, ru, pl, zh
## Installation
@ -86,15 +86,12 @@ If you wish to change language, change `laguage` variable in `.config/waypaper/c
## Roadmap
- Additional options for ~subfolders~, ~color~, ~sorting~, ~randomizing~ and setting a uniform color.
- ~Support for other backends like swww, feh, wallutils~, and maybe hyprpaper.
- ~Better keyboard-driven experience and hjkl support.~
- ~Support for multiple monitors with swww~ and swaybg
- Possible support for other backends like mpvpaper, and maybe hyprpaper.
- Support for various `swww` options
- Display animated previews of gif wallpapers
- Translations
## Contributions
Feel free to propose PR and suggest the improvements. I'll also appreciate any help with packaging for various distributions. Also, if you wish to contribute with translation into your language, plese translate `translation_en.py` file, and I'll do the rest.
Feel free to propose PR and suggest the improvements. I'll highly appreciate help with packaging for various distributions. If you wish to contribute with translation into your language, please see the `translations.py` file.
If you'd like to support the development, consider [donations](https://www.buymeacoffee.com/angryprofessor).

41
data/waypaper.1.gz Normal file
View File

@ -0,0 +1,41 @@
.TH WAYPAPER 2024-01-01
.nh
.SH NAME
waypaper \- GUI wallpaper manager for Wayland and X11
.SH SYNOPSIS
.B waypaper
[ \fIOPTION\fR ]
.SH DESCRIPTION
.B Waypaper
is a TUI wallpaper setter for Wayland and Xorg window managers. It works as a frontend for popular wallpaper backends like swaybg, swww, wallutils and feh. \fIhttps://anufrievroman.gitbook.com/waypaper \fR
.SH OPTIONS
.TP
\fB\-v \fP
Print version.
.TP
\fB\-h \fP
Print help.
.TP
\fB\-\-restore \fP
Restore the last chosen wallpaper. Useful at launch of the window manager.
.TP
\fB\-\-random \fP
Set a random wallpaper.
.TP
\fB\-\-backend XXX \fP
Specify which backend to use, which can be either \fIswaybg, swww, feh\fR, or \fIwallutils\fR. Useful if you use waypaper on both Wayland and Xorg on the same machine. By default, last used backend is used.
.TP
\fB\-\-fill XXX \fP
Specifies filling type, which can be eiher \fIfill, stretch, fit, center\fR, or \fItile\fR.
.SH COMMANDS DURING USE
Press '\fB?\fP' during use to get a list of keybindings.
.br
.SH AUTHOR
Written by Roman Anufriev. For more information, visit \fIhttps://anufrievroman.gitbook.com/waypaper\fR

View File

@ -18,7 +18,7 @@ setuptools.setup(
]
},
install_requires=["PyGObject", "importlib_metadata", "platformdirs", "Pillow"],
version='2.0.3',
version='2.0.4',
python_requires='>3.9',
classifiers=[
"Development Status :: 4 - Beta",
@ -43,6 +43,9 @@ setuptools.setup(
),
('share/applications',
['data/waypaper.desktop']
)
),
('share/man/man1',
['data/waypaper.1.gz']
),
],
)

View File

@ -7,7 +7,7 @@ import argparse
from waypaper.config import Config
from waypaper.app import App
from waypaper.changer import change_wallpaper
from waypaper.common import get_random_file, check_missing_backends
from waypaper.common import get_random_file
from waypaper.aboutdata import AboutData
from waypaper.options import FILL_OPTIONS, BACKEND_OPTIONS
from waypaper.translations import English, German, French, Russian, Polish, Chinese
@ -39,12 +39,10 @@ parser.add_argument("--backend", help=txt.msg_arg_back, choices=BACKEND_OPTIONS)
args = parser.parse_args()
def run():
"""Read user arguments and either run GUI app or just reset the wallpaper"""
cf.read_parameters_from_user_arguments(args)
missing_backends = check_missing_backends()
# Set the wallpaper and quit:
if args.restore:
@ -55,7 +53,7 @@ def run():
if wallpaper is None:
continue
change_wallpaper(wallpaper, cf, monitor, txt, missing_backends)
change_wallpaper(wallpaper, cf, monitor, txt)
time.sleep(0.1)
exit(0)

View File

@ -4,14 +4,14 @@ import subprocess
import threading
import os
import gi
import shutil
from pathlib import Path
from platformdirs import user_cache_path
from PIL import Image
from waypaper.aboutdata import AboutData
from waypaper.changer import change_wallpaper
from waypaper.config import Config
from waypaper.common import get_image_paths, get_random_file, check_missing_backends
from waypaper.common import get_image_paths, get_random_file
from waypaper.options import FILL_OPTIONS, BACKEND_OPTIONS, SORT_OPTIONS, SORT_DISPLAYS
gi.require_version("Gtk", "3.0")
@ -27,7 +27,7 @@ def read_webp_image(image_path):
return pixbuf
def cache_image(image_path, cachedir):
def cache_image(image_path, cache_dir):
"""Resize and cache images using gtk library"""
ext = os.path.splitext(image_path)[1].lower()
if ext == ".webp":
@ -38,7 +38,7 @@ def cache_image(image_path, cachedir):
scaled_width = 240
scaled_height = int(scaled_width / aspect_ratio)
scaled_pixbuf = pixbuf.scale_simple(scaled_width, scaled_height, GdkPixbuf.InterpType.BILINEAR)
output_file = cachedir / Path(os.path.basename(image_path))
output_file = cache_dir / Path(os.path.basename(image_path))
scaled_pixbuf.savev(str(output_file), "jpeg", [], [])
@ -47,15 +47,14 @@ class App(Gtk.Window):
def __init__(self, txt):
super().__init__(title="Waypaper")
self.cf = Config()
self.about = AboutData()
self.txt = txt
self.check_backends()
self.set_default_size(780, 600)
self.connect("delete-event", Gtk.main_quit)
self.selected_index = 0
self.highlighted_image_row = 0
self.aboutData = AboutData()
self.cachePath = user_cache_path(self.aboutData.applicationName())
self.cf = Config()
self.init_ui()
# Start the image processing in a separate thread:
@ -96,16 +95,13 @@ class App(Gtk.Window):
# Create a backend dropdown menu:
self.backend_option_combo = Gtk.ComboBoxText()
for backend, is_missing in zip(BACKEND_OPTIONS, self.missing_backends):
if not is_missing:
self.backend_option_combo.append_text(backend)
for backend in self.cf.installed_backends:
self.backend_option_combo.append_text(backend)
# Set as active line the backend from config, if it is installed:
try:
installed_backends = [value for value, miss in zip(BACKEND_OPTIONS, self.missing_backends) if not miss]
active_num = installed_backends.index(self.cf.backend)
except:
active_num = 0
active_num = 0
if self.cf.backend in self.cf.installed_backends:
active_num = self.cf.installed_backends.index(self.cf.backend)
self.backend_option_combo.set_active(active_num)
self.backend_option_combo.connect("changed", self.on_backend_option_changed)
self.backend_option_combo.set_tooltip_text(self.txt.tip_backend)
@ -192,7 +188,6 @@ class App(Gtk.Window):
def monitor_option_display(self):
"""Display monitor option if backend is swww"""
self.options_box.remove(self.monitor_option_combo)
# if "swww" not in self.missing_backends and self.cf.backend not in ["wallutils", "feh"]:
if self.cf.backend == "swww":
# Check available monitors:
@ -219,12 +214,9 @@ class App(Gtk.Window):
def check_backends(self):
"""Before running the app, check which backends are installed"""
self.missing_backends = check_missing_backends()
# Show error message if no backends are installed:
if all(self.missing_backends):
self.show_message(sefl.txt.err_backend)
"""Before running the app, check which backends are installed or show the error"""
if not self.cf.installed_backends:
self.show_message(self.txt.err_backend)
exit()
@ -275,9 +267,9 @@ class App(Gtk.Window):
self.image_paths.remove(image_path)
continue
# If this image is not cached yet, resize and cache it:
cached_image_path = self.cachePath/os.path.basename(image_path)
cached_image_path = self.cf.cache_dir / os.path.basename(image_path)
if not cached_image_path.exists():
cache_image(image_path, self.cachePath)
cache_image(image_path, self.cf.cache_dir)
# Load cached thumbnail:
thumbnail = GdkPixbuf.Pixbuf.new_from_file(str(cached_image_path))
@ -406,7 +398,7 @@ class App(Gtk.Window):
self.load_image_grid()
print(self.txt.msg_path, self.cf.selected_wallpaper)
self.cf.fill_option = self.fill_option_combo.get_active_text() or self.cf.fill_option
change_wallpaper(self.cf.selected_wallpaper, self.cf, self.cf.selected_monitor, self.txt, self.missing_backends)
change_wallpaper(self.cf.selected_wallpaper, self.cf, self.cf.selected_monitor, self.txt)
self.cf.save()
@ -433,17 +425,17 @@ class App(Gtk.Window):
return
print(self.txt.msg_path, self.cf.selected_wallpaper)
self.cf.fill_option = self.fill_option_combo.get_active_text() or self.cf.fill_option
change_wallpaper(self.cf.selected_wallpaper, self.cf, self.cf.selected_monitor, self.txt, self.missing_backends)
change_wallpaper(self.cf.selected_wallpaper, self.cf, self.cf.selected_monitor, self.txt)
self.cf.save()
def clear_cache(self):
"""Delete cache folder and reprocess the images"""
try:
shutil.rmtree(self.cachePath)
os.makedirs(self.cachePath)
shutil.rmtree(self.cf.cache_dir)
os.makedirs(self.cf.cache_dir)
except OSError as e:
print(f"{self.txt.err_cache} '{self.cachePath}': {e}")
print(f"{self.txt.err_cache} '{self.cf.cache_dir}': {e}")
threading.Thread(target=self.process_images).start()
@ -501,7 +493,7 @@ class App(Gtk.Window):
print(self.txt.msg_path, self.cf.selected_wallpaper)
self.cf.backend = self.backend_option_combo.get_active_text()
self.cf.fill_option = self.fill_option_combo.get_active_text() or self.cf.fill_option
change_wallpaper(self.cf.selected_wallpaper, self.cf, self.cf.selected_monitor, self.txt, self.missing_backends)
change_wallpaper(self.cf.selected_wallpaper, self.cf, self.cf.selected_monitor, self.txt)
self.cf.save()
# Prevent other default key handling:

View File

@ -6,13 +6,14 @@ import time
from waypaper.options import BACKEND_OPTIONS
def change_wallpaper(image_path, cf, monitor, txt, missing_backends):
"""Run a system command to change the wallpaper depending on the backend"""
def change_wallpaper(image_path, cf, monitor, txt):
"""Run system commands to change the wallpaper depending on the backend"""
fill_option = cf.fill_option
color = cf.color
backend = cf.backend
swww_transition = cf.swww_transition
installed_backends = cf.installed_backends
try:
# swaybg backend:
@ -41,14 +42,12 @@ def change_wallpaper(image_path, cf, monitor, txt, missing_backends):
"tile": "no",
}
fill = fill_types[fill_option.lower()]
is_swaybg_installed = not missing_backends[BACKEND_OPTIONS.index("swaybg")]
if is_swaybg_installed:
if "swaybg" in installed_backends:
try:
subprocess.Popen(["killall", "swaybg"])
time.sleep(0.005)
except Exception as e:
print(f"{ERR_KILL} {e}")
print(missing_backends)
subprocess.Popen(["swww", "init"])
command = ["swww", "img", image_path]
command.extend(["--resize", fill])

View File

@ -38,12 +38,15 @@ def get_random_file(folder, include_subfolders):
return None
def check_missing_backends():
def check_installed_backends():
"""Check which backends are installed in the system"""
missing_backends = []
installed_backends = []
for backend in BACKEND_OPTIONS:
if backend == "wallutils":
backend = "setwallpaper"
is_backend_missing = not bool(shutil.which(backend))
missing_backends.append(is_backend_missing)
return missing_backends
binary_name = "setwallpaper"
else:
binary_name = backend
is_installed = bool(shutil.which(binary_name))
if is_installed:
installed_backends.append(backend)
return installed_backends

View File

@ -7,31 +7,34 @@ from sys import exit
from platformdirs import user_config_path, user_pictures_path, user_cache_path
from waypaper.aboutdata import AboutData
from waypaper.options import FILL_OPTIONS, SORT_OPTIONS, SWWW_TRANSITIONS
from waypaper.options import FILL_OPTIONS, SORT_OPTIONS, SWWW_TRANSITIONS, BACKEND_OPTIONS
from waypaper.common import check_installed_backends
class Config:
"""User configuration loaded from the config.ini file"""
def __init__(self):
self.image_folder = user_pictures_path()
self.installed_backends = check_installed_backends()
self.selected_wallpaper = ""
self.selected_monitor = "All"
self.fill_option = "fill"
self.sort_option = "name"
self.backend = "swaybg"
self.fill_option = FILL_OPTIONS[0]
self.sort_option = SORT_OPTIONS[0]
self.backend = self.installed_backends[0] if self.installed_backends else BACKEND_OPTIONS[0]
self.color = "#ffffff"
self.swww_transition = "any"
self.swww_transition = SWWW_TRANSITIONS[0]
self.lang = "en"
self.monitors = [self.selected_monitor]
self.wallpaper = []
self.include_subfolders = False
self.aboutData = AboutData()
self.cachePath = user_cache_path(self.aboutData.applicationName())
self.configPath = user_config_path(self.aboutData.applicationName())
self.config_file = self.configPath / "config.ini"
self.about = AboutData()
self.cache_dir = user_cache_path(self.about.applicationName())
self.config_dir = user_config_path(self.about.applicationName())
self.config_file = self.config_dir / "config.ini"
# Create config and cache folders:
self.configPath.mkdir(parents=True, exist_ok=True)
self.cachePath.mkdir(parents=True,exist_ok=True)
self.config_dir.mkdir(parents=True, exist_ok=True)
self.cache_dir.mkdir(parents=True,exist_ok=True)
self.read()

View File

@ -14,5 +14,5 @@ IMAGE_EXTENSIONS = {
BACKEND_OPTIONS[3]: ['.gif', '.jpg', '.jpeg', '.png'],
}
SWWW_TRANSITIONS = ["none", "simple", "fade", "wipe", "left", "right", "top", "bottom",
"wave", "grow", "center", "any", "outer", "random"]
SWWW_TRANSITIONS = ["any", "none", "simple", "fade", "wipe", "left", "right", "top",
"bottom", "wave", "grow", "center", "outer", "random"]