mirror of
https://github.com/anufrievroman/waypaper.git
synced 2024-08-16 19:50:46 +03:00
Implementing support for webp and other formats
This resolves the issue #19. Also there is improved translation handling
This commit is contained in:
parent
26371745f0
commit
3cd2f7ff9c
4
setup.py
4
setup.py
@ -17,8 +17,8 @@ setuptools.setup(
|
||||
"waypaper = waypaper.__main__:run"
|
||||
]
|
||||
},
|
||||
install_requires=["PyGObject", "importlib_metadata", "platformdirs"],
|
||||
version='0.0.2',
|
||||
install_requires=["PyGObject", "importlib_metadata", "platformdirs", "Pillow"],
|
||||
version='2.0.3',
|
||||
python_requires='>3.9',
|
||||
classifiers=[
|
||||
"Development Status :: 4 - Beta",
|
||||
|
@ -10,25 +10,26 @@ from waypaper.changer import change_wallpaper
|
||||
from waypaper.common import get_random_file
|
||||
from waypaper.aboutdata import AboutData
|
||||
from waypaper.options import FILL_OPTIONS, BACKEND_OPTIONS
|
||||
from waypaper.translations import English, German, French, Russian, Polish, Chinese
|
||||
|
||||
aboutData = AboutData()
|
||||
about = AboutData()
|
||||
cf = Config()
|
||||
|
||||
if cf.lang == "de":
|
||||
from waypaper.translations import German as txt
|
||||
txt = German()
|
||||
elif cf.lang == "fr":
|
||||
from waypaper.translations import French as txt
|
||||
txt = French()
|
||||
elif cf.lang == "ru":
|
||||
from waypaper.translations import Russian as txt
|
||||
txt = Russian()
|
||||
elif cf.lang == "pl":
|
||||
from waypaper.translations import Polish as txt
|
||||
txt = Polish()
|
||||
elif cf.lang == "zh":
|
||||
from waypaper.translations import Chinese as txt
|
||||
txt = Chinese()
|
||||
else:
|
||||
from waypaper.translations import English as txt
|
||||
txt = English()
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(prog = aboutData.applicationName(), description = txt.msg_desc,
|
||||
parser = argparse.ArgumentParser(prog = about.applicationName(), description = txt.msg_desc,
|
||||
epilog = txt.msg_info)
|
||||
parser.add_argument("-v", "--version", help=txt.msg_arg_help, action="store_true")
|
||||
parser.add_argument("--restore", help=txt.msg_arg_rest, action="store_true")
|
||||
@ -59,7 +60,7 @@ def run():
|
||||
|
||||
# Print the version and quit:
|
||||
if args.version:
|
||||
print(f"{aboutData.applicationName()} v.{aboutData.applicationVersion()}")
|
||||
print(f"{about.applicationName()} v.{about.applicationVersion()}")
|
||||
exit(0)
|
||||
|
||||
# Start GUI:
|
||||
|
@ -7,6 +7,7 @@ import shutil
|
||||
import gi
|
||||
from pathlib import Path
|
||||
from platformdirs import user_cache_path
|
||||
from PIL import Image
|
||||
|
||||
from waypaper.aboutdata import AboutData
|
||||
from waypaper.changer import change_wallpaper
|
||||
@ -18,9 +19,22 @@ gi.require_version("Gtk", "3.0")
|
||||
from gi.repository import Gtk, GdkPixbuf, Gdk, GLib
|
||||
|
||||
|
||||
def read_webp_image(image_path):
|
||||
"""Read webp images using Pillow library and convert it to pixbuf format"""
|
||||
img = Image.open(image_path)
|
||||
data = img.tobytes()
|
||||
width, height = img.size
|
||||
pixbuf = GdkPixbuf.Pixbuf.new_from_data(data, GdkPixbuf.Colorspace.RGB, False, 8, width, height, width * 3)
|
||||
return pixbuf
|
||||
|
||||
|
||||
def cache_image(image_path, cachedir):
|
||||
"""Resize and cache images using gtk library"""
|
||||
pixbuf = GdkPixbuf.Pixbuf.new_from_file(str(image_path))
|
||||
ext = os.path.splitext(image_path)[1].lower()
|
||||
if ext == ".webp":
|
||||
pixbuf = read_webp_image(str(image_path))
|
||||
else:
|
||||
pixbuf = GdkPixbuf.Pixbuf.new_from_file(str(image_path))
|
||||
aspect_ratio = pixbuf.get_width() / pixbuf.get_height()
|
||||
scaled_width = 240
|
||||
scaled_height = int(scaled_width / aspect_ratio)
|
||||
@ -79,7 +93,7 @@ class App(Gtk.Window):
|
||||
self.include_subfolders_checkbox = Gtk.ToggleButton(label=self.txt.msg_subfolders)
|
||||
self.include_subfolders_checkbox.set_active(self.cf.include_subfolders)
|
||||
self.include_subfolders_checkbox.connect("toggled", self.on_include_subfolders_toggled)
|
||||
self.include_subfolders_checkbox.set_tooltip_text(TIP_SUBFOLDER)
|
||||
self.include_subfolders_checkbox.set_tooltip_text(self.txt.tip_subfolder)
|
||||
|
||||
# Create a backend dropdown menu:
|
||||
self.backend_option_combo = Gtk.ComboBoxText()
|
||||
@ -95,7 +109,7 @@ class App(Gtk.Window):
|
||||
active_num = 0
|
||||
self.backend_option_combo.set_active(active_num)
|
||||
self.backend_option_combo.connect("changed", self.on_backend_option_changed)
|
||||
self.backend_option_combo.set_tooltip_text(TIP_BACKEND)
|
||||
self.backend_option_combo.set_tooltip_text(self.txt.tip_backend)
|
||||
|
||||
# Create a fill option dropdown menu:
|
||||
self.fill_option_combo = Gtk.ComboBoxText()
|
||||
@ -104,7 +118,7 @@ class App(Gtk.Window):
|
||||
self.fill_option_combo.append_text(capitalized_option)
|
||||
self.fill_option_combo.set_active(0)
|
||||
self.fill_option_combo.connect("changed", self.on_fill_option_changed)
|
||||
self.fill_option_combo.set_tooltip_text(TIP_FILL)
|
||||
self.fill_option_combo.set_tooltip_text(self.txt.tip_fill)
|
||||
|
||||
# Create a color picker:
|
||||
self.color_picker_button = Gtk.ColorButton()
|
||||
@ -113,7 +127,7 @@ class App(Gtk.Window):
|
||||
rgba_color.parse(self.cf.color)
|
||||
self.color_picker_button.set_rgba(rgba_color)
|
||||
self.color_picker_button.connect("color-set", self.on_color_set)
|
||||
self.color_picker_button.set_tooltip_text(TIP_COLOR)
|
||||
self.color_picker_button.set_tooltip_text(self.txt.tip_color)
|
||||
|
||||
# Create a sort option dropdown menu:
|
||||
self.sort_option_combo = Gtk.ComboBoxText()
|
||||
@ -122,23 +136,23 @@ class App(Gtk.Window):
|
||||
active_num = SORT_OPTIONS.index(self.cf.sort_option)
|
||||
self.sort_option_combo.set_active(active_num)
|
||||
self.sort_option_combo.connect("changed", self.on_sort_option_changed)
|
||||
self.sort_option_combo.set_tooltip_text(TIP_SORTING)
|
||||
self.sort_option_combo.set_tooltip_text(self.txt.tip_sorting)
|
||||
|
||||
|
||||
# Create exit button:
|
||||
self.exit_button = Gtk.Button(label=self.txt.msg_exit)
|
||||
self.exit_button.connect("clicked", self.on_exit_clicked)
|
||||
self.exit_button.set_tooltip_text(TIP_EXIT)
|
||||
self.exit_button.set_tooltip_text(self.txt.tip_exit)
|
||||
|
||||
# Create refresh button:
|
||||
self.refresh_button = Gtk.Button(label=self.txt.msg_refresh)
|
||||
self.refresh_button.connect("clicked", self.on_refresh_clicked)
|
||||
self.refresh_button.set_tooltip_text(TIP_REFRESH)
|
||||
self.refresh_button.set_tooltip_text(self.txt.tip_refresh)
|
||||
|
||||
# Create random button:
|
||||
self.random_button = Gtk.Button(label=self.txt.msg_random)
|
||||
self.random_button.connect("clicked", self.on_random_clicked)
|
||||
self.random_button.set_tooltip_text(TIP_RANDOM)
|
||||
self.random_button.set_tooltip_text(self.txt.tip_random)
|
||||
|
||||
# Create a box to contain the bottom row of buttons with margin:
|
||||
self.bottom_button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=20)
|
||||
@ -199,7 +213,7 @@ class App(Gtk.Window):
|
||||
self.monitor_option_combo.append_text(monitor)
|
||||
self.monitor_option_combo.set_active(0)
|
||||
self.monitor_option_combo.connect("changed", self.on_monitor_option_changed)
|
||||
self.monitor_option_combo.set_tooltip_text(TIP_DISPLAY)
|
||||
self.monitor_option_combo.set_tooltip_text(self.txt.tip_display)
|
||||
|
||||
# Add it to the row of buttons:
|
||||
self.options_box.pack_start(self.monitor_option_combo, False, False, 0)
|
||||
@ -250,7 +264,7 @@ class App(Gtk.Window):
|
||||
def process_images(self):
|
||||
"""Load images from the selected folder, resize them, and arrange into a grid"""
|
||||
|
||||
self.image_paths = get_image_paths(self.cf.image_folder, self.cf.include_subfolders, depth=1)
|
||||
self.image_paths = get_image_paths(self.cf.backend, self.cf.image_folder, self.cf.include_subfolders, depth=1)
|
||||
self.sort_images()
|
||||
|
||||
# Show caching label:
|
||||
@ -346,22 +360,23 @@ class App(Gtk.Window):
|
||||
def on_choose_folder_clicked(self, widget):
|
||||
"""Choosing the folder of images, saving the path, and reloading images"""
|
||||
self.choose_folder()
|
||||
self.cf.save()
|
||||
|
||||
|
||||
def on_include_subfolders_toggled(self, toggle):
|
||||
"""On chosing to include subfolders"""
|
||||
cf.include_subfolders = toggle.get_active()
|
||||
self.cf.include_subfolders = toggle.get_active()
|
||||
threading.Thread(target=self.process_images).start()
|
||||
|
||||
|
||||
def on_fill_option_changed(self, combo):
|
||||
"""Save fill parameter when it was changed"""
|
||||
cf.fill_option = combo.get_active_text()
|
||||
self.cf.fill_option = combo.get_active_text()
|
||||
|
||||
|
||||
def on_monitor_option_changed(self, combo):
|
||||
"""Save monitor parameter when it was changed"""
|
||||
cf.selected_monitor = combo.get_active_text()
|
||||
self.cf.selected_monitor = combo.get_active_text()
|
||||
|
||||
|
||||
def on_sort_option_changed(self, combo):
|
||||
|
@ -3,15 +3,16 @@
|
||||
import os
|
||||
import random
|
||||
|
||||
from waypaper.options import IMAGE_EXTENSIONS
|
||||
|
||||
def has_image_extension(file_path):
|
||||
def has_image_extension(file_path, backend):
|
||||
"""Check if the file has image extension"""
|
||||
image_extensions = ['.gif', '.jpg', '.jpeg', '.png']
|
||||
image_extensions = IMAGE_EXTENSIONS[backend]
|
||||
ext = os.path.splitext(file_path)[1].lower()
|
||||
return ext in image_extensions
|
||||
|
||||
|
||||
def get_image_paths(root_folder, include_subfolders=False, depth=None):
|
||||
def get_image_paths(backend, root_folder, include_subfolders=False, depth=None):
|
||||
"""Get a list of file paths depending of weather we include subfolders and how deep we scan"""
|
||||
image_paths = []
|
||||
for root, directories, files in os.walk(root_folder):
|
||||
@ -22,7 +23,7 @@ def get_image_paths(root_folder, include_subfolders=False, depth=None):
|
||||
if current_depth > depth:
|
||||
continue
|
||||
for filename in files:
|
||||
if has_image_extension(filename):
|
||||
if has_image_extension(filename, backend):
|
||||
image_paths.append(os.path.join(root, filename))
|
||||
return image_paths
|
||||
|
||||
|
@ -13,7 +13,7 @@ class Config:
|
||||
"""User configuration loaded from the config.ini file"""
|
||||
def __init__(self):
|
||||
self.image_folder = user_pictures_path()
|
||||
self.selected_wallpaper = None
|
||||
self.selected_wallpaper = ""
|
||||
self.selected_monitor = "All"
|
||||
self.fill_option = "fill"
|
||||
self.sort_option = "name"
|
||||
@ -21,39 +21,19 @@ class Config:
|
||||
self.color = "#ffffff"
|
||||
self.lang = "en"
|
||||
self.monitors = [self.selected_monitor]
|
||||
self.wallpaper = str()
|
||||
self.wallpaper = []
|
||||
self.include_subfolders = False
|
||||
self.aboutData = AboutData()
|
||||
self.cachePath = user_cache_path(self.aboutData.applicationName())
|
||||
self.configPath = user_config_path(self.aboutData.applicationName())
|
||||
self.config_file = self.configPath / "config.ini"
|
||||
|
||||
# Create all directories we use here
|
||||
# Create config folder:
|
||||
# Create config and cache folders:
|
||||
self.configPath.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Create cache folder:
|
||||
self.cachePath.mkdir(parents=True,exist_ok=True)
|
||||
|
||||
self.read()
|
||||
|
||||
def create(self):
|
||||
"""Create a default config.ini file if it does not exist"""
|
||||
config = configparser.ConfigParser()
|
||||
config["Settings"] = {
|
||||
"folder": str(self.image_folder),
|
||||
"fill": str(self.fill_option),
|
||||
"sort": str(self.sort_option),
|
||||
"backend": str(self.backend),
|
||||
"color": str(self.color),
|
||||
"language": str(self.lang),
|
||||
"subfolders": str(self.include_subfolders),
|
||||
"wallpaper": str(self.selected_wallpaper),
|
||||
"monitors": str(self.selected_monitor),
|
||||
}
|
||||
with open(self.config_file, "w") as configfile:
|
||||
config.write(configfile)
|
||||
|
||||
|
||||
def read(self):
|
||||
"""Load data from the config.ini or use default if it does not exists"""
|
||||
@ -75,10 +55,11 @@ class Config:
|
||||
if self.monitors_str is not None:
|
||||
self.monitors = [str(monitor) for monitor in self.monitors_str.split(",")]
|
||||
|
||||
self.wallpaper_str = config.get("Settings", "wallpaper", fallback=self.wallpaper, raw=True)
|
||||
self.wallpaper_str = config.get("Settings", "wallpaper", fallback="", raw=True)
|
||||
if self.wallpaper_str is not None:
|
||||
self.wallpaper = [str(paper) for paper in self.wallpaper_str.split(",")]
|
||||
|
||||
|
||||
def save(self):
|
||||
"""Update the parameters and save them to the configuration file"""
|
||||
|
||||
|
@ -6,3 +6,11 @@ SORT_DISPLAYS = {
|
||||
"namerev": "Name ↑",
|
||||
"date": "Date ↓",
|
||||
"daterev": "Date ↑"}
|
||||
|
||||
IMAGE_EXTENSIONS = {
|
||||
BACKEND_OPTIONS[0]: ['.gif', '.jpg', '.jpeg', '.png'],
|
||||
BACKEND_OPTIONS[1]: ['.gif', '.jpg', '.jpeg', '.png', '.webp', '.bmp', '.pnm', '.tiff'],
|
||||
BACKEND_OPTIONS[2]: ['.gif', '.jpg', '.jpeg', '.png', '.bmp', '.pnm', '.tiff'],
|
||||
BACKEND_OPTIONS[3]: ['.gif', '.jpg', '.jpeg', '.png'],
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user