Compare commits
No commits in common. "main" and "wallman-releases" have entirely different histories.
main
...
wallman-re
23 changed files with 410 additions and 938 deletions
188
.gitignore
vendored
188
.gitignore
vendored
|
@ -1,188 +0,0 @@
|
||||||
### Python ###
|
|
||||||
# Byte-compiled / optimized / DLL files
|
|
||||||
__pycache__/
|
|
||||||
*.py[cod]
|
|
||||||
*$py.class
|
|
||||||
|
|
||||||
# C extensions
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Distribution / packaging
|
|
||||||
.Python
|
|
||||||
build/
|
|
||||||
develop-eggs/
|
|
||||||
dist/
|
|
||||||
downloads/
|
|
||||||
eggs/
|
|
||||||
.eggs/
|
|
||||||
lib/
|
|
||||||
lib64/
|
|
||||||
parts/
|
|
||||||
sdist/
|
|
||||||
var/
|
|
||||||
wheels/
|
|
||||||
share/python-wheels/
|
|
||||||
*.egg-info/
|
|
||||||
.installed.cfg
|
|
||||||
*.egg
|
|
||||||
|
|
||||||
# PyInstaller
|
|
||||||
# Usually these files are written by a python script from a template
|
|
||||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
||||||
*.manifest
|
|
||||||
*.spec
|
|
||||||
|
|
||||||
# Installer logs
|
|
||||||
pip-log.txt
|
|
||||||
pip-delete-this-directory.txt
|
|
||||||
|
|
||||||
# Unit test / coverage reports
|
|
||||||
htmlcov/
|
|
||||||
.tox/
|
|
||||||
.nox/
|
|
||||||
.coverage
|
|
||||||
.coverage.*
|
|
||||||
.cache
|
|
||||||
nosetests.xml
|
|
||||||
coverage.xml
|
|
||||||
*.cover
|
|
||||||
*.py,cover
|
|
||||||
.hypothesis/
|
|
||||||
.pytest_cache/
|
|
||||||
cover/
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
*.mo
|
|
||||||
*.pot
|
|
||||||
|
|
||||||
# Django stuff:
|
|
||||||
*.log
|
|
||||||
local_settings.py
|
|
||||||
db.sqlite3
|
|
||||||
db.sqlite3-journal
|
|
||||||
|
|
||||||
# Flask stuff:
|
|
||||||
instance/
|
|
||||||
.webassets-cache
|
|
||||||
|
|
||||||
# Scrapy stuff:
|
|
||||||
.scrapy
|
|
||||||
|
|
||||||
# Sphinx documentation
|
|
||||||
docs/_build/
|
|
||||||
|
|
||||||
# PyBuilder
|
|
||||||
.pybuilder/
|
|
||||||
target/
|
|
||||||
|
|
||||||
# Jupyter Notebook
|
|
||||||
.ipynb_checkpoints
|
|
||||||
|
|
||||||
# IPython
|
|
||||||
profile_default/
|
|
||||||
ipython_config.py
|
|
||||||
|
|
||||||
# pyenv
|
|
||||||
# For a library or package, you might want to ignore these files since the code is
|
|
||||||
# intended to run in multiple environments; otherwise, check them in:
|
|
||||||
# .python-version
|
|
||||||
|
|
||||||
# pipenv
|
|
||||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
||||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
||||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
||||||
# install all needed dependencies.
|
|
||||||
#Pipfile.lock
|
|
||||||
|
|
||||||
# poetry
|
|
||||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
||||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
||||||
# commonly ignored for libraries.
|
|
||||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
||||||
#poetry.lock
|
|
||||||
|
|
||||||
# pdm
|
|
||||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
||||||
#pdm.lock
|
|
||||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
||||||
# in version control.
|
|
||||||
# https://pdm.fming.dev/#use-with-ide
|
|
||||||
.pdm.toml
|
|
||||||
|
|
||||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
||||||
__pypackages__/
|
|
||||||
|
|
||||||
# Celery stuff
|
|
||||||
celerybeat-schedule
|
|
||||||
celerybeat.pid
|
|
||||||
|
|
||||||
# SageMath parsed files
|
|
||||||
*.sage.py
|
|
||||||
|
|
||||||
# Environments
|
|
||||||
.env
|
|
||||||
.venv
|
|
||||||
env/
|
|
||||||
venv/
|
|
||||||
ENV/
|
|
||||||
env.bak/
|
|
||||||
venv.bak/
|
|
||||||
|
|
||||||
# Spyder project settings
|
|
||||||
.spyderproject
|
|
||||||
.spyproject
|
|
||||||
|
|
||||||
# Rope project settings
|
|
||||||
.ropeproject
|
|
||||||
|
|
||||||
# mkdocs documentation
|
|
||||||
/site
|
|
||||||
|
|
||||||
# mypy
|
|
||||||
.mypy_cache/
|
|
||||||
.dmypy.json
|
|
||||||
dmypy.json
|
|
||||||
|
|
||||||
# Pyre type checker
|
|
||||||
.pyre/
|
|
||||||
|
|
||||||
# pytype static type analyzer
|
|
||||||
.pytype/
|
|
||||||
|
|
||||||
# Cython debug symbols
|
|
||||||
cython_debug/
|
|
||||||
|
|
||||||
# PyCharm
|
|
||||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
||||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
||||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
||||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
||||||
#.idea/
|
|
||||||
|
|
||||||
### Python Patch ###
|
|
||||||
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
|
|
||||||
poetry.toml
|
|
||||||
|
|
||||||
# ruff
|
|
||||||
.ruff_cache/
|
|
||||||
|
|
||||||
# LSP config files
|
|
||||||
pyrightconfig.json
|
|
||||||
|
|
||||||
### VirtualEnv ###
|
|
||||||
# Virtualenv
|
|
||||||
# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/
|
|
||||||
[Bb]in
|
|
||||||
[Ii]nclude
|
|
||||||
[Ll]ib
|
|
||||||
[Ll]ib64
|
|
||||||
[Ll]ocal
|
|
||||||
[Ss]cripts
|
|
||||||
pyvenv.cfg
|
|
||||||
pip-selfcheck.json
|
|
||||||
|
|
||||||
### Arch build files ###
|
|
||||||
*.tar.gz
|
|
||||||
**.tar.zst
|
|
||||||
pkg/
|
|
||||||
src/
|
|
|
@ -1,16 +0,0 @@
|
||||||
repos:
|
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
||||||
rev: v0.6.2
|
|
||||||
hooks:
|
|
||||||
- id: ruff
|
|
||||||
args: [--fix]
|
|
||||||
- id: ruff-format
|
|
||||||
|
|
||||||
# Pre-commit's built-in hooks for minor fixes
|
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
||||||
rev: v4.6.0
|
|
||||||
hooks:
|
|
||||||
- id: trailing-whitespace # Remove trailing whitespace
|
|
||||||
- id: end-of-file-fixer # Ensure newline at end of file
|
|
||||||
- id: check-merge-conflict # Prevent merge conflicts from being committed
|
|
||||||
- id: detect-private-key # Prevent committing private keys
|
|
Binary file not shown.
Before Width: | Height: | Size: 508 KiB |
|
@ -1,5 +1,2 @@
|
||||||
|
graft src
|
||||||
include sample_config.toml
|
include sample_config.toml
|
||||||
include packaging/wallman.desktop
|
|
||||||
include DefaultFallbackWallpaper.jpg
|
|
||||||
recursive-include icons *
|
|
||||||
exclude README.org
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
# Wallman
|
|
||||||
|
|
||||||
For the full documentation, please see: **[README.org](https://git.entheuer.de/emma/Wallman/src/branch/main/README.org)**
|
|
67
README.org
67
README.org
|
@ -12,7 +12,6 @@ Wallman currently has three main features:
|
||||||
+ Reading configuration details from a TOML file
|
+ Reading configuration details from a TOML file
|
||||||
+ Choosing from a set of Wallpapers and then setting the rest of the wallpapers accordingly
|
+ Choosing from a set of Wallpapers and then setting the rest of the wallpapers accordingly
|
||||||
+ Settings Wallpapers at a specific time of the day
|
+ Settings Wallpapers at a specific time of the day
|
||||||
+ Be controlled via a systray
|
|
||||||
|
|
||||||
* Installation
|
* Installation
|
||||||
** Depedencies
|
** Depedencies
|
||||||
|
@ -22,54 +21,30 @@ Wallman currently has three main features:
|
||||||
+ feh (Used for setting the wallpapers, hard dependency)
|
+ feh (Used for setting the wallpapers, hard dependency)
|
||||||
*** Optional
|
*** Optional
|
||||||
+ libnotify (for desktop notification support)
|
+ libnotify (for desktop notification support)
|
||||||
+ pillow (For systray support)
|
|
||||||
+ pystray (For systray support)
|
|
||||||
*** Build dependencies
|
*** Build dependencies
|
||||||
+ setuptools
|
+ setuptools
|
||||||
+ build
|
+ build
|
||||||
|
|
||||||
|
|
||||||
** Installing with package Manager
|
|
||||||
*** Gentoo
|
|
||||||
Gentoo support has been dropped for now, as I currently don't have a Gentoo system to build on.
|
|
||||||
|
|
||||||
*** Arch Linux
|
|
||||||
Wallman is available in the AUR. Install it using your favorite AUR helper (I'm using paru as an example here)
|
|
||||||
#+BEGIN_SRC shell
|
|
||||||
paru -Syu wallman
|
|
||||||
mkdir ~/.config/wallman
|
|
||||||
cp /etc/wallman/sample_config.toml ~/.config/wallman/wallman.toml
|
|
||||||
#+END_SRC
|
|
||||||
Simply edit the config file and you're done.
|
|
||||||
|
|
||||||
*** Others
|
|
||||||
I will potentially write a version for nixpkgs and will also bundle wallman as a flatpak.
|
|
||||||
|
|
||||||
** Installing with pip
|
** Installing with pip
|
||||||
Wallman is available on PyPI. Simply run:
|
Wallman is available on PYPI. Simply run:
|
||||||
#+BEGIN_SRC shell
|
#+BEGIN_SRC shell
|
||||||
pip install wallman
|
pip install wallman
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
|
** Installing with package Manager
|
||||||
|
Versions in the AUR and an ebuild for Gentoo will be added soon. A flatpak and Nixpkgs version are on the horizon, too.
|
||||||
|
|
||||||
** Installing manually
|
** Installing manually
|
||||||
+ Install libnotify and feh, (preferably, also install the python dependencies from there) from your package manager
|
+ Clone this git repo
|
||||||
|
+ Create a log file and a configuration file:
|
||||||
#+BEGIN_SRC shell
|
#+BEGIN_SRC shell
|
||||||
git clone https://git.entheuer.de/emma/Wallman.git
|
mkdir -p ~/.local/share/wallman
|
||||||
cd Wallman/
|
|
||||||
pip install -r requirements.txt
|
|
||||||
pip install installer
|
|
||||||
sudo mkdir -p /var/log/wallman
|
|
||||||
sudo chmod 733 /var/log/wallman
|
|
||||||
touch /var/log/wallman
|
|
||||||
mkdir -p ~/.config/wallman
|
mkdir -p ~/.config/wallman
|
||||||
|
touch ~/.local/share/wallman/wallman.log
|
||||||
cp sample_config.toml ~/.config/wallman/wallman.toml
|
cp sample_config.toml ~/.config/wallman/wallman.toml
|
||||||
sudo mkdir -p /etc/wallman/
|
|
||||||
sudo cp -r icons /etc/wallman
|
|
||||||
sudo cp -r DefaultFallbackWallpaper /etc/wallman/DefaultFallbackWallpaper.jpg
|
|
||||||
python -m build
|
|
||||||
sudo python -m installer --destdir=/ dist/*.whl
|
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
+ Edit the sample config
|
+ Edit the sample config
|
||||||
|
+ (Optional): Adjust the loglevel to your liking. This will be part of the config or a command line argument soon.
|
||||||
+ Profit
|
+ Profit
|
||||||
|
|
||||||
* Configuration
|
* Configuration
|
||||||
|
@ -77,7 +52,7 @@ This is a short guide on how to correctly configure wallman. Look in the sample
|
||||||
** TOML Dictionaries
|
** TOML Dictionaries
|
||||||
First of all, the config file is structured via different TOML dictionaries. There are two TOML dictionaries: general and changing_times that must be present in every config. Aside from that, further dictionaries are needed depending on how wallman is configured. You need to create a dictionary with the name of each wallpaper set defined in the used_sets list (more on that later). You should probably just configure wallman by editing the sample config as it is by far the easiest way to do it.
|
First of all, the config file is structured via different TOML dictionaries. There are two TOML dictionaries: general and changing_times that must be present in every config. Aside from that, further dictionaries are needed depending on how wallman is configured. You need to create a dictionary with the name of each wallpaper set defined in the used_sets list (more on that later). You should probably just configure wallman by editing the sample config as it is by far the easiest way to do it.
|
||||||
*** general
|
*** general
|
||||||
In general, you need to always define 3 variables and you can optionally add three more:
|
In general, you need to always define 3 variables and you can optionally add two more:
|
||||||
+ enable_wallpaper_sets: bool
|
+ enable_wallpaper_sets: bool
|
||||||
A simple switch that states if you want to use different sets of wallpapers or not.
|
A simple switch that states if you want to use different sets of wallpapers or not.
|
||||||
+ used_sets: list
|
+ used_sets: list
|
||||||
|
@ -86,21 +61,8 @@ In general, you need to always define 3 variables and you can optionally add thr
|
||||||
The amount of wallpapers that you use in each set. It should be an integer.
|
The amount of wallpapers that you use in each set. It should be an integer.
|
||||||
+ Optional: notify: bool
|
+ Optional: notify: bool
|
||||||
This defaults to "false". Enable to set send a desktop notification when the wallpaper is changed. The program will still work correctly, even if this option is not defined at all.
|
This defaults to "false". Enable to set send a desktop notification when the wallpaper is changed. The program will still work correctly, even if this option is not defined at all.
|
||||||
+ Optional: fallback_wallpaper: string
|
+ Optional: fallback_wallpaper: bool
|
||||||
Wallpaper to be set if an error is found in the config or the wallpaper intended to be set cannot be found. Defaults to "/etc/wallman/DefaultFallbackWallpaper.jpg". If none is set and the config has been written incorrectly, a ConfigError is raised and the program is exited. If an error in the config occurs but the fallback wallpaper has been defined, it will be set and wallman will raise a config error. If The config is written correctly but the wallpaper intended to be set can't be found, wallman will set the fallback wallpaper and continue to try setting future wallpapers. You can both change this location (recommended) or the wallpaper that the path points to.
|
Wallpaper to be set if an error is found in the config. Defaults to None. If none is set and the config is written incorrectly, a ConfigError is raised and the program is exited. If an error in the config occurs but the fallback wallpaper has been defined, it will be set and wallman will exit with Code 1.
|
||||||
+ Optional: loglevel: string
|
|
||||||
Loglevel to be used by wallman. Defaults to INFO. Choices MUST be DEBUG, INFO, WARNING, ERROR or CRITICAL. Using any capitalization is valid, all caps is reccomended. Wallman will crash if a value is specified that is not one of the specified ones.
|
|
||||||
+ Optional: systray: bool
|
|
||||||
This defaults to "true". This enables support for a systray that has the features to re-set your wallpaper (Mostly useful if feh shits itself or if you want to set the correct wallpaper for a specific time of day after your device was suspended) without rerolling the wallpaper set used, a button to reroll and then re-set the wallpaper, as well as a Quit button. Disable this to save a very tiny amount of memory.
|
|
||||||
+ Optional: behavior: string
|
|
||||||
This defaults to ~--bg-fill~. This is also the value that will be used when an invalid configuration option has been set. This supports 6 different modes, each mode has 2 possible keywords, resulting in 12 total valid keywords.
|
|
||||||
~--bg~, ~plain~. What happens when feh is used with the ~--bg~ flag. Sets a wallpaper without any scaling.
|
|
||||||
~--bg-tile~, ~tile~. Sets the wallpaper only on one monitor, the other ones get filled with black or white. Mostly useful for Xinerama setups with overlapping monitors. Equivalent to ~--bg-tile~ in feh.
|
|
||||||
~--bg-center~, ~center~. Sets a wallpaper with the center of the wallpaper centered on the middle of the screen. Crops edges. Equivalent to ~--bg-center~ in feh.
|
|
||||||
~--bg-fill~, ~fill~ (Default). Fills the whole screen with the wallpaper and zooms to preserve the original aspect ratio. Equivalent to ~--bg-fill~ in feh.
|
|
||||||
~--bg-max~, ~max~. Scales the image to the maximum possible size. Equivalent to ~--bg-max~ in feh.
|
|
||||||
~--bg-scale~, ~scale~. Scales the wallpaper to fill the screen, but does not preserve the original aspect ratio. Leads to squishing if aspect ratio of screen and wallpaper don't match. Equivalent to ~--bg-scale~ in feh.
|
|
||||||
|
|
||||||
|
|
||||||
*** changing_times
|
*** changing_times
|
||||||
The changing_times dictionary is used to specify the times of the day when your wallpaper is switched. The names of the keys do not matter here, the values must always be strings in the "XX:YY:ZZ" 24 hour time system. use 00:00:00 for midnight. Note that XX should be in the range of 00-23 and YY and ZZ should be in the range of 00-59.
|
The changing_times dictionary is used to specify the times of the day when your wallpaper is switched. The names of the keys do not matter here, the values must always be strings in the "XX:YY:ZZ" 24 hour time system. use 00:00:00 for midnight. Note that XX should be in the range of 00-23 and YY and ZZ should be in the range of 00-59.
|
||||||
|
@ -116,10 +78,13 @@ The keys in the dictionary once again do not matter, the names of the keys in ea
|
||||||
+ Add documentation for developers
|
+ Add documentation for developers
|
||||||
|
|
||||||
** Technical Details
|
** Technical Details
|
||||||
+ Improve Modularity (Partially done)
|
+ Improve Modularity
|
||||||
|
+ Make the enabled flag in wallpaper_sets actually useful by making the used_sets field optional
|
||||||
|
+ Add support for different loglevels in the config file or as a command line argument
|
||||||
+ Drop the feh dependecy and set wallpapers using pywlroots or python-xlib
|
+ Drop the feh dependecy and set wallpapers using pywlroots or python-xlib
|
||||||
|
|
||||||
** Features
|
** Features
|
||||||
|
+ Add support for setting a fallback wallpaper if a wallpaper the user set is not found
|
||||||
+ Add support for wallpapers that dynamically change with the time of day (Morning, noon, evening, night or light levels) rather than to times set in the config
|
+ Add support for wallpapers that dynamically change with the time of day (Morning, noon, evening, night or light levels) rather than to times set in the config
|
||||||
+ Add support for wallpapers that change by the weather
|
+ Add support for wallpapers that change by the weather
|
||||||
+ Add support for live wallpapers
|
+ Add support for live wallpapers
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 833 KiB |
Binary file not shown.
Before Width: | Height: | Size: 2 KiB |
|
@ -1,24 +0,0 @@
|
||||||
pkgbase = wallman
|
|
||||||
pkgdesc = A simple program to set dynamic wallpapers on standalone X11 window managers and wayland compositors
|
|
||||||
pkgver = 1.5.2.8
|
|
||||||
pkgrel = 1
|
|
||||||
url = https://git.entheuer.de/emma/wallman
|
|
||||||
arch = any
|
|
||||||
license = MIT
|
|
||||||
makedepends = python-build
|
|
||||||
makedepends = python-setuptools
|
|
||||||
makedepends = python-wheel
|
|
||||||
makedepends = python-installer
|
|
||||||
depends = feh
|
|
||||||
depends = python
|
|
||||||
depends = python-apscheduler
|
|
||||||
depends = python-pillow
|
|
||||||
depends = python-pystray
|
|
||||||
provides = wallman=1.5.2.8
|
|
||||||
backup = etc/wallman/icons/WallmanLogo.jpg
|
|
||||||
backup = etc/wallman/icons/systrayIcon.jpg
|
|
||||||
backup = etc/wallman/DefaultFallbackWallpaper.jpg
|
|
||||||
source = https://files.pythonhosted.org/packages/4e/fe/0998cd577e5b4e72cc60a7a3d95d11ff9b86c7c5e848aa0d8dc4690f75e8/wallman-1.5.2.8.tar.gz
|
|
||||||
b2sums = 116ef95afff6569cea63d0777b13a59da07ea6e0cf08692a3c470d1b3d135b7051adc0892f5558aecba2a49c81f7a2c848baa63763dceffe53d8565952654b3a
|
|
||||||
|
|
||||||
pkgname = wallman
|
|
|
@ -1,48 +0,0 @@
|
||||||
# Maintainer: Emma Nora Theuer <wallman@entheuer.de>
|
|
||||||
|
|
||||||
pkgname=wallman
|
|
||||||
pkgver=1.5.2.8
|
|
||||||
pkgrel=1
|
|
||||||
pkgdesc="A simple program to set dynamic wallpapers on standalone X11 window managers and wayland compositors"
|
|
||||||
arch=('any')
|
|
||||||
url="https://git.entheuer.de/emma/wallman"
|
|
||||||
license=('MIT')
|
|
||||||
depends=('feh' 'python' 'python-apscheduler' 'python-pillow' 'python-pystray')
|
|
||||||
makedepends=('python-build' 'python-setuptools' 'python-wheel' 'python-installer')
|
|
||||||
provides=("$pkgname=$pkgver")
|
|
||||||
source=("https://files.pythonhosted.org/packages/4e/fe/0998cd577e5b4e72cc60a7a3d95d11ff9b86c7c5e848aa0d8dc4690f75e8/wallman-1.5.2.8.tar.gz")
|
|
||||||
b2sums=('116ef95afff6569cea63d0777b13a59da07ea6e0cf08692a3c470d1b3d135b7051adc0892f5558aecba2a49c81f7a2c848baa63763dceffe53d8565952654b3a')
|
|
||||||
# Treating this as config files seems to be necessary for python to do not complain
|
|
||||||
backup=('etc/wallman/icons/WallmanLogo.jpg' 'etc/wallman/icons/systrayIcon.jpg' 'etc/wallman/DefaultFallbackWallpaper.jpg')
|
|
||||||
|
|
||||||
|
|
||||||
build(){
|
|
||||||
cd "$pkgname-$pkgver"
|
|
||||||
python -m build --wheel --no-isolation
|
|
||||||
}
|
|
||||||
|
|
||||||
package(){
|
|
||||||
cd "$pkgname-$pkgver"
|
|
||||||
python -m installer --destdir="$pkgdir" dist/*.whl
|
|
||||||
|
|
||||||
# Copy LICENSE
|
|
||||||
install -D -m 644 LICENSE -t "$pkgdir/usr/share/licenses/$pkgname/"
|
|
||||||
|
|
||||||
# Copy Icons
|
|
||||||
if [ -d "icons" ]; then
|
|
||||||
install -d -m 755 "$pkgdir/etc/$pkgname/icons"
|
|
||||||
cp -r icons/* "$pkgdir/etc/$pkgname/icons/"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Copy sample config
|
|
||||||
install -D -m 644 sample_config.toml -t "$pkgdir/etc/$pkgname/"
|
|
||||||
|
|
||||||
# Copy Fallback Wallpaper
|
|
||||||
install -D -m 644 DefaultFallbackWallpaper.jpg -t "$pkgdir/etc/$pkgname/"
|
|
||||||
|
|
||||||
# Copy .desktop file
|
|
||||||
install -D -m 755 packaging/wallman.desktop -t "$pkgdir/usr/share/application/$pkgname.desktop"
|
|
||||||
|
|
||||||
# Create logdirectory
|
|
||||||
install -d -m 733 "$pkgdir/var/log/$pkgname"
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
[Desktop Entry]
|
|
||||||
Name=wallman
|
|
||||||
Comment=run wallman
|
|
||||||
Exec=wallman
|
|
||||||
Icon=/etc/wallman/icons/WallmanLogo.jpg
|
|
||||||
Terminal=false
|
|
||||||
Type=Application
|
|
||||||
Categories=Utility;
|
|
196
pypireadme.md
Normal file
196
pypireadme.md
Normal file
|
@ -0,0 +1,196 @@
|
||||||
|
# Table of Contents
|
||||||
|
|
||||||
|
1. [Overwiev](#org19ef638)
|
||||||
|
1. [What is this?](#org148aad0)
|
||||||
|
2. [What can it do?](#org0041bf1)
|
||||||
|
2. [Installation](#orgc40ee8b)
|
||||||
|
1. [Depedencies](#org2cb62ef)
|
||||||
|
1. [Always Required](#org0d0b0f7)
|
||||||
|
2. [Optional](#orgf04ec77)
|
||||||
|
3. [Build dependencies](#org3951087)
|
||||||
|
2. [Installing with pip](#org24bde1c)
|
||||||
|
3. [Installing with package Manager](#org9575c55)
|
||||||
|
4. [Installing manually](#org2c63e0f)
|
||||||
|
3. [Configuration](#org812c501)
|
||||||
|
1. [TOML Dictionaries](#org51e3333)
|
||||||
|
1. [general](#org79747a5)
|
||||||
|
2. [changing<sub>times</sub>](#orgc425813)
|
||||||
|
3. [The other dictionaries](#org9f47b06)
|
||||||
|
4. [TODOs](#orgc26c243)
|
||||||
|
1. [Structuring](#org8dd22d4)
|
||||||
|
2. [Technical Details](#org6103451)
|
||||||
|
3. [Features](#org38d78ba)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a id="org19ef638"></a>
|
||||||
|
|
||||||
|
# Overwiev
|
||||||
|
|
||||||
|
|
||||||
|
<a id="org148aad0"></a>
|
||||||
|
|
||||||
|
## What is this?
|
||||||
|
|
||||||
|
This is my project wallman. Wallman is a simple python program used for setting Dynamic Wallpapers on minimalist X11 Window Managers and Wayland compositors. The name is a reference to TomSka: <https://www.youtube.com/watch?v=k4Q3qD93rgI&t=131s>
|
||||||
|
This version is an early Alpha. As of now, it supports the most important features for my usecase, those being randomly selected wallpaper sets and wallpaper sets that change by the time of day. The program is not modular yet and I would expect a lot of bugs related to the configuration file. Just how it is, I’m working on it.
|
||||||
|
As such, please make absolutely sure you follow the instructions on how to write the config file very closely. I will implement better config handling with more meaningful error output in the future. For now, follow everything really closely and read the logs if needed. If you do that, it *should* work.
|
||||||
|
|
||||||
|
|
||||||
|
<a id="org0041bf1"></a>
|
||||||
|
|
||||||
|
## What can it do?
|
||||||
|
|
||||||
|
Wallman currently has three main features:
|
||||||
|
|
||||||
|
- Reading configuration details from a TOML file
|
||||||
|
- Choosing from a set of Wallpapers and then setting the rest of the wallpapers accordingly
|
||||||
|
- Settings Wallpapers at a specific time of the day
|
||||||
|
|
||||||
|
|
||||||
|
<a id="orgc40ee8b"></a>
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
|
||||||
|
|
||||||
|
<a id="org2cb62ef"></a>
|
||||||
|
|
||||||
|
## Depedencies
|
||||||
|
|
||||||
|
|
||||||
|
<a id="org0d0b0f7"></a>
|
||||||
|
|
||||||
|
### Always Required
|
||||||
|
|
||||||
|
- Python 3.11 or newer (Required because of tomllib)
|
||||||
|
- APScheduler (Install python-apscheduler or APScheduler, depending on the package manager)
|
||||||
|
- feh (Used for setting the wallpapers, hard dependency)
|
||||||
|
|
||||||
|
|
||||||
|
<a id="orgf04ec77"></a>
|
||||||
|
|
||||||
|
### Optional
|
||||||
|
|
||||||
|
- libnotify (for desktop notification support)
|
||||||
|
|
||||||
|
|
||||||
|
<a id="org3951087"></a>
|
||||||
|
|
||||||
|
### Build dependencies
|
||||||
|
|
||||||
|
- setuptools
|
||||||
|
- build
|
||||||
|
|
||||||
|
|
||||||
|
<a id="org24bde1c"></a>
|
||||||
|
|
||||||
|
## Installing with pip
|
||||||
|
|
||||||
|
Wallman is available on PYPI. Simply run:
|
||||||
|
|
||||||
|
pip install wallman
|
||||||
|
|
||||||
|
|
||||||
|
<a id="org9575c55"></a>
|
||||||
|
|
||||||
|
## Installing with package Manager
|
||||||
|
|
||||||
|
Versions in the AUR and an ebuild for Gentoo will be added soon. A flatpak and Nixpkgs version are on the horizon, too.
|
||||||
|
|
||||||
|
|
||||||
|
<a id="org2c63e0f"></a>
|
||||||
|
|
||||||
|
## Installing manually
|
||||||
|
|
||||||
|
- Clone this git repo
|
||||||
|
- Create a log file and a configuration file:
|
||||||
|
|
||||||
|
mkdir -p ~/.local/share/wallman
|
||||||
|
mkdir -p ~/.config/wallman
|
||||||
|
touch ~/.local/share/wallman/wallman.log
|
||||||
|
cp sample_config.toml ~/.config/wallman/wallman.toml
|
||||||
|
|
||||||
|
- Edit the sample config
|
||||||
|
- (Optional): Adjust the loglevel to your liking. This will be part of the config or a command line argument soon.
|
||||||
|
- Profit
|
||||||
|
|
||||||
|
|
||||||
|
<a id="org812c501"></a>
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
|
||||||
|
This is a short guide on how to correctly configure wallman. Look in the sample config for additional context.
|
||||||
|
|
||||||
|
|
||||||
|
<a id="org51e3333"></a>
|
||||||
|
|
||||||
|
## TOML Dictionaries
|
||||||
|
|
||||||
|
First of all, the config file is structured via different TOML dictionaries. There are two TOML dictionaries: general and changing<sub>times</sub> that must be present in every config. Aside from that, further dictionaries are needed depending on how wallman is configured. You need to create a dictionary with the name of each wallpaper set defined in the used<sub>sets</sub> list (more on that later). You should probably just configure wallman by editing the sample config as it is by far the easiest way to do it.
|
||||||
|
|
||||||
|
|
||||||
|
<a id="org79747a5"></a>
|
||||||
|
|
||||||
|
### general
|
||||||
|
|
||||||
|
In general, you need to always define 3 variables and you can optionally add two more:
|
||||||
|
|
||||||
|
- enable<sub>wallpaper</sub><sub>sets</sub>: bool
|
||||||
|
A simple switch that states if you want to use different sets of wallpapers or not.
|
||||||
|
- used<sub>sets</sub>: list
|
||||||
|
A list that includes the names of the wallpaper sets you want to use. If you want to use only one, the list should have one entry.
|
||||||
|
- wallpapers<sub>per</sub><sub>set</sub>: int
|
||||||
|
The amount of wallpapers that you use in each set. It should be an integer.
|
||||||
|
- Optional: notify: bool
|
||||||
|
This defaults to “false”. Enable to set send a desktop notification when the wallpaper is changed. The program will still work correctly, even if this option is not defined at all.
|
||||||
|
- Optional: fallback<sub>wallpaper</sub>: bool
|
||||||
|
Wallpaper to be set if an error is found in the config. Defaults to None. If none is set and the config is written incorrectly, a ConfigError is raised and the program is exited. If an error in the config occurs but the fallback wallpaper has been defined, it will be set and wallman will exit with Code 1.
|
||||||
|
|
||||||
|
|
||||||
|
<a id="orgc425813"></a>
|
||||||
|
|
||||||
|
### changing<sub>times</sub>
|
||||||
|
|
||||||
|
The changing<sub>times</sub> dictionary is used to specify the times of the day when your wallpaper is switched. The names of the keys do not matter here, the values must always be strings in the “XX:YY:ZZ” 24 hour time system. use 00:00:00 for midnight. Note that XX should be in the range of 00-23 and YY and ZZ should be in the range of 00-59.
|
||||||
|
|
||||||
|
|
||||||
|
<a id="org9f47b06"></a>
|
||||||
|
|
||||||
|
### The other dictionaries
|
||||||
|
|
||||||
|
The other dictionaries must always have the names of the wallpaper sets from used<sub>sets</sub>. If you have one wallpaper set, you need one additional dictionary, if you have two you need two etc. The standard config uses nature and anime, these names can be whatever you please as long as they are the same as the ones specified in used<sub>sets</sub>.
|
||||||
|
The keys in the dictionary once again do not matter, the names of the keys in each dictionary must be strings and be absolute paths. They should not include spaces unless prefaced by a backslash.
|
||||||
|
|
||||||
|
|
||||||
|
<a id="orgc26c243"></a>
|
||||||
|
|
||||||
|
# TODOs
|
||||||
|
|
||||||
|
|
||||||
|
<a id="org8dd22d4"></a>
|
||||||
|
|
||||||
|
## Structuring
|
||||||
|
|
||||||
|
- Write unittests
|
||||||
|
- Add documentation for developers
|
||||||
|
|
||||||
|
|
||||||
|
<a id="org6103451"></a>
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
|
||||||
|
- Improve Modularity
|
||||||
|
- Make the enabled flag in wallpaper<sub>sets</sub> actually useful by making the used<sub>sets</sub> field optional
|
||||||
|
- Add support for different loglevels in the config file or as a command line argument
|
||||||
|
- Drop the feh dependecy and set wallpapers using pywlroots or python-xlib
|
||||||
|
|
||||||
|
|
||||||
|
<a id="org38d78ba"></a>
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Add support for setting a fallback wallpaper if a wallpaper the user set is not found
|
||||||
|
- Add support for wallpapers that dynamically change with the time of day (Morning, noon, evening, night or light levels) rather than to times set in the config
|
||||||
|
- Add support for wallpapers that change by the weather
|
||||||
|
- Add support for live wallpapers
|
||||||
|
|
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "wallman"
|
name = "wallman"
|
||||||
version = "1.5.2.8"
|
version = "1.3.0b1"
|
||||||
authors = [
|
authors = [
|
||||||
{name = "Emma Nora Theuer", email = "wallman@entheuer.de"},
|
{name = "Emma Nora Theuer", email = "wallman@entheuer.de"},
|
||||||
]
|
]
|
||||||
|
@ -24,22 +24,9 @@ classifiers = [
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"APScheduler",
|
"APScheduler",
|
||||||
"pillow",
|
|
||||||
"pystray",
|
|
||||||
'importlib-metadata; python_version<"3.10"',
|
'importlib-metadata; python_version<"3.10"',
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.setuptools.packages.find]
|
|
||||||
where = ["."]
|
|
||||||
include = ["wallman*"]
|
|
||||||
exclude = ["icons*", "packaging*", "DefaultFallbackWallpaper.jpg"]
|
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
Homepage = "https://git.entheuer.de/emma/Wallman"
|
Homepage = "https://git.entheuer.de/emma/Wallman"
|
||||||
Issues = "https://git.entheuer.de/emma/Wallman/issues"
|
Issues = "https://git.entheuer.de/emma/Wallman/issues"
|
||||||
|
|
||||||
[project.scripts]
|
|
||||||
wallman = "wallman.main:main"
|
|
||||||
|
|
||||||
[tool.mypy]
|
|
||||||
disable_error_code = ["import-untyped", "no-redef"]
|
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
APScheduler==3.11.0
|
|
||||||
pillow==11.0.0
|
|
||||||
pystray==0.19.5
|
|
||||||
setuptools==75.6.0
|
|
||||||
build==1.2.2.post1
|
|
||||||
twine==6.0.1
|
|
|
@ -4,11 +4,8 @@
|
||||||
enable_wallpaper_sets = true
|
enable_wallpaper_sets = true
|
||||||
used_sets = ["anime", "nature"]
|
used_sets = ["anime", "nature"]
|
||||||
wallpapers_per_set = 5
|
wallpapers_per_set = 5
|
||||||
notify = false
|
notify = False
|
||||||
fallback_wallpaper = "/etc/wallman/DefaultFallbackWallpaper.jpg" # Feel free to use a different filepath for this!
|
fallback_wallpaper = "/path/to/paper"
|
||||||
loglevel = "INFO"
|
|
||||||
systray = true
|
|
||||||
behavior = "fill"
|
|
||||||
|
|
||||||
# Enter the hours at which you want the wallpaper to change.
|
# Enter the hours at which you want the wallpaper to change.
|
||||||
# If the script is called between one of this times, it will go back to the previous time.
|
# If the script is called between one of this times, it will go back to the previous time.
|
||||||
|
|
12
src/__main__.py
Normal file
12
src/__main__.py
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import wallman_lib
|
||||||
|
|
||||||
|
def main():
|
||||||
|
validator = wallman_lib.ConfigValidity()
|
||||||
|
logic = wallman_lib.WallpaperLogic()
|
||||||
|
validator.validate_config()
|
||||||
|
logic.set_wallpaper_by_time()
|
||||||
|
logic.schedule_wallpapers()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
182
src/wallman_lib.py
Normal file
182
src/wallman_lib.py
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
from sys import exit
|
||||||
|
from os import chdir, getenv, system
|
||||||
|
import logging
|
||||||
|
import tomllib
|
||||||
|
from datetime import datetime, time
|
||||||
|
from apscheduler.schedulers.blocking import BlockingScheduler
|
||||||
|
from apscheduler.triggers.cron import CronTrigger
|
||||||
|
|
||||||
|
# setup logging
|
||||||
|
chdir(str(getenv("HOME")) + "/.local/share/wallman/")
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
logging.basicConfig(filename="wallman.log", encoding="utf-8", level=logging.WARNING)
|
||||||
|
|
||||||
|
# read config
|
||||||
|
# a = list(data["changing_times"].values())
|
||||||
|
# print(a[0])
|
||||||
|
|
||||||
|
class ConfigError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class _ConfigLib:
|
||||||
|
def _initialize_config(self) -> dict:
|
||||||
|
chdir(str(getenv("HOME")) + "/.config/wallman/")
|
||||||
|
with open("wallman.toml", "rb") as config_file:
|
||||||
|
data = tomllib.load(config_file)
|
||||||
|
return data
|
||||||
|
# a = list(data["changing_times"].values())
|
||||||
|
# print(a[0])
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.config_file: dict = self._initialize_config() # Full config
|
||||||
|
# Dictionaries
|
||||||
|
self.config_general: dict = self.config_file["general"]
|
||||||
|
self.config_changing_times: dict = self.config_file["changing_times"]
|
||||||
|
# Values in Dicts
|
||||||
|
self.config_wallpaper_sets_enabled: bool = self.config_general["enable_wallpaper_sets"]
|
||||||
|
self.config_used_sets: list = self.config_general["used_sets"]
|
||||||
|
self.config_wallpapers_per_set: int = self.config_general["wallpapers_per_set"]
|
||||||
|
self.config_total_changing_times: int = len(self.config_changing_times)
|
||||||
|
try:
|
||||||
|
self.config_notify = self.config_general["notify"]
|
||||||
|
except KeyError:
|
||||||
|
self.config_notify = False
|
||||||
|
logger.warning("'notify' is not set in dictionary general in the config file, defaulting to 'false'.")
|
||||||
|
|
||||||
|
def _set_fallback_wallpaper(self):
|
||||||
|
if self.config_general["fallback_wallpaper"]:
|
||||||
|
system(f"feh --bg-fill --no-fehbg {self.config_general['fallback_wallpaper']}")
|
||||||
|
logger.info("The fallback Wallpaper has been set.")
|
||||||
|
else:
|
||||||
|
logger.critical("An Error occured and no fallback wallpaper was provided, exiting...")
|
||||||
|
raise ConfigError("An error occured and no fallback wallpaper has been set, exiting...")
|
||||||
|
|
||||||
|
class ConfigValidity(_ConfigLib):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def _check_fallback_wallpaper(self):
|
||||||
|
if self.config_general["fallback_wallpaper"]:
|
||||||
|
logger.debug("A fallback wallpaper has been defined.")
|
||||||
|
else:
|
||||||
|
logger.warning("No fallback wallpaper has been provided. If the config is written incorrectly, the program will not be able to be executed.")
|
||||||
|
|
||||||
|
def _check_wallpapers_per_set_and_changing_times(self) -> None:
|
||||||
|
# Check if the amount of wallpapers_per_set and given changing times match
|
||||||
|
if self.config_total_changing_times == self.config_wallpapers_per_set:
|
||||||
|
logger.debug("The amount of changing times and wallpapers per set is set correctly")
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
self._set_fallback_wallpaper()
|
||||||
|
logger.error("The amount of changing_times and the amount of wallpapers_per_set does not much, the fallback wallpaper has been set.")
|
||||||
|
print("ERROR: The amount of changing_times and the amount of wallpapers_per_set does not much, the fallback wallpaper has been set.")
|
||||||
|
exit(1)
|
||||||
|
except ConfigError:
|
||||||
|
logger.critical("The amount of changing times and the amount of wallpapers per set does not match, exiting...")
|
||||||
|
raise ConfigError("Please provide an amount of changing_times equal to wallpapers_per_set, exiting...")
|
||||||
|
|
||||||
|
def _check_general_validity(self) -> None:
|
||||||
|
if len(self.config_general) < 3:
|
||||||
|
try:
|
||||||
|
self._set_fallback_wallpaper()
|
||||||
|
logger.error("An insufficient amount of elements has been provided for general, the fallback wallpaper has been set.")
|
||||||
|
print("ERROR: An insufficient amount of wallpapers has been provided for general, the fallback wallpaper has been set.")
|
||||||
|
exit(1)
|
||||||
|
except ConfigError:
|
||||||
|
logger.critical("An insufficient amount of elements for general has been provided, exiting...")
|
||||||
|
raise ConfigError("general should have at least 3 elements, exiting...")
|
||||||
|
|
||||||
|
def _check_wallpaper_dicts(self)-> None:
|
||||||
|
# This block checks if a dictionary for each wallpaper set exists
|
||||||
|
for wallpaper_set in self.config_used_sets:
|
||||||
|
if wallpaper_set in self.config_file:
|
||||||
|
logger.debug(f"The dictionary {wallpaper_set} has been found in config.")
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
self._set_fallback_wallpaper()
|
||||||
|
logger.error(f"The dictionary {wallpaper_set} has not been found in the config, the fallback wallpaper has been set.")
|
||||||
|
print(f"ERROR: The dictionary {wallpaper_set} has not been found in the config, the fallback wallpaper has been set.")
|
||||||
|
exit(1)
|
||||||
|
except ConfigError:
|
||||||
|
logger.critical(f"No dictionary {wallpaper_set} has been found in the config exiting...")
|
||||||
|
raise ConfigError(f"The dictionary {wallpaper_set} has not been found in the config, exiting...")
|
||||||
|
|
||||||
|
def _check_wallpaper_amount(self) -> None:
|
||||||
|
# This block checks if if each wallpaper set dictionary provides enough wallpapers to satisfy wallpapers_per_set
|
||||||
|
for wallpaper_set in self.config_used_sets:
|
||||||
|
if len(self.config_file[wallpaper_set]) == self.config_wallpapers_per_set:
|
||||||
|
logger.debug(f"Dictionary {wallpaper_set} has sufficient values.")
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
self._set_fallback_wallpaper()
|
||||||
|
logger.error(f"The Dictionary {wallpaper_set} does not have sufficient entries, the fallback wallpaper has been set.")
|
||||||
|
print(f"ERROR: The Dictionaty {wallpaper_set} does not have sufficient entries, the fallback wallpaper has been set.")
|
||||||
|
exit(1)
|
||||||
|
except ConfigError:
|
||||||
|
logger.critical(f"Dictionary {wallpaper_set} does not have sufficient entries, exciting...")
|
||||||
|
raise ConfigError(f"Dictionary {wallpaper_set} does not have the correct amount of entries, exciting...")
|
||||||
|
|
||||||
|
def validate_config(self) -> None:
|
||||||
|
self._check_fallback_wallpaper()
|
||||||
|
self._check_wallpapers_per_set_and_changing_times()
|
||||||
|
self._check_general_validity()
|
||||||
|
self._check_wallpaper_dicts()
|
||||||
|
self._check_wallpaper_amount()
|
||||||
|
logger.debug("The config file has been validated successfully (No Errors)")
|
||||||
|
|
||||||
|
class WallpaperLogic(_ConfigLib):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.chosen_wallpaper_set = False
|
||||||
|
|
||||||
|
# Returns a list of a split string that contains a changing time from the config file
|
||||||
|
def _clean_times(self, desired_time) -> list:
|
||||||
|
unclean_times = list(self.config_changing_times.values())[desired_time]
|
||||||
|
return unclean_times.split(":")
|
||||||
|
|
||||||
|
def _choose_wallpaper_set(self) -> None:
|
||||||
|
from random import choice as choose_from
|
||||||
|
self.chosen_wallpaper_set = choose_from(self.config_used_sets)
|
||||||
|
self.wallpaper_list = list(self.config_file[self.chosen_wallpaper_set].values())
|
||||||
|
logger.debug(f"Chose wallpaper set {self.chosen_wallpaper_set}")
|
||||||
|
|
||||||
|
# Verify if a given time is in a given range
|
||||||
|
def _time_in_range(self, start, end, x) -> bool:
|
||||||
|
if start <= end:
|
||||||
|
return start <= x <= end
|
||||||
|
else:
|
||||||
|
return start <= x or x < end
|
||||||
|
|
||||||
|
def _notify_user(self):
|
||||||
|
system("notify-send 'Wallman' 'A new Wallpaper has been set.'")
|
||||||
|
logger.debug("Sent desktop notification.")
|
||||||
|
|
||||||
|
def set_wallpaper_by_time(self) -> None:
|
||||||
|
# Ensure use of a consistent wallpaper set
|
||||||
|
if self.chosen_wallpaper_set is False:
|
||||||
|
self._choose_wallpaper_set()
|
||||||
|
for time_range in range(self.config_total_changing_times - 1):
|
||||||
|
clean_time = self._clean_times(time_range)
|
||||||
|
clean_time_two = self._clean_times(time_range + 1)
|
||||||
|
# Check if the current time is between a given and the following changing time and if so, set that wallpaper. If not, keep trying.
|
||||||
|
if self._time_in_range(time(int(clean_time[0]), int(clean_time[1]), int(clean_time[2])), time(int(clean_time_two[0]), int(clean_time_two[1]), int(clean_time_two[2])), datetime.now().time()):
|
||||||
|
system(f"feh --bg-scale --no-fehbg {self.wallpaper_list[time_range]}")
|
||||||
|
if self.config_notify:
|
||||||
|
self._notify_user()
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
|
system(f"feh --bg-scale --no-fehbg {self.wallpaper_list[-1]}")
|
||||||
|
if self.config_notify:
|
||||||
|
self._notify_user()
|
||||||
|
|
||||||
|
def schedule_wallpapers(self):
|
||||||
|
scheduler = BlockingScheduler()
|
||||||
|
# Create a scheduled job for every changing time
|
||||||
|
for changing_time in range(len(self.config_changing_times)):
|
||||||
|
clean_time = self._clean_times(changing_time)
|
||||||
|
scheduler.add_job(self.set_wallpaper_by_time, trigger=CronTrigger(hour=clean_time[0], minute=clean_time[1], second=clean_time[2]))
|
||||||
|
scheduler.start()
|
||||||
|
logger.info("The scheduler has been started.")
|
|
@ -1,6 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
from wallman.main import main
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
|
@ -1,12 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
from wallman.wallman_lib import WallpaperLogic
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
|
||||||
logic: WallpaperLogic = WallpaperLogic()
|
|
||||||
logic.set_wallpaper_by_time()
|
|
||||||
logic.schedule_wallpapers()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
|
@ -1,28 +0,0 @@
|
||||||
from typing import TypedDict, List, Dict
|
|
||||||
|
|
||||||
"""
|
|
||||||
This is where library classes, like the config error
|
|
||||||
And other utility things like TypedDicts for better
|
|
||||||
linting and type checking go. I should also consider
|
|
||||||
to move some other import here
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigError(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigGeneral(TypedDict):
|
|
||||||
enable_wallpaper_sets: bool
|
|
||||||
used_sets: List[str]
|
|
||||||
wallpapers_per_set: int
|
|
||||||
notify: bool
|
|
||||||
fallback_wallpaper: str
|
|
||||||
log_level: str
|
|
||||||
systray: bool
|
|
||||||
behavior: str
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigFile(TypedDict):
|
|
||||||
general: ConfigGeneral
|
|
||||||
changing_times: Dict[str, str]
|
|
|
@ -1,485 +0,0 @@
|
||||||
from os import chdir, getenv, system, path
|
|
||||||
import logging
|
|
||||||
import tomllib
|
|
||||||
from datetime import datetime, time
|
|
||||||
from apscheduler.schedulers.background import BackgroundScheduler
|
|
||||||
from apscheduler.triggers.cron import CronTrigger
|
|
||||||
from typing import Dict, List
|
|
||||||
|
|
||||||
from wallman.wallman_classes import ConfigError, ConfigGeneral, ConfigFile
|
|
||||||
|
|
||||||
# Setup Logging. NOTE: Declaration as a global variable is necessary to ensure correct functionality across multiple modules.
|
|
||||||
global logger
|
|
||||||
logger = logging.getLogger("wallman")
|
|
||||||
|
|
||||||
|
|
||||||
class _Config:
|
|
||||||
# Initializes the most important config values.
|
|
||||||
def __init__(self) -> None:
|
|
||||||
# Config file
|
|
||||||
self.config_file: ConfigFile = self._initialize_config() # Full config
|
|
||||||
# Config general
|
|
||||||
valid_general: bool = self._initialize_general()
|
|
||||||
if not valid_general:
|
|
||||||
logger.critical("The general dictionary was not found or contains errors")
|
|
||||||
print("CRITICAL: The general dictionary was not found or contains errors")
|
|
||||||
raise ConfigError("The general dictionary was not found or contains errors")
|
|
||||||
# Changing times
|
|
||||||
valid_changing_times: bool = self._initialize_changing_times()
|
|
||||||
if not valid_changing_times:
|
|
||||||
logger.critical(
|
|
||||||
"The amount of provided changing times does not match the amount of wallpapers per set, or the dictionary has not been found in the config file."
|
|
||||||
)
|
|
||||||
print(
|
|
||||||
"CRITICAL: The amount of provided changing times does not match the amount of wallpapers per set, or the dictionary has not been found in the config file."
|
|
||||||
)
|
|
||||||
raise ConfigError
|
|
||||||
# Wallpaper sets
|
|
||||||
valid_wallpaper_amount: bool = self._check_wallpaper_amount()
|
|
||||||
if not valid_wallpaper_amount:
|
|
||||||
raise ConfigError(
|
|
||||||
"The amount of wallpapers in a set does not match the amount of wallpapers_per_set provided in general."
|
|
||||||
)
|
|
||||||
|
|
||||||
# Read config
|
|
||||||
def _initialize_config(self) -> ConfigFile:
|
|
||||||
chdir(str(getenv("HOME")) + "/.config/wallman/")
|
|
||||||
try:
|
|
||||||
with open("wallman.toml", "rb") as config_file:
|
|
||||||
data: ConfigFile = tomllib.load(config_file) # type: ignore #pyright:ignore
|
|
||||||
return data
|
|
||||||
except FileNotFoundError:
|
|
||||||
raise FileNotFoundError(
|
|
||||||
"No config file could be found in ~/.config/wallman/wallman.toml"
|
|
||||||
)
|
|
||||||
except tomllib.TOMLDecodeError as e:
|
|
||||||
print("ERROR: Config could not be parsed: Invalid TOML Syntax")
|
|
||||||
raise e
|
|
||||||
|
|
||||||
def _verify_systray_deps(self):
|
|
||||||
from importlib import util
|
|
||||||
|
|
||||||
if util.find_spec("pystray") is None or util.find_spec("PIL") is None:
|
|
||||||
logger.error(
|
|
||||||
"systray is enabled, but dependencies for the systray couldn't be found. Are pystray and pillow installed?"
|
|
||||||
)
|
|
||||||
logger.info("Setting self.config_systray to false.")
|
|
||||||
print(
|
|
||||||
"ERROR: systray is enabled, but dependencies for the systray couldn't be found. Are pystray and pillow installed?"
|
|
||||||
)
|
|
||||||
self.config_systray = False
|
|
||||||
|
|
||||||
def _set_log_level(self) -> None:
|
|
||||||
global logging
|
|
||||||
global logger
|
|
||||||
chdir("/var/log/wallman/")
|
|
||||||
numeric_level: int = getattr(logging, self.config_log_level, logging.INFO)
|
|
||||||
logger.setLevel(numeric_level)
|
|
||||||
if not path.exists("wallman.log"):
|
|
||||||
system("touch wallman.log")
|
|
||||||
logging.basicConfig(
|
|
||||||
filename="wallman.log", encoding="utf-8", level=numeric_level
|
|
||||||
)
|
|
||||||
|
|
||||||
def _set_behavior(self) -> str:
|
|
||||||
try:
|
|
||||||
behavior: str = self.config_general["behavior"]
|
|
||||||
except KeyError:
|
|
||||||
logger.warning(
|
|
||||||
"There is no wallpaper behavior specified in general, defaulting to fill..."
|
|
||||||
)
|
|
||||||
print(
|
|
||||||
"WARNING: There is no wallpaper behavior specified in general, defaulting to fill..."
|
|
||||||
)
|
|
||||||
|
|
||||||
human_behaviors: List[str] = ["plain", "tile", "center", "fill", "max", "scale"]
|
|
||||||
machine_behaviors: List[str] = [
|
|
||||||
"--bg",
|
|
||||||
"--bg-tile",
|
|
||||||
"--bg-center",
|
|
||||||
"--bg-fill",
|
|
||||||
"--bg-max",
|
|
||||||
"--bg-scale",
|
|
||||||
]
|
|
||||||
behavior: str = self.config_general.get("behavior", "--bg-fill").lower()
|
|
||||||
if behavior not in human_behaviors and behavior not in machine_behaviors:
|
|
||||||
logging.error(
|
|
||||||
f"The value provided for behaviors, {behavior}, is not valid. Defaulting to fill..."
|
|
||||||
)
|
|
||||||
print(
|
|
||||||
f"ERROR: The value provided for behaviors, {behavior}, is not valid. Defaulting to --bg-fill..."
|
|
||||||
)
|
|
||||||
|
|
||||||
if behavior not in machine_behaviors:
|
|
||||||
match behavior:
|
|
||||||
case "plain":
|
|
||||||
behavior = "--bg"
|
|
||||||
case "tile":
|
|
||||||
behavior = "--bg-tile"
|
|
||||||
case "center":
|
|
||||||
behavior = "--bg-center"
|
|
||||||
case "max":
|
|
||||||
behavior = "--bg-max"
|
|
||||||
case "scale":
|
|
||||||
behavior = "--bg-scale"
|
|
||||||
case _:
|
|
||||||
behavior = "--bg-fill"
|
|
||||||
|
|
||||||
logger.info(f"The wallpaper behavior '{behavior}' has been set.")
|
|
||||||
return behavior
|
|
||||||
|
|
||||||
def _set_fallback_wallpaper(self) -> None:
|
|
||||||
if self.config_fallback_wallpaper:
|
|
||||||
successfully_set: int = system(
|
|
||||||
f"feh {self.config_behavior} --no-fehbg {self.config_fallback_wallpaper}"
|
|
||||||
)
|
|
||||||
if successfully_set == 0:
|
|
||||||
logger.info("The fallback Wallpaper has been set.")
|
|
||||||
else:
|
|
||||||
logger.critical(
|
|
||||||
"An Error occured and no fallback wallpaper was provided, exiting..."
|
|
||||||
)
|
|
||||||
raise ConfigError(
|
|
||||||
"An error occured and no fallback wallpaper has been set, exiting..."
|
|
||||||
)
|
|
||||||
|
|
||||||
def _initialize_general(self) -> bool:
|
|
||||||
# Create Config General Dict
|
|
||||||
try:
|
|
||||||
self.config_general: ConfigGeneral = self.config_file["general"]
|
|
||||||
except KeyError:
|
|
||||||
print("CRITICAL: No general dictionary found in Config file.")
|
|
||||||
raise ConfigError(
|
|
||||||
"The general dictionary could not be found in the config, exiting!"
|
|
||||||
)
|
|
||||||
# Set up logger.
|
|
||||||
self.config_log_level = self.config_general.get("log_level", "INFO").upper()
|
|
||||||
self._set_log_level()
|
|
||||||
logger.debug(f"Log level has been set to {self.config_log_level}")
|
|
||||||
logger.debug("Logger initialized successfully")
|
|
||||||
# Set up fallback wallpaper
|
|
||||||
self.config_fallback_wallpaper: str = self.config_general.get(
|
|
||||||
"fallback_wallpaper", "/etc/wallman/DefaultFallbackWallpaper.jpg"
|
|
||||||
)
|
|
||||||
logger.debug(f"Set fallback wallpaper: {self.config_fallback_wallpaper}")
|
|
||||||
# Wallpapers per set
|
|
||||||
try:
|
|
||||||
self.config_wallpapers_per_set: int = self.config_general[
|
|
||||||
"wallpapers_per_set"
|
|
||||||
]
|
|
||||||
logger.debug(
|
|
||||||
f"Set config_wallpapers_per_set to {self.config_wallpapers_per_set}"
|
|
||||||
)
|
|
||||||
except KeyError:
|
|
||||||
print(
|
|
||||||
"CRITICAL: No option wallpapers_per_set provided in the general dictionary. Attempting to set the fallback wallpaper"
|
|
||||||
)
|
|
||||||
logger.critical(
|
|
||||||
"No option wallpapers_per_set provided in the general dictionary. Attempting to set the fallback wallpaper"
|
|
||||||
)
|
|
||||||
self._set_fallback_wallpaper()
|
|
||||||
return False
|
|
||||||
# Are wallpaper sets enabled to begin with?
|
|
||||||
try:
|
|
||||||
self.config_wallpaper_sets_enabled: bool = self.config_general[
|
|
||||||
"enable_wallpaper_sets"
|
|
||||||
]
|
|
||||||
logger.debug(
|
|
||||||
f"Set config_wallpaper_sets_enabled to {self.config_wallpaper_sets_enabled}"
|
|
||||||
)
|
|
||||||
except KeyError:
|
|
||||||
logger.critical(
|
|
||||||
"No option enable_wallpaper_sets provided in the general dictionary. Attempting to set the fallback wallpaper"
|
|
||||||
)
|
|
||||||
print(
|
|
||||||
"CRITICAL: No option enable_wallpaper_sets provided in the general dictionary. Attempting to set the fallback wallpaper"
|
|
||||||
)
|
|
||||||
self._set_fallback_wallpaper()
|
|
||||||
return False
|
|
||||||
# Configure used sets
|
|
||||||
if self.config_wallpaper_sets_enabled:
|
|
||||||
try:
|
|
||||||
self.config_used_sets: List[str] = self.config_general["used_sets"]
|
|
||||||
logger.debug(
|
|
||||||
f"These wallpaper sets are in use: {self.config_used_sets}"
|
|
||||||
)
|
|
||||||
except KeyError:
|
|
||||||
print(
|
|
||||||
"CRITICAL: No array used_sets provided in the general dictionary. Attempting to set the fallback wallpaper."
|
|
||||||
)
|
|
||||||
logger.critical(
|
|
||||||
"No array used_sets provided in the general dictionary. Attempting to set the fallback wallpaper."
|
|
||||||
)
|
|
||||||
self._set_fallback_wallpaper()
|
|
||||||
return False
|
|
||||||
# Systray
|
|
||||||
try:
|
|
||||||
self.config_systray: bool = self.config_general["systray"]
|
|
||||||
logger.debug(f"config_systray has been set to: {self.config_systray}")
|
|
||||||
except KeyError:
|
|
||||||
self.config_systray: bool = True
|
|
||||||
logger.warning("No option systray found in general. Defaulting to true...")
|
|
||||||
if self.config_systray:
|
|
||||||
self._verify_systray_deps()
|
|
||||||
# Wallpaper behavior
|
|
||||||
self.config_behavior = self._set_behavior()
|
|
||||||
# Notifications
|
|
||||||
try:
|
|
||||||
self.config_notify: bool = self.config_general["notify"]
|
|
||||||
logger.debug(f"Set config_notify to {self.config_notify}.")
|
|
||||||
except KeyError:
|
|
||||||
self.config_notify: bool = False
|
|
||||||
logger.warning(
|
|
||||||
"notify is not set in dictionary general in the config file, defaulting to 'false'."
|
|
||||||
)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _initialize_changing_times(self) -> bool:
|
|
||||||
try:
|
|
||||||
self.config_changing_times: Dict[str, str] = self.config_file[
|
|
||||||
"changing_times"
|
|
||||||
]
|
|
||||||
self.config_total_changing_times: int = len(self.config_changing_times)
|
|
||||||
logger.debug(f"Changing times are {self.config_changing_times}")
|
|
||||||
except KeyError:
|
|
||||||
logger.critical(
|
|
||||||
"No dictionary called changing_times has been found in the config file."
|
|
||||||
)
|
|
||||||
print(
|
|
||||||
"CRITICAL: No dictionary called changing_times has been found in the config file."
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
return self._wallpapers_per_set_and_changing_times_match()
|
|
||||||
|
|
||||||
def _wallpapers_per_set_and_changing_times_match(self) -> bool:
|
|
||||||
# Check if the amount of wallpapers_per_set and given changing times match
|
|
||||||
if self.config_total_changing_times == self.config_wallpapers_per_set:
|
|
||||||
logger.debug(
|
|
||||||
"The amount of changing times and wallpapers per set is set correctly"
|
|
||||||
)
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
self._set_fallback_wallpaper()
|
|
||||||
logger.critical(
|
|
||||||
"The amount of changing_times and the amount of wallpapers_per_set does not match, the fallback wallpaper has been set."
|
|
||||||
)
|
|
||||||
print(
|
|
||||||
"CRITICAL: The amount of changing_times and the amount of wallpapers_per_set does not match, the fallback wallpaper has been set."
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
except ConfigError:
|
|
||||||
logger.critical(
|
|
||||||
"The amount of changing times and the amount of wallpapers per set does not match, exiting..."
|
|
||||||
)
|
|
||||||
print(
|
|
||||||
"CRITICAL: The amount of changing times and the amount of wallpapers per set does not match, exiting..."
|
|
||||||
)
|
|
||||||
raise ConfigError(
|
|
||||||
"The amount of changing times and the amount of wallpapers per set does not match."
|
|
||||||
)
|
|
||||||
|
|
||||||
def _check_wallpaper_amount(self) -> bool:
|
|
||||||
# This block checks if if each wallpaper set dictionary provides enough wallpapers to satisfy wallpapers_per_set
|
|
||||||
for wallpaper_set in self.config_used_sets:
|
|
||||||
if len(self.config_file[wallpaper_set]) == self.config_wallpapers_per_set: # type: ignore
|
|
||||||
logger.debug(f"Dictionary {wallpaper_set} has sufficient values.")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
self._set_fallback_wallpaper()
|
|
||||||
logger.error(
|
|
||||||
f"The Dictionary {wallpaper_set} does not have sufficient entries, the fallback wallpaper has been set."
|
|
||||||
)
|
|
||||||
print(
|
|
||||||
f"ERROR: The Dictionaty {wallpaper_set} does not have sufficient entries, the fallback wallpaper has been set."
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
except ConfigError:
|
|
||||||
logger.critical(
|
|
||||||
f"Dictionary {wallpaper_set} does not have sufficient entries, exciting..."
|
|
||||||
)
|
|
||||||
print(
|
|
||||||
f"Dictionary {wallpaper_set} does not have sufficient entries, exciting..."
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: Improve modularity. See notes inside the class for more details.
|
|
||||||
# TODO: Ensure functionality and if needed add handling for the 1 wallpaper per set case.
|
|
||||||
class WallpaperLogic(_Config):
|
|
||||||
def __init__(self) -> None:
|
|
||||||
super().__init__()
|
|
||||||
self.wallpaper_list: List[str] = None # type: ignore # pyright: ignore
|
|
||||||
self.chosen_wallpaper_set: str = None # type: ignore # pyright: ignore
|
|
||||||
|
|
||||||
# NOTE: This function could be in a different file because it's not needed in the case only 1 wallpaper per set is needed.
|
|
||||||
# Returns a list of a split string that contains a changing time from the config file
|
|
||||||
def _clean_times(self, desired_time: int) -> List[str]:
|
|
||||||
unclean_times: str = list(self.config_changing_times.values())[desired_time]
|
|
||||||
return unclean_times.split(":")
|
|
||||||
|
|
||||||
# NOTE: This could be in a different file because it's not needed in the "Only one wallpaper set" case.
|
|
||||||
def _choose_wallpaper_set(self) -> None:
|
|
||||||
from random import choice as choose_from
|
|
||||||
|
|
||||||
self.chosen_wallpaper_set = choose_from(self.config_used_sets)
|
|
||||||
self.wallpaper_list: List[str] = list(
|
|
||||||
self.config_file[self.chosen_wallpaper_set].values()
|
|
||||||
) # type: ignore
|
|
||||||
logger.debug(f"Chose wallpaper set {self.chosen_wallpaper_set}")
|
|
||||||
|
|
||||||
# NOTE: Same as _clean_times()
|
|
||||||
# Verify if a given time is in a given range
|
|
||||||
def _time_in_range(self, start: time, end: time, x: time) -> bool:
|
|
||||||
if start <= end:
|
|
||||||
return start <= x <= end
|
|
||||||
else:
|
|
||||||
return start <= x or x < end
|
|
||||||
|
|
||||||
# NOTE: Potentially add handling for this to be also usable for notify_user and add logging if notify_user fails. Consider adding an argument that is where it's called from and handle accordingly.
|
|
||||||
def _check_system_exitcode(self, code: int) -> bool:
|
|
||||||
if code != 0:
|
|
||||||
try:
|
|
||||||
self._set_fallback_wallpaper()
|
|
||||||
logger.error(
|
|
||||||
f"The wallpaper {self.wallpaper_list[self.current_time_range]} has not been found, the fallback wallpaper has been set. Future wallpapers will still attempted to be set."
|
|
||||||
)
|
|
||||||
print(
|
|
||||||
f"ERROR: The wallpaper {self.wallpaper_list[self.current_time_range]} has not been found, the fallback wallpaper has been set. Future wallpapers will still attempted to be set."
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
except ConfigError:
|
|
||||||
logger.error(
|
|
||||||
f"The wallpaper {self.wallpaper_list[self.current_time_range]} has not been found and no fallback wallpaper has been set. Future wallpapers will still attempted to be set."
|
|
||||||
)
|
|
||||||
print(
|
|
||||||
f"ERROR: The wallpaper {self.wallpaper_list[self.current_time_range]} has not been found and no fallback wallpaper has been set. Future wallpapers will still attempted to be set."
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
logger.info(
|
|
||||||
f"The wallpaper {self.wallpaper_list[self.current_time_range]} has been set."
|
|
||||||
)
|
|
||||||
return True
|
|
||||||
|
|
||||||
# NOTE: Add error handling in case libnotify is not installed or notify-send fails for any other reason.
|
|
||||||
# TODO: Add a check whether config[notify] is true or not.
|
|
||||||
def _notify_user(self):
|
|
||||||
system("notify-send 'Wallman' 'A new Wallpaper has been set.'")
|
|
||||||
logger.debug("Sent desktop notification.")
|
|
||||||
|
|
||||||
# TODO: Clean this up. It's way too large and way too intimidating.
|
|
||||||
# NOTE: This could be in a different for the case that the user only wants 1 wallpaper per set.
|
|
||||||
# TODO: Add an way for the user to choose if the wallpaper should scale, fill or otherwise. This needs to be editable in the config file.
|
|
||||||
def set_wallpaper_by_time(self) -> bool:
|
|
||||||
# Ensure use of a consistent wallpaper set
|
|
||||||
if not self.chosen_wallpaper_set:
|
|
||||||
self._choose_wallpaper_set()
|
|
||||||
for time_range in range(self.config_total_changing_times - 1):
|
|
||||||
self.current_time_range = (
|
|
||||||
time_range # Store current time for better debugging output
|
|
||||||
)
|
|
||||||
clean_time: List[str] = self._clean_times(time_range)
|
|
||||||
clean_time_two: List[str] = self._clean_times(time_range + 1)
|
|
||||||
# HACK on this to make it more readable. This function call is way too long. Consider storing these in a bunch of temporary variables, though keep function length in mind.
|
|
||||||
# HACK on this to see if this logic can be simplified. It's very ugly to check it that way.
|
|
||||||
# Check if the current time is between a given and the following changing time and if so, set that wallpaper. If not, keep trying.
|
|
||||||
if self._time_in_range(
|
|
||||||
time(int(clean_time[0]), int(clean_time[1]), int(clean_time[2])),
|
|
||||||
time(
|
|
||||||
int(clean_time_two[0]),
|
|
||||||
int(clean_time_two[1]),
|
|
||||||
int(clean_time_two[2]),
|
|
||||||
),
|
|
||||||
datetime.now().time(),
|
|
||||||
):
|
|
||||||
exitcode: int = system(
|
|
||||||
f"feh {self.config_behavior} --no-fehbg --quiet {self.wallpaper_list[time_range]}"
|
|
||||||
)
|
|
||||||
has_wallpaper_been_set: bool = self._check_system_exitcode(exitcode)
|
|
||||||
# TODO: Add this check to _notify_user.
|
|
||||||
if self.config_notify:
|
|
||||||
self._notify_user()
|
|
||||||
return has_wallpaper_been_set
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
|
|
||||||
exitcode: int = system(
|
|
||||||
f"feh {self.config_behavior} --no-fehbg {self.wallpaper_list[-1]}"
|
|
||||||
)
|
|
||||||
has_wallpaper_been_set: bool = self._check_system_exitcode(exitcode)
|
|
||||||
if self.config_notify:
|
|
||||||
self._notify_user()
|
|
||||||
return has_wallpaper_been_set
|
|
||||||
|
|
||||||
# NOTE: Consider avoiding nested functions.
|
|
||||||
def schedule_wallpapers(self) -> None:
|
|
||||||
def _schedule_background_wallpapers() -> BackgroundScheduler:
|
|
||||||
from apscheduler.schedulers.background import BackgroundScheduler
|
|
||||||
|
|
||||||
scheduler = BackgroundScheduler()
|
|
||||||
# Create a scheduled job for every changing time
|
|
||||||
# NOTE: This should be a function.
|
|
||||||
for changing_time in range(len(self.config_changing_times)):
|
|
||||||
clean_time = self._clean_times(changing_time)
|
|
||||||
scheduler.add_job(
|
|
||||||
self.set_wallpaper_by_time,
|
|
||||||
trigger=CronTrigger(
|
|
||||||
hour=clean_time[0], minute=clean_time[1], second=clean_time[2]
|
|
||||||
),
|
|
||||||
)
|
|
||||||
scheduler.start()
|
|
||||||
logger.info("The background scheduler has been started.")
|
|
||||||
return scheduler
|
|
||||||
|
|
||||||
def _schedule_blocking_wallpapers() -> None:
|
|
||||||
from apscheduler.schedulers.blocking import BlockingScheduler
|
|
||||||
|
|
||||||
scheduler = BlockingScheduler()
|
|
||||||
# Create a scheduled job for every changing time
|
|
||||||
# NOTE: Thisshould be a function.
|
|
||||||
for changing_time in range(len(self.config_changing_times)):
|
|
||||||
clean_time = self._clean_times(changing_time)
|
|
||||||
scheduler.add_job(
|
|
||||||
self.set_wallpaper_by_time,
|
|
||||||
trigger=CronTrigger(
|
|
||||||
hour=clean_time[0], minute=clean_time[1], second=clean_time[2]
|
|
||||||
),
|
|
||||||
)
|
|
||||||
logger.info("The blocking scheduler has been started.")
|
|
||||||
scheduler.start()
|
|
||||||
|
|
||||||
if self.config_systray:
|
|
||||||
import wallman.wallman_systray as systray
|
|
||||||
from functools import partial
|
|
||||||
|
|
||||||
scheduler: BackgroundScheduler = _schedule_background_wallpapers()
|
|
||||||
menu: systray.Menu = systray.Menu(
|
|
||||||
systray.item(
|
|
||||||
"Re-Set Wallpaper",
|
|
||||||
partial(
|
|
||||||
systray.set_wallpaper_again,
|
|
||||||
wallpaper_setter=self.set_wallpaper_by_time,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
systray.item(
|
|
||||||
"Reroll Wallpapers",
|
|
||||||
partial(
|
|
||||||
systray.reroll_wallpapers,
|
|
||||||
wallpaper_chooser=self._choose_wallpaper_set,
|
|
||||||
wallpaper_setter=self.set_wallpaper_by_time,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
systray.item(
|
|
||||||
"Quit",
|
|
||||||
partial(systray.on_quit, shutdown_scheduler=scheduler.shutdown),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
icon = systray.Icon(
|
|
||||||
"wallman_icon", systray.icon_image, "My Tray Icon", menu
|
|
||||||
)
|
|
||||||
icon.run()
|
|
||||||
else:
|
|
||||||
_schedule_blocking_wallpapers()
|
|
|
@ -1,40 +0,0 @@
|
||||||
from os import chdir
|
|
||||||
import logging
|
|
||||||
from PIL import Image
|
|
||||||
from pystray import Icon, MenuItem as item, Menu # noqa: F401
|
|
||||||
|
|
||||||
# Use logger that is also in wallman_lib
|
|
||||||
logger = logging.getLogger("wallman")
|
|
||||||
|
|
||||||
|
|
||||||
# This should always be ran with "set_wallpaper_by_time" as input!
|
|
||||||
def set_wallpaper_again(icon, item, wallpaper_setter): # noqa: F811
|
|
||||||
logging.info("Re-Setting wallpaper due to systray input.")
|
|
||||||
wallpaper_setter()
|
|
||||||
|
|
||||||
|
|
||||||
def reroll_wallpapers(icon, item, wallpaper_chooser, wallpaper_setter): # noqa: F811
|
|
||||||
logging.info(
|
|
||||||
"Rerolling Wallpaper sets and resetting wallpaper due to systray input"
|
|
||||||
)
|
|
||||||
wallpaper_chooser()
|
|
||||||
wallpaper_setter()
|
|
||||||
|
|
||||||
|
|
||||||
# This should always be ran with "scheduler.shutdown" as input!
|
|
||||||
def on_quit(icon, item, shutdown_scheduler): # noqa: F811
|
|
||||||
logging.info("Shutting down wallman due to systray input.")
|
|
||||||
shutdown_scheduler()
|
|
||||||
icon.stop()
|
|
||||||
|
|
||||||
|
|
||||||
chdir("/etc/wallman/icons/")
|
|
||||||
try:
|
|
||||||
icon_image: Image.Image = Image.open("systrayIcon.jpg")
|
|
||||||
except FileNotFoundError:
|
|
||||||
logger.error(
|
|
||||||
"/etc/wallman/icons/systrayIcon.jpg has not been found, wallman will launch without a systray."
|
|
||||||
)
|
|
||||||
print(
|
|
||||||
"ERROR: /etc/wallman/icons/systrayIcon.jpg has not been found, wallman will launch without a systray."
|
|
||||||
)
|
|
Loading…
Reference in a new issue