License as MIT, KDE support, nix package and flake

This commit is contained in:
Kat Inskip 2023-11-12 12:56:47 -08:00
parent 3c196d83d0
commit 4a18fa6337
Signed by: kat
GPG key ID: 465E64DECEA8CF0F
19 changed files with 219 additions and 39 deletions

7
LICENSE.txt Normal file
View file

@ -0,0 +1,7 @@
Copyright 2023 kittywitch
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -1,5 +1,5 @@
seconds = 300 interval = 300
automatic = true rotate = true
source = "konachan" source = "konachan"
tags = [ tags = [
"rating:s" "rating:s"

61
flake.lock generated Normal file
View file

@ -0,0 +1,61 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1694529238,
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1699099776,
"narHash": "sha256-X09iKJ27mGsGambGfkKzqvw5esP1L/Rf8H3u3fCqIiU=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "85f1ba3e51676fa8cc604a3d863d729026a6b8eb",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

21
flake.nix Normal file
View file

@ -0,0 +1,21 @@
{
description = "konawall-py";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = {
self,
nixpkgs,
flake-utils,
...
} @ inputs:
flake-utils.lib.eachDefaultSystem (system: let
pkgs = nixpkgs.legacyPackages.${system};
in {
packages = rec {
konawall-py = pkgs.python3.pkgs.callPackage ./package.nix {};
default = konawall-py;
};
});
}

View file

@ -1,7 +1,7 @@
import logging import logging
import tempfile import tempfile
import requests import requests
from custom_print import kv_print from konawall.custom_print import kv_print
""" """
Download files given a list of URLs Download files given a list of URLs

View file

@ -1,8 +1,8 @@
import sys import sys
import os import os
import logging import logging
from custom_errors import UnsupportedPlatform from konawall.custom_errors import UnsupportedPlatform
from module_loader import environment_handlers from konawall.module_loader import environment_handlers
""" """
This detects the DE/WM from the Linux environment because it's not provided by the platform This detects the DE/WM from the Linux environment because it's not provided by the platform
@ -21,6 +21,7 @@ def detect_linux_environment():
"kde", # qdbus "kde", # qdbus
] ]
modified_mapping = { modified_mapping = {
"plasma": "kde",
"fluxbox": "feh", "fluxbox": "feh",
"blackbox": "feh", "blackbox": "feh",
"openbox": "feh", "openbox": "feh",

View file

@ -1,5 +1,5 @@
import subprocess import subprocess
from module_loader import add_environment from konawall.module_loader import add_environment
""" """
This sets wallpapers on Darwin. This sets wallpapers on Darwin.

View file

@ -0,0 +1,16 @@
import subprocess
from konawall.module_loader import add_environment
"""
This sets the wallpaper on KDE.
"""
@add_environment("kde_setter")
def set_wallpapers(files: list, displays: list):
for file in files:
kde_script = f"""
for (const desktop of desktops()) {{
desktop.currentConfigGroup = ["Wallpaper", "org.kde.image", "General"]
desktop.writeConfig("Image", "{file}")
}}
"""
subprocess.run(["qdbus", "org.kde.plasmashell", "/PlasmaShell", "org.kde.PlasmaShell.evaluateScript", kde_script])

View file

@ -1,8 +1,8 @@
import os import os
import ctypes import ctypes
import logging import logging
from imager import combine_to_viewport from konawall.imager import combine_to_viewport
from module_loader import add_environment from konawall.module_loader import add_environment
""" """
Pre-setting on Windows Pre-setting on Windows

56
konawall/gui.py Normal file → Executable file
View file

@ -9,15 +9,15 @@ import screeninfo
import tomllib import tomllib
import subprocess import subprocess
import importlib.metadata import importlib.metadata
from environment import set_environment_wallpapers, detect_environment from konawall.environment import set_environment_wallpapers, detect_environment
from module_loader import import_dir, environment_handlers, source_handlers from konawall.module_loader import import_dir, environment_handlers, source_handlers
from custom_print import kv_print from konawall.custom_print import kv_print
from humanfriendly import format_timespan from humanfriendly import format_timespan
class Konawall(wx.adv.TaskBarIcon): class Konawall(wx.adv.TaskBarIcon):
def __init__(self, version, file_logger): def __init__(self, version, file_logger):
# Prevents it from closing before it has done any work on macOS # Prevents it from closing before it has done any work on macOS
if wx.Platform == "__WXMAC__": if wx.Platform == "__WXMAC__" or wx.Platform == "__WXGTK__":
self.hidden_frame = wx.Frame(None) self.hidden_frame = wx.Frame(None)
self.hidden_frame.Hide() self.hidden_frame.Hide()
@ -37,6 +37,17 @@ class Konawall(wx.adv.TaskBarIcon):
self.wallpaper_rotation_timer = wx.Timer(self, wx.ID_ANY) self.wallpaper_rotation_timer = wx.Timer(self, wx.ID_ANY)
# Reload (actually load) the config and modules. # Reload (actually load) the config and modules.
if wx.Platform == "__WXGTK__":
from xdg_base_dirs import xdg_config_home
self.config_path = os.path.join(xdg_config_home(), "konawall", "config.toml")
if wx.Platform == "__WXMAC__":
config_path_string = "~/Library/Application Support/konawall/config.toml"
self.config_path = os.path.expanduser(config_path_string)
elif wx.Platform == "__WXMSW__":
config_path_string = "%APPDATA%\\konawall\\config.toml"
self.config_path = os.path.expandvars(config_path_string)
else:
self.config_path = os.path.join(os.path.expanduser("~"), ".config", "konawall", "config.toml")
self.reload_config() self.reload_config()
self.import_modules() self.import_modules()
@ -65,7 +76,7 @@ class Konawall(wx.adv.TaskBarIcon):
if "wxMSW" in wx.PlatformInfo: if "wxMSW" in wx.PlatformInfo:
image = image.resize((16, 16)) image = image.resize((16, 16))
elif "wxGTK" in wx.PlatformInfo: elif "wxGTK" in wx.PlatformInfo:
image = image.rescale((22, 22)) image = image.resize((22, 22))
# Write image to temporary file # Write image to temporary file
temp = tempfile.NamedTemporaryFile(suffix=".png", delete=False) temp = tempfile.NamedTemporaryFile(suffix=".png", delete=False)
@ -77,7 +88,7 @@ class Konawall(wx.adv.TaskBarIcon):
return icon return icon
def toggle_timed_wallpaper_rotation_status(self): def toggle_timed_wallpaper_rotation_status(self):
return f"{'Dis' if self.rotate_wallpapers else 'En'}able Timer" return f"{'Dis' if self.rotate else 'En'}able Timer"
# Load in our source and environment handlers # Load in our source and environment handlers
def import_modules(self): def import_modules(self):
@ -88,9 +99,9 @@ class Konawall(wx.adv.TaskBarIcon):
# Load a TOML file's key-value pairs into our class # Load a TOML file's key-value pairs into our class
def load_config(self): def load_config(self):
if os.path.isfile("config.toml"): if os.path.isfile(self.config_path):
# If the config file exists, load it as a dictionary into the config variable. # If the config file exists, load it as a dictionary into the config variable.
with open("config.toml", "rb") as f: with open(self.config_path, "rb") as f:
config = tomllib.load(f) config = tomllib.load(f)
# for every key-value pair in the config variable , set the corresponding attribute of our class to it # for every key-value pair in the config variable , set the corresponding attribute of our class to it
for k, v in config.items(): for k, v in config.items():
@ -100,14 +111,14 @@ class Konawall(wx.adv.TaskBarIcon):
# If there is no config file, get complainy. # If there is no config file, get complainy.
dialog = wx.MessageDialog( dialog = wx.MessageDialog(
None, None,
"No config file found, using defaults.", f"No config file found at {self.config_path}, using defaults.",
self.title_string, self.title_string,
wx.OK|wx.ICON_INFORMATION wx.OK|wx.ICON_INFORMATION
) )
dialog.ShowModal() dialog.ShowModal()
dialog.Destroy() dialog.Destroy()
# Set some arbitrary defaults # Set some arbitrary defaults
self.rotate_wallpapers = True self.rotate = True
self.interval = 10*60 self.interval = 10*60
self.tags = ["rating:s"] self.tags = ["rating:s"]
self.logging = {} self.logging = {}
@ -123,7 +134,10 @@ class Konawall(wx.adv.TaskBarIcon):
return item return item
def create_info_item(menu, label, help=""): def create_info_item(menu, label, help=""):
item = wx.MenuItem(menu, wx.ID_ANY, label) item = wx.MenuItem(menu, wx.ID_ANY, label)
item.Enable(False) if wx.Platform == "__WXGTK__":
menu.Bind(wx.EVT_MENU, (lambda x: x), id=item.GetId())
else:
item.Enable(False)
menu.Append(item) menu.Append(item)
return item return item
def create_separator(menu): def create_separator(menu):
@ -195,21 +209,21 @@ class Konawall(wx.adv.TaskBarIcon):
# Interactively edit the config file # Interactively edit the config file
def edit_config_menu_item(self, event): def edit_config_menu_item(self, event):
kv_print("User is editing", "config.toml") kv_print("User is editing", self.config_path)
# Check if we're on Windows, if so use Notepad # Check if we're on Windows, if so use Notepad
if sys.platform == "win32": if sys.platform == "win32":
# I don't even know how to detect the default editor on Windows # I don't even know how to detect the default editor on Windows
subprocess.call("notepad.exe config.toml") subprocess.call(f"notepad.exe {self.config_path}", shell=True)
else: else:
# Open config file in default editor # Open config file in default editor
subprocess.call(f"{os.environ['SHELL']} {os.environ['EDITOR']} config.toml") subprocess.call(f"{os.environ['SHELL']} {os.environ['EDITOR']} {self.config_path}", shell=True)
# When file is done being edited, reload config # When file is done being edited, reload config
kv_print("User has edited", "config.toml") kv_print("User has edited", self.config_path)
self.reload_config() self.reload_config()
# Reload the application # Reload the application
def reload_config(self): def reload_config(self):
kv_print(f"{'Rel' if self.loaded_before else 'L'}oading config from", "config.toml") kv_print(f"{'Rel' if self.loaded_before else 'L'}oading config from", self.config_path)
self.load_config() self.load_config()
# Handle finding the log level # Handle finding the log level
@ -250,14 +264,14 @@ class Konawall(wx.adv.TaskBarIcon):
# Set whether to rotate wallpapers automatically or not # Set whether to rotate wallpapers automatically or not
def toggle_timed_wallpaper_rotation(self, event): def toggle_timed_wallpaper_rotation(self, event):
self.rotate_wallpapers = not self.rotate_wallpapers self.rotate = not self.rotate
self.respect_timed_wallpaper_rotation_toggle() self.respect_timed_wallpaper_rotation_toggle()
# Update the timer and the menu item to reflect our current state # Update the timer and the menu item to reflect our current state
def respect_timed_wallpaper_rotation_toggle(self): def respect_timed_wallpaper_rotation_toggle(self):
if self.rotate_wallpapers and not self.wallpaper_rotation_timer.IsRunning(): if self.rotate and not self.wallpaper_rotation_timer.IsRunning():
self.wallpaper_rotation_timer.Start(1000) self.wallpaper_rotation_timer.Start(1000)
elif not self.rotate_wallpapers and self.wallpaper_rotation_timer.IsRunning(): elif not self.rotate and self.wallpaper_rotation_timer.IsRunning():
self.wallpaper_rotation_timer.Stop() self.wallpaper_rotation_timer.Stop()
# Set the time left counter to show that it is disabled # Set the time left counter to show that it is disabled
self.timed_wallpaper_rotation_status_menu_item.SetItemLabel(f"Automatic wallpaper rotation disabled") self.timed_wallpaper_rotation_status_menu_item.SetItemLabel(f"Automatic wallpaper rotation disabled")
@ -316,12 +330,12 @@ def main():
version = "testing version" version = "testing version"
file_logger = logging.FileHandler("app.log", mode="a") file_logger = logging.FileHandler("app.log", mode="a")
#console_logger = logging.StreamHandler() console_logger = logging.StreamHandler()
logging.basicConfig( logging.basicConfig(
level=logging.DEBUG, level=logging.DEBUG,
format="%(asctime)s - %(levelname)s - %(message)s", format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[ handlers=[
#console_logger, console_logger,
file_logger, file_logger,
] ]
) )

View file

@ -3,7 +3,7 @@ import os
import re import re
import inspect import inspect
import logging import logging
from custom_print import kv_print from konawall.custom_print import kv_print
global environment_handlers global environment_handlers
global source_handlers global source_handlers

View file

View file

@ -1,9 +1,9 @@
import requests import requests
import logging import logging
from custom_print import kv_print from konawall.custom_print import kv_print
from custom_errors import RequestFailed from konawall.custom_errors import RequestFailed
from module_loader import add_source from konawall.module_loader import add_source
from downloader import download_files from konawall.downloader import download_files
""" """
Turn a list of tags and a count into a list of URLs to download from Turn a list of tags and a count into a list of URLs to download from

32
package.nix Normal file
View file

@ -0,0 +1,32 @@
{
lib,
buildPythonPackage,
python311Packages,
}: let
pyproject = builtins.fromTOML (builtins.readFile ./pyproject.toml);
poetryBlock = pyproject.tool.poetry;
dependencyReplacements = {
wxpython = python311Packages.wxPython_4_2;
};
in
buildPythonPackage rec {
pname = poetryBlock.name;
version = poetryBlock.version;
src = ./.;
doCheck = false;
propagatedBuildInputs = let
dependencyNames = lib.attrNames poetryBlock.dependencies;
dependencies = map (name: python311Packages.${name} or dependencyReplacements.${name}) dependencyNames;
in
dependencies;
meta = with lib; {
description = poetryBlock.description;
homepage = poetryBlock.homepage;
license = licenses.${toLower poetryBlock.license};
maintainers = with lib.maintainers; [kittywitch];
};
}

View file

@ -1,6 +1,7 @@
[tool.poetry] [tool.poetry]
name = "konawall" name = "konawall"
version = "0.1.0" version = "0.1.0"
license = "MIT"
description = "A hopefully cross-platform service for fetching wallpapers and setting them" description = "A hopefully cross-platform service for fetching wallpapers and setting them"
authors = [ authors = [
"Kat Inskip <kat@inskip.me>" "Kat Inskip <kat@inskip.me>"
@ -10,6 +11,9 @@ packages = [
{include = "konawall"} {include = "konawall"}
] ]
[tool.poetry.scripts]
gui = "konawall.script"
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.11" python = "^3.11"
pillow = "^10.0.1" pillow = "^10.0.1"
@ -18,9 +22,8 @@ requests = "^2.31.0"
termcolor = "^2.3.0" termcolor = "^2.3.0"
wxpython = "^4.2.1" wxpython = "^4.2.1"
humanfriendly = "^10.0" humanfriendly = "^10.0"
xdg-base-dirs = "^6.0.1"
[build-system] [build-system]
requires = [ requires = [ "poetry-core" ]
"poetry-core" build-backend = "poetry.core.masonry.api"
]
build-backend = "poetry.core.masonry.api"

View file

@ -3,4 +3,5 @@ screeninfo
requests requests
termcolor termcolor
wxpython wxpython
humanfriendly humanfriendly
xdg-base-dirs

24
setup.py Normal file
View file

@ -0,0 +1,24 @@
from setuptools import setup
with open("README.MD", "r") as f:
long_description = f.read()
import tomllib
with open("pyproject.toml", "rb") as f:
pyproject = tomllib.load(f)
poetryBlock = pyproject["tool"]["poetry"]
setup(
name = poetryBlock["name"],
version = poetryBlock["version"],
author = poetryBlock["authors"][0].split(" <")[0],
author_email = poetryBlock["authors"][0].split(" <")[1][:-1],
description = poetryBlock["description"],
long_description = long_description,
entry_points = {
"console_scripts": [
"konawall = konawall.gui:main",
],
},
)