13 Commits

Author SHA1 Message Date
1c6060d8d5 Small fixes, changlelog 2024-02-15 17:34:07 +01:00
TobiZog
8da056350a Merge pull request #16 from blogdron/main
Add Russian translation
2024-02-15 17:24:25 +01:00
0ab3297054 Adding support for Login screen wallpaper 2024-02-15 17:04:20 +01:00
blogdron
6ac34770ff Add Russian translation 2024-02-15 16:46:46 +03:00
45e95d0e2b Readme 2024-02-15 13:20:59 +01:00
d2d954b25f Small fix 2024-02-12 12:17:33 +01:00
72144f76c6 Bugfixes, translation 2024-02-12 12:15:45 +01:00
755664230a Adding dutch, german and spanish (rcalixte) translation 2024-02-11 19:34:58 +01:00
aae46e4b1e Readme 2024-02-04 16:11:31 +01:00
14497e21f2 Changelog, Readme, dynamic background example image 2024-02-04 16:08:05 +01:00
34e44f4b3c Observer pattern, Bugfixes 2024-02-04 14:45:12 +01:00
53f5984eb5 MVVM pattern finished, adding multiple location provider 2024-02-02 19:56:14 +01:00
8acc18c7fe Start migration to MVVM 2024-02-02 15:30:33 +01:00
126 changed files with 2957 additions and 1141 deletions

1
.gitignore vendored
View File

@@ -2,6 +2,7 @@
*.glade#
*.txt
*.json
*.tar.gz
extracted_images/
selected/
__pycache__

View File

@@ -1,3 +1,18 @@
# Version 2.4
- Adding Login window support
- Adding Russian translation (Thanks to blogdron)
# Version 2.3
- Adding Dutch translation
- Adding German translation
- Adding Spanish translation (Thanks to rcalixte)
# Version 2.2
- Bugfixes
- Migrate code to MVVM pattern
- Adding option to change the location provider
- Adding example image for dynamic background color
# Version 2.1
- Bugfixes
- Smaller UI for displays with reduced resolution (< 1000px height)

View File

