mirror of
https://github.com/kittywitch/konawall-py.git
synced 2026-02-09 12:29:19 -08:00
Add a systray based GUI o:
This commit is contained in:
parent
d894d7176c
commit
96f1ee3583
8 changed files with 198 additions and 12 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -159,5 +159,6 @@ cython_debug/
|
||||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
#.idea/
|
#.idea/
|
||||||
|
|
||||||
# App logs
|
# App specific files
|
||||||
app.log
|
app.log
|
||||||
|
config.toml
|
||||||
10
config.toml.example
Normal file
10
config.toml.example
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
seconds = 300
|
||||||
|
automatic = true
|
||||||
|
source = "konachan"
|
||||||
|
tags = [
|
||||||
|
"rating:s"
|
||||||
|
]
|
||||||
|
|
||||||
|
[logging]
|
||||||
|
file = "INFO"
|
||||||
|
console = "DEBUG"
|
||||||
|
|
@ -6,9 +6,9 @@ Print a key-value pair with a key and value coloured differently.
|
||||||
|
|
||||||
:param key: The key to print
|
:param key: The key to print
|
||||||
:param value: The value to print
|
:param value: The value to print
|
||||||
:param newline: Whether to print a newline after the value
|
:param level: The logging level to print at
|
||||||
:returns: None
|
:returns: None
|
||||||
"""
|
"""
|
||||||
def kv_print(key: str, value: str, newline: bool = False) -> None:
|
def kv_print(key: str, value: str, level: str = "INFO") -> None:
|
||||||
logging.info(f"{key}: {value}")
|
logger = getattr(logging, level.lower())
|
||||||
print(termcolor.colored(key, "cyan") + ": " + termcolor.colored(value, "white"), end="\n" if newline else " ")
|
logger(termcolor.colored(key, "cyan") + ": " + termcolor.colored(value, "white"))
|
||||||
|
|
@ -25,7 +25,7 @@ def download_files(files: list) -> list:
|
||||||
# Close the file
|
# Close the file
|
||||||
image_file.close()
|
image_file.close()
|
||||||
# Give the user data about the downloaded image
|
# Give the user data about the downloaded image
|
||||||
kv_print(f"Image {str(i+1)}", image_file.name, newline=True)
|
kv_print(f"Image {str(i+1)}", image_file.name)
|
||||||
# Add the file to the list of downloaded files
|
# Add the file to the list of downloaded files
|
||||||
downloaded_files.append(image_file.name)
|
downloaded_files.append(image_file.name)
|
||||||
return downloaded_files
|
return downloaded_files
|
||||||
174
gui.py
Normal file
174
gui.py
Normal file
|
|
@ -0,0 +1,174 @@
|
||||||
|
import wx
|
||||||
|
import wx.adv
|
||||||
|
import tempfile
|
||||||
|
from PIL import Image, ImageDraw
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
import screeninfo
|
||||||
|
import tomllib
|
||||||
|
import subprocess
|
||||||
|
from environment import set_environment_wallpapers, detect_environment
|
||||||
|
from module_loader import import_dir, environment_handlers, source_handlers
|
||||||
|
from custom_print import kv_print
|
||||||
|
|
||||||
|
def create_icon():
|
||||||
|
width = 128
|
||||||
|
height = 128
|
||||||
|
# Missing texture
|
||||||
|
image = Image.new('RGB', (width, height), (0, 0, 0))
|
||||||
|
dc = ImageDraw.Draw(image)
|
||||||
|
dc.rectangle((0, 0, width//2, height//2), fill=(255, 0, 255))
|
||||||
|
dc.rectangle((width//2, height//2, width, height), fill=(255, 0, 255))
|
||||||
|
# Write image to temporary file
|
||||||
|
temp = tempfile.NamedTemporaryFile(suffix=".png", delete=False)
|
||||||
|
image.save(temp.name)
|
||||||
|
icon = wx.Icon()
|
||||||
|
icon.CopyFromBitmap(wx.Bitmap(temp.name))
|
||||||
|
return icon
|
||||||
|
|
||||||
|
|
||||||
|
def create_menu_item(menu, label, func):
|
||||||
|
item = wx.MenuItem(menu, -1, label)
|
||||||
|
menu.Bind(wx.EVT_MENU, func, id=item.GetId())
|
||||||
|
menu.Append(item)
|
||||||
|
return item
|
||||||
|
|
||||||
|
class Konawall(wx.adv.TaskBarIcon):
|
||||||
|
def __init__(self, file_logger, console_logger):
|
||||||
|
self.file_logger = file_logger
|
||||||
|
self.console_logger = console_logger
|
||||||
|
self.loaded = False
|
||||||
|
wx.adv.TaskBarIcon.__init__(self)
|
||||||
|
# Pre-setup initialization
|
||||||
|
self.environment = detect_environment()
|
||||||
|
self.automatic_item = None
|
||||||
|
self.automatic_timer = wx.Timer(self, wx.ID_ANY)
|
||||||
|
# Reload (actually load) the config
|
||||||
|
self.reload()
|
||||||
|
self.load_modules()
|
||||||
|
|
||||||
|
self.automatic_timer.Start(self.seconds * 1000)
|
||||||
|
# Set up the taskbar icon
|
||||||
|
self.SetIcon(create_icon(), "Konawall")
|
||||||
|
self.create_menu()
|
||||||
|
self.create_bindings()
|
||||||
|
|
||||||
|
def automatic_item_state(self):
|
||||||
|
if self.automatic:
|
||||||
|
return "Disable Automatic"
|
||||||
|
else:
|
||||||
|
return "Enable Automatic"
|
||||||
|
|
||||||
|
def load_modules(self):
|
||||||
|
import_dir(os.path.join(os.path.dirname(os.path.abspath( __file__ )), "sources"))
|
||||||
|
kv_print("Loaded source handlers", ", ".join(source_handlers), level="debug")
|
||||||
|
import_dir(os.path.join(os.path.dirname(os.path.abspath( __file__ )), "environments"))
|
||||||
|
kv_print("Loaded environment handlers", ", ".join(environment_handlers), level="debug")
|
||||||
|
|
||||||
|
def read_config(self):
|
||||||
|
# check if config file exists
|
||||||
|
if os.path.isfile("config.toml"):
|
||||||
|
with open("config.toml", "rb") as f:
|
||||||
|
config = tomllib.load(f)
|
||||||
|
for k, v in config.items():
|
||||||
|
kv_print(f"Loaded {k}", v)
|
||||||
|
setattr(self, k, v)
|
||||||
|
else:
|
||||||
|
dialog = wx.MessageDialog(
|
||||||
|
None,
|
||||||
|
"No config file found, using defaults.",
|
||||||
|
"Konawall",
|
||||||
|
wx.OK|wx.ICON_INFORMATION
|
||||||
|
)
|
||||||
|
dialog.ShowModal()
|
||||||
|
dialog.Destroy()
|
||||||
|
self.automatic = True
|
||||||
|
self.seconds = 10*60
|
||||||
|
self.tags = ["rating:s"]
|
||||||
|
|
||||||
|
def create_menu(self):
|
||||||
|
self.menu = wx.Menu()
|
||||||
|
create_menu_item(self.menu, "Run", self.run)
|
||||||
|
self.automatic_item = create_menu_item(self.menu, self.automatic_item_state(), self.toggle_automatic)
|
||||||
|
create_menu_item(self.menu, "Edit Config", self.edit_config)
|
||||||
|
self.menu.Append(wx.ID_EXIT, "Exit")
|
||||||
|
|
||||||
|
def edit_config(self, event):
|
||||||
|
kv_print("User is editing", "config.toml")
|
||||||
|
# Check if we're on Windows, if so use Notepad
|
||||||
|
if sys.platform == "win32":
|
||||||
|
# I don't even know how to detect the default editor on Windows
|
||||||
|
subprocess.call("notepad.exe config.toml")
|
||||||
|
else:
|
||||||
|
# Open config file in default editor
|
||||||
|
subprocess.call(f"{os.environ['EDITOR']} config.toml")
|
||||||
|
# When file is done being edited, reload config
|
||||||
|
kv_print("User has edited", "config.toml")
|
||||||
|
self.reload()
|
||||||
|
|
||||||
|
def reload(self):
|
||||||
|
if self.loaded:
|
||||||
|
kv_print("Reloading config from", "config.toml")
|
||||||
|
else:
|
||||||
|
kv_print("Loading config from", "config.toml")
|
||||||
|
self.read_config()
|
||||||
|
# Handle finding the log level
|
||||||
|
if "file" in self.logging:
|
||||||
|
file_log_level = getattr(logging, self.logging["file"])
|
||||||
|
else:
|
||||||
|
file_log_level = logging.INFO
|
||||||
|
self.file_logger.setLevel(file_log_level)
|
||||||
|
if "console" in self.logging:
|
||||||
|
console_log_level = getattr(logging, self.logging["console"])
|
||||||
|
else:
|
||||||
|
console_log_level = logging.INFO
|
||||||
|
self.console_logger.setLevel(console_log_level)
|
||||||
|
# Finished loading
|
||||||
|
self.loaded = True
|
||||||
|
|
||||||
|
# Handle the automatic timer
|
||||||
|
if self.automatic and self.automatic_timer.IsRunning():
|
||||||
|
self.automatic_timer.Stop()
|
||||||
|
self.automatic_timer.Start(self.seconds * 1000)
|
||||||
|
self.menu.SetLabel(self.automatic_item.Id, self.automatic_item_state())
|
||||||
|
elif not self.automatic and self.automatic_timer.IsRunning():
|
||||||
|
self.automatic_timer.Stop()
|
||||||
|
self.menu.SetLabel(self.automatic_item.Id, self.automatic_item_state())
|
||||||
|
|
||||||
|
def toggle_automatic(self, event):
|
||||||
|
self.automatic = not self.automatic
|
||||||
|
if self.automatic:
|
||||||
|
self.automatic_timer.Start(self.seconds * 1000)
|
||||||
|
else:
|
||||||
|
self.automatic_timer.Stop()
|
||||||
|
self.menu.SetLabel(self.automatic_item.Id, self.automatic_item_state())
|
||||||
|
|
||||||
|
def show_menu(self, event):
|
||||||
|
self.PopupMenu(self.menu)
|
||||||
|
|
||||||
|
def run(self, event):
|
||||||
|
displays = screeninfo.get_monitors()
|
||||||
|
count = len(displays)
|
||||||
|
files = source_handlers[self.source](count, self.tags)
|
||||||
|
set_environment_wallpapers(self.environment, files, displays)
|
||||||
|
|
||||||
|
def create_bindings(self):
|
||||||
|
self.Bind(wx.adv.EVT_TASKBAR_LEFT_DOWN, self.run)
|
||||||
|
self.Bind(wx.adv.EVT_TASKBAR_RIGHT_DOWN, self.show_menu)
|
||||||
|
self.Bind(wx.EVT_TIMER, self.run, self.automatic_timer)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
file_logger = logging.FileHandler("app.log", mode="a")
|
||||||
|
console_logger = logging.StreamHandler()
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.DEBUG,
|
||||||
|
format="%(asctime)s - %(levelname)s - %(message)s",
|
||||||
|
handlers=[
|
||||||
|
console_logger,
|
||||||
|
file_logger,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
app = wx.App(False)
|
||||||
|
Konawall(file_logger, console_logger)
|
||||||
|
app.MainLoop()
|
||||||
|
|
@ -3,6 +3,7 @@ import os
|
||||||
import re
|
import re
|
||||||
import inspect
|
import inspect
|
||||||
import logging
|
import logging
|
||||||
|
from custom_print import kv_print
|
||||||
|
|
||||||
global environment_handlers
|
global environment_handlers
|
||||||
global source_handlers
|
global source_handlers
|
||||||
|
|
@ -49,7 +50,7 @@ def add_environment(environment: str) -> callable:
|
||||||
path = frame[0].f_code.co_filename
|
path = frame[0].f_code.co_filename
|
||||||
def wrapper(function):
|
def wrapper(function):
|
||||||
environment_handlers[environment] = function
|
environment_handlers[environment] = function
|
||||||
logging.debug(f"Loaded environment handler {environment} from {path}")
|
kv_print(f"Loaded environment handler {environment} from", path, level="debug")
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
@ -65,5 +66,5 @@ def add_source(source: str) -> callable:
|
||||||
path = frame[0].f_code.co_filename
|
path = frame[0].f_code.co_filename
|
||||||
def wrapper(function):
|
def wrapper(function):
|
||||||
source_handlers[source] = function
|
source_handlers[source] = function
|
||||||
logging.debug(f"Loaded wallpaper source {source} from {path}")
|
kv_print(f"Loaded wallpaper source {source} from", path, level="debug")
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
@ -34,9 +34,9 @@ def request_posts(count: int, tags: list) -> list:
|
||||||
kv_print("Post ID", post["id"])
|
kv_print("Post ID", post["id"])
|
||||||
kv_print("Author", post["author"])
|
kv_print("Author", post["author"])
|
||||||
kv_print("Rating", post["rating"])
|
kv_print("Rating", post["rating"])
|
||||||
kv_print("Resolution", f"{post['width']}x{post['height']}", newline=True)
|
kv_print("Resolution", f"{post['width']}x{post['height']}")
|
||||||
kv_print("Tags", post["tags"], newline=True)
|
kv_print("Tags", post["tags"])
|
||||||
kv_print("URL", post["file_url"], newline=True)
|
kv_print("URL", post["file_url"])
|
||||||
# Append the URL to the list
|
# Append the URL to the list
|
||||||
post_urls.append(post["file_url"])
|
post_urls.append(post["file_url"])
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue