HEIC import, reimplement image change system

This commit is contained in:
2024-01-04 19:13:53 +01:00
parent 22920ad712
commit 66e4023b7a
18 changed files with 188 additions and 457 deletions

View File

@@ -0,0 +1,36 @@
import os, json
class Cinnamon_Pref_Handler:
def __init__(self) -> None:
# Location of the Cinnamon preference file since Cinnamon 5.4
self.pref_location = os.path.expanduser("~") + \
"/.config/cinnamon/spices/cinnamon-dynamic-wallpaper@TobiZog/cinnamon-dynamic-wallpaper@TobiZog.json"
self.prefs = {}
self.load_preferences()
def load_preferences(self):
with open(self.pref_location, "r") as pref_file:
pref_data = json.load(pref_file)
for i in pref_data:
try:
self.prefs[i] = pref_data[i]["value"]
except:
pass
def store_preferences(self):
with open(self.pref_location, "r") as pref_file:
pref_data = json.load(pref_file)
for i in pref_data:
try:
pref_data[i]["value"] = self.prefs[i]
except:
pass
with open(self.pref_location, "w") as pref_file:
json.dump(pref_data, pref_file, separators=(',', ':'), indent=4)

View File

@@ -1,58 +0,0 @@
/**
* @name Cinnamon-Dynamic-Wallpaper
* @alias TobiZog
* @since 2023-08-25
*
* @description Handles communications with the user (notifications, logs)
*/
/******************** Imports ********************/
const St = imports.gi.St;
const Main = imports.ui.main;
const Util = imports.misc.util;
const MessageTray = imports.ui.messageTray;
const UUID = "cinnamon-dynamic-wallpaper@TobiZog";
const DIRECTORY = imports.ui.extensionSystem.extensionMeta[UUID];
/******************** Functions ********************/
/**
* Displaying a desktop notification
*
* @param {string} title The Title in the notification
* @param {string} text The text in the notification
* @param {boolean} showOpenSettings Display the "Open settings" button in the notification,
* defaults to false
*/
function showNotification(title, text, showOpenSettings = false) {
let source = new MessageTray.Source(this.uuid);
// Parameter
let params = {
icon: new St.Icon({
icon_name: "icon",
icon_type: St.IconType.FULLCOLOR,
icon_size: source.ICON_SIZE
})
};
// The notification itself
let notification = new MessageTray.Notification(source, title, text, params);
// Display the "Open settings" button, if showOpenSettings
if (showOpenSettings) {
notification.addButton("open-settings", _("Open settings"));
notification.connect("action-invoked", () =>
Util.spawnCommandLine("/usr/bin/env python3 " +
DIRECTORY.path + "/preferences/preferences.py"));
}
// Put all together
Main.messageTray.add(source);
// Display it
source.notify(notification);
}

View File

@@ -0,0 +1,10 @@
import os
class Images:
def __init__(self) -> None:
pass
def get_images_from_folder(self, URI: str) -> list:
items = os.listdir(URI)
items.sort()
return items

View File

@@ -1,31 +0,0 @@
/**
* @name Cinnamon-Dynamic-Wallpaper
* @alias TobiZog
* @since 2023
*
* @description Functions to estimate the user location
*/
/******************** Imports ********************/
const Soup = imports.gi.Soup;
/******************** Functions ********************/
/**
* Estimate the location of the user
*
* @returns Location data if succeded or -1 if failed
*/
function estimateLocation() {
let sessionSync = new Soup.SessionSync();
let msg = Soup.Message.new('GET', "https://get.geojs.io/v1/ip/geo.json");
sessionSync.send_message(msg);
if (msg.status_code == 200) {
return JSON.parse(msg.response_body.data);
} else {
return -1;
}
}

View File

@@ -0,0 +1,17 @@
import urllib.request, json
from threading import Thread
class Location(Thread):
def __init__(self):
Thread.__init__(self)
self.GEO_URL = "https://get.geojs.io/v1/ip/geo.json"
def run(self) -> dict:
request = urllib.request.urlopen(self.GEO_URL)
data = json.load(request)
self.result = {
"latitude": data["latitude"],
"longitude": data["longitude"]
}

View File

@@ -1,173 +0,0 @@
/**
* @name Cinnamon-Dynamic-Wallpaper
* @alias TobiZog
* @since 2023
*
* @description Functions to calculate sun time periods
*/
/******************** Constants ********************/
const DAYPERIOD = {
MTWILIGHT: 0,
SUNRISE: 1,
MORNING: 2,
NOON: 3,
AFTERNOON: 4,
EVENING: 5,
SUNSET: 6,
NTWILIGHT: 7,
NIGHT: 8,
NONE: 10
}
const DAYMS = 1000 * 60 * 60 * 24
const J1970 = 2440588
const J2000 = 2451545
/******************** Functions ********************/
function fromJulian(j) {
let ms_date = (j + 0.5 - J1970) * DAYMS
return new Date(ms_date)
}
/**
* Calculating specific events of the sun during a day
*
* @param {float} latitude Location latitude
* @param {float} longitude Location longitude
*
* @returns List of sun events in a day: dawn, sunrise, noon, sunset, dusk
*/
function sunEventsOfDay(latitude, longitude, date) {
let rad = Math.PI / 180
let lw = rad * (-longitude)
let d = (date / DAYMS) - 0.5 + J1970 - J2000
let n = Math.round(d - 0.0009 - lw / (2 * Math.PI))
let ds = 0.0009 + lw / (2 * Math.PI) + n
let M = rad * (357.5291 + 0.98560028 * ds)
let C = rad * (1.9148 * Math.sin(M) + 0.02 * Math.sin(2 * M) + 0.0003 * Math.sin(3 * M))
let P = rad * 102.9372
let L = M + C + P + Math.PI
let dec = Math.asin(Math.sin(rad * 23.4397) * Math.sin(L))
// Angles for the sun
let angles = [-0.833, -6]
for(var i = 0; i < angles.length; i++) {
angles[i] = angles[i] * rad
angles[i] = Math.acos((Math.sin(angles[i]) - Math.sin(rad * latitude) * Math.sin(dec)) / (Math.cos(rad * latitude) * Math.cos(dec)))
angles[i] = 0.0009 + (angles[i] + lw) / (2 * Math.PI) + n
}
let jnoon = J2000 + ds + 0.0053 * Math.sin(M) - 0.0069 * Math.sin(2 * L)
return {
dawn: fromJulian(jnoon - (J2000 + angles[1] + 0.0053 * Math.sin(M) - 0.0069 * Math.sin(2 * L) - jnoon)),
sunrise: fromJulian(jnoon - (J2000 + angles[0] + 0.0053 * Math.sin(M) - 0.0069 * Math.sin(2 * L) - jnoon)),
noon: fromJulian(jnoon),
sunset: fromJulian(J2000 + angles[0] + 0.0053 * Math.sin(M) - 0.0069 * Math.sin(2 * L)),
dusk: fromJulian(J2000 + angles[1] + 0.0053 * Math.sin(M) - 0.0069 * Math.sin(2 * L))
}
}
function addMinutesToTime(date, minutes = 0) {
let newDate = new Date(date)
newDate.setMinutes(date.getMinutes() + minutes)
return newDate
}
function subTimesToMinutes(date1, date2) {
let diff = new Date(date1 - date2)
return diff.getUTCHours() * 60 + diff.getMinutes()
}
/**
* Calculating the start and end time of all time periods of the day
*
* @param {float} latitude Location latitude
* @param {float} longitude Location longitude
*
* @returns JSON of time periods
*/
function calcTimePeriod(latitude, longitude) {
let todaySunEventsDay = sunEventsOfDay(latitude, longitude, Date.now())
let tomorrowSunEventsDay = sunEventsOfDay(latitude, longitude, addMinutesToTime(new Date(), 1440))
return {
morning_twilight: [
todaySunEventsDay.dawn,
addMinutesToTime(todaySunEventsDay.sunrise, -1)
],
sunrise: [
todaySunEventsDay.sunrise,
addMinutesToTime(
todaySunEventsDay.sunrise,
subTimesToMinutes(todaySunEventsDay.noon, todaySunEventsDay.sunrise) / 8 - 1
)
],
morning: [
addMinutesToTime(
todaySunEventsDay.sunrise,
(subTimesToMinutes(todaySunEventsDay.noon, todaySunEventsDay.sunrise) / 8) * 1
),
addMinutesToTime(
todaySunEventsDay.sunrise,
(subTimesToMinutes(todaySunEventsDay.noon, todaySunEventsDay.sunrise) / 8)*6 - 1
)
],
noon: [
addMinutesToTime(
todaySunEventsDay.sunrise,
(subTimesToMinutes(todaySunEventsDay.noon, todaySunEventsDay.sunrise) / 8) * 6
),
addMinutesToTime(
todaySunEventsDay.noon,
(subTimesToMinutes(todaySunEventsDay.sunset, todaySunEventsDay.noon) / 8) * 2 - 1
)
],
afternoon: [
addMinutesToTime(
todaySunEventsDay.noon,
(subTimesToMinutes(todaySunEventsDay.sunset, todaySunEventsDay.noon) / 8) * 2
),
addMinutesToTime(
todaySunEventsDay.noon,
(subTimesToMinutes(todaySunEventsDay.sunset, todaySunEventsDay.noon) / 8) * 5 - 1
)
],
evening: [
addMinutesToTime(
todaySunEventsDay.noon,
(subTimesToMinutes(todaySunEventsDay.sunset, todaySunEventsDay.noon) / 8) * 5
),
addMinutesToTime(
todaySunEventsDay.noon,
(subTimesToMinutes(todaySunEventsDay.sunset, todaySunEventsDay.noon) / 8) * 7 - 1
)
],
sunset: [
addMinutesToTime(
todaySunEventsDay.noon,
(subTimesToMinutes(todaySunEventsDay.sunset, todaySunEventsDay.noon) / 8) * 7 - 1
),
todaySunEventsDay.sunset
],
night_twilight: [
addMinutesToTime(todaySunEventsDay.sunset, 1),
todaySunEventsDay.dusk
],
night: [
addMinutesToTime(todaySunEventsDay.dusk, 1),
addMinutesToTime(tomorrowSunEventsDay.dawn, -1)
],
}
}

View File

@@ -0,0 +1,140 @@
from math import pi, sin, asin, acos, cos, floor, atan, tan
from datetime import datetime, timezone, time
class Suntimes:
""" Calculates all time periods based on latitude and longitude
Inspired by https://github.com/SatAgro/suntime
author: TobiZog
"""
def __init__(self) -> None:
""" Initialization
Args:
latitude (float): Latitude of the position
longitude (float): Longitude of the position
"""
self.today = datetime.now()
def calc_suntimes(self, latitude: float, longitude: float) -> None:
self.latitude = latitude
self.longitude = longitude
self.calc_sun_events()
def to_range(self, value: float, range_max: float) -> float:
""" Converting a variable to a given range
Args:
value (float): The given value
range_max (float): Upper boundary
Returns:
float: Corrected value inside range 0 to range_max
"""
if value < 0:
return value + range_max
elif value >= range_max:
return value - range_max
else:
return value
def calc_sun_events(self):
civial_dawn_start = self.calc_sunrise_sunset_time(True, 96)
sunrise_start = self.calc_sunrise_sunset_time(True)
morning_start = self.calc_sunrise_sunset_time(True, 89.167)
sunset_start = self.calc_sunrise_sunset_time(False, 89.167)
civial_dusk_start = self.calc_sunrise_sunset_time(False)
night_start = self.calc_sunrise_sunset_time(False, 96)
light_period_duration = (sunset_start - morning_start) / 8
noon_start = morning_start + 3 * light_period_duration
afternoon_start = morning_start + 5 * light_period_duration
evening_start = morning_start + 7 * light_period_duration
self.day_periods = [
time(hour=0, minute=0),
time(civial_dawn_start.hour, civial_dawn_start.minute),
time(sunrise_start.hour, sunrise_start.minute),
time(morning_start.hour, morning_start.minute),
time(noon_start.hour, noon_start.minute),
time(afternoon_start.hour, afternoon_start.minute),
time(evening_start.hour, evening_start.minute),
time(sunset_start.hour, sunset_start.minute),
time(civial_dusk_start.hour, civial_dusk_start.minute),
time(night_start.hour, night_start.minute),
]
def calc_sunrise_sunset_time(self, is_sunrise: bool, zenith=90.833) -> datetime:
""" Calculate all values to estimate the day periods
"""
RAD = pi / 180
# Day of the year
day_of_year = self.today.timetuple().tm_yday
# 2
lng_hour = self.longitude / 15
if is_sunrise:
t = day_of_year + ((6 - lng_hour) / 24)
else:
t = day_of_year + ((18 - lng_hour) / 24)
# 3
M = (0.9856 * t) - 3.289
# 4
L = self.to_range(M + (1.916 * sin(RAD * M)) + (0.020 * sin(RAD * 2 * M)) + 282.634, 360)
# 5
RA = self.to_range((1 / RAD) * atan(0.91764 * tan(RAD * L)), 360)
RA += ((floor(L / 90)) * 90 - (floor(RA / 90)) * 90)
RA /= 15
# 6
sin_dec = 0.39782 * sin(RAD * L)
cos_dec = cos(asin(sin_dec))
# 7a
cos_h = (cos(RAD * zenith) - (sin_dec * sin(RAD * self.latitude))) / (cos_dec * cos(RAD * self.latitude))
# The sun rises or sets never
if cos_h > 1 or cos_h < -1:
return None
# 7b
if is_sunrise:
H = 360 - (1 / RAD) * acos(cos_h)
else: #setting
H = (1 / RAD) * acos(cos_h)
H = H / 15
# 8
T = H + RA - (0.06571 * t) - 6.622
# 9
UT = T - lng_hour
UT = self.to_range(UT, 24) # UTC time in decimal format (e.g. 23.23)
# 10
hr = self.to_range(int(UT), 24)
min = round((UT - int(UT))*60, 0)
if min == 60:
hr += 1
min = 0
res = datetime(self.today.year, self.today.month, self.today.day, hr, int(min))
return res.replace(tzinfo=timezone.utc).astimezone(tz=None)

View File

@@ -0,0 +1,153 @@
import math
class Time_Bar_Chart:
def __init__(self) -> None:
self.image_code = []
self.colors = [
"00193d",
"05597f",
"54babf",
"bfe3c2",
"ffbf6b",
"fdb55c",
"f37f73",
"b45bbc",
"7e38ce",
"00285f"
]
self.bar_pos_x = []
def create_bar_chart_with_polylines(self, save_location: str, image_width: int, image_height: int, times: list):
""" Create a time bar chart
Args:
image_width (int): Width of the image in pixel
image_height (int): Height of the image in pixel
times (list): List of start times of the periods in minutes since midnight
"""
self.create_bar(image_width, image_height, times)
self.create_polylines(image_width, image_height)
self.create_time_markers(image_width, image_height)
# Write to file
self.image_code.insert(0, '<svg xmlns="http://www.w3.org/2000/svg" width="%s" height="%s">' % (image_width, image_height))
self.image_code.append('</svg>')
file = open(save_location + "/time_bar_polylines.svg", "w")
for i in self.image_code:
file.write(i + '\n')
self.image_code.clear()
self.bar_pos_x.clear()
def create_bar_chart(self, save_location: str, image_width: int, image_height: int, times: list):
self.create_bar(image_width, image_height, times)
self.create_time_markers(image_width, image_height)
# Write to file
self.image_code.insert(0, '<svg xmlns="http://www.w3.org/2000/svg" width="%s" height="%s">' % (image_width, image_height))
self.image_code.append('</svg>')
file = open(save_location + "/time_bar.svg", "w")
for i in self.image_code:
file.write(i + '\n')
self.image_code.clear()
self.bar_pos_x.clear()
def create_bar(self, image_width: int, image_height: int, times: list):
""" Generates the code for the horizontal multi-color bar chart
Args:
image_width (int): Total width of the image
image_height (int): Total height of the image
times (list): List of start times of the periods, in minutes
"""
x = 0
y = 40
width = 0
height = image_height - 80
if times[len(times) - 1] != 1440:
times.append(1440)
# Adding the bar parts
for i in range(1, len(times)):
width = math.ceil((((100 / 1440) * (times[i] - times[i - 1]) / 100) * image_width))
self.image_code.append(
'<rect fill="#%s" x="%s" y="%s" width="%s" height="%s"/>' % (self.colors[i - 1], x, y, width, height)
)
self.bar_pos_x.append(x)
x += width
def create_time_markers(self, image_width: int, image_height: int):
""" Generates the code for the vertical hour markers
Args:
image_width (int): Total width of the image
image_height (int): Total height of the image
"""
for i in range(0, 8):
self.image_code.append(
'<line x1="%s" y1="40" x2="%s" y2="%s" stroke="gray" stroke-width="2" />' %
(i * (image_width // 8), i * (image_width // 8), image_height - 40)
)
self.image_code.append(
'<text x="%s" y="%s" fill="gray" font-size="20" font-family="Liberation Sans">%s</text>' %
(i * (image_width // 8) + 5, image_height - 45, i * 3)
)
def create_polylines(self, image_width: int, image_height: int):
""" Generates the code for the polylines which connect the images with the bar sections
Args:
image_width (int): Total width of the image
image_height (int): Total height of the image
"""
bar_x_start = 0
self.bar_pos_x.append(image_width)
for i in range(0, len(self.bar_pos_x) - 1):
# X-Middle of a bar
bar_mid = bar_x_start + (self.bar_pos_x[i + 1] - bar_x_start) / 2
# Position of the image in the window
image_x = (image_width - 32) / 10 + ((i // 2) % 5) * image_width / 5
# i == 0, 2, 4, ... => Upper Polylines
if (i % 2 == 0):
polyline_y = 0
else:
polyline_y = image_height
if i == 0 or i == 8:
polyline_x = 30
elif i == 2 or i == 6:
polyline_x = 20
elif i == 1 or i == 9:
polyline_x = image_height - 30
elif i == 3 or i == 7:
polyline_x = image_height - 20
elif i == 5:
polyline_x = image_height - 10
else:
polyline_x = 10
self.image_code.append(
'<polyline points="%s,%s %s,%s %s,%s %s,%s" stroke="#%s" fill="none" stroke-width="5" />' %
(image_x, polyline_y, image_x, polyline_x, bar_mid, polyline_x, bar_mid, image_height / 2, self.colors[i])
)
# Store the end point of the bar as start point of the next
bar_x_start = self.bar_pos_x[i + 1]