@@ -1,5 +1,5 @@
# Cinnamon Dynamic Wallpaper
<img src="cinnamon-dynamic-wallpaper@TobiZog/5.4/icons/icon.svg" alt="drawing" width="200" style="margin-left:auto; margin-right:auto; width:50%; display:block"/>
<img src="cinnamon-dynamic-wallpaper@TobiZog/5.4/res/icons/icon.svg" alt="drawing" width="200" style="margin-left:auto; margin-right:auto; width:50%; display:block"/>
![](res/wallpaper_merged.jpg)
@@ -11,11 +11,11 @@ This extension switches the background image of your Cinnamon desktop multiple t
- 10 day periods
- HEIF converter
- Image configuration assistent with simple one-click setup for image choices
- Online location estimation or offline with manual latitude and longitude input
- Online location estimation (three provider) or offline with manual latitude and longitude input
- Time periods individual configured by user
- Offline sun angles estimation
- Image stretching over multiple displays or repeat image for every display
- Show image on lock screen
- Creating a color gradient based on the current wallpaper for images which not fill the whole screen
### Tested Cinnamon versions
- 5.4 (Mint 21)
@@ -43,7 +43,7 @@ This extension switches the background image of your Cinnamon desktop multiple t
3. Search and download it
### From the repo
1. Download the Repository
1. Download the latest from the Releases page on GitHub: https://github.com/TobiZog/cinnamon-dynamic-wallpaper/releases
2. Extract the files
3. Copy the folder `cinnamon-dynamic-wallpaper@TobiZog` to `~/.local/share/cinnamon/extensions/`
---
@@ -59,6 +59,17 @@ Because of the lack of configuration options in the standard Cinnamon configurat
All configuration will be handled there. You can choose between included image sets, a HEIC file or a folder source and set the image to ten different daytime periods. Time periods will be estimated via network, custom coordinations or custom time periods. Some behaviour preferences (strech image, fill empty background with gradient color) are also here.
![](res/image_configurator.png)
---
## Contribute
### Translation
You want to contribute a language which isn't supported yet? Here is how to do:
1. Fork the cinnamon-spices-extensions project: https://github.com/linuxmint/cinnamon-spices-extensions
2. Pull the repository
3. Open `cinnamon-dynamic-wallpaper@TobiZog/files/cinnamon-dynamic-wallpaper@TobiZog/po/cinnamon-dynamic-wallpaper@TobiZog.pot` with a po-tool like poedit (https://poedit.net/).
4. Create a new translation in your language
5. Push the changes to your repository
6. Create a pull request
---
## Included image sets
@@ -66,12 +77,12 @@ The image sets are from https://github.com/adi1090x/dynamic-wallpaper
| Aurora | Beach | Bitday |
| ------ | ----- | ------ |
| ![](cinnamon-dynamic-wallpaper@TobiZog/5.4/images/included_image_sets/aurora/5.jpg) | ![](cinnamon-dynamic-wallpaper@TobiZog/5.4/images/included_image_sets/beach/4.jpg) | ![](cinnamon-dynamic-wallpaper@TobiZog/5.4/images/included_image_sets/bitday/4.jpg) |
| ![](cinnamon-dynamic-wallpaper@TobiZog/5.4/res/images/included_image_sets/aurora/5.jpg) | ![](cinnamon-dynamic-wallpaper@TobiZog/5.4/res/images/included_image_sets/beach/4.jpg) | ![](cinnamon-dynamic-wallpaper@TobiZog/5.4/res/images/included_image_sets/bitday/4.jpg) |
| Cliffs | Earth | Gradient |
| -------- | --------- | ------ |
| ![](cinnamon-dynamic-wallpaper@TobiZog/5.4/images/included_image_sets/cliffs/4.jpg) | ![](cinnamon-dynamic-wallpaper@TobiZog/5.4/images/included_image_sets/earth/4.jpg) | ![](cinnamon-dynamic-wallpaper@TobiZog/5.4/images/included_image_sets/gradient/4.jpg) |
| ![](cinnamon-dynamic-wallpaper@TobiZog/5.4/res/images/included_image_sets/cliffs/4.jpg) | ![](cinnamon-dynamic-wallpaper@TobiZog/5.4/res/images/included_image_sets/earth/4.jpg) | ![](cinnamon-dynamic-wallpaper@TobiZog/5.4/res/images/included_image_sets/gradient/4.jpg) |
| Lakeside | Mountains | Sahara |
| --------- | ------ | ------ |
| ![](cinnamon-dynamic-wallpaper@TobiZog/5.4/images/included_image_sets/lakeside/4.jpg) | ![](cinnamon-dynamic-wallpaper@TobiZog/5.4/images/included_image_sets/mountains/4.jpg) | ![](cinnamon-dynamic-wallpaper@TobiZog/5.4/images/included_image_sets/sahara/4.jpg) |
| ![](cinnamon-dynamic-wallpaper@TobiZog/5.4/res/images/included_image_sets/lakeside/4.jpg) | ![](cinnamon-dynamic-wallpaper@TobiZog/5.4/res/images/included_image_sets/mountains/4.jpg) | ![](cinnamon-dynamic-wallpaper@TobiZog/5.4/res/images/included_image_sets/sahara/4.jpg) |

View File

@@ -17,7 +17,8 @@ const Gio = imports.gi.Gio;
const MessageTray = imports.ui.messageTray;
const St = imports.gi.St;
const Main = imports.ui.main;
const Gettext = imports.gettext;
const GLib = imports.gi.GLib;
/******************** Constants ********************/
@@ -44,6 +45,15 @@ function CinnamonDynamicWallpaperExtension(uuid) {
}
function _(str) {
let customTranslation = Gettext.dgettext(UUID, str);
if (customTranslation !== str) {
return customTranslation;
}
return Gettext.gettext(str);
}
CinnamonDynamicWallpaperExtension.prototype = {
/******************** Lifecycle ********************/
@@ -56,16 +66,18 @@ CinnamonDynamicWallpaperExtension.prototype = {
_init: function(uuid) {
this.settings = new Settings.ExtensionSettings(this, uuid);
Gettext.bindtextdomain(UUID, GLib.get_home_dir() + '/.local/share/locale');
// Check for the first startup
if (this.settings.getValue("first_start")) {
// Welcome notification
this.showNotification("Welcome to Cinnamon Dynamic Wallpaper",
"Check the preferences to choose a dynamic wallpaper", true)
this.showNotification(_("Welcome to Cinnamon Dynamic Wallpaper"),
_("Check the preferences to choose a dynamic wallpaper"), true)
// Hide the notification on system restart
this.settings.setValue("first_start", false)
this.settings.setValue("source_folder", DIRECTORY["path"] + "/images/included_image_sets/lakeside/")
this.settings.setValue("source_folder", DIRECTORY["path"] + "/res/images/included_image_sets/lakeside/")
}
// Start the main loop, checks in fixed time periods the
@@ -96,10 +108,10 @@ CinnamonDynamicWallpaperExtension.prototype = {
_loop: function () {
if (looping) {
try {
Util.spawnCommandLine("/usr/bin/env python3 " + DIRECTORY.path + "/loop.py")
Util.spawnCommandLine("/usr/bin/env python3 " + DIRECTORY.path + "/src/main.py loop")
} catch(e) {
this.showNotification("Error!",
"Cinnamon Dynamic Wallpaper got an error while running the loop script. Please create an issue on GitHub.")
this.showNotification(_("Error!"),
_("Cinnamon Dynamic Wallpaper got an error while running the loop script. Please create an issue on GitHub."))
}
// Refresh every 60 seconds
@@ -127,8 +139,7 @@ CinnamonDynamicWallpaperExtension.prototype = {
notification.addButton("open-settings", _("Open settings"));
notification.connect("action-invoked", () =>
Util.spawnCommandLine("/usr/bin/env python3 " +
DIRECTORY.path + "/preferences.py"));
Util.spawnCommandLine("/usr/bin/env python3 " + DIRECTORY.path + "/src/main.py"));
}
// Put all together

View File

@@ -1 +1 @@
icons/icon.svg
res/icons/icon.svg

View File

@@ -1,110 +0,0 @@
#!/usr/bin/python3
from scripts.cinnamon_pref_handler import *
from scripts.suntimes import *
from datetime import datetime, time
from enums.PeriodSourceEnum import *
from scripts.location import *
from gi.repository import Gio
from PIL import Image
class Loop():
def __init__(self) -> None:
self.prefs = Cinnamon_Pref_Handler()
self.suntimes = Suntimes()
self.location = Location()
self.background_settings = Gio.Settings.new("org.cinnamon.desktop.background")
# Position should estimate by network
if self.prefs.period_source == PeriodSourceEnum.NETWORKLOCATION:
current_location = self.location.get_location()
self.suntimes.calc_suntimes(float(current_location["latitude"]), float(current_location["longitude"]))
self.start_times = self.suntimes.day_periods
# Position is given by user
elif self.prefs.period_source == PeriodSourceEnum.CUSTOMLOCATION:
self.suntimes.calc_suntimes(float(self.prefs.latitude_custom), float(self.prefs.longitude_custom))
self.start_times = self.suntimes.day_periods
# No position, concrete times
else:
def string_to_time_converter(raw_str: str) -> time:
hour = raw_str[0:raw_str.find(":")]
minute = raw_str[raw_str.find(":") + 1:]
return time(hour=int(hour), minute=int(minute))
self.start_times = [
string_to_time_converter(self.prefs.period_custom_start_time[0]),
string_to_time_converter(self.prefs.period_custom_start_time[1]),
string_to_time_converter(self.prefs.period_custom_start_time[2]),
string_to_time_converter(self.prefs.period_custom_start_time[3]),
string_to_time_converter(self.prefs.period_custom_start_time[4]),
string_to_time_converter(self.prefs.period_custom_start_time[5]),
string_to_time_converter(self.prefs.period_custom_start_time[6]),
string_to_time_converter(self.prefs.period_custom_start_time[7]),
string_to_time_converter(self.prefs.period_custom_start_time[8]),
string_to_time_converter(self.prefs.period_custom_start_time[9])
]
def exchange_image(self):
""" Replace the desktop image
"""
# Get the time of day
time_now = time(datetime.now().hour, datetime.now().minute)
# Assign the last image as fallback
self.current_image_uri = self.prefs.source_folder + self.prefs.period_images[9]
for i in range(0, 9):
# Replace the image URI, if it's not the last time period of the day
if self.start_times[i] <= time_now and time_now < self.start_times[i + 1]:
self.current_image_uri = self.prefs.source_folder + self.prefs.period_images[i]
break
# Set the background
self.background_settings['picture-uri'] = "file://" + self.current_image_uri
# Set background stretching
self.background_settings['picture-options'] = self.prefs.picture_aspect
self.set_background_gradient()
def set_background_gradient(self):
""" Setting a gradient background to hide images, which are not high enough
"""
# Load the image
try:
im = Image.open(self.current_image_uri)
pix = im.load()
# Width and height of the current setted image
width, height = im.size
# Color of the top and bottom pixel in the middle of the image
top_color = pix[width / 2,0]
bottom_color = pix[width / 2, height - 1]
# Create the gradient
self.background_settings['color-shading-type'] = "vertical"
if self.prefs.dynamic_background_color:
self.background_settings['primary-color'] = f"#{top_color[0]:x}{top_color[1]:x}{top_color[2]:x}"
self.background_settings['secondary-color'] = f"#{bottom_color[0]:x}{bottom_color[1]:x}{bottom_color[2]:x}"
else:
self.background_settings['primary-color'] = "#000000"
self.background_settings['secondary-color'] = "#000000"
except:
self.background_settings['primary-color'] = "#000000"
self.background_settings['secondary-color'] = "#000000"
# Needed for JavaScript
if __name__ == "__main__":
l = Loop()
l.exchange_image()

View File

@@ -1,642 +0,0 @@
#!/usr/bin/python3
############################################################
# Imports #
############################################################
# Packages
import gi, os, subprocess, time
from datetime import timedelta
# Local scripts
from scripts import cinnamon_pref_handler, dialogs, display, images, location, suntimes, time_bar_chart, ui
from loop import *
from enums.ImageSourceEnum import ImageSourceEnum
from enums.PeriodSourceEnum import PeriodSourceEnum
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GdkPixbuf
# Global definitions
PREFERENCES_URI = os.path.dirname(os.path.abspath(__file__))
GLADE_URI = PREFERENCES_URI + "/preferences.glade"
class Preferences:
""" Preference window class
"""
############################################################
# Lifecycle #
############################################################
def __init__(self) -> None:
# Objects from external scripts
self.prefs = cinnamon_pref_handler.Cinnamon_Pref_Handler()
self.dialogs = dialogs.Dialogs()
self.display = display.Display()
self.images = images.Images()
self.location = location.Location()
self.suntimes = suntimes.Suntimes()
self.time_bar_chart = time_bar_chart.Time_Bar_Chart()
# Glade
self.builder = Gtk.Builder()
self.builder.add_from_file(GLADE_URI)
self.builder.connect_signals(self)
self.ui = ui.UI(self.builder)
# Local Config
self.smaller_ui_height = 1000
def show(self):
""" Display the window to the screen
"""
window = self.builder.get_object("window_main")
window.show_all()
# Load from preferences
if self.prefs.image_source == ImageSourceEnum.IMAGESET:
self.ui.tb_image_set.set_active(True)
elif self.prefs.image_source == ImageSourceEnum.HEICFILE:
self.ui.tb_heic_file.set_active(True)
elif self.prefs.image_source == ImageSourceEnum.SOURCEFOLDER:
self.ui.tb_source_folder.set_active(True)
# Remove icons in the ToggleButtons if the screen height resolution < 1000 px
if self.display.get_screen_height() < self.smaller_ui_height:
self.ui.img_tb_image_set.clear()
self.ui.img_tb_heic_file.clear()
self.ui.img_tb_source_folder.clear()
self.ui.img_tb_network_location.clear()
self.ui.img_tb_custom_location.clear()
self.ui.img_tb_time_periods.clear()
picture_aspects = ["centered", "scaled", "stretched", "zoom", "spanned"]
self.ui.add_items_to_combo_box(self.ui.cb_picture_aspect, picture_aspects)
self.ui.set_active_combobox_item(self.ui.cb_picture_aspect, self.prefs.picture_aspect)
self.ui.sw_dynamic_background_color.set_active(self.prefs.dynamic_background_color)
if self.prefs.period_source == PeriodSourceEnum.NETWORKLOCATION:
self.ui.tb_network_location.set_active(True)
elif self.prefs.period_source == PeriodSourceEnum.CUSTOMLOCATION:
self.ui.tb_custom_location.set_active(True)
elif self.prefs.period_source == PeriodSourceEnum.CUSTOMTIMEPERIODS:
self.ui.tb_time_periods.set_active(True)
# Time diagram
try:
self.refresh_chart()
except:
pass
# Show the main window
Gtk.main()
def on_destroy(self, *args):
""" Lifecycle handler when window will be destroyed
"""
Gtk.main_quit()
############################################################
# Local methods #
############################################################
def refresh_chart(self):
""" Recomputes both time bar charts and load them to the UI
"""
# Stores the start times of the periods in minutes since midnight
time_periods_min = []
if self.prefs.period_source == PeriodSourceEnum.CUSTOMTIMEPERIODS:
for i in range(0, 10):
time_str = self.prefs.period_custom_start_time[i]
time_periods_min.append(int(time_str[0:2]) * 60 + int(time_str[3:5]))
else:
if self.prefs.period_source == PeriodSourceEnum.NETWORKLOCATION:
self.suntimes.calc_suntimes(float(self.prefs.latitude_auto), float(self.prefs.longitude_auto))
else:
self.suntimes.calc_suntimes(float(self.ui.etr_latitude.get_text()), float(self.ui.etr_longitude.get_text()))
# Get all time periods. Store the minutes to the list and print the values to the text views
for i in range(0, 10):
time_range_now = self.suntimes.day_periods[i]
if i != 9:
time_range_next = self.suntimes.day_periods[i + 1]
else:
time_range_next = time(hour=23, minute=59)
self.ui.etr_periods[i].set_text(
str(time_range_now.hour).rjust(2, '0') + ":" + str(time_range_now.minute).rjust(2, '0') + \
" - " + str(time_range_next.hour).rjust(2, '0') + ":" + str(time_range_next.minute).rjust(2, '0'))
time_periods_min.append(time_range_now.hour * 60 + time_range_now.minute)
# Create time bar
# Reduce size for small displays
if self.display.get_screen_height() < self.smaller_ui_height:
bar_width = 1150
bar_height = 110
else:
bar_width = 1300
bar_height = 150
self.time_bar_chart.create_bar_chart_with_polylines(PREFERENCES_URI, bar_width, bar_height, time_periods_min)
self.time_bar_chart.create_bar_chart(PREFERENCES_URI, bar_width, bar_height, time_periods_min)
# Load to the views
pixbuf = GdkPixbuf.Pixbuf.new_from_file(PREFERENCES_URI + "/time_bar_polylines.svg")
self.ui.img_bar_images.set_from_pixbuf(pixbuf)
pixbuf2 = GdkPixbuf.Pixbuf.new_from_file(PREFERENCES_URI + "/time_bar.svg")
self.ui.img_bar_times.set_from_pixbuf(pixbuf2)
def load_image_options_to_combo_boxes(self, options: list):
""" Add a list of Strings to all image option comboboxes
Args:
options (list): All possible options
"""
options.insert(0, "")
for combobox in self.ui.cb_periods:
self.ui.add_items_to_combo_box(combobox, options)
def load_image_to_preview(self, image_preview: Gtk.Image, image_src: str):
""" Scales the image to a lower resoultion and put them into the time bar chart
Args:
image_preview (Gtk.Image): Gtk Image where it will be displayed
image_src (str): Absolute path to the image
"""
try:
pixbuf = GdkPixbuf.Pixbuf.new_from_file(image_src)
screen_height = self.display.get_screen_height()
# Scaling the images smaller for screens
if screen_height < self.smaller_ui_height:
pixbuf = pixbuf.scale_simple(221, 128, GdkPixbuf.InterpType.BILINEAR)
else:
pixbuf = pixbuf.scale_simple(260, 150, GdkPixbuf.InterpType.BILINEAR)
image_preview.set_from_pixbuf(pixbuf)
except:
pass
############################################################
# Callbacks #
############################################################
## Image Configuration
# +-----------+-----------+---------------+
# | Image Set | HEIC file | Source Folder |
# +-----------+-----------+---------------+
def on_toggle_button_image_set_clicked(self, button: Gtk.ToggleButton):
""" Clicked on ToggleButton "Image Set"
Args:
button (Gtk.ToggleButton): Clicked ToggleButton
"""
if button.get_active():
self.prefs.image_source = ImageSourceEnum.IMAGESET
self.ui.tb_heic_file.set_active(False)
self.ui.tb_source_folder.set_active(False)
self.ui.lbr_image_set.set_visible(True)
self.ui.lbr_heic_file.set_visible(False)
self.ui.lbr_source_folder.set_visible(False)
image_set_choices = [
"aurora", "beach",
"bitday", "cliffs",
"earth", "gradient",
"lakeside", "mountains",
"sahara"
]
self.ui.add_items_to_combo_box(self.ui.cb_image_set, image_set_choices)
self.ui.set_active_combobox_item(self.ui.cb_image_set, self.prefs.selected_image_set)
for i, combobox in enumerate(self.ui.cb_periods):
selected_image_name = self.prefs.period_images[i]
self.ui.set_active_combobox_item(combobox, selected_image_name)
# Make the comboboxes invisible
for combobox in self.ui.cb_periods:
combobox.set_visible(False)
def on_toggle_button_heic_file_clicked(self, button: Gtk.ToggleButton):
""" Clicked on ToggleButton "Heic file"
Args:
button (Gtk.ToggleButton): Clicked ToggleButton
"""
if button.get_active():
self.prefs.image_source = ImageSourceEnum.HEICFILE
self.ui.tb_image_set.set_active(False)
self.ui.tb_source_folder.set_active(False)
self.ui.lbr_image_set.set_visible(False)
self.ui.lbr_heic_file.set_visible(True)
self.ui.lbr_source_folder.set_visible(False)
# Make the comboboxes visible
for combobox in self.ui.cb_periods:
combobox.set_visible(True)
# Load images from source folder
files = self.images.get_images_from_folder(self.prefs.source_folder)
if len(files) != 0:
self.load_image_options_to_combo_boxes(files)
# Load the values for the images from the preferences
for i in range(0, 10):
self.ui.set_active_combobox_item(self.ui.cb_periods[i], self.prefs.period_images[i])
else:
print("No image files!")
def on_toggle_button_source_folder_clicked(self, button: Gtk.ToggleButton):
""" Clicked on ToggleButton "Source Folder"
Args:
button (Gtk.ToggleButton): Clicked ToggleButton
"""
if button.get_active():
self.prefs.image_source = ImageSourceEnum.SOURCEFOLDER
self.ui.tb_image_set.set_active(False)
self.ui.tb_heic_file.set_active(False)
self.ui.lbr_image_set.set_visible(False)
self.ui.lbr_heic_file.set_visible(False)
self.ui.lbr_source_folder.set_visible(True)
# Make the comboboxes visible
for combobox in self.ui.cb_periods:
combobox.set_visible(True)
# Load the source folder to the view
# This will update the comboboxes in the preview to contain the right items
self.ui.lbl_source_folder.set_label(self.prefs.source_folder)
# Load files from saved source folder
files = self.images.get_images_from_folder(self.prefs.source_folder)
if len(files) != 0:
self.load_image_options_to_combo_boxes(files)
# Load the values for the images from the preferences
for i in range(0, 10):
self.ui.set_active_combobox_item(self.ui.cb_periods[i], self.prefs.period_images[i])
else:
print("No image files!")
# +------------------------------------+
# | Select an image set | aurora ▼ |
# +------------------------------------+
def on_cb_image_set_changed(self, combobox: Gtk.ComboBox):
""" User select on of the included image sets
Args:
combobox (Gtk.ComboBox): The used ComboBox
"""
tree_iter = combobox.get_active_iter()
if tree_iter is not None and self.prefs.image_source == ImageSourceEnum.IMAGESET:
# Get the selected value
model = combobox.get_model()
selected_image_set = model[tree_iter][0]
# Store to the preferences
self.prefs.selected_image_set = selected_image_set
self.prefs.source_folder = os.path.abspath(os.path.join(PREFERENCES_URI, os.pardir)) + \
"/5.4/images/included_image_sets/" + selected_image_set + "/"
# Load all possible options to the comboboxes
image_names = self.images.get_images_from_folder(self.prefs.source_folder)
self.load_image_options_to_combo_boxes(image_names)
# Image sets have the same names for the images:
# 9.jpg = Period 0
# 1.jpg = Period 1
# 2.jpg = Period 2
# and so on....
for i in range(0, 10):
self.ui.cb_periods[i].set_active(i + 1)
# +----------------------------------------------+
# | Select a heic file to import | (None) 📄 |
# +----------------------------------------------+
def on_fc_heic_file_file_set(self, fc_button: Gtk.FileChooser):
""" User has a heic file selected with the FileChooserDialog
Args:
fc_button (Gtk.FileChooser): Parameter about the selected file
"""
# The the absolute path to the heic file
file_path: str = fc_button.get_filename()
# Extract the heic file
result = self.images.extract_heic_file(file_path)
# Update the preferences
self.prefs.selected_image_set = ""
self.prefs.source_folder = PREFERENCES_URI + "/images/extracted_images/"
# Load images only if the extraction was successfully
if result:
# Collect all extracted images and push them to the comboboxes
image_names = self.images.get_images_from_folder(self.prefs.source_folder)
self.load_image_options_to_combo_boxes(image_names)
else:
self.dialogs.message_dialog("Error during extraction")
# +------------------------------------------------------------+
# | Select a source folder | 📂 Open file selection dialog |
# | /home/developer/Downloads/
# +------------------------------------------------------------+
def on_btn_source_folder_clicked(self, button: Gtk.Button):
""" Button to choose an image source folder was clicked
Args:
button (Gtk.Button): The clicked button
"""
folder = self.dialogs.source_folder_dialog()
files = self.images.get_images_from_folder(folder)
# Update the preferences
self.prefs.selected_image_set = ""
self.prefs.source_folder = folder + "/"
# Update the label
self.ui.lbl_source_folder.set_label(folder)
# Update the image comboboxes
self.load_image_options_to_combo_boxes(files)
# Load the values for the images from the preferences
for i in range(0, 10):
self.ui.cb_periods[i].set_active(0)
if len(files) == 1:
self.dialogs.message_dialog("No image files found!")
def on_cb_period_preview_changed(self, combobox: Gtk.ComboBox):
""" User select an image from the ComboBox for the time period
Args:
combobox (Gtk.ComboBox): The used ComboBox
"""
tree_iter = combobox.get_active_iter()
combobox_name = Gtk.Buildable.get_name(combobox)
period_index = int(combobox_name[10:11])
if tree_iter is not None:
# Get the selected value
model = combobox.get_model()
image_file_name = model[tree_iter][0]
# Store selection to preferences
self.prefs.period_images[period_index] = image_file_name
# Build up image path
image_path = self.prefs.source_folder + image_file_name
self.load_image_to_preview(self.ui.img_periods[period_index], image_path)
## Location & Times
def on_toggle_button_network_location_clicked(self, button: Gtk.ToggleButton):
""" User clicks on the ToggleButton for the network location
Args:
button (Gtk.ToggleButton): Clicked ToggleButton
"""
if button.get_active():
self.prefs.period_source = PeriodSourceEnum.NETWORKLOCATION
self.ui.tb_custom_location.set_active(False)
self.ui.tb_time_periods.set_active(False)
self.ui.lbr_network_location.set_visible(True)
self.ui.lbr_current_location.set_visible(True)
self.ui.lbr_custom_location_longitude.set_visible(False)
self.ui.lbr_custom_location_latitude.set_visible(False)
self.ui.lbr_time_periods.set_visible(False)
self.ui.spb_network_location_refresh_time.set_value(self.prefs.location_refresh_intervals)
# Display the location in the UI
current_location = self.location.get_location()
self.ui.lb_current_location.set_text("Latitude: " + current_location["latitude"] + \
", Longitude: " + current_location["longitude"])
# Store the location to the preferences
self.prefs.latitude_auto = float(current_location["latitude"])
self.prefs.longitude_auto = float(current_location["longitude"])
self.refresh_chart()
def on_toggle_button_custom_location_clicked(self, button: Gtk.ToggleButton):
if button.get_active():
self.prefs.period_source = PeriodSourceEnum.CUSTOMLOCATION
self.ui.tb_network_location.set_active(False)
self.ui.tb_time_periods.set_active(False)
self.ui.lbr_network_location.set_visible(False)
self.ui.lbr_current_location.set_visible(False)
self.ui.lbr_custom_location_longitude.set_visible(True)
self.ui.lbr_custom_location_latitude.set_visible(True)
self.ui.lbr_time_periods.set_visible(False)
self.ui.etr_latitude.set_text(str(self.prefs.latitude_custom))
self.ui.etr_longitude.set_text(str(self.prefs.longitude_custom))
def on_toggle_button_time_periods_clicked(self, button: Gtk.ToggleButton):
if button.get_active():
self.prefs.period_source = PeriodSourceEnum.CUSTOMTIMEPERIODS
self.ui.tb_network_location.set_active(False)
self.ui.tb_custom_location.set_active(False)
self.ui.lbr_network_location.set_visible(False)
self.ui.lbr_current_location.set_visible(False)
self.ui.lbr_custom_location_longitude.set_visible(False)
self.ui.lbr_custom_location_latitude.set_visible(False)
self.ui.lbr_time_periods.set_visible(True)
for i in range(0, 9):
pref_value = self.prefs.period_custom_start_time[i + 1]
time_parts = [int(pref_value[0:pref_value.find(":")]), int(pref_value[pref_value.find(":") + 1:])]
self.ui.spb_periods_hour[i].set_value(time_parts[0])
self.ui.spb_periods_minute[i].set_value(time_parts[1])
def on_spb_period_value_changed(self, spin_button: Gtk.SpinButton):
""" Callback if one of the time spinners (minute or hour) will be clicked
(1) (2) (3)
Previous period Current period Next period
12:34 - 14:40 14:41 - 16:20 16:21 - 17:30
^
Variable to change
Args:
spin_button (Gtk.SpinButton): SpinButton which was changed
"""
spin_button_name = Gtk.Buildable.get_name(spin_button)
index = int(spin_button_name[11:12]) - 1
# Determe time string and store to prefs
time_current_start = datetime(2024,1,1, int(self.ui.spb_periods_hour[index].get_value()), int(self.ui.spb_periods_minute[index].get_value()))
time_current_start_str = str(time_current_start.hour).rjust(2, '0') + ":" + str(time_current_start.minute).rjust(2, '0')
self.prefs.period_custom_start_time[index + 1] = time_current_start_str
time_previous_end = time_current_start - timedelta(minutes=1)
self.ui.lb_period_end[index].set_text(str(time_previous_end.hour).rjust(2, '0') + ":" + str(time_previous_end.minute).rjust(2, '0'))
self.refresh_chart()
def on_spb_network_location_refresh_time_changed(self, spin_button: Gtk.SpinButton):
""" User changed the refresh time of network location estimation
Args:
spin_button (Gtk.SpinButton): The used SpinButton
"""
self.prefs.location_refresh_intervals = spin_button.get_value()
def on_etr_longitude_changed(self, entry: Gtk.Entry):
""" User changes the value of the longitude Entry
Args:
entry (Gtk.Entry): The manipulated Entry object
"""
try:
self.prefs.longitude_custom = float(entry.get_text())
self.refresh_chart()
except:
pass
def on_etr_latitude_changed(self, entry: Gtk.Entry):
""" User changes the value of the latitude Entry
Args:
entry (Gtk.Entry): The manipulated Entry object
"""
try:
self.prefs.latitude_custom = float(entry.get_text())
self.refresh_chart()
except:
pass
# Behaviour
def on_cb_picture_aspect_changed(self, combobox: Gtk.ComboBox):
tree_iter = combobox.get_active_iter()
if tree_iter is not None:
model = combobox.get_model()
self.prefs.picture_aspect = model[tree_iter][0]
def on_sw_dynamic_background_color_state_set(self, switch: Gtk.Switch, state):
self.prefs.dynamic_background_color = state
# About
def on_cinnamon_spices_website_button_clicked(self, button: Gtk.Button):
""" Callback for the button to navigate to the Cinnamon Spices web page of this project
Args:
button (Gtk.Button): Button which was clicked
"""
subprocess.Popen(["xdg-open", "https://cinnamon-spices.linuxmint.com/extensions/view/97"])
def on_github_website_button_clicked(self, button: Gtk.Button):
""" Callback for the button to navigate to the GitHub web page of this project
Args:
button (Gtk.Button): Button which was clicked
"""
subprocess.Popen(["xdg-open", "https://github.com/TobiZog/cinnamon-dynamic-wallpaper"])
def on_create_issue_button_clicked(self, button):
""" Callback for the button to navigate to the Issues page on GitHub of this project
Args:
button (Gtk.Button): Button which was clicked
"""
subprocess.Popen(["xdg-open", "https://github.com/TobiZog/cinnamon-dynamic-wallpaper/issues/new"])
def on_ok(self, *args):
""" Callback for the OK button in the top bar
"""
try:
self.on_apply()
except:
pass
# Close the window
self.on_destroy()
def on_apply(self, *args):
""" Callback for the Apply button in the top bar
"""
# Store all values to the JSON file
self.prefs.store_preferences()
# Use the new settings
loop = Loop()
loop.exchange_image()
if __name__ == "__main__":
Preferences().show()

View File

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Some files were not shown because too many files have changed in this diff Show More