Compare commits

...

13 Commits

Author SHA1 Message Date
Roman
5d8483f337 Merge remote-tracking branch 'thaodan-fork/xdg_dirs' into thaodan_rewritre 2023-12-11 20:10:03 +01:00
Björn Bidar
5ad8b85a15 Import and update French translation
Signed-off-by: Björn Bidar <bjorn.bidar@thaodan.de>
2023-10-20 23:52:00 +03:00
Björn Bidar
683bc85127 Update translation instruction
Signed-off-by: Björn Bidar <bjorn.bidar@thaodan.de>
2023-10-20 23:52:00 +03:00
Björn Bidar
d53d013db4 Import and update German translation
Signed-off-by: Björn Bidar <bjorn.bidar@thaodan.de>
2023-10-20 23:51:58 +03:00
Björn Bidar
8545e45f4f Add translation template and English translation
Signed-off-by: Björn Bidar <bjorn.bidar@thaodan.de>
2023-10-20 23:50:28 +03:00
Björn Bidar
d23f342876 Add script to generate translation template
Signed-off-by: Björn Bidar <bjorn.bidar@thaodan.de>
2023-10-20 23:50:28 +03:00
Björn Bidar
79b2f386c8 Replace python based translation with gettext, remove wildcard import
Wildcard imports are forbidden or very frowned upon, it is easier for
user and developers to use a translation system such as gettext instead.

Signed-off-by: Björn Bidar <bjorn.bidar@thaodan.de>
2023-10-20 23:50:28 +03:00
Björn Bidar
1dff8bde0b Skip generating a thumbnail of images that GdkPixbuf coudn't render
E.g. in case they use the wrong extension for their real mime type.

Signed-off-by: Björn Bidar <bjorn.bidar@thaodan.de>
2023-10-20 23:50:26 +03:00
Björn Bidar
0bb54963df Skip zero byte images when generating thumbnails
Signed-off-by: Björn Bidar <bjorn.bidar@thaodan.de>
2023-10-20 23:45:57 +03:00
Björn Bidar
296c07491c Rewrite main to use AboutData, drop arguments
Retrieve application meta data from AboutData,
instead of manually writing the version and application name.

Also initialize config class once.

Since the argument parser was only used here drop it,
move to main.

Signed-off-by: Björn Bidar <bjorn.bidar@thaodan.de>
2023-10-20 23:45:57 +03:00
Björn Bidar
fd588c5c34 Make use of platformdirs and AboutData in App
Signed-off-by: Björn Bidar <bjorn.bidar@thaodan.de>
2023-10-20 23:45:55 +03:00
Björn Bidar
eb5bfedb95 Rewrite Config class to use AboutData and platformdirs
Instead of hardcoding paths use platformdirs for configuration-,
pictures- and cache-directory.

Also instead of creating a global instance, initialize the class
in the constructor, including the creation of all directories we use.

Also it's not required to create the config file if it doesn't exist,
configparser will simply not an emtpy configuration object.

Change default value of wallpaper to be a string, the
config parser later uses a string, if the value is a list as it
was said it doesn't work a list doesn't have a .split() method.

Signed-off-by: Björn Bidar <bjorn.bidar@thaodan.de>
2023-10-20 23:41:13 +03:00
Björn Bidar
059b107445 Add class retrieve metadata from importlib
Signed-off-by: Björn Bidar <bjorn.bidar@thaodan.de>
2023-10-16 17:40:22 +03:00
17 changed files with 1295 additions and 341 deletions

View File

@ -46,6 +46,10 @@ The `waypaper` package is available thanks to Basil Keeler.
- `swww` or `swaybg` or `feh` or `wallutils`
- gobject python library (it might be called `python-gobject` or `python3-gi` or `python3-gobject` in your package manager.)
- `python-importlib_metadata`
- `python-platformdirs`
- `gettext`
- `setuptools-gettext`
## Usage
@ -93,6 +97,16 @@ If you wish to change language, change `laguage` variable in `.config/waypaper/c
## 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 also appreciate any help with packaging for various distributions.
If you wish to contribute with translation into your language, first make sure `gettext` is installed.
After doing so switch your copy of the wayper sources, run the command below and follow the instructions:
```
cd po; msginit
```
For more information about the translation process visit the gettext [manual](https://www.gnu.org/software/gettext/manual/gettext.html).
If you'd like to support the development, consider [donations](https://www.buymeacoffee.com/angryprofessor).

84
misc/update_translations.py Executable file
View File

@ -0,0 +1,84 @@
#!/usr/bin/python3
"""Scan source files and to update translations"""
# pylint: disable=global-statement, exec-used
import os
import sys
import subprocess
from pathlib import Path
import setuptools
# Global variables
exclude_files = ['setup.py',
os.path.basename(sys.argv[0]),
'build']
project_root=Path(subprocess.getoutput("git rev-parse --show-toplevel"))
po_dir=project_root / "po"
metadata = {}
# Load metadata from setup.py
# First create a mock setup() function
def setup(**kwargs):
"""Retrieve metadata from setup.py
@returns: metadata as 'metadata'
"""
global metadata
metadata = kwargs
setuptools.setup = setup
# Now open setup.py and use setup to store metadata
# in the dictionary metadata
with open(project_root/"setup.py", mode='r', encoding='utf-8') as setup_fd:
setup_str = setup_fd.read()
exec(setup_str)
os.makedirs(po_dir, exist_ok=True)
# Generate po files
xgettext = ['xgettext', '--indent', '--language=Python',
'--foreign-use', '--output-dir', str(po_dir),
f'--package-name={metadata["name"]}',
f'--package-version={metadata["version"]}',
f'--default-domain={metadata["name"]}',
f'--output={metadata["name"]}.pot',
'--msgid-bugs-address=none@none']
msgmerge = [ 'msgmerge', '--update']
msginit = ['msginit',
'--no-translator', '--locale=en',
f'--output-file={str(po_dir)}/en.po',
f'--input={str(po_dir)}/{metadata["name"]}.pot']
package_path = Path(metadata["name"])
src_files = [ str(src_file.relative_to(project_root)) \
for src_file in (project_root/package_path).glob('**/*.py') \
if src_file.name not in exclude_files]
os.chdir(project_root)
try:
subprocess.run([*xgettext, *src_files], check=True)
except subprocess.CalledProcessError as e:
print(e)
# Now update existing translations if needed
for po_file in po_dir.glob('*.po'):
try:
subprocess.run([*msgmerge, po_file, str(po_dir)+f'/{metadata["name"]}.pot'],
check=True)
except subprocess.CalledProcessError as e:
print(e)
# Create or update English translation
english_translation = po_dir/'en.po'
if english_translation.exists():
english_translation.unlink()
try:
subprocess.run([*msginit],
check=True)
except subprocess.CalledProcessError as e:
print(e)

233
po/de.po Normal file
View File

@ -0,0 +1,233 @@
# Language deDE translations for waypaper package.
# This file is put in the public domain.
# bidar <me@thaodan.de>, 2023.
#
msgid ""
msgstr ""
"Project-Id-Version: waypaper 0.0.2\n"
"Report-Msgid-Bugs-To: none@none\n"
"POT-Creation-Date: 2023-10-16 03:54+0300\n"
"PO-Revision-Date: 2023-10-16 18:43+0300\n"
"Last-Translator: Björn Bidar <me@thaodan.de>\n"
"Language-Team: Language de\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: waypaper/__main__.py:28
#, python-brace-format
msgid ""
"GUI wallpaper setter for Wayland and X11. It works as a frontend for "
"{backends}"
msgstr ""
"Grafisches Hintergrundbild-Auswahlwerkzeug für Wayland und X11. Es dient als "
"Frontend für{backends}"
#: waypaper/__main__.py:29
#, python-brace-format
msgid "For more information, visit: {homePage}"
msgstr "Weitere Informationen finden Sie unter: {homePage}"
#: waypaper/__main__.py:31
msgid "print version of the program"
msgstr "gibt die Programmversion aus"
#: waypaper/__main__.py:33
msgid "restore last wallpaper"
msgstr "stellt das zuletzt verwendete Hintergrundbild wieder her"
#: waypaper/__main__.py:34
msgid "set a random wallpaper"
msgstr "Ein zufälliges Hintergrundbild auswählen"
#: waypaper/__main__.py:35
msgid "specify how to fill the screen with chosen image"
msgstr ""
"legt fest, wie das Bild skaliert werden soll, um den gesamten Bildschirm "
"auszufüllen"
#: waypaper/__main__.py:37
msgid "specify which backend to use to set wallpaper"
msgstr ""
"legt das Backend fest, welches zum Setzen des Hintergrundbildes verwendet "
"werden soll"
#: waypaper/changer.py:23 waypaper/changer.py:46
#, python-brace-format
msgid "Warning related to killall: {error}"
msgstr "Warnung im Zusammenhang mit dem Befehl killall: {error}"
#: waypaper/changer.py:84
#, python-brace-format
msgid "Sent command to set wallpaper was set with {backend}"
msgstr "Hintergrundbild wurde mit folgendem Befehl gesetzt {backend}"
#: waypaper/changer.py:88
#, python-brace-format
msgid "The backend is not supported: {backend}"
msgstr "Das folgende Backend wird nicht unterstützt: {backend}"
#: waypaper/changer.py:92
#, python-brace-format
msgid "Error changing wallpaper: {error}"
msgstr "Fehler beim Ändern des Hintergrundbildes: {error}"
#: waypaper/app.py:69
msgid "Change wallpaper folder"
msgstr "Hintergrundbild-Ordner ändern"
#: waypaper/app.py:89
msgid "Subfolders"
msgstr "Unterordner"
#: waypaper/app.py:92
msgid "Include/exclude images in subfolders"
msgstr "Wählen, ob Unterordner mit einbezogen werden sollen"
#: waypaper/app.py:108
msgid "Choose backen"
msgstr "Backend auswählen"
#: waypaper/app.py:112
msgid "Fill"
msgstr "Füllen"
#: waypaper/app.py:113
msgid "Stretch"
msgstr "Dehnen"
#: waypaper/app.py:114
msgid "Fit"
msgstr "Passen"
#: waypaper/app.py:115
msgid "Center"
msgstr "Zentrieren"
#: waypaper/app.py:116
msgid "Tile"
msgstr "Kacheln"
#: waypaper/app.py:121
msgid "Choose fill type"
msgstr "Skalierungsart auswählen"
#: waypaper/app.py:130
msgid "Choose background color"
msgstr "Hintergrundfarbe auswählen"
#: waypaper/app.py:134 waypaper/app.py:135
msgid "Name"
msgstr "Name"
#: waypaper/app.py:136 waypaper/app.py:137
msgid "Date"
msgstr "Datum"
#: waypaper/app.py:144
msgid "Choose sorting type"
msgstr "Sortierungsart auswählen"
#: waypaper/app.py:148
msgid "Exit"
msgstr "Beenden"
#: waypaper/app.py:150
msgid "Exit the application"
msgstr "Das Programm beenden"
#: waypaper/app.py:153
msgid "Refresh"
msgstr "Aktualisieren"
#: waypaper/app.py:156
msgid "Reache the folder of images"
msgstr "Erneutes einlesen des Hintergrundbild-Ordners"
#: waypaper/app.py:159
msgid "Random"
msgstr "Zufällig"
#: waypaper/app.py:161
msgid "Set random wallpaper"
msgstr "Ein zufälliges Hintergrundbild auswählen"
#: waypaper/app.py:214
#, python-brace-format
msgid "Error determining monitor names: {error}"
msgstr "Fehler beim Ermitteln der Monitor-Namen {error}"
#: waypaper/app.py:222
msgid "Choose Display"
msgstr "Bildschirm auswählen"
#: waypaper/app.py:239
#, python-brace-format
msgid ""
"\n"
" Looks like none of the wallpaper backends is installed in the "
"system.\n"
" Use your package manager to install at least one of these "
"backends:\n"
" {backends}\n"
" For more information, visit: {url}"
msgstr ""
"\n"
"Es sieht aus als würde keines der Hintergrund Backend\n"
"installiert seit auf dem Systm\n"
"Verwende deinen Paket-Manager um mindestens eines zu installieren:\n"
"{backends}\n"
"Für mehr Informationen, besuche: {url}"
#: waypaper/app.py:282
msgid "Caching wallpapers.."
msgstr "Hintergrundbilder werden zwischengespeichert..."
#: waypaper/app.py:364
msgid "Please choose a folder"
msgstr "Bitte wählen Sie einen Ordner aus"
#: waypaper/app.py:365
msgid "Select"
msgstr "Auswählen"
#: waypaper/app.py:425 waypaper/app.py:451 waypaper/app.py:530
#, python-brace-format
msgid "Selected image path: {image}"
msgstr "Pfad zum ausgewählten Bild: {image}"
#: waypaper/app.py:463
#, fuzzy, python-brace-format
msgid "Error deleting cache '{cacheDir}': '{error}'"
msgstr "Fehler beim Löschen des Zwischenspeichers '{cacheDir}': '{error}'"
#: waypaper/app.py:513
#, fuzzy, python-brace-format
msgid ""
"Waypaper's hotkeys:\n"
" hjkl - Navigation (←↓↑→)\n"
" f - Change wallpaper folder\n"
" g - Scroll to top\n"
" G - Scroll to bottom\n"
" R - Set random wallpaper\n"
" r - Recache wallpapers\n"
" s - Include/exclude images in subfolders\n"
" ? - Help\n"
" q - Exit\n"
" For more information, visit:\n"
" {url}\n"
" "
msgstr ""
"Waypapers Tastenkürzel:\n"
"Navigation (←↓↑→)\n"
"f - Hintergrundbild-Ordner ändern\n"
"g - Zum Anfang scrollen\\nG - Zum Ende scrollen\n"
"G - Zum Ende scrollen\n"
"R - Zufälliges Hintergrundbild\n"
"r - Hintergrundbilder zwischenspeichern\n"
"s - Unterordner mit einbeziehen\n"
"? - Hilfe\n"
"q - Beenden\n"
"Weitere Informationen finden Sie unter:\n"
"{url}\n"

232
po/en.po Normal file
View File

@ -0,0 +1,232 @@
# English translations for waypaper package.
# This file is put in the public domain.
# Automatically generated, 2023.
#
msgid ""
msgstr ""
"Project-Id-Version: waypaper 0.0.2\n"
"Report-Msgid-Bugs-To: none@none\n"
"POT-Creation-Date: 2023-10-16 18:48+0300\n"
"PO-Revision-Date: 2023-10-16 18:48+0300\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: waypaper/__main__.py:28
#, python-brace-format
msgid ""
"GUI wallpaper setter for Wayland and X11. It works as a frontend for "
"{backends}"
msgstr ""
"GUI wallpaper setter for Wayland and X11. It works as a frontend for "
"{backends}"
#: waypaper/__main__.py:29
#, python-brace-format
msgid "For more information, visit: {homePage}"
msgstr "For more information, visit: {homePage}"
#: waypaper/__main__.py:31
msgid "print version of the program"
msgstr "print version of the program"
#: waypaper/__main__.py:33
msgid "restore last wallpaper"
msgstr "restore last wallpaper"
#: waypaper/__main__.py:34
msgid "set a random wallpaper"
msgstr "set a random wallpaper"
#: waypaper/__main__.py:35
msgid "specify how to fill the screen with chosen image"
msgstr "specify how to fill the screen with chosen image"
#: waypaper/__main__.py:37
msgid "specify which backend to use to set wallpaper"
msgstr "specify which backend to use to set wallpaper"
#: waypaper/app.py:69
msgid "Change wallpaper folder"
msgstr "Change wallpaper folder"
#: waypaper/app.py:89
msgid "Subfolders"
msgstr "Subfolders"
#: waypaper/app.py:92
msgid "Include/exclude images in subfolders"
msgstr "Include/exclude images in subfolders"
#: waypaper/app.py:108
msgid "Choose backend"
msgstr "Choose backend"
#: waypaper/app.py:112
msgid "Fill"
msgstr "Fill"
#: waypaper/app.py:113
msgid "Stretch"
msgstr "Stretch"
#: waypaper/app.py:114
msgid "Fit"
msgstr "Fit"
#: waypaper/app.py:115
msgid "Center"
msgstr "Center"
#: waypaper/app.py:116
msgid "Tile"
msgstr "Tile"
#: waypaper/app.py:121
msgid "Choose fill type"
msgstr "Choose fill type"
#: waypaper/app.py:130
msgid "Choose background color"
msgstr "Choose background color"
#: waypaper/app.py:134 waypaper/app.py:135
msgid "Name"
msgstr "Name"
#: waypaper/app.py:136 waypaper/app.py:137
msgid "Date"
msgstr "Date"
#: waypaper/app.py:144
msgid "Choose sorting type"
msgstr "Choose sorting type"
#: waypaper/app.py:148
msgid "Exit"
msgstr "Exit"
#: waypaper/app.py:150
msgid "Exit the application"
msgstr "Exit the application"
#: waypaper/app.py:153
msgid "Refresh"
msgstr "Refresh"
#: waypaper/app.py:156
msgid "Reache the folder of images"
msgstr "Reache the folder of images"
#: waypaper/app.py:159
msgid "Random"
msgstr "Random"
#: waypaper/app.py:161
msgid "Set random wallpaper"
msgstr "Set random wallpaper"
#: waypaper/app.py:214
#, python-brace-format
msgid "Error determining monitor names: {error}"
msgstr "Error determining monitor names: {error}"
#: waypaper/app.py:222
msgid "Choose Display"
msgstr "Choose Display"
#: waypaper/app.py:239
#, python-brace-format
msgid ""
"\n"
" Looks like none of the wallpaper backends is installed in the "
"system.\n"
" Use your package manager to install at least one of these "
"backends:\n"
" {backends}\n"
" For more information, visit: {url}"
msgstr ""
"\n"
" Looks like none of the wallpaper backends is installed in the "
"system.\n"
" Use your package manager to install at least one of these "
"backends:\n"
" {backends}\n"
" For more information, visit: {url}"
#: waypaper/app.py:282
msgid "Caching wallpapers.."
msgstr "Caching wallpapers.."
#: waypaper/app.py:364
msgid "Please choose a folder"
msgstr "Please choose a folder"
#: waypaper/app.py:365
msgid "Select"
msgstr "Select"
#: waypaper/app.py:425 waypaper/app.py:451 waypaper/app.py:530
#, python-brace-format
msgid "Selected image path: {image}"
msgstr "Selected image path: {image}"
#: waypaper/app.py:463
#, python-brace-format
msgid "Error deleting cache '{cacheDir}': '{error}'"
msgstr "Error deleting cache '{cacheDir}': '{error}'"
#: waypaper/app.py:513
#, python-brace-format
msgid ""
"Waypaper's hotkeys:\n"
" hjkl - Navigation (←↓↑→)\n"
" f - Change wallpaper folder\n"
" g - Scroll to top\n"
" G - Scroll to bottom\n"
" R - Set random wallpaper\n"
" r - Recache wallpapers\n"
" s - Include/exclude images in subfolders\n"
" ? - Help\n"
" q - Exit\n"
" For more information, visit:\n"
" {url}\n"
" "
msgstr ""
"Waypaper's hotkeys:\n"
" hjkl - Navigation (←↓↑→)\n"
" f - Change wallpaper folder\n"
" g - Scroll to top\n"
" G - Scroll to bottom\n"
" R - Set random wallpaper\n"
" r - Recache wallpapers\n"
" s - Include/exclude images in subfolders\n"
" ? - Help\n"
" q - Exit\n"
" For more information, visit:\n"
" {url}\n"
" "
#: waypaper/changer.py:23 waypaper/changer.py:46
#, python-brace-format
msgid "Warning related to killall: {error}"
msgstr "Warning related to killall: {error}"
#: waypaper/changer.py:84
#, python-brace-format
msgid "Sent command to set wallpaper was set with {backend}"
msgstr "Sent command to set wallpaper was set with {backend}"
#: waypaper/changer.py:88
#, python-brace-format
msgid "The backend is not supported: {backend}"
msgstr "The backend is not supported: {backend}"
#: waypaper/changer.py:92
#, python-brace-format
msgid "Error changing wallpaper: {error}"
msgstr "Error changing wallpaper: {error}"

230
po/fr.po Normal file
View File

@ -0,0 +1,230 @@
# French translations for waypaper package.
# This file is put in the public domain.
# bidar <bjorn.bidar@thaodan.de>, 2023.
#
msgid ""
msgstr ""
"Project-Id-Version: waypaper 0.0.2\n"
"Report-Msgid-Bugs-To: none@none\n"
"POT-Creation-Date: 2023-10-16 17:25+0300\n"
"PO-Revision-Date: 2023-10-16 18:21+0300\n"
"Last-Translator: Björn Bidar <bjorn.bidar@thaodan.de>\n"
"Language-Team: French <traduc@traduc.org>\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: waypaper/__main__.py:28
#, python-brace-format
msgid ""
"GUI wallpaper setter for Wayland and X11. It works as a frontend for "
"{backends}"
msgstr "Sélecteur de papier peint graphique pour Wayland et X11. Il fonctionne comme une interface pour {backends}"
#: waypaper/__main__.py:29
#, python-brace-format
msgid "For more information, visit: {homePage}"
msgstr "Pour plus d'informations, visitez {homePage}"
#: waypaper/__main__.py:31
msgid "print version of the program"
msgstr "afficher la version du programme"
#: waypaper/__main__.py:33
msgid "restore last wallpaper"
msgstr "restaurer le dernier papier peint"
#: waypaper/__main__.py:34
msgid "set a random wallpaper"
msgstr "définir un papier peint aléatoire"
#: waypaper/__main__.py:35
msgid "specify how to fill the screen with chosen image"
msgstr "spécifier comment remplir l'écran avec l'image choisie"
#: waypaper/__main__.py:37
msgid "specify which backend to use to set wallpaper"
msgstr "spécifier quel backend utiliser pour définir le papier peint"
#: waypaper/changer.py:23 waypaper/changer.py:46
#, python-brace-format
msgid "Warning related to killall: {error}"
msgstr "Avertissement lié à killall: {error}"
#: waypaper/changer.py:84
#, python-brace-format
msgid "Sent command to set wallpaper was set with {backend}"
msgstr "La commande envoyée pour définir le papier peint a été définie avec {backend}"
#: waypaper/changer.py:88
#, python-brace-format
msgid "The backend is not supported: {backend}"
msgstr "Le backend n'est pas pris en charge: {backend}"
#: waypaper/changer.py:92
#, python-brace-format
msgid "Error changing wallpaper: {error}"
msgstr "Erreur lors du changement de papie peint: {error}"
#: waypaper/app.py:69
msgid "Change wallpaper folder"
msgstr "Changer de dossier de papier peint"
#: waypaper/app.py:89
msgid "Subfolders"
msgstr "Sous-dossiers"
#: waypaper/app.py:92
msgid "Include/exclude images in subfolders"
msgstr "Inclure/exclure les images des sous-dossiers"
#: waypaper/app.py:108
msgid "Choose backen"
msgstr "Choisir le backend"
#: waypaper/app.py:112
msgid "Fill"
msgstr "Remplir"
#: waypaper/app.py:113
msgid "Stretch"
msgstr "Étirer"
#: waypaper/app.py:114
msgid "Fit"
msgstr "S'adapter"
#: waypaper/app.py:115
msgid "Center"
msgstr "Centrer"
#: waypaper/app.py:116
msgid "Tile"
msgstr "Carreler"
#: waypaper/app.py:121
msgid "Choose fill type"
msgstr "Choisir le type de remplissage"
#: waypaper/app.py:130
msgid "Choose background color"
msgstr "Choisir la couleur de fond"
#: waypaper/app.py:134 waypaper/app.py:135
msgid "Name"
msgstr "Nom"
#: waypaper/app.py:136 waypaper/app.py:137
msgid "Date"
msgstr "Date"
#: waypaper/app.py:144
msgid "Choose sorting type"
msgstr "Choisir le type de tri"
#: waypaper/app.py:148
msgid "Exit"
msgstr "Quitter"
#: waypaper/app.py:150
msgid "Exit the application"
msgstr "Quitter l'application"
#: waypaper/app.py:153
msgid "Refresh"
msgstr "Actualiser"
#: waypaper/app.py:156
msgid "Reache the folder of images"
msgstr "Recréer le dossier d'images"
#: waypaper/app.py:159
msgid "Random"
msgstr "Aléatoire"
#: waypaper/app.py:161
msgid "Set random wallpaper"
msgstr "Définir un papier peint aléatoire"
#: waypaper/app.py:214
#, python-brace-format
msgid "Error determining monitor names: {error}"
msgstr "Erreur lors de la détermination des noms des moniteurs: {error}"
#: waypaper/app.py:222
msgid "Choose Display"
msgstr "Choisir l'affichage"
#: waypaper/app.py:239
#, python-brace-format
msgid ""
"\n"
" Looks like none of the wallpaper backends is installed in the "
"system.\n"
" Use your package manager to install at least one of these "
"backends:\n"
" {backends}\n"
" For more information, visit: {url}"
msgstr ""
"\n"
"Il semble qu'aucun des backends de papier peint ne soit installé sur le système.\n"
"Utilisez votre gestionnaire de paquets pour installer au moins l'un de ces backends:\n"
"{backends}\n"
"\n"
"Pour plus d'informations, visitez: {url}"
#: waypaper/app.py:282
msgid "Caching wallpapers.."
msgstr "Mise en cache des papiers peints.."
#: waypaper/app.py:364
msgid "Please choose a folder"
msgstr "Veuillez choisir un dossier"
#: waypaper/app.py:365
msgid "Select"
msgstr "Sélectionner"
#: waypaper/app.py:425 waypaper/app.py:451 waypaper/app.py:530
#, python-brace-format
msgid "Selected image path: {image}"
msgstr "Chemin de l'image sélectionnée: {image}"
#: waypaper/app.py:463
#, python-brace-format
msgid "Error deleting cache '{cacheDir}': '{error}'"
msgstr "Erreur lors de la suppression du cache '{cacheDir}': '{error}'"
#: waypaper/app.py:513
#, python-brace-format
msgid ""
"Waypaper's hotkeys:\n"
" hjkl - Navigation (←↓↑→)\n"
" f - Change wallpaper folder\n"
" g - Scroll to top\n"
" G - Scroll to bottom\n"
" R - Set random wallpaper\n"
" r - Recache wallpapers\n"
" s - Include/exclude images in subfolders\n"
" ? - Help\n"
" q - Exit\n"
" For more information, visit:\n"
" {url}\n"
" "
msgstr ""
"Raccourcis clavier de Waypaper:\n"
"hjkl - Navigation (←↓↑→)\n"
"f - Changer de dossier de papier peint\n"
"g - Faire défiler vers le haut\n"
"G - Faire défiler vers le bas\n"
"R - Définir un papier peint aléatoire\n"
"r - Recréer le cache des papiers peints\n"
"s - Inclure/exclure les images des sous-dossiers\n"
"? - Aide\n"
"q - Quitter\n"
"\n"
"Pour plus d'informations, visitez:\n"
"{url}\n"
" "

206
po/waypaper.pot Normal file
View File

@ -0,0 +1,206 @@
# SOME DESCRIPTIVE TITLE.
# This file is put in the public domain.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr "Project-Id-Version: waypaper 0.0.2\n"
"Report-Msgid-Bugs-To: none@none\n"
"POT-Creation-Date: 2023-10-16 18:48+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: waypaper/__main__.py:28
#, python-brace-format
msgid "GUI wallpaper setter for Wayland and X11. It works as a frontend for "
"{backends}"
msgstr ""
#: waypaper/__main__.py:29
#, python-brace-format
msgid "For more information, visit: {homePage}"
msgstr ""
#: waypaper/__main__.py:31
msgid "print version of the program"
msgstr ""
#: waypaper/__main__.py:33
msgid "restore last wallpaper"
msgstr ""
#: waypaper/__main__.py:34
msgid "set a random wallpaper"
msgstr ""
#: waypaper/__main__.py:35
msgid "specify how to fill the screen with chosen image"
msgstr ""
#: waypaper/__main__.py:37
msgid "specify which backend to use to set wallpaper"
msgstr ""
#: waypaper/app.py:69
msgid "Change wallpaper folder"
msgstr ""
#: waypaper/app.py:89
msgid "Subfolders"
msgstr ""
#: waypaper/app.py:92
msgid "Include/exclude images in subfolders"
msgstr ""
#: waypaper/app.py:108
msgid "Choose backend"
msgstr ""
#: waypaper/app.py:112
msgid "Fill"
msgstr ""
#: waypaper/app.py:113
msgid "Stretch"
msgstr ""
#: waypaper/app.py:114
msgid "Fit"
msgstr ""
#: waypaper/app.py:115
msgid "Center"
msgstr ""
#: waypaper/app.py:116
msgid "Tile"
msgstr ""
#: waypaper/app.py:121
msgid "Choose fill type"
msgstr ""
#: waypaper/app.py:130
msgid "Choose background color"
msgstr ""
#: waypaper/app.py:134 waypaper/app.py:135
msgid "Name"
msgstr ""
#: waypaper/app.py:136 waypaper/app.py:137
msgid "Date"
msgstr ""
#: waypaper/app.py:144
msgid "Choose sorting type"
msgstr ""
#: waypaper/app.py:148
msgid "Exit"
msgstr ""
#: waypaper/app.py:150
msgid "Exit the application"
msgstr ""
#: waypaper/app.py:153
msgid "Refresh"
msgstr ""
#: waypaper/app.py:156
msgid "Reache the folder of images"
msgstr ""
#: waypaper/app.py:159
msgid "Random"
msgstr ""
#: waypaper/app.py:161
msgid "Set random wallpaper"
msgstr ""
#: waypaper/app.py:214
#, python-brace-format
msgid "Error determining monitor names: {error}"
msgstr ""
#: waypaper/app.py:222
msgid "Choose Display"
msgstr ""
#: waypaper/app.py:239
#, python-brace-format
msgid "\n"
" Looks like none of the wallpaper backends is installed "
"in the system.\n"
" Use your package manager to install at least one of "
"these backends:\n"
" {backends}\n"
" For more information, visit: {url}"
msgstr ""
#: waypaper/app.py:282
msgid "Caching wallpapers.."
msgstr ""
#: waypaper/app.py:364
msgid "Please choose a folder"
msgstr ""
#: waypaper/app.py:365
msgid "Select"
msgstr ""
#: waypaper/app.py:425 waypaper/app.py:451 waypaper/app.py:530
#, python-brace-format
msgid "Selected image path: {image}"
msgstr ""
#: waypaper/app.py:463
#, python-brace-format
msgid "Error deleting cache '{cacheDir}': '{error}'"
msgstr ""
#: waypaper/app.py:513
#, python-brace-format
msgid "Waypaper's hotkeys:\n"
" hjkl - Navigation (←↓↑→)\n"
" f - Change wallpaper folder\n"
" g - Scroll to top\n"
" G - Scroll to bottom\n"
" R - Set random wallpaper\n"
" r - Recache wallpapers\n"
" s - Include/exclude images in subfolders\n"
" ? - Help\n"
" q - Exit\n"
" For more information, visit:\n"
" {url}\n"
" "
msgstr ""
#: waypaper/changer.py:23 waypaper/changer.py:46
#, python-brace-format
msgid "Warning related to killall: {error}"
msgstr ""
#: waypaper/changer.py:84
#, python-brace-format
msgid "Sent command to set wallpaper was set with {backend}"
msgstr ""
#: waypaper/changer.py:88
#, python-brace-format
msgid "The backend is not supported: {backend}"
msgstr ""
#: waypaper/changer.py:92
#, python-brace-format
msgid "Error changing wallpaper: {error}"
msgstr ""

View File

@ -1,4 +1,4 @@
[build-system]
requires = ["setuptools", "wheel"]
requires = ["setuptools", "wheel", "setuptools-gettext"]
build-backend = "setuptools.build_meta"

View File

@ -1,20 +1,25 @@
from pathlib import Path
import re
import setuptools
setup_dir = Path(__file__).resolve().parent
version = re.search( r'__version__ = "(.*)"', Path(setup_dir, 'waypaper/__main__.py').open().read())
if version is None:
raise SystemExit("Could not determine version to use")
version = version.group(1)
long_description = ''
try:
long_description = \
Path(setup_dir, 'README.md').open().read()
except:
# In case we don't find README.md ignore it,
# e.g. when we update translations
pass
setuptools.setup(
name='waypaper',
author='Roman Anufriev',
author_email='anufriev.roman@protonmail.com',
url='https://github.com/anufrievroman/waypaper',
description='GUI wallpaper setter for Wayland',
long_description=Path(setup_dir, 'README.md').open().read(),
long_description=long_description,
long_description_content_type='text/markdown',
license='GPL',
entry_points={
@ -22,8 +27,8 @@ setuptools.setup(
"waypaper = waypaper.__main__:run"
]
},
install_requires=["PyGObject"],
version=version,
install_requires=["PyGObject", "importlib_metadata", "platformdirs"],
version='0.0.2',
python_requires='>3.9',
classifiers=[
"Development Status :: 4 - Beta",

View File

@ -1,21 +1,42 @@
"""Main module that runs the program and either runs GUI or just changer wallpaper"""
import time
import argparse
import gettext
from waypaper.config import cf
from waypaper.arguments import args
from waypaper.config import Config
from waypaper.app import App
from waypaper.changer import change_wallpaper
from waypaper.common import get_random_file
from waypaper.aboutdata import AboutData
from waypaper.options import FILL_OPTIONS, BACKEND_OPTIONS
__version__ = "2.0.2"
def run():
"""Read user arguments and either run GUI app or just reset the wallpaper"""
aboutData = AboutData()
gettext.install(aboutData.applicationName())
cf = Config()
parser = argparse.ArgumentParser(prog = aboutData.applicationName(), description =
_("GUI wallpaper setter for Wayland and X11. It works as a frontend for {backends}".format(backends=BACKEND_OPTIONS)),
epilog = _("For more information, visit: {homePage}").
format(homePage=aboutData.homePage()))
parser.add_argument("-v", "--version", help=_("print version of the program"),
action="store_true")
parser.add_argument("--restore", help=_("restore last wallpaper"), action="store_true")
parser.add_argument("--random", help=_("set a random wallpaper"), action="store_true")
parser.add_argument("--fill", help=_("specify how to fill the screen with chosen image"),
action="store_true")
parser.add_argument("--backend", help=_("specify which backend to use to set wallpaper"),
choices=BACKEND_OPTIONS)
args = parser.parse_args()
cf.read_parameters_from_user_arguments(args)
# Set the wallpaper and quit:
@ -29,12 +50,12 @@ def run():
continue
change_wallpaper(wallpaper, cf.fill_option, cf.color, cf.backend, monitor)
time.sleep(0.1)
exit()
exit(0)
# Print the version and quit:
if args.version:
print(f"waypaper v.{__version__}")
exit()
print(f"{aboutData.applicationName()} v.{aboutData.applicationVersion()}")
exit(0)
# Start GUI:
app = App()

19
waypaper/aboutdata.py Normal file
View File

@ -0,0 +1,19 @@
"""Module to store acces application metadata"""
from importlib_metadata import metadata
class AboutData():
"""This class stores application about data in a central place"""
def __init__(self):
self.__app_metadata = metadata(__package__)
def applicationName(self):
return self.__app_metadata['Name']
def applicationSummary(self):
return self.__app_metadata['Summary']
def applicationLogo(self):
return str(self.__app_metadata['Name'])+".svg"
def applicationVersion(self):
return self.__app_metadata['Version'] # pylint: disable=no-member
def homePage(self):
return self.__app_metadata['Home-page']
def Author(self):
return self.__app_metadata['Author']

View File

@ -5,38 +5,37 @@ import threading
import os
import shutil
import gi
from pathlib import Path
from platformdirs import user_cache_path
import gettext
from waypaper.aboutdata import AboutData
from waypaper.changer import change_wallpaper
from waypaper.config import cf
from waypaper.config import Config
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")
from gi.repository import Gtk, GdkPixbuf, Gdk, GLib
if cf.lang == "de":
from waypaper.translation_de import *
elif cf.lang == "fr":
from waypaper.translation_fr import *
elif cf.lang == "ru":
from waypaper.translation_ru import *
elif cf.lang == "pl":
from waypaper.translation_pl import *
elif cf.lang == "zh":
from waypaper.translation_zh import *
else:
from waypaper.translation_en import *
def cache_image(image_path):
def cache_image(image_path, cachedir):
"""Resize and cache images using gtk library"""
pixbuf = GdkPixbuf.Pixbuf.new_from_file(image_path)
try:
pixbuf = GdkPixbuf.Pixbuf.new_from_file(str(image_path))
except GLib.Error as err:
if err.matches(GdkPixbuf.pixbuf_error_quark(), GdkPixbuf.PixbufError.CORRUPT_IMAGE):
# Ignore errors where GdkPixbuf tries to open an image by its extension
# but the extension doesn't match the real mime type
return False
else:
raise
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", [], [])
output_file = cachedir / Path(os.path.basename(image_path))
scaled_pixbuf.savev(str(output_file), "jpeg", [], [])
class App(Gtk.Window):
@ -46,15 +45,19 @@ class App(Gtk.Window):
super().__init__(title="Waypaper")
self.check_backends()
self.set_default_size(780, 600)
self.init_ui()
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())
gettext.install(self.aboutData.applicationName())
self.cf = Config()
self.init_ui()
# Start the image processing in a separate thread:
threading.Thread(target=self.process_images).start()
def init_ui(self):
"""Initialize the UI elements of the application"""
@ -63,7 +66,7 @@ class App(Gtk.Window):
self.add(self.main_box)
# Create a button to open folder dialog:
self.choose_folder_button = Gtk.Button(label=MSG_CHANGEFOLDER)
self.choose_folder_button = Gtk.Button(label=_("Change wallpaper folder"))
self.choose_folder_button.connect("clicked", self.on_choose_folder_clicked)
self.main_box.pack_start(self.choose_folder_button, False, False, 0)
@ -83,10 +86,10 @@ class App(Gtk.Window):
self.scrolled_window.add(self.grid)
# Create subfolder toggle:
self.include_subfolders_checkbox = Gtk.ToggleButton(label=MSG_SUBFOLDERS)
self.include_subfolders_checkbox.set_active(cf.include_subfolders)
self.include_subfolders_checkbox = Gtk.ToggleButton(label=_("Subfolders"))
self.include_subfolders_checkbox.set_active(self.cf.include_subfolders)
self.include_subfolders_checkbox.connect("toggled", self.on_include_subfolders_toggled)
self.include_subfolders_checkbox.set_tooltip_text(TIP_SUBFOLDER)
self.include_subfolders_checkbox.set_tooltip_text(_("Include/exclude images in subfolders"))
# Create a backend dropdown menu:
self.backend_option_combo = Gtk.ComboBoxText()
@ -97,55 +100,65 @@ class App(Gtk.Window):
# 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(cf.backend)
active_num = installed_backends.index(self.cf.backend)
except:
active_num = 0
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(TIP_BACKEND)
self.backend_option_combo.set_tooltip_text(_("Choose backend"))
# Create a fill option dropdown menu:
self.fill_option_combo = Gtk.ComboBoxText()
for option in FILL_OPTIONS:
capitalized_option = option[0].upper() + option[1:]
self.fill_option_combo.append_text(capitalized_option)
fil_options_text_tr = [ _("Fill"),
_("Stretch"),
_("Fit"),
_("Center"),
_("Tile")]
for index in range(len(FILL_OPTIONS)):
self.fill_option_combo.append_text(fil_options_text_tr[index])
self.fill_option_combo.set_active(0)
self.fill_option_combo.connect("changed", self.on_fill_option_changed)
self.fill_option_combo.set_tooltip_text(TIP_FILL)
self.fill_option_combo.set_tooltip_text(_("Choose fill type"))
# Create a color picker:
self.color_picker_button = Gtk.ColorButton()
self.color_picker_button.set_use_alpha(True)
rgba_color = Gdk.RGBA()
rgba_color.parse(cf.color)
rgba_color.parse(self.cf.color)
self.color_picker_button.set_rgba(rgba_color)
self.color_picker_button.connect("color-set", self.on_color_set)
self.color_picker_button.set_tooltip_text(TIP_COLOR)
self.color_picker_button.set_tooltip_text(_("Choose background color"))
# 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)
sort_options_text_tr = [ _("Name")+"",
_("Name")+"",
_("Date")+"",
_("Date")+""]
for option in range(len(sort_options_text_tr)):
self.sort_option_combo.append_text(sort_options_text_tr[option])
active_num = SORT_OPTIONS.index(self.cf.sort_option)
self.sort_option_combo.set_active(active_num)
self.sort_option_combo.connect("changed", self.on_sort_option_changed)
self.sort_option_combo.set_tooltip_text(TIP_SORTING)
self.sort_option_combo.set_tooltip_text(
_("Choose sorting type"))
# Create exit button:
self.exit_button = Gtk.Button(label=MSG_EXIT)
self.exit_button = Gtk.Button(label=_("Exit"))
self.exit_button.connect("clicked", self.on_exit_clicked)
self.exit_button.set_tooltip_text(TIP_EXIT)
self.exit_button.set_tooltip_text(_("Exit the application"))
# Create refresh button:
self.refresh_button = Gtk.Button(label=MSG_REFRESH)
self.refresh_button = Gtk.Button(label=_("Refresh"))
self.refresh_button.connect("clicked", self.on_refresh_clicked)
self.refresh_button.set_tooltip_text(TIP_REFRESH)
self.refresh_button.set_tooltip_text(
_("Reache the folder of images"))
# Create random button:
self.random_button = Gtk.Button(label=MSG_RANDOM)
self.random_button = Gtk.Button(label=_("Random"))
self.random_button.connect("clicked", self.on_random_clicked)
self.random_button.set_tooltip_text(TIP_RANDOM)
self.random_button.set_tooltip_text(_("Set random wallpaper"))
# Create a box to contain the bottom row of buttons with margin:
self.bottom_button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=20)
@ -186,8 +199,8 @@ 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 cf.backend not in ["wallutils", "feh"]:
if cf.backend == "swww":
# if "swww" not in self.missing_backends and self.cf.backend not in ["wallutils", "feh"]:
if self.cf.backend == "swww":
# Check available monitors:
monitor_names = ["All"]
@ -198,7 +211,7 @@ class App(Gtk.Window):
for monitor in monitors[:-1]:
monitor_names.append(monitor.split(':')[0])
except Exception as e:
print(f"{ERR_DISP} {e}")
print(_("Error determining monitor names: {error}").format(error=e))
# Create a monitor option dropdown menu:
self.monitor_option_combo = Gtk.ComboBoxText()
@ -206,7 +219,7 @@ class App(Gtk.Window):
self.monitor_option_combo.append_text(monitor)
self.monitor_option_combo.set_active(0)
self.monitor_option_combo.connect("changed", self.on_monitor_option_changed)
self.monitor_option_combo.set_tooltip_text(TIP_DISPLAY)
self.monitor_option_combo.set_tooltip_text(_("Choose Display"))
# Add it to the row of buttons:
self.options_box.pack_start(self.monitor_option_combo, False, False, 0)
@ -223,8 +236,13 @@ class App(Gtk.Window):
# Show error message if no backends are installed:
if all(self.missing_backends):
self.show_message(ERR_BACKEND)
exit()
self.show_message(_("""
Looks like none of the wallpaper backends is installed in the system.
Use your package manager to install at least one of these backends:
{backends}
For more information, visit: {url}""").format(backends=BACKEND_OPTIONS,
url=self.aboutData.homePage()))
exit(1)
def show_message(self, message):
@ -242,13 +260,13 @@ class App(Gtk.Window):
def sort_images(self):
"""Sort images depending on the sorting option"""
if cf.sort_option == "name":
if self.cf.sort_option == "name":
self.image_paths.sort(key=lambda x: os.path.basename(x))
elif cf.sort_option == "namerev":
elif self.cf.sort_option == "namerev":
self.image_paths.sort(key=lambda x: os.path.basename(x), reverse=True)
elif cf.sort_option == "date":
elif self.cf.sort_option == "date":
self.image_paths.sort(key=lambda x: os.path.getmtime(x))
elif cf.sort_option == "daterev":
elif self.cf.sort_option == "daterev":
self.image_paths.sort(key=lambda x: os.path.getmtime(x), reverse=True)
else:
pass
@ -257,11 +275,11 @@ class App(Gtk.Window):
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.image_paths = get_image_paths(self.cf.image_folder, self.cf.include_subfolders, depth=1)
self.sort_images()
# Show caching label:
self.loading_label = Gtk.Label(label=MSG_CACHING)
self.loading_label = Gtk.Label(label=_("Caching wallpapers.."))
self.bottom_loading_box.add(self.loading_label)
self.show_all()
@ -269,14 +287,20 @@ class App(Gtk.Window):
self.image_names = []
for image_path in self.image_paths:
if os.path.getsize(image_path) == 0:
# Skip zero byte files inside the image_path
self.image_paths.remove(image_path)
continue
# 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)
cached_image_path = self.cachePath/os.path.basename(image_path)
if not cached_image_path.exists():
if not cache_image(image_path, self.cachePath):
# Cache image didn't work move on to next image
self.image_paths.remove(image_path)
continue
# 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)
thumbnail = GdkPixbuf.Pixbuf.new_from_file(str(cached_image_path))
self.thumbnails.append(thumbnail)
self.image_names.append(os.path.basename(image_path))
@ -337,12 +361,12 @@ class App(Gtk.Window):
"""Choosing the folder of images, saving the path, and reloading images"""
dialog = Gtk.FileChooserDialog(
MSG_CHOOSEFOLDER, self, Gtk.FileChooserAction.SELECT_FOLDER,
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, MSG_SELECT, Gtk.ResponseType.OK)
_("Please choose a folder"), self, Gtk.FileChooserAction.SELECT_FOLDER,
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, _("Select"), Gtk.ResponseType.OK)
)
response = dialog.run()
if response == Gtk.ResponseType.OK:
cf.image_folder = dialog.get_filename()
self.cf.image_folder = dialog.get_filename()
threading.Thread(target=self.process_images).start()
dialog.destroy()
@ -354,32 +378,32 @@ class App(Gtk.Window):
def on_include_subfolders_toggled(self, toggle):
"""On chosing to include subfolders"""
cf.include_subfolders = toggle.get_active()
self.cf.include_subfolders = toggle.get_active()
threading.Thread(target=self.process_images).start()
def on_fill_option_changed(self, combo):
"""Save fill parameter when it was changed"""
cf.fill_option = combo.get_active_text()
self.cf.fill_option = FILL_OPTIONS[combo.get_active()]
def on_monitor_option_changed(self, combo):
"""Save monitor parameter when it was changed"""
cf.selected_monitor = combo.get_active_text()
self.cf.selected_monitor = 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]
self.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()
cf.selected_monitor = "All"
self.cf.backend = combo.get_active_text()
self.cf.selected_monitor = "All"
self.monitor_option_display()
self.show_all()
@ -390,18 +414,18 @@ class App(Gtk.Window):
red = int(rgba_color.red * 255)
green = int(rgba_color.green * 255)
blue = int(rgba_color.blue * 255)
cf.color = "#{:02X}{:02X}{:02X}".format(red, green, blue)
self.cf.color = "#{:02X}{:02X}{:02X}".format(red, green, blue)
def on_image_clicked(self, widget, path):
"""On clicking an image, set it as a wallpaper and save"""
cf.selected_wallpaper = path
self.cf.selected_wallpaper = path
self.selected_index = self.image_paths.index(path)
self.load_image_grid()
print(MSG_PATH, cf.selected_wallpaper)
cf.fill_option = self.fill_option_combo.get_active_text() or cf.fill_option
change_wallpaper(cf.selected_wallpaper, cf.fill_option, cf.color, cf.backend, cf.selected_monitor)
cf.save()
print(_("Selected image path: {image}").format(image=self.cf.selected_wallpaper))
self.cf.fill_option = FILL_OPTIONS[self.fill_option_combo.get_active()]
change_wallpaper(self.cf.selected_wallpaper, self.cf.fill_option, self.cf.color, self.cf.backend, self.cf.selected_monitor)
self.cf.save()
def on_refresh_clicked(self, widget):
@ -421,23 +445,23 @@ class App(Gtk.Window):
def set_random_wallpaper(self):
"""Choose a random image and set it as the wallpaper"""
cf.selected_wallpaper = get_random_file(cf.image_folder, cf.include_subfolders)
if cf.selected_wallpaper is None:
self.cf.selected_wallpaper = get_random_file(self.cf.image_folder, self.cf.include_subfolders)
if self.cf.selected_wallpaper is None:
return
print(MSG_PATH, cf.selected_wallpaper)
cf.fill_option = self.fill_option_combo.get_active_text() or cf.fill_option
change_wallpaper(cf.selected_wallpaper, cf.fill_option, cf.color, cf.backend, cf.selected_monitor)
cf.save()
print(_("Selected image path: {image}").format(image=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.fill_option, self.cf.color, self.cf.backend, self.cf.selected_monitor)
self.cf.save()
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)
shutil.rmtree(self.cachePath)
os.makedirs(self.cachePath)
except OSError as e:
print(f"{ERR_CACHE} '{cache_folder}': {e}")
print(_("Error deleting cache '{cachePath}': '{error}'".format(
cachePath=self.cachePath, error=e)))
threading.Thread(target=self.process_images).start()
@ -486,16 +510,28 @@ class App(Gtk.Window):
self.scroll_to_selected_image()
elif event.keyval == Gdk.KEY_question:
message = MSG_HELP
self.show_message(message)
self.show_message(_("""Waypaper's hotkeys:
hjkl - Navigation ()
f - Change wallpaper folder
g - Scroll to top
G - Scroll to bottom
R - Set random wallpaper
r - Recache wallpapers
s - Include/exclude images in subfolders
? - Help
q - Exit
For more information, visit:
{url}
""").format(url=self.aboutData.homePage()))
elif event.keyval == Gdk.KEY_Return or event.keyval == Gdk.KEY_KP_Enter:
wallpaper_path = self.image_paths[self.selected_index]
cf.selected_wallpaper = wallpaper_path
print(MSG_PATH, cf.selected_wallpaper)
cf.fill_option = self.fill_option_combo.get_active_text() or cf.fill_option
change_wallpaper(cf.selected_wallpaper, cf.fill_option, cf.color, cf.backend, cf.selected_monitor)
cf.save()
self.cf.selected_wallpaper = wallpaper_path
print(_("Selected image path: {image}").format(image=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.fill_option,
self.cf.color, self.cf.backend, self.cf.selected_monitor)
self.cf.save()
# Prevent other default key handling:
if event.keyval in [Gdk.KEY_Up, Gdk.KEY_Down, Gdk.KEY_Left, Gdk.KEY_Right, Gdk.KEY_Return, Gdk.KEY_KP_Enter]:

View File

@ -5,20 +5,6 @@ import argparse
from waypaper.options import FILL_OPTIONS, BACKEND_OPTIONS
from waypaper.config import cf
if cf.lang == "de":
from waypaper.translation_de import *
elif cf.lang == "fr":
from waypaper.translation_fr import *
elif cf.lang == "ru":
from waypaper.translation_ru import *
elif cf.lang == "pl":
from waypaper.translation_pl import *
elif cf.lang == "zh":
from waypaper.translation_zh import *
else:
from waypaper.translation_en import *
parser = argparse.ArgumentParser(prog = "waypaper", description = MSG_DESC, epilog = MSG_INFO)
parser.add_argument("-v", "--version", help=MSG_ARG_HELP, action="store_true")
parser.add_argument("--restore", help=MSG_ARG_REST, action="store_true")

View File

@ -2,99 +2,91 @@
import subprocess
import time
from gettext import gettext as _
from waypaper.config import cf
if cf.lang == "de":
from waypaper.translation_de import *
elif cf.lang == "fr":
from waypaper.translation_fr import *
elif cf.lang == "ru":
from waypaper.translation_ru import *
elif cf.lang == "pl":
from waypaper.translation_pl import *
elif cf.lang == "zh":
from waypaper.translation_zh import *
else:
from waypaper.translation_en import *
from waypaper.config import Config
from waypaper.options import BACKEND_OPTIONS
def change_wallpaper(image_path, fill_option, color, backend, monitor):
"""Run a system command to change the wallpaper depending on the backend"""
cf = Config()
try:
# swaybg backend:
if backend == "swaybg":
fill = fill_option.lower()
try:
subprocess.Popen(["killall", "swaybg"])
time.sleep(0.005)
except Exception as e:
print(f"{ERR_KILL} {e}")
command = ["swaybg"]
# if monitor != "All":
# command.extend(["-o", monitor])
command.extend(["-i", image_path])
command.extend(["-m", fill, "-c", color])
subprocess.Popen(command)
print(f"{MSG_SETWITH} {backend}")
if backend in BACKEND_OPTIONS:
if backend == "swaybg":
fill = fill_option.lower()
try:
subprocess.Popen(["killall", "swaybg"])
time.sleep(0.005)
except Exception as e:
print(_("Warning related to killall: {error}".format(
error=e)))
command = ["swaybg"]
# if monitor != "All":
# command.extend(["-o", monitor])
command.extend(["-i", image_path])
command.extend(["-m", fill, "-c", color])
subprocess.Popen(command)
# swww backend:
elif backend == "swww":
fill_types = {
# swww backend:
elif backend == "swww":
fill_types = {
"fill": "crop",
"fit": "fit",
"center": "no",
"stretch": "crop",
"tile": "no",
}
fill = fill_types[fill_option.lower()]
try:
subprocess.Popen(["killall", "swaybg"])
time.sleep(0.005)
except Exception as e:
print(f"{ERR_KILL} {e}")
subprocess.Popen(["swww", "init"])
command = ["swww", "img", image_path]
command.extend(["--resize", fill])
command.extend(["--fill-color", color])
command.extend(["--transition-step", str(10)])
if monitor != "All":
command.extend(["--outputs", monitor])
subprocess.Popen(command)
print(f"{MSG_SETWITH} {backend}")
}
fill = fill_types[fill_option.lower()]
try:
subprocess.Popen(["killall", "swaybg"])
time.sleep(0.005)
except Exception as e:
print(_("Warning related to killall: {error}".format(
error=e)))
subprocess.Popen(["swww", "init"])
command = ["swww", "img", image_path]
command.extend(["--resize", fill])
command.extend(["--fill-color", color])
command.extend(["--transition-step", str(10)])
if monitor != "All":
command.extend(["--outputs", monitor])
subprocess.Popen(command)
# feh backend:
elif backend == "feh":
fill_types = {
# feh backend:
elif backend == "feh":
fill_types = {
"fill": "--bg-fill",
"fit": "--bg-max",
"center": "--bg-center",
"stretch": "--bg-scale",
"tile": "--bg-tile",
}
fill = fill_types[fill_option.lower()]
command = ["feh", fill, "--image-bg", color]
command.extend([image_path])
subprocess.Popen(command)
print(f"{MSG_SETWITH} {backend}")
}
fill = fill_types[fill_option.lower()]
command = ["feh", fill, "--image-bg", color]
command.extend([image_path])
subprocess.Popen(command)
# wallutils backend:
elif backend == "wallutils":
fill_types = {
# wallutils backend:
elif backend == "wallutils":
fill_types = {
"fill": "scale",
"fit": "scale",
"center": "center",
"stretch": "stretch",
"tile": "tile",
}
fill = fill_types[fill_option.lower()]
}
fill = fill_types[fill_option.lower()]
subprocess.Popen(["setwallpaper", "--mode", fill, image_path])
print(f"{MSG_SETWITH} {backend}")
subprocess.Popen(["setwallpaper", "--mode", fill, image_path])
print(_("Sent command to set wallpaper was set with {backend}").format(
backend=backend))
else:
print(f"{ERR_NOTSUP} {backend}")
print(_("The backend is not supported: {backend}").format(
backend=backend))
except Exception as e:
print(f"{ERR_WALL} {e}")
print(_("Error changing wallpaper: {error}".format(error=e)))

View File

@ -18,7 +18,7 @@ def get_image_paths(root_folder, include_subfolders=False, depth=None):
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)
current_depth = root.count(os.path.sep) - str(root_folder).count(os.path.sep)
if current_depth > depth:
continue
for filename in files:

View File

@ -3,16 +3,16 @@
import configparser
import pathlib
import os
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
class Config:
"""User configuration loaded from the config.ini file"""
def __init__(self):
self.image_folder = str(pathlib.Path.home())
if os.path.exists(str(pathlib.Path.home()) + "/Pictures"):
self.image_folder = str(pathlib.Path.home()) + "/Pictures"
self.image_folder = user_pictures_path()
self.selected_wallpaper = None
self.selected_monitor = "All"
self.fill_option = "fill"
@ -21,11 +21,21 @@ class Config:
self.color = "#ffffff"
self.lang = "en"
self.monitors = [self.selected_monitor]
self.wallpaper = []
self.wallpaper = str()
self.include_subfolders = False
self.config_folder = str(pathlib.Path.home()) + "/.config/waypaper"
self.config_file = self.config_folder + "/config.ini"
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"
# Create all directories we use here
# Create config folder:
self.configPath.mkdir(parents=True, exist_ok=True)
# Create cache folder:
self.cachePath.mkdir(parents=True,exist_ok=True)
self.read()
def create(self):
"""Create a default config.ini file if it does not exist"""
@ -41,38 +51,33 @@ class Config:
"wallpaper": str(self.selected_wallpaper),
"monitors": str(self.selected_monitor),
}
with open(cf.config_file, "w") as configfile:
with open(self.config_file, "w") as configfile:
config.write(configfile)
def read(self):
"""Load data from the config.ini or use default if it does not exists"""
try:
config = configparser.ConfigParser()
config.read(self.config_file, 'utf-8')
self.image_folder = config.get("Settings", "folder", fallback=self.image_folder)
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.lang = config.get("Settings", "language", fallback=self.lang)
self.include_subfolders = config.getboolean("Settings", "subfolders", fallback=self.include_subfolders)
config = configparser.ConfigParser()
config.read(self.config_file, 'utf-8')
self.image_folder = config.get("Settings", "folder", fallback=self.image_folder)
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.lang = config.get("Settings", "language", fallback=self.lang)
self.include_subfolders = config.getboolean("Settings", "subfolders", fallback=self.include_subfolders)
self.monitors_str = config.get("Settings", "monitors", fallback=self.selected_monitor, raw=True)
if self.monitors_str is not None:
self.monitors = [str(monitor) for monitor in self.monitors_str.split(",")]
self.wallpaper_str = config.get("Settings", "wallpaper", fallback=self.wallpaper, raw=True)
if self.wallpaper_str is not None:
self.wallpaper = [str(paper) for paper in self.wallpaper_str.split(",")]
except Exception as e:
print(e)
exit()
self.monitors_str = config.get("Settings", "monitors", fallback=self.selected_monitor, raw=True)
if self.monitors_str is not None:
self.monitors = [str(monitor) for monitor in self.monitors_str.split(",")]
self.wallpaper_str = config.get("Settings", "wallpaper", fallback=self.wallpaper, raw=True)
if self.wallpaper_str is not None:
self.wallpaper = [str(paper) for paper in self.wallpaper_str.split(",")]
def save(self):
"""Update the parameters and save them to the configuration file"""
@ -91,16 +96,18 @@ class Config:
# Write configuration to the file:
config = configparser.ConfigParser()
config.read(self.config_file)
config.set("Settings", "folder", cf.image_folder)
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", "language", cf.lang)
config.set("Settings", "subfolders", str(cf.include_subfolders))
if not config.has_section("Settings"):
config.add_section("Settings")
config.set("Settings", "folder", self.image_folder)
config.set("Settings", "fill", self.fill_option)
config.set("Settings", "sort", self.sort_option)
config.set("Settings", "backend", self.backend)
config.set("Settings", "color", self.color)
config.set("Settings", "language", self.lang)
config.set("Settings", "subfolders", str(self.include_subfolders))
config.set("Settings", "wallpaper", ",".join(self.wallpaper))
config.set("Settings", "monitors", ",".join(self.monitors))
with open(cf.config_file, "w") as configfile:
with open(self.config_file, "w") as configfile:
config.write(configfile)
@ -111,20 +118,3 @@ class Config:
if args.fill:
self.fill_option = args.fill
cf = Config()
# Create config folder:
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()
# Read config file:
cf.read()

View File

@ -1,47 +0,0 @@
"""German translations of the program interface (deutsche übersetzungen der Benutzeroberfläche)"""
MSG_DESC = "Grafisches Hintergrundbild-Auswahlwerkzeug für Wayland und X11. Es dient als Frontend für feh, swaybg, wallutils und swww."
MSG_INFO = "Weitere Informationen finden Sie unter:\nhttps://github.com/anufrievroman/waypaper"
MSG_ARG_HELP = "gibt die Programmversion aus"
MSG_ARG_FILL = "legt fest, wie das Bild skaliert werden soll, um den gesamten Bildschirm auszufüllen"
MSG_ARG_REST = "stellt das zuletzt verwendete Hintergrundbild wieder her"
MSG_ARG_BACK = "legt das Backend fest, welches zum Setzen des Hintergrundbildes verwendet werden soll"
MSG_ARG_RAND = "wählt ein zufälliges Hintergrundbild aus"
MSG_PATH = "Pfad zum ausgewählten Bild:"
MSG_SELECT = "Auswählen"
MSG_REFRESH = "Aktualisieren"
MSG_RANDOM = "Zufällig"
MSG_EXIT = "Beenden"
MSG_SUBFOLDERS = "Unterordner"
MSG_CHANGEFOLDER = "Hintergrundbild-Ordner ändern"
MSG_CHOOSEFOLDER = "Bitte wählen Sie einen Ordner aus"
MSG_CACHING = "Hintergrundbilder werden zwischengespeichert..."
MSG_SETWITH = "Hintergrundbild wurde mit folgendem Befehl gesetzt"
MSG_HELP = "Waypapers Tastenkürzel:\n\nhjkl - Navigation (←↓↑→)\nf - Hintergrundbild-Ordner ändern\n"
MSG_HELP += "g - Zum Anfang scrollen\nG - Zum Ende scrollen\nR - Zufälliges Hintergrundbild\nr - Hintergrundbilder zwischenspeichern\n"
MSG_HELP += "s - Unterordner mit einbeziehen\n? - Hilfe\nq - Beenden\n\n"
MSG_HELP += MSG_INFO
ERR_CACHE = "Fehler beim Löschen des Zwischenspeichers"
ERR_BACKEND = "Es konnte kein Hintergrundbild-Backend gefunden werden.\n"
ERR_BACKEND += "Installieren Sie mindestens eines der folgenden Backends:\n\n"
ERR_BACKEND += "- swaybg (für Wayland)\n- swww (für Wayland)\n"
ERR_BACKEND += "- feh (für Xorg)\n- wallutils (für Xorg & Wayland)\n\n"
ERR_BACKEND += MSG_INFO
ERR_WALL = "Fehler beim Ändern des Hintergrundbildes:"
ERR_NOTSUP = "Das folgende Backend wird nicht unterstützt:"
ERR_DISP = "Fehler beim Ermitteln der Monitor-Namen:"
ERR_KILL = "Warnung im Zusammenhang mit dem Befehl killall:"
TIP_SUBFOLDER = "Wählen, ob Unterordner mit einbezogen werden sollen"
TIP_REFRESH = "Erneutes einlesen des Hintergrundbild-Ordners"
TIP_FILL = "Skalierungsart auswählen"
TIP_BACKEND = "Backend auswählen"
TIP_SORTING = "Sortierungsart auswählen"
TIP_DISPLAY = "Bildschirm auswählen"
TIP_COLOR = "Hintergrundfarbe auswählen"
TIP_RANDOM = "Ein zufälliges Hintergrundbild auswählen"
TIP_EXIT = "Das Programm beenden"

View File

@ -1,47 +0,0 @@
"""Traductions en français de l'interface du programme"""
MSG_DESC = "Sélecteur de papier peint graphique pour Wayland et X11. Il fonctionne comme une interface pour feh, swaybg, wallutils et swww."
MSG_INFO = "Pour plus d'informations, visitez :\nhttps://github.com/anufrievroman/waypaper"
MSG_ARG_HELP = "afficher la version du programme"
MSG_ARG_FILL = "spécifier comment remplir l'écran avec l'image choisie"
MSG_ARG_REST = "restaurer le dernier papier peint"
MSG_ARG_BACK = "spécifier quel backend utiliser pour définir le papier peint"
MSG_ARG_RAND = "définir un papier peint aléatoire"
MSG_PATH = "Chemin de l'image sélectionnée :"
MSG_SELECT = "Sélectionner"
MSG_REFRESH = "Actualiser"
MSG_RANDOM = "Aléatoire"
MSG_EXIT = "Quitter"
MSG_SUBFOLDERS = "Sous-dossiers"
MSG_CHANGEFOLDER = "Changer de dossier de papier peint"
MSG_CHOOSEFOLDER = "Veuillez choisir un dossier"
MSG_CACHING = "Mise en cache des papiers peints..."
MSG_SETWITH = "La commande envoyée pour définir le papier peint a été définie avec"
MSG_HELP = "Raccourcis clavier de Waypaper :\n\nhjkl - Navigation (←↓↑→)\nf - Changer de dossier de papier peint\n"
MSG_HELP += "g - Faire défiler vers le haut\nG - Faire défiler vers le bas\nR - Définir un papier peint aléatoire\nr - Recréer le cache des papiers peints\n"
MSG_HELP += "s - Inclure/exclure les images des sous-dossiers\n? - Aide\nq - Quitter\n\n"
MSG_HELP += MSG_INFO
ERR_CACHE = "Erreur lors de la suppression du cache"
ERR_BACKEND = "Il semble qu'aucun des backends de papier peint ne soit installé sur le système.\n"
ERR_BACKEND += "Utilisez votre gestionnaire de paquets pour installer au moins l'un de ces backends :\n\n"
ERR_BACKEND += "- swaybg (pour Wayland)\n- swww (pour Wayland)\n"
ERR_BACKEND += "- feh (pour Xorg)\n- wallutils (pour Xorg & Wayland)\n\n"
ERR_BACKEND += MSG_INFO
ERR_WALL = "Erreur lors du changement de papier peint :"
ERR_NOTSUP = "Le backend n'est pas pris en charge :"
ERR_DISP = "Erreur lors de la détermination des noms des moniteurs :"
ERR_KILL = "Avertissement lié à killall :"
TIP_SUBFOLDER = "Inclure/exclure les images des sous-dossiers"
TIP_REFRESH = "Recréer le dossier d'images"
TIP_FILL = "Choisir le type de remplissage"
TIP_BACKEND = "Choisir le backend"
TIP_SORTING = "Choisir le type de tri"
TIP_DISPLAY = "Choisir l'affichage"
TIP_COLOR = "Choisir la couleur de fond"
TIP_RANDOM = "Définir un papier peint aléatoire"
TIP_EXIT = "Quitter l'application"