MVVM pattern finished, adding multiple location provider

This commit is contained in:
2024-02-02 19:56:14 +01:00
parent 8acc18c7fe
commit 53f5984eb5
14 changed files with 360 additions and 354 deletions

View File

@@ -96,7 +96,7 @@ CinnamonDynamicWallpaperExtension.prototype = {
_loop: function () { _loop: function () {
if (looping) { if (looping) {
try { 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) { } catch(e) {
this.showNotification("Error!", this.showNotification("Error!",
"Cinnamon Dynamic Wallpaper got an error while running the loop script. Please create an issue on GitHub.") "Cinnamon Dynamic Wallpaper got an error while running the loop script. Please create an issue on GitHub.")

View File

@@ -1221,6 +1221,7 @@
<object class="GtkComboBox" id="cb_network_provider"> <object class="GtkComboBox" id="cb_network_provider">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">False</property> <property name="can-focus">False</property>
<signal name="changed" handler="on_cb_network_provider_changed" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>

View File

@@ -71,6 +71,10 @@
"type": "generic", "type": "generic",
"default": 15 "default": 15
}, },
"network_location_provider": {
"type": "generic",
"default": "geojs.io"
},
"latitude_auto": { "latitude_auto": {
"type": "generic", "type": "generic",
"default": 0 "default": 0

View File

@@ -1,5 +1,4 @@
from enum import Enum class NetworkLocationProvider(enumerate):
GEOJS = "geojs.io"
class NetworkLocationProvider(Enum): IPAPI = "ip-api.com"
GEOJS = "https://get.geojs.io/v1/ip/geo.json" IPWHOIS = "ipwho.is"
IPAPI = "http://ip-api.com/json/?fields=61439"

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

@@ -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()

View File

@@ -1,10 +1,12 @@
import os, json import os, json
from pathlib import Path from PIL import Image
from gi.repository import Gio
from service.display import * from service.display import *
from service.cinnamon_pref_handler import * from service.cinnamon_pref_handler import *
from service.suntimes import * from service.suntimes import *
from service.time_bar_chart import * from service.time_bar_chart import *
from service.location import *
from enums.PeriodSourceEnum import * from enums.PeriodSourceEnum import *
class Main_View_Model: class Main_View_Model:
@@ -22,13 +24,16 @@ class Main_View_Model:
# Datasets # Datasets
self.image_sets = ["aurora", "beach", "bitday", "cliffs", "earth", "gradient", "lakeside", "mountains", "sahara"] self.image_sets = ["aurora", "beach", "bitday", "cliffs", "earth", "gradient", "lakeside", "mountains", "sahara"]
self.picture_aspects = ["centered", "scaled", "stretched", "zoom", "spanned"] 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 # Objects from scripts
self.screen_height = Display().get_screen_height() self.screen_height = Display().get_screen_height()
self.cinnamon_prefs = Cinnamon_Pref_Handler() self.cinnamon_prefs = Cinnamon_Pref_Handler()
self.time_bar_chart = Time_Bar_Chart() self.time_bar_chart = Time_Bar_Chart()
self.suntimes = Suntimes() self.suntimes = Suntimes()
self.location = Location()
self.background_settings = Gio.Settings.new("org.cinnamon.desktop.background")
# Breakpoint for smaller UI # Breakpoint for smaller UI
self.breakpoint_ui = 1000 self.breakpoint_ui = 1000
@@ -47,25 +52,15 @@ class Main_View_Model:
if self.cinnamon_prefs.period_source == PeriodSourceEnum.NETWORKLOCATION: if self.cinnamon_prefs.period_source == PeriodSourceEnum.NETWORKLOCATION:
self.suntimes.calc_suntimes(float(self.cinnamon_prefs.latitude_auto), float(self.cinnamon_prefs.longitude_auto)) self.suntimes.calc_suntimes(float(self.cinnamon_prefs.latitude_auto), float(self.cinnamon_prefs.longitude_auto))
else: else:
pass self.suntimes.calc_suntimes(float(self.cinnamon_prefs.latitude_custom), float(self.cinnamon_prefs.longitude_custom))
# todo 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 # Get all time periods. Store the minutes to the list and print the values to the text views
for i in range(0, 10): for i in range(0, 10):
time_range_now = self.suntimes.day_periods[i] 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) time_periods_min.append(time_range_now.hour * 60 + time_range_now.minute)
# Create time bar # Create time bar
# Reduce size for small displays # Reduce size for small displays
if self.screen_height < self.breakpoint_ui: if self.screen_height < self.breakpoint_ui:
@@ -79,3 +74,114 @@ class Main_View_Model:
self.time_bar_chart.create_bar_chart(self.TIMEBAR_URI, 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"

View File

@@ -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()

View File

@@ -11,90 +11,122 @@ class Cinnamon_Pref_Handler:
self.load_preferences() 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): def load_preferences(self):
""" Load the JSON preferences to the Preference object """ Load the JSON preferences to the Preference object
""" """
with open(self.pref_location, "r") as pref_file: 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.picture_aspect = self.extract_json('picture_aspect')
self.dynamic_background_color = pref_data['dynamic_background_color']['value'] self.dynamic_background_color = self.extract_json('dynamic_background_color')
self.image_source = pref_data['image_source']['value'] self.image_source = self.extract_json('image_source')
self.selected_image_set = pref_data['selected_image_set']['value'] self.selected_image_set = self.extract_json('selected_image_set')
self.source_folder = pref_data['source_folder']['value'] self.source_folder = self.extract_json('source_folder')
self.period_images = [ self.period_images = [
pref_data['period_0_image']['value'], self.extract_json('period_0_image'),
pref_data['period_1_image']['value'], self.extract_json('period_1_image'),
pref_data['period_2_image']['value'], self.extract_json('period_2_image'),
pref_data['period_3_image']['value'], self.extract_json('period_3_image'),
pref_data['period_4_image']['value'], self.extract_json('period_4_image'),
pref_data['period_5_image']['value'], self.extract_json('period_5_image'),
pref_data['period_6_image']['value'], self.extract_json('period_6_image'),
pref_data['period_7_image']['value'], self.extract_json('period_7_image'),
pref_data['period_8_image']['value'], self.extract_json('period_8_image'),
pref_data['period_9_image']['value'] self.extract_json('period_9_image')
] ]
self.period_source = pref_data['period_source']['value'] self.period_source =self.extract_json('period_source')
self.location_refresh_intervals = pref_data['location_refresh_intervals']['value'] self.location_refresh_intervals =self.extract_json('location_refresh_intervals')
self.latitude_auto = pref_data['latitude_auto']['value'] self.network_location_provider =self.extract_json('network_location_provider')
self.longitude_auto = pref_data['longitude_auto']['value'] self.latitude_auto =self.extract_json('latitude_auto')
self.latitude_custom = pref_data['latitude_custom']['value'] self.longitude_auto =self.extract_json('longitude_auto')
self.longitude_custom = pref_data['longitude_custom']['value'] self.latitude_custom =self.extract_json('latitude_custom')
self.longitude_custom =self.extract_json('longitude_custom')
self.period_custom_start_time = [ self.period_custom_start_time = [
pref_data['period_0_custom_start_time']['value'], self.extract_json('period_0_custom_start_time'),
pref_data['period_1_custom_start_time']['value'], self.extract_json('period_1_custom_start_time'),
pref_data['period_2_custom_start_time']['value'], self.extract_json('period_2_custom_start_time'),
pref_data['period_3_custom_start_time']['value'], self.extract_json('period_3_custom_start_time'),
pref_data['period_4_custom_start_time']['value'], self.extract_json('period_4_custom_start_time'),
pref_data['period_5_custom_start_time']['value'], self.extract_json('period_5_custom_start_time'),
pref_data['period_6_custom_start_time']['value'], self.extract_json('period_6_custom_start_time'),
pref_data['period_7_custom_start_time']['value'], self.extract_json('period_7_custom_start_time'),
pref_data['period_8_custom_start_time']['value'], self.extract_json('period_8_custom_start_time'),
pref_data['period_9_custom_start_time']['value'] 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): def store_preferences(self):
""" Store the values of the Preference object to the JSON file """ Store the values of the Preference object to the JSON file
""" """
with open(self.pref_location, "r") as pref_file: self.value_to_json('picture_aspect', self.picture_aspect)
pref_data = json.load(pref_file) 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 # Write to file
with open(self.pref_location, "w") as pref_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)

View File

@@ -4,26 +4,36 @@ from enums.NetworkLocationProvider import NetworkLocationProvider
class Location(): class Location():
""" Class to handle location requests """ Class to handle location requests
""" """
def __init__(self):
pass
def get_location(self, provider: NetworkLocationProvider) -> dict: def get_location(self, provider: NetworkLocationProvider) -> dict:
""" Request the location via network """ Request the location via network
Returns: Returns:
dict: latitude and longitude dict: latitude and longitude
""" """
request = urllib.request.urlopen(provider.value) if provider == NetworkLocationProvider.GEOJS:
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) data = json.load(request)
if provider == NetworkLocationProvider.GEOJS: if provider == NetworkLocationProvider.GEOJS or provider == NetworkLocationProvider.IPWHOIS:
param_lat = "latitude"
param_lon = "longitude"
else:
param_lat = "lat"
param_lon = "lon"
return { return {
"latitude": data["latitude"], "latitude": float(data[param_lat]),
"longitude": data["longitude"] "longitude": float(data[param_lon]),
"success": True
} }
elif provider == NetworkLocationProvider.IPAPI: except:
return { return {
"latitude": data["lat"], "success": False
"longitude": data["lon"]
} }

View File

@@ -1,6 +0,0 @@
#!/usr/bin/python3
import ui.main_window
main = ui.main_window.Main_Window()
main.show()

View File

@@ -43,7 +43,7 @@ class Dialogs(Gtk.Window):
return location 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 """ Displaying a Gtk Message dialog to the user
Args: Args:
@@ -52,7 +52,7 @@ class Dialogs(Gtk.Window):
dialog = Gtk.MessageDialog( dialog = Gtk.MessageDialog(
transient_for=self, transient_for=self,
flags=0, flags=0,
message_type=Gtk.MessageType.INFO, message_type=type,
buttons=Gtk.ButtonsType.OK, buttons=Gtk.ButtonsType.OK,
text=message text=message
) )

View File

@@ -14,7 +14,6 @@ from datetime import timedelta
# Local scripts # Local scripts
from model.main_view_model import * from model.main_view_model import *
from service.images import * from service.images import *
from service.location import *
from service.suntimes import * from service.suntimes import *
from service.time_bar_chart import * from service.time_bar_chart import *
from ui.dialogs import * from ui.dialogs import *
@@ -46,7 +45,6 @@ class Main_Window:
# Objects from scripts # Objects from scripts
self.images = Images() self.images = Images()
self.dialogs = Dialogs() self.dialogs = Dialogs()
self.location = Location()
self.suntimes = Suntimes() self.suntimes = Suntimes()
self.time_bar_chart = Time_Bar_Chart() self.time_bar_chart = Time_Bar_Chart()
@@ -160,6 +158,8 @@ class Main_Window:
def show(self): def show(self):
""" Display the window to the screen
"""
self.builder.get_object("window_main").show_all() self.builder.get_object("window_main").show_all()
# Smaller UI handling # Smaller UI handling
@@ -265,9 +265,12 @@ class Main_Window:
image_preview.set_from_pixbuf(pixbuf) image_preview.set_from_pixbuf(pixbuf)
except: except:
pass self.dialogs.message_dialog("Error on load images. Please check the configuration!", Gtk.MessageType.ERROR)
def refresh_charts(self): def refresh_charts(self):
""" Refresh the charts and put them to the image views
"""
self.view_model.refresh_charts() self.view_model.refresh_charts()
# Load to the views # Load to the views
@@ -286,6 +289,20 @@ class Main_Window:
## Image Configuration ## 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 | # | Image Set | HEIC file | Source Folder |
# +-----------+-----------+---------------+ # +-----------+-----------+---------------+
@@ -298,12 +315,7 @@ class Main_Window:
""" """
if button.get_active(): if button.get_active():
self.view_model.cinnamon_prefs.image_source = ImageSourceEnum.IMAGESET self.view_model.cinnamon_prefs.image_source = ImageSourceEnum.IMAGESET
self.tb_heic_file.set_active(False) self.show_image_configuration_entries(1)
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.set_active_combobox_item(self.cb_image_set, self.view_model.cinnamon_prefs.selected_image_set) 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] selected_image_name = self.view_model.cinnamon_prefs.period_images[i]
self.set_active_combobox_item(combobox, selected_image_name) 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): def on_toggle_button_heic_file_clicked(self, button: Gtk.ToggleButton):
""" Clicked on ToggleButton "Heic file" """ Clicked on ToggleButton "Heic file"
@@ -324,16 +332,7 @@ class Main_Window:
""" """
if button.get_active(): if button.get_active():
self.view_model.cinnamon_prefs.image_source = ImageSourceEnum.HEICFILE self.view_model.cinnamon_prefs.image_source = ImageSourceEnum.HEICFILE
self.tb_image_set.set_active(False) self.show_image_configuration_entries(2)
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)
# Load images from source folder # Load images from source folder
files = self.images.get_images_from_folder(self.view_model.cinnamon_prefs.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(): if button.get_active():
self.view_model.cinnamon_prefs.image_source = ImageSourceEnum.SOURCEFOLDER self.view_model.cinnamon_prefs.image_source = ImageSourceEnum.SOURCEFOLDER
self.tb_image_set.set_active(False) self.show_image_configuration_entries(3)
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)
# Load the source folder to the view # Load the source folder to the view
# This will update the comboboxes in the preview to contain the right items # 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: # Image sets have the same names for the images:
# 9.jpg = Period 0 # 9.jpg = Period 0
# 1.jpg = Period 1 # 1.jpg = Period 1
# 2.jpg = Period 2 # 2.jpg = Period 2...
# and so on....
for i in range(0, 10): for i in range(0, 10):
self.cb_periods[i].set_active(i + 1) 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) image_names = self.images.get_images_from_folder(self.view_model.cinnamon_prefs.source_folder)
self.load_image_options_to_combo_boxes(image_names) self.load_image_options_to_combo_boxes(image_names)
else: else:
self.dialogs.message_dialog("Error during extraction") self.dialogs.message_dialog("Error during extraction!", Gtk.MessageType.ERROR)
# +------------------------------------------------------------+ # +------------------------------------------------------------+
@@ -508,6 +497,25 @@ class Main_Window:
## Location & Times ## 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): def on_toggle_button_network_location_clicked(self, button: Gtk.ToggleButton):
""" User clicks on the ToggleButton for the network location """ User clicks on the ToggleButton for the network location
@@ -516,26 +524,10 @@ class Main_Window:
""" """
if button.get_active(): if button.get_active():
self.view_model.cinnamon_prefs.period_source = PeriodSourceEnum.NETWORKLOCATION self.view_model.cinnamon_prefs.period_source = PeriodSourceEnum.NETWORKLOCATION
self.tb_custom_location.set_active(False) self.show_location_times_entries(1)
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.spb_network_refresh_time.set_value(self.view_model.cinnamon_prefs.location_refresh_intervals) self.spb_network_refresh_time.set_value(self.view_model.cinnamon_prefs.location_refresh_intervals)
self.set_active_combobox_item(self.cb_network_provider, self.view_model.cinnamon_prefs.network_location_provider)
# 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.refresh_charts() self.refresh_charts()
@@ -543,14 +535,7 @@ class Main_Window:
def on_toggle_button_custom_location_clicked(self, button: Gtk.ToggleButton): def on_toggle_button_custom_location_clicked(self, button: Gtk.ToggleButton):
if button.get_active(): if button.get_active():
self.view_model.cinnamon_prefs.period_source = PeriodSourceEnum.CUSTOMLOCATION self.view_model.cinnamon_prefs.period_source = PeriodSourceEnum.CUSTOMLOCATION
self.tb_network_location.set_active(False) self.show_location_times_entries(2)
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.etr_latitude.set_text(str(self.view_model.cinnamon_prefs.latitude_custom)) 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)) 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): def on_toggle_button_time_periods_clicked(self, button: Gtk.ToggleButton):
if button.get_active(): if button.get_active():
self.view_model.cinnamon_prefs.period_source = PeriodSourceEnum.CUSTOMTIMEPERIODS self.view_model.cinnamon_prefs.period_source = PeriodSourceEnum.CUSTOMTIMEPERIODS
self.tb_network_location.set_active(False) self.show_location_times_entries(3)
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)
for i in range(0, 9): for i in range(0, 9):
pref_value = self.view_model.cinnamon_prefs.period_custom_start_time[i + 1] 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]) self.spb_periods_minute[i].set_value(time_parts[1])
def on_spb_period_value_changed(self, spin_button: Gtk.SpinButton): def on_spb_period_value_changed(self, spin_button: Gtk.SpinButton):
""" Callback if one of the time spinners (minute or hour) will be clicked """ 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() 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): def on_etr_longitude_changed(self, entry: Gtk.Entry):
""" User changes the value of the longitude Entry """ User changes the value of the longitude Entry
@@ -624,7 +622,7 @@ class Main_Window:
""" """
try: try:
self.view_model.cinnamon_prefs.longitude_custom = float(entry.get_text()) self.view_model.cinnamon_prefs.longitude_custom = float(entry.get_text())
self.refresh_chart() self.refresh_charts()
except: except:
pass pass
@@ -637,7 +635,7 @@ class Main_Window:
""" """
try: try:
self.view_model.cinnamon_prefs.latitude_custom = float(entry.get_text()) self.view_model.cinnamon_prefs.latitude_custom = float(entry.get_text())
self.refresh_chart() self.refresh_charts()
except: except:
pass pass
@@ -645,13 +643,25 @@ class Main_Window:
# Behaviour # Behaviour
def on_cb_picture_aspect_changed(self, combobox: Gtk.ComboBox): 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() tree_iter = combobox.get_active_iter()
if tree_iter is not None: if tree_iter is not None:
model = combobox.get_model() model = combobox.get_model()
self.view_model.cinnamon_prefs.picture_aspect = model[tree_iter][0] 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 self.view_model.cinnamon_prefs.dynamic_background_color = state

View File

@@ -1,5 +1,5 @@
{ {
"external-configuration-app": "src/test.py", "external-configuration-app": "src/main.py",
"uuid": "cinnamon-dynamic-wallpaper@TobiZog", "uuid": "cinnamon-dynamic-wallpaper@TobiZog",
"name": "Cinnamon Dynamic Wallpaper", "name": "Cinnamon Dynamic Wallpaper",
"description": "Cinnamon extension for dynamic desktop backgrounds based on time and location", "description": "Cinnamon extension for dynamic desktop backgrounds based on time and location",