From 53f5984eb576add24ecb294908a4336f3c428a1a Mon Sep 17 00:00:00 2001 From: tobias Date: Fri, 2 Feb 2024 19:56:14 +0100 Subject: [PATCH] MVVM pattern finished, adding multiple location provider --- .../5.4/extension.js | 2 +- .../5.4/res/preferences.glade | 1 + .../5.4/settings-schema.json | 4 + .../5.4/src/enums/NetworkLocationProvider.py | 9 +- .../5.4/src/loop.py | 110 ------------ .../5.4/src/main.py | 17 ++ .../5.4/src/model/main_view_model.py | 136 ++++++++++++-- .../5.4/src/preferences.py | 57 ------ .../5.4/src/service/cinnamon_pref_handler.py | 166 +++++++++++------- .../5.4/src/service/location.py | 36 ++-- .../5.4/src/test.py | 6 - .../5.4/src/ui/dialogs.py | 4 +- .../5.4/src/ui/main_window.py | 164 +++++++++-------- .../metadata.json | 2 +- 14 files changed, 360 insertions(+), 354 deletions(-) delete mode 100644 cinnamon-dynamic-wallpaper@TobiZog/5.4/src/loop.py create mode 100755 cinnamon-dynamic-wallpaper@TobiZog/5.4/src/main.py delete mode 100755 cinnamon-dynamic-wallpaper@TobiZog/5.4/src/preferences.py delete mode 100755 cinnamon-dynamic-wallpaper@TobiZog/5.4/src/test.py diff --git a/cinnamon-dynamic-wallpaper@TobiZog/5.4/extension.js b/cinnamon-dynamic-wallpaper@TobiZog/5.4/extension.js index 0fd8ab6..43e6c51 100644 --- a/cinnamon-dynamic-wallpaper@TobiZog/5.4/extension.js +++ b/cinnamon-dynamic-wallpaper@TobiZog/5.4/extension.js @@ -96,7 +96,7 @@ 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.") diff --git a/cinnamon-dynamic-wallpaper@TobiZog/5.4/res/preferences.glade b/cinnamon-dynamic-wallpaper@TobiZog/5.4/res/preferences.glade index fefd337..6478fe2 100644 --- a/cinnamon-dynamic-wallpaper@TobiZog/5.4/res/preferences.glade +++ b/cinnamon-dynamic-wallpaper@TobiZog/5.4/res/preferences.glade @@ -1221,6 +1221,7 @@ True False + False diff --git a/cinnamon-dynamic-wallpaper@TobiZog/5.4/settings-schema.json b/cinnamon-dynamic-wallpaper@TobiZog/5.4/settings-schema.json index 94b2cf0..61fa3b5 100644 --- a/cinnamon-dynamic-wallpaper@TobiZog/5.4/settings-schema.json +++ b/cinnamon-dynamic-wallpaper@TobiZog/5.4/settings-schema.json @@ -71,6 +71,10 @@ "type": "generic", "default": 15 }, + "network_location_provider": { + "type": "generic", + "default": "geojs.io" + }, "latitude_auto": { "type": "generic", "default": 0 diff --git a/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/enums/NetworkLocationProvider.py b/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/enums/NetworkLocationProvider.py index 02d4486..e1de45c 100644 --- a/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/enums/NetworkLocationProvider.py +++ b/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/enums/NetworkLocationProvider.py @@ -1,5 +1,4 @@ -from enum import Enum - -class NetworkLocationProvider(Enum): - GEOJS = "https://get.geojs.io/v1/ip/geo.json" - IPAPI = "http://ip-api.com/json/?fields=61439" \ No newline at end of file +class NetworkLocationProvider(enumerate): + GEOJS = "geojs.io" + IPAPI = "ip-api.com" + IPWHOIS = "ipwho.is" diff --git a/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/loop.py b/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/loop.py deleted file mode 100644 index 916e6e4..0000000 --- a/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/loop.py +++ /dev/null @@ -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() diff --git a/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/main.py b/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/main.py new file mode 100755 index 0000000..5b5175a --- /dev/null +++ b/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/main.py @@ -0,0 +1,17 @@ +#!/usr/bin/python3 + +import sys +from ui.main_window import * +from model.main_view_model import * + +if __name__ == "__main__": + if len(sys.argv) == 1: + # Load the configuration window + main = Main_Window() + main.show() + + elif sys.argv[1] == "loop": + # Run the methods which updates the data + view_model = Main_View_Model() + view_model.refresh_image() + view_model.set_background_gradient() diff --git a/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/model/main_view_model.py b/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/model/main_view_model.py index a8fd7f1..e962098 100644 --- a/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/model/main_view_model.py +++ b/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/model/main_view_model.py @@ -1,10 +1,12 @@ import os, json -from pathlib import Path +from PIL import Image +from gi.repository import Gio from service.display import * from service.cinnamon_pref_handler import * from service.suntimes import * from service.time_bar_chart import * +from service.location import * from enums.PeriodSourceEnum import * class Main_View_Model: @@ -22,13 +24,16 @@ class Main_View_Model: # Datasets self.image_sets = ["aurora", "beach", "bitday", "cliffs", "earth", "gradient", "lakeside", "mountains", "sahara"] self.picture_aspects = ["centered", "scaled", "stretched", "zoom", "spanned"] - self.network_location_provider = ["geojs.io", "ip-api.com"] + self.network_location_provider = ["geojs.io", "ip-api.com", "ipwho.is"] # Objects from scripts self.screen_height = Display().get_screen_height() self.cinnamon_prefs = Cinnamon_Pref_Handler() self.time_bar_chart = Time_Bar_Chart() self.suntimes = Suntimes() + self.location = Location() + + self.background_settings = Gio.Settings.new("org.cinnamon.desktop.background") # Breakpoint for smaller UI self.breakpoint_ui = 1000 @@ -47,25 +52,15 @@ class Main_View_Model: if self.cinnamon_prefs.period_source == PeriodSourceEnum.NETWORKLOCATION: self.suntimes.calc_suntimes(float(self.cinnamon_prefs.latitude_auto), float(self.cinnamon_prefs.longitude_auto)) else: - pass - # todo self.suntimes.calc_suntimes(float(self.ui.etr_latitude.get_text()), float(self.ui.etr_longitude.get_text())) + self.suntimes.calc_suntimes(float(self.cinnamon_prefs.latitude_custom), float(self.cinnamon_prefs.longitude_custom)) # 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) - - # todo 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.screen_height < self.breakpoint_ui: @@ -78,4 +73,115 @@ class Main_View_Model: self.time_bar_chart.create_bar_chart_with_polylines(self.TIMEBAR_URI_POLYLINES, bar_width, bar_height, time_periods_min) self.time_bar_chart.create_bar_chart(self.TIMEBAR_URI, bar_width, bar_height, time_periods_min) - + + def refresh_location(self) -> bool: + """ Updating the location by IP, store the result to cinnamon_prefs + + Returns: + bool: Successful or not + """ + current_location = self.location.get_location(self.cinnamon_prefs.network_location_provider) + + if current_location['success']: + self.cinnamon_prefs.latitude_auto = current_location['latitude'] + self.cinnamon_prefs.longitude_auto = current_location['longitude'] + + return current_location['success'] + + + def string_to_time_converter(raw_str: str) -> time: + """ Convert a time string like "12:34" to a time object + + Args: + raw_str (str): Raw string + + Returns: + time: Time object + """ + hour = raw_str[0:raw_str.find(":")] + minute = raw_str[raw_str.find(":") + 1:] + + return time(hour=int(hour), minute=int(minute)) + + + def calulate_time_periods(self) -> list: + if self.cinnamon_prefs.period_source == PeriodSourceEnum.CUSTOMTIMEPERIODS: + # User uses custom time periods + return [ + self.string_to_time_converter(self.cinnamon_prefs.period_custom_start_time[0]), + self.string_to_time_converter(self.cinnamon_prefs.period_custom_start_time[1]), + self.string_to_time_converter(self.cinnamon_prefs.period_custom_start_time[2]), + self.string_to_time_converter(self.cinnamon_prefs.period_custom_start_time[3]), + self.string_to_time_converter(self.cinnamon_prefs.period_custom_start_time[4]), + self.string_to_time_converter(self.cinnamon_prefs.period_custom_start_time[5]), + self.string_to_time_converter(self.cinnamon_prefs.period_custom_start_time[6]), + self.string_to_time_converter(self.cinnamon_prefs.period_custom_start_time[7]), + self.string_to_time_converter(self.cinnamon_prefs.period_custom_start_time[8]), + self.string_to_time_converter(self.cinnamon_prefs.period_custom_start_time[9]) + ] + else: + # Time periods have to be estimate by coordinates + if self.cinnamon_prefs.period_source == PeriodSourceEnum.NETWORKLOCATION: + # Get coordinates from the network + self.refresh_location() + self.suntimes.calc_suntimes(self.cinnamon_prefs.latitude_auto, self.cinnamon_prefs.longitude_auto) + + elif self.cinnamon_prefs.period_source == PeriodSourceEnum.CUSTOMLOCATION: + # Get coordinates from user input + self.suntimes.calc_suntimes(self.cinnamon_prefs.latitude_custom, self.cinnamon_prefs.longitude_custom) + + # Return the time periods + return self.suntimes.day_periods + + + def refresh_image(self): + """ Replace the desktop image if needed + """ + start_times = self.calulate_time_periods() + + # 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.cinnamon_prefs.source_folder + self.cinnamon_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 start_times[i] <= time_now and time_now < start_times[i + 1]: + self.current_image_uri = self.cinnamon_prefs.source_folder + self.cinnamon_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.cinnamon_prefs.picture_aspect + + + 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.cinnamon_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" diff --git a/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/preferences.py b/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/preferences.py deleted file mode 100755 index da0326d..0000000 --- a/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/preferences.py +++ /dev/null @@ -1,57 +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 -from enums.NetworkLocationProvider import NetworkLocationProvider - -gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, GdkPixbuf - - - - - - -class Preferences: - """ Preference window class - """ - - ############################################################ - # Lifecycle # - ############################################################ - - def __init__(self) -> None: - # UI helper object - self.ui = ui.UI(self.builder) - - # Layout breakpoint for smaller displays - self.smaller_ui_height = 1000 - - - def show(self): - """ Display the window to the screen - """ - self.ui.show_main_window(self.builder, self.smaller_ui_height, self.prefs) - - # Time diagram - try: - self.refresh_chart() - except: - self.dialogs.message_dialog("Error on creating time bar!") - - - - -if __name__ == "__main__": - Preferences().show() \ No newline at end of file diff --git a/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/service/cinnamon_pref_handler.py b/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/service/cinnamon_pref_handler.py index 37794dc..5162ec3 100644 --- a/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/service/cinnamon_pref_handler.py +++ b/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/service/cinnamon_pref_handler.py @@ -10,91 +10,123 @@ class Cinnamon_Pref_Handler: self.load_preferences() + + def extract_json(self, parameter: str) -> str: + """ Get a parameter from the json dictionary safely + + Args: + parameter (str): Parameter to request + + Returns: + str: Value of the parameter (or "" if not existing) + """ + try: + return self.pref_data[parameter]['value'] + except: + return "" + def load_preferences(self): """ Load the JSON preferences to the Preference object """ with open(self.pref_location, "r") as pref_file: - pref_data = json.load(pref_file) + self.pref_data = json.load(pref_file) - self.picture_aspect = pref_data['picture_aspect']['value'] - self.dynamic_background_color = pref_data['dynamic_background_color']['value'] - self.image_source = pref_data['image_source']['value'] - self.selected_image_set = pref_data['selected_image_set']['value'] - self.source_folder = pref_data['source_folder']['value'] + self.picture_aspect = self.extract_json('picture_aspect') + self.dynamic_background_color = self.extract_json('dynamic_background_color') + self.image_source = self.extract_json('image_source') + self.selected_image_set = self.extract_json('selected_image_set') + self.source_folder = self.extract_json('source_folder') self.period_images = [ - pref_data['period_0_image']['value'], - pref_data['period_1_image']['value'], - pref_data['period_2_image']['value'], - pref_data['period_3_image']['value'], - pref_data['period_4_image']['value'], - pref_data['period_5_image']['value'], - pref_data['period_6_image']['value'], - pref_data['period_7_image']['value'], - pref_data['period_8_image']['value'], - pref_data['period_9_image']['value'] + self.extract_json('period_0_image'), + self.extract_json('period_1_image'), + self.extract_json('period_2_image'), + self.extract_json('period_3_image'), + self.extract_json('period_4_image'), + self.extract_json('period_5_image'), + self.extract_json('period_6_image'), + self.extract_json('period_7_image'), + self.extract_json('period_8_image'), + self.extract_json('period_9_image') ] - self.period_source = pref_data['period_source']['value'] - self.location_refresh_intervals = pref_data['location_refresh_intervals']['value'] - self.latitude_auto = pref_data['latitude_auto']['value'] - self.longitude_auto = pref_data['longitude_auto']['value'] - self.latitude_custom = pref_data['latitude_custom']['value'] - self.longitude_custom = pref_data['longitude_custom']['value'] + self.period_source =self.extract_json('period_source') + self.location_refresh_intervals =self.extract_json('location_refresh_intervals') + self.network_location_provider =self.extract_json('network_location_provider') + self.latitude_auto =self.extract_json('latitude_auto') + self.longitude_auto =self.extract_json('longitude_auto') + self.latitude_custom =self.extract_json('latitude_custom') + self.longitude_custom =self.extract_json('longitude_custom') self.period_custom_start_time = [ - pref_data['period_0_custom_start_time']['value'], - pref_data['period_1_custom_start_time']['value'], - pref_data['period_2_custom_start_time']['value'], - pref_data['period_3_custom_start_time']['value'], - pref_data['period_4_custom_start_time']['value'], - pref_data['period_5_custom_start_time']['value'], - pref_data['period_6_custom_start_time']['value'], - pref_data['period_7_custom_start_time']['value'], - pref_data['period_8_custom_start_time']['value'], - pref_data['period_9_custom_start_time']['value'] - ] + self.extract_json('period_0_custom_start_time'), + self.extract_json('period_1_custom_start_time'), + self.extract_json('period_2_custom_start_time'), + self.extract_json('period_3_custom_start_time'), + self.extract_json('period_4_custom_start_time'), + self.extract_json('period_5_custom_start_time'), + self.extract_json('period_6_custom_start_time'), + self.extract_json('period_7_custom_start_time'), + self.extract_json('period_8_custom_start_time'), + self.extract_json('period_9_custom_start_time') + ] + + + def value_to_json(self, parameter: str, value: str): + """ Storing safely a value to the dictionary + + Args: + parameter (str): Parameter to write + value (str): Value to write + """ + try: + self.pref_data[parameter]['value'] = value + except: + self.pref_data[parameter] = { + 'type': 'generic', + 'value': value + } + print(self.pref_data) def store_preferences(self): """ Store the values of the Preference object to the JSON file """ - with open(self.pref_location, "r") as pref_file: - pref_data = json.load(pref_file) + self.value_to_json('picture_aspect', self.picture_aspect) + self.value_to_json('dynamic_background_color', self.dynamic_background_color) + self.value_to_json('image_source', self.image_source) + self.value_to_json('selected_image_set', self.selected_image_set) + self.value_to_json('source_folder', self.source_folder) + self.value_to_json('period_0_image', self.period_images[0]) + self.value_to_json('period_1_image', self.period_images[1]) + self.value_to_json('period_2_image', self.period_images[2]) + self.value_to_json('period_3_image', self.period_images[3]) + self.value_to_json('period_4_image', self.period_images[4]) + self.value_to_json('period_5_image', self.period_images[5]) + self.value_to_json('period_6_image', self.period_images[6]) + self.value_to_json('period_7_image', self.period_images[7]) + self.value_to_json('period_8_image', self.period_images[8]) + self.value_to_json('period_9_image', self.period_images[9]) + self.value_to_json('period_source', self.period_source) + self.value_to_json('location_refresh_intervals', self.location_refresh_intervals) + self.value_to_json('network_location_provider', self.network_location_provider) + self.value_to_json('latitude_auto', self.latitude_auto) + self.value_to_json('longitude_auto', self.longitude_auto) + self.value_to_json('latitude_custom', self.latitude_custom) + self.value_to_json('longitude_custom', self.longitude_custom) + self.value_to_json('period_0_custom_start_time', self.period_custom_start_time[0]) + self.value_to_json('period_1_custom_start_time', self.period_custom_start_time[1]) + self.value_to_json('period_2_custom_start_time', self.period_custom_start_time[2]) + self.value_to_json('period_3_custom_start_time', self.period_custom_start_time[3]) + self.value_to_json('period_4_custom_start_time', self.period_custom_start_time[4]) + self.value_to_json('period_5_custom_start_time', self.period_custom_start_time[5]) + self.value_to_json('period_6_custom_start_time', self.period_custom_start_time[6]) + self.value_to_json('period_7_custom_start_time', self.period_custom_start_time[7]) + self.value_to_json('period_8_custom_start_time', self.period_custom_start_time[8]) + self.value_to_json('period_9_custom_start_time', self.period_custom_start_time[9]) - pref_data['picture_aspect']['value'] = self.picture_aspect - pref_data['dynamic_background_color']['value'] = self.dynamic_background_color - pref_data['image_source']['value'] = self.image_source - pref_data['selected_image_set']['value'] = self.selected_image_set - pref_data['source_folder']['value'] = self.source_folder - pref_data['period_0_image']['value'] = self.period_images[0] - pref_data['period_1_image']['value'] = self.period_images[1] - pref_data['period_2_image']['value'] = self.period_images[2] - pref_data['period_3_image']['value'] = self.period_images[3] - pref_data['period_4_image']['value'] = self.period_images[4] - pref_data['period_5_image']['value'] = self.period_images[5] - pref_data['period_6_image']['value'] = self.period_images[6] - pref_data['period_7_image']['value'] = self.period_images[7] - pref_data['period_8_image']['value'] = self.period_images[8] - pref_data['period_9_image']['value'] = self.period_images[9] - pref_data['period_source']['value'] = self.period_source - pref_data['location_refresh_intervals']['value'] = self.location_refresh_intervals - pref_data['latitude_auto']['value'] = self.latitude_auto - pref_data['longitude_auto']['value'] = self.longitude_auto - pref_data['latitude_custom']['value'] = self.latitude_custom - pref_data['longitude_custom']['value'] = self.longitude_custom - pref_data['period_0_custom_start_time']['value'] = self.period_custom_start_time[0] - pref_data['period_1_custom_start_time']['value'] = self.period_custom_start_time[1] - pref_data['period_2_custom_start_time']['value'] = self.period_custom_start_time[2] - pref_data['period_3_custom_start_time']['value'] = self.period_custom_start_time[3] - pref_data['period_4_custom_start_time']['value'] = self.period_custom_start_time[4] - pref_data['period_5_custom_start_time']['value'] = self.period_custom_start_time[5] - pref_data['period_6_custom_start_time']['value'] = self.period_custom_start_time[6] - pref_data['period_7_custom_start_time']['value'] = self.period_custom_start_time[7] - pref_data['period_8_custom_start_time']['value'] = self.period_custom_start_time[8] - pref_data['period_9_custom_start_time']['value'] = self.period_custom_start_time[9] # Write to file with open(self.pref_location, "w") as pref_file: - json.dump(pref_data, pref_file, separators=(',', ':'), indent=4) + json.dump(self.pref_data, pref_file, separators=(',', ':'), indent=4) diff --git a/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/service/location.py b/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/service/location.py index 0794971..40831a1 100644 --- a/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/service/location.py +++ b/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/service/location.py @@ -4,26 +4,36 @@ from enums.NetworkLocationProvider import NetworkLocationProvider class Location(): """ Class to handle location requests """ - def __init__(self): - pass - def get_location(self, provider: NetworkLocationProvider) -> dict: """ Request the location via network Returns: dict: latitude and longitude """ - request = urllib.request.urlopen(provider.value) - - data = json.load(request) - if provider == NetworkLocationProvider.GEOJS: - return { - "latitude": data["latitude"], - "longitude": data["longitude"] - } + url = "http://get.geojs.io/v1/ip/geo.json" elif provider == NetworkLocationProvider.IPAPI: + url = "http://ip-api.com/json/?fields=61439" + elif provider == NetworkLocationProvider.IPWHOIS: + url = "http://ipwho.is" + + try: + request = urllib.request.urlopen(url) + data = json.load(request) + + if provider == NetworkLocationProvider.GEOJS or provider == NetworkLocationProvider.IPWHOIS: + param_lat = "latitude" + param_lon = "longitude" + else: + param_lat = "lat" + param_lon = "lon" + return { - "latitude": data["lat"], - "longitude": data["lon"] + "latitude": float(data[param_lat]), + "longitude": float(data[param_lon]), + "success": True + } + except: + return { + "success": False } diff --git a/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/test.py b/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/test.py deleted file mode 100755 index dcbe5d1..0000000 --- a/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/test.py +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/python3 - -import ui.main_window - -main = ui.main_window.Main_Window() -main.show() diff --git a/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/ui/dialogs.py b/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/ui/dialogs.py index 75c020b..c0a8cd8 100644 --- a/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/ui/dialogs.py +++ b/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/ui/dialogs.py @@ -43,7 +43,7 @@ class Dialogs(Gtk.Window): return location - def message_dialog(self, message: str): + def message_dialog(self, message: str, type: Gtk.MessageType = Gtk.MessageType.INFO): """ Displaying a Gtk Message dialog to the user Args: @@ -52,7 +52,7 @@ class Dialogs(Gtk.Window): dialog = Gtk.MessageDialog( transient_for=self, flags=0, - message_type=Gtk.MessageType.INFO, + message_type=type, buttons=Gtk.ButtonsType.OK, text=message ) diff --git a/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/ui/main_window.py b/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/ui/main_window.py index 42e1db9..16f3b13 100644 --- a/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/ui/main_window.py +++ b/cinnamon-dynamic-wallpaper@TobiZog/5.4/src/ui/main_window.py @@ -14,7 +14,6 @@ from datetime import timedelta # Local scripts from model.main_view_model import * from service.images import * -from service.location import * from service.suntimes import * from service.time_bar_chart import * from ui.dialogs import * @@ -46,7 +45,6 @@ class Main_Window: # Objects from scripts self.images = Images() self.dialogs = Dialogs() - self.location = Location() self.suntimes = Suntimes() self.time_bar_chart = Time_Bar_Chart() @@ -160,6 +158,8 @@ class Main_Window: def show(self): + """ Display the window to the screen + """ self.builder.get_object("window_main").show_all() # Smaller UI handling @@ -265,9 +265,12 @@ class Main_Window: image_preview.set_from_pixbuf(pixbuf) except: - pass + self.dialogs.message_dialog("Error on load images. Please check the configuration!", Gtk.MessageType.ERROR) + def refresh_charts(self): + """ Refresh the charts and put them to the image views + """ self.view_model.refresh_charts() # Load to the views @@ -285,7 +288,21 @@ class Main_Window: ############################################################ ## Image Configuration - + + def show_image_configuration_entries(self, button_id: int): + self.tb_image_set.set_active(button_id == 1) + self.tb_heic_file.set_active(button_id == 2) + self.tb_source_folder.set_active(button_id == 3) + + self.lbr_image_set.set_visible(button_id == 1) + self.lbr_heic_file.set_visible(button_id == 2) + self.lbr_source_folder.set_visible(button_id == 3) + + # Make the comboboxes invisible + for combobox in self.cb_periods: + combobox.set_visible(button_id != 1) + + # +-----------+-----------+---------------+ # | Image Set | HEIC file | Source Folder | # +-----------+-----------+---------------+ @@ -298,12 +315,7 @@ class Main_Window: """ if button.get_active(): self.view_model.cinnamon_prefs.image_source = ImageSourceEnum.IMAGESET - self.tb_heic_file.set_active(False) - self.tb_source_folder.set_active(False) - - self.lbr_image_set.set_visible(True) - self.lbr_heic_file.set_visible(False) - self.lbr_source_folder.set_visible(False) + self.show_image_configuration_entries(1) self.set_active_combobox_item(self.cb_image_set, self.view_model.cinnamon_prefs.selected_image_set) @@ -311,10 +323,6 @@ class Main_Window: selected_image_name = self.view_model.cinnamon_prefs.period_images[i] self.set_active_combobox_item(combobox, selected_image_name) - # Make the comboboxes invisible - for combobox in self.cb_periods: - combobox.set_visible(False) - def on_toggle_button_heic_file_clicked(self, button: Gtk.ToggleButton): """ Clicked on ToggleButton "Heic file" @@ -324,16 +332,7 @@ class Main_Window: """ if button.get_active(): self.view_model.cinnamon_prefs.image_source = ImageSourceEnum.HEICFILE - self.tb_image_set.set_active(False) - self.tb_source_folder.set_active(False) - - self.lbr_image_set.set_visible(False) - self.lbr_heic_file.set_visible(True) - self.lbr_source_folder.set_visible(False) - - # Make the comboboxes visible - for combobox in self.cb_periods: - combobox.set_visible(True) + self.show_image_configuration_entries(2) # Load images from source folder files = self.images.get_images_from_folder(self.view_model.cinnamon_prefs.source_folder) @@ -356,16 +355,7 @@ class Main_Window: """ if button.get_active(): self.view_model.cinnamon_prefs.image_source = ImageSourceEnum.SOURCEFOLDER - self.tb_image_set.set_active(False) - self.tb_heic_file.set_active(False) - - self.lbr_image_set.set_visible(False) - self.lbr_heic_file.set_visible(False) - self.lbr_source_folder.set_visible(True) - - # Make the comboboxes visible - for combobox in self.cb_periods: - combobox.set_visible(True) + self.show_image_configuration_entries(3) # Load the source folder to the view # This will update the comboboxes in the preview to contain the right items @@ -414,8 +404,7 @@ class Main_Window: # Image sets have the same names for the images: # 9.jpg = Period 0 # 1.jpg = Period 1 - # 2.jpg = Period 2 - # and so on.... + # 2.jpg = Period 2... for i in range(0, 10): self.cb_periods[i].set_active(i + 1) @@ -446,7 +435,7 @@ class Main_Window: image_names = self.images.get_images_from_folder(self.view_model.cinnamon_prefs.source_folder) self.load_image_options_to_combo_boxes(image_names) else: - self.dialogs.message_dialog("Error during extraction") + self.dialogs.message_dialog("Error during extraction!", Gtk.MessageType.ERROR) # +------------------------------------------------------------+ @@ -507,6 +496,25 @@ class Main_Window: ## Location & Times + + def show_location_times_entries(self, button_id: int): + """ Show or hide parts of the Locations & Times menu + + Args: + button_id (int): ID of the button, 1 = Network, 2 = Custom Location, 3 = Custom Time Periods + """ + self.tb_network_location.set_active(button_id == 1) + self.tb_custom_location.set_active(button_id == 2) + self.tb_time_periods.set_active(button_id == 3) + + # Show/Hide the right ListBoxRows + self.lbr_network_refresh_time.set_visible(button_id == 1) + self.lbr_network_provider.set_visible(button_id == 1) + self.lbr_current_location.set_visible(button_id == 1) + self.lbr_custom_location_longitude.set_visible(button_id == 2) + self.lbr_custom_location_latitude.set_visible(button_id == 2) + self.lbr_time_periods.set_visible(button_id == 3) + def on_toggle_button_network_location_clicked(self, button: Gtk.ToggleButton): """ User clicks on the ToggleButton for the network location @@ -516,26 +524,10 @@ class Main_Window: """ if button.get_active(): self.view_model.cinnamon_prefs.period_source = PeriodSourceEnum.NETWORKLOCATION - self.tb_custom_location.set_active(False) - self.tb_time_periods.set_active(False) - - self.lbr_network_refresh_time.set_visible(True) - self.lbr_current_location.set_visible(True) - self.lbr_custom_location_longitude.set_visible(False) - self.lbr_custom_location_latitude.set_visible(False) - self.lbr_time_periods.set_visible(False) + self.show_location_times_entries(1) self.spb_network_refresh_time.set_value(self.view_model.cinnamon_prefs.location_refresh_intervals) - - - # Display the location in the UI - current_location = self.location.get_location(NetworkLocationProvider.GEOJS) - self.lb_current_location.set_text("Latitude: " + current_location["latitude"] + \ - ", Longitude: " + current_location["longitude"]) - - # Store the location to the preferences - self.view_model.cinnamon_prefs.latitude_auto = float(current_location["latitude"]) - self.view_model.cinnamon_prefs.longitude_auto = float(current_location["longitude"]) + self.set_active_combobox_item(self.cb_network_provider, self.view_model.cinnamon_prefs.network_location_provider) self.refresh_charts() @@ -543,14 +535,7 @@ class Main_Window: def on_toggle_button_custom_location_clicked(self, button: Gtk.ToggleButton): if button.get_active(): self.view_model.cinnamon_prefs.period_source = PeriodSourceEnum.CUSTOMLOCATION - self.tb_network_location.set_active(False) - self.tb_time_periods.set_active(False) - - self.lbr_network_refresh_time.set_visible(False) - self.lbr_current_location.set_visible(False) - self.lbr_custom_location_longitude.set_visible(True) - self.lbr_custom_location_latitude.set_visible(True) - self.lbr_time_periods.set_visible(False) + self.show_location_times_entries(2) self.etr_latitude.set_text(str(self.view_model.cinnamon_prefs.latitude_custom)) self.etr_longitude.set_text(str(self.view_model.cinnamon_prefs.longitude_custom)) @@ -559,15 +544,7 @@ class Main_Window: def on_toggle_button_time_periods_clicked(self, button: Gtk.ToggleButton): if button.get_active(): self.view_model.cinnamon_prefs.period_source = PeriodSourceEnum.CUSTOMTIMEPERIODS - self.tb_network_location.set_active(False) - self.tb_custom_location.set_active(False) - - self.lbr_network_refresh_time.set_visible(False) - self.lbr_current_location.set_visible(False) - self.lbr_custom_location_longitude.set_visible(False) - self.lbr_custom_location_latitude.set_visible(False) - self.lbr_time_periods.set_visible(True) - + self.show_location_times_entries(3) for i in range(0, 9): pref_value = self.view_model.cinnamon_prefs.period_custom_start_time[i + 1] @@ -577,7 +554,6 @@ class Main_Window: self.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 @@ -616,6 +592,28 @@ class Main_Window: self.view_model.cinnamon_prefs.location_refresh_intervals = spin_button.get_value() + def on_cb_network_provider_changed(self, combobox: Gtk.ComboBox): + """ User changed the provider to estimate the location + + Args: + combobox (Gtk.ComboBox): The used ComboBox + """ + tree_iter = combobox.get_active_iter() + + if tree_iter is not None: + model = combobox.get_model() + self.view_model.cinnamon_prefs.network_location_provider = model[tree_iter][0] + + success = self.view_model.refresh_location() + + if success: + self.lb_current_location.set_text(\ + "Latitude: " + str(self.view_model.cinnamon_prefs.latitude_auto) + ", Longitude: " + str(self.view_model.cinnamon_prefs.longitude_auto)) + else: + self.dialogs.message_dialog("Error during fetching location. Are you connected to the network?", Gtk.MessageType.ERROR) + self.lb_current_location.set_text("Latitude: ?, Longitude: ?") + + def on_etr_longitude_changed(self, entry: Gtk.Entry): """ User changes the value of the longitude Entry @@ -624,7 +622,7 @@ class Main_Window: """ try: self.view_model.cinnamon_prefs.longitude_custom = float(entry.get_text()) - self.refresh_chart() + self.refresh_charts() except: pass @@ -637,7 +635,7 @@ class Main_Window: """ try: self.view_model.cinnamon_prefs.latitude_custom = float(entry.get_text()) - self.refresh_chart() + self.refresh_charts() except: pass @@ -645,13 +643,25 @@ class Main_Window: # Behaviour def on_cb_picture_aspect_changed(self, combobox: Gtk.ComboBox): + """ User changes the value for the picture aspect ratio + + Args: + combobox (Gtk.ComboBox): The used ComboBox + """ tree_iter = combobox.get_active_iter() if tree_iter is not None: model = combobox.get_model() self.view_model.cinnamon_prefs.picture_aspect = model[tree_iter][0] - - def on_sw_dynamic_background_color_state_set(self, switch: Gtk.Switch, state): + + + def on_sw_dynamic_background_color_state_set(self, _: Gtk.Switch, state: bool): + """ User switches dynamic background on or off + + Args: + _ (Gtk.Switch): Used Switch + state (bool): Current state + """ self.view_model.cinnamon_prefs.dynamic_background_color = state diff --git a/cinnamon-dynamic-wallpaper@TobiZog/metadata.json b/cinnamon-dynamic-wallpaper@TobiZog/metadata.json index cc6bfbe..2ead12b 100644 --- a/cinnamon-dynamic-wallpaper@TobiZog/metadata.json +++ b/cinnamon-dynamic-wallpaper@TobiZog/metadata.json @@ -1,5 +1,5 @@ { - "external-configuration-app": "src/test.py", + "external-configuration-app": "src/main.py", "uuid": "cinnamon-dynamic-wallpaper@TobiZog", "name": "Cinnamon Dynamic Wallpaper", "description": "Cinnamon extension for dynamic desktop backgrounds based on time and location",