Merge branch 'main' into feature/#311
This commit is contained in:
commit
b409cfe544
@ -232,14 +232,14 @@ which the given font should be scaled to.</p></li>
|
||||
|
||||
<dl class="py function">
|
||||
<dt class="sig sig-object py" id="inkycal.custom.functions.draw_border">
|
||||
<span class="sig-prename descclassname"><span class="pre">inkycal.custom.functions.</span></span><span class="sig-name descname"><span class="pre">draw_border</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">image</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">xy</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">size</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">radius</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">5</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">thickness</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">1</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">shrinkage</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">(0.1,</span> <span class="pre">0.1)</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#inkycal.custom.functions.draw_border" title="Link to this definition"></a></dt>
|
||||
<span class="sig-prename descclassname"><span class="pre">inkycal.custom.functions.</span></span><span class="sig-name descname"><span class="pre">draw_border</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="pre">image:</span> <span class="pre"><module</span> <span class="pre">'PIL.Image'</span> <span class="pre">from</span> <span class="pre">'/home/runner/work/Inkycal/Inkycal/venv/lib/python3.11/site-packages/PIL/Image.py'>,</span> <span class="pre">xy:</span> <span class="pre">~typing.Tuple[int,</span> <span class="pre">int],</span> <span class="pre">size:</span> <span class="pre">~typing.Tuple[int,</span> <span class="pre">int],</span> <span class="pre">radius:</span> <span class="pre">int</span> <span class="pre">=</span> <span class="pre">5,</span> <span class="pre">thickness:</span> <span class="pre">int</span> <span class="pre">=</span> <span class="pre">1,</span> <span class="pre">shrinkage:</span> <span class="pre">~typing.Tuple[int,</span> <span class="pre">int]</span> <span class="pre">=</span> <span class="pre">(0.1,</span> <span class="pre">0.1)</span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">→</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#inkycal.custom.functions.draw_border" title="Link to this definition"></a></dt>
|
||||
<dd><p>Draws a border at given coordinates.</p>
|
||||
<dl class="simple">
|
||||
<dt>Args:</dt><dd><ul class="simple">
|
||||
<li><p>image: The image on which the border should be drawn (usually im_black or
|
||||
im_colour.</p></li>
|
||||
im_colour).</p></li>
|
||||
<li><p>xy: Tuple representing the top-left corner of the border e.g. (32, 100)
|
||||
where 32 is the x co-ordinate and 100 is the y-coordinate.</p></li>
|
||||
where 32 is the x-coordinate and 100 is the y-coordinate.</p></li>
|
||||
<li><p>size: Size of the border as a tuple -> (width, height).</p></li>
|
||||
<li><p>radius: Radius of the corners, where 0 = plain rectangle, 5 = round corners.</p></li>
|
||||
<li><p>thickness: Thickness of the border in pixels.</p></li>
|
||||
@ -288,14 +288,14 @@ printed fonts of this function:</p>
|
||||
<p>The extracted timezone can be used to show the local time instead of UTC. e.g.</p>
|
||||
<div class="doctest highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="kn">import</span> <span class="nn">arrow</span>
|
||||
<span class="gp">>>> </span><span class="nb">print</span><span class="p">(</span><span class="n">arrow</span><span class="o">.</span><span class="n">now</span><span class="p">())</span> <span class="c1"># returns non-timezone-aware time</span>
|
||||
<span class="gp">>>> </span><span class="nb">print</span><span class="p">(</span><span class="n">arrow</span><span class="o">.</span><span class="n">now</span><span class="p">(</span><span class="n">tz</span><span class="o">=</span><span class="n">get_system_tz</span><span class="p">())</span> <span class="c1"># prints timezone aware time.</span>
|
||||
<span class="gp">>>> </span><span class="nb">print</span><span class="p">(</span><span class="n">arrow</span><span class="o">.</span><span class="n">now</span><span class="p">(</span><span class="n">tz</span><span class="o">=</span><span class="n">get_system_tz</span><span class="p">()))</span> <span class="c1"># prints timezone aware time.</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py function">
|
||||
<dt class="sig sig-object py" id="inkycal.custom.functions.internet_available">
|
||||
<span class="sig-prename descclassname"><span class="pre">inkycal.custom.functions.</span></span><span class="sig-name descname"><span class="pre">internet_available</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#inkycal.custom.functions.internet_available" title="Link to this definition"></a></dt>
|
||||
<span class="sig-prename descclassname"><span class="pre">inkycal.custom.functions.</span></span><span class="sig-name descname"><span class="pre">internet_available</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">→</span> <span class="sig-return-typehint"><span class="pre">bool</span></span></span><a class="headerlink" href="#inkycal.custom.functions.internet_available" title="Link to this definition"></a></dt>
|
||||
<dd><p>checks if the internet is available.</p>
|
||||
<p>Attempts to connect to google.com with a timeout of 5 seconds to check
|
||||
if the network can be reached.</p>
|
||||
@ -315,7 +315,7 @@ if the network can be reached.</p>
|
||||
|
||||
<dl class="py function">
|
||||
<dt class="sig sig-object py" id="inkycal.custom.functions.text_wrap">
|
||||
<span class="sig-prename descclassname"><span class="pre">inkycal.custom.functions.</span></span><span class="sig-name descname"><span class="pre">text_wrap</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">text</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">font</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">max_width</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#inkycal.custom.functions.text_wrap" title="Link to this definition"></a></dt>
|
||||
<span class="sig-prename descclassname"><span class="pre">inkycal.custom.functions.</span></span><span class="sig-name descname"><span class="pre">text_wrap</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">text</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">font</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">max_width</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#inkycal.custom.functions.text_wrap" title="Link to this definition"></a></dt>
|
||||
<dd><p>Splits a very long text into smaller parts</p>
|
||||
<p>Splits a long text to smaller lines which can fit in a line with max_width.
|
||||
Uses a Font object for more accurate calculations.</p>
|
||||
@ -334,7 +334,7 @@ splitting the text into the next chunk.</p></li>
|
||||
|
||||
<dl class="py function">
|
||||
<dt class="sig sig-object py" id="inkycal.custom.functions.write">
|
||||
<span class="sig-prename descclassname"><span class="pre">inkycal.custom.functions.</span></span><span class="sig-name descname"><span class="pre">write</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">image</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">xy</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">box_size</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">text</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">font</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">kwargs</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#inkycal.custom.functions.write" title="Link to this definition"></a></dt>
|
||||
<span class="sig-prename descclassname"><span class="pre">inkycal.custom.functions.</span></span><span class="sig-name descname"><span class="pre">write</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="pre">image:</span> <span class="pre"><module</span> <span class="pre">'PIL.Image'</span> <span class="pre">from</span> <span class="pre">'/home/runner/work/Inkycal/Inkycal/venv/lib/python3.11/site-packages/PIL/Image.py'>,</span> <span class="pre">xy:</span> <span class="pre">~typing.Tuple[int,</span> <span class="pre">int],</span> <span class="pre">box_size:</span> <span class="pre">~typing.Tuple[int,</span> <span class="pre">int],</span> <span class="pre">text:</span> <span class="pre">str,</span> <span class="pre">font=None,</span> <span class="pre">**kwargs</span></em><span class="sig-paren">)</span><a class="headerlink" href="#inkycal.custom.functions.write" title="Link to this definition"></a></dt>
|
||||
<dd><p>Writes text on an image.</p>
|
||||
<p>Writes given text at given position on the specified image.</p>
|
||||
<dl class="simple">
|
||||
|
BIN
fonts/MaterialIcons/MaterialIcons.ttf
Normal file
BIN
fonts/MaterialIcons/MaterialIcons.ttf
Normal file
Binary file not shown.
@ -8,9 +8,9 @@ import logging
|
||||
import os
|
||||
import time
|
||||
import traceback
|
||||
from typing import Tuple
|
||||
|
||||
import arrow
|
||||
import PIL
|
||||
import requests
|
||||
import tzlocal
|
||||
from PIL import Image
|
||||
@ -73,7 +73,7 @@ def get_system_tz() -> str:
|
||||
|
||||
>>> import arrow
|
||||
>>> print(arrow.now()) # returns non-timezone-aware time
|
||||
>>> print(arrow.now(tz=get_system_tz()) # prints timezone aware time.
|
||||
>>> print(arrow.now(tz=get_system_tz())) # prints timezone aware time.
|
||||
"""
|
||||
try:
|
||||
local_tz = tzlocal.get_localzone().key
|
||||
@ -111,7 +111,7 @@ def auto_fontsize(font, max_height):
|
||||
return font
|
||||
|
||||
|
||||
def write(image, xy, box_size, text, font=None, **kwargs):
|
||||
def write(image: Image, xy: Tuple[int, int], box_size: Tuple[int, int], text: str, font=None, **kwargs):
|
||||
"""Writes text on an image.
|
||||
|
||||
Writes given text at given position on the specified image.
|
||||
@ -161,7 +161,7 @@ def write(image, xy, box_size, text, font=None, **kwargs):
|
||||
text_bbox = font.getbbox(text)
|
||||
text_width = text_bbox[2] - text_bbox[0]
|
||||
text_bbox_height = font.getbbox("hg")
|
||||
text_height = text_bbox_height[3] - text_bbox_height[1]
|
||||
text_height = abs(text_bbox_height[3]) # - abs(text_bbox_height[1])
|
||||
|
||||
while text_width < int(box_width * fill_width) and text_height < int(box_height * fill_height):
|
||||
size += 1
|
||||
@ -169,12 +169,12 @@ def write(image, xy, box_size, text, font=None, **kwargs):
|
||||
text_bbox = font.getbbox(text)
|
||||
text_width = text_bbox[2] - text_bbox[0]
|
||||
text_bbox_height = font.getbbox("hg")
|
||||
text_height = text_bbox_height[3] - text_bbox_height[1]
|
||||
text_height = abs(text_bbox_height[3]) # - abs(text_bbox_height[1])
|
||||
|
||||
text_bbox = font.getbbox(text)
|
||||
text_width = text_bbox[2] - text_bbox[0]
|
||||
text_bbox_height = font.getbbox("hg")
|
||||
text_height = text_bbox_height[3] - text_bbox_height[1]
|
||||
text_height = abs(text_bbox_height[3]) # - abs(text_bbox_height[1])
|
||||
|
||||
# Truncate text if text is too long, so it can fit inside the box
|
||||
if (text_width, text_height) > (box_width, box_height):
|
||||
@ -184,7 +184,7 @@ def write(image, xy, box_size, text, font=None, **kwargs):
|
||||
text_bbox = font.getbbox(text)
|
||||
text_width = text_bbox[2] - text_bbox[0]
|
||||
text_bbox_height = font.getbbox("hg")
|
||||
text_height = text_bbox_height[3] - text_bbox_height[1]
|
||||
text_height = abs(text_bbox_height[3]) # - abs(text_bbox_height[1])
|
||||
logger.debug(text)
|
||||
|
||||
# Align text to desired position
|
||||
@ -195,10 +195,13 @@ def write(image, xy, box_size, text, font=None, **kwargs):
|
||||
elif alignment == "right":
|
||||
x = int(box_width - text_width)
|
||||
|
||||
# Vertical centering
|
||||
y = int((box_height / 2) - (text_height / 2))
|
||||
|
||||
# Draw the text in the text-box
|
||||
draw = ImageDraw.Draw(image)
|
||||
space = Image.new('RGBA', (box_width, box_height))
|
||||
ImageDraw.Draw(space).text((x, 0), text, fill=colour, font=font)
|
||||
ImageDraw.Draw(space).text((x, y), text, fill=colour, font=font)
|
||||
|
||||
# Uncomment following two lines, comment out above two lines to show
|
||||
# red text-box with white text (debugging purposes)
|
||||
@ -213,7 +216,7 @@ def write(image, xy, box_size, text, font=None, **kwargs):
|
||||
image.paste(space, xy, space)
|
||||
|
||||
|
||||
def text_wrap(text, font=None, max_width=None):
|
||||
def text_wrap(text: str, font=None, max_width=None):
|
||||
"""Splits a very long text into smaller parts
|
||||
|
||||
Splits a long text to smaller lines which can fit in a line with max_width.
|
||||
@ -249,7 +252,7 @@ def text_wrap(text, font=None, max_width=None):
|
||||
return lines
|
||||
|
||||
|
||||
def internet_available():
|
||||
def internet_available() -> bool:
|
||||
"""checks if the internet is available.
|
||||
|
||||
Attempts to connect to google.com with a timeout of 5 seconds to check
|
||||
@ -274,15 +277,16 @@ def internet_available():
|
||||
return False
|
||||
|
||||
|
||||
def draw_border(image, xy, size, radius=5, thickness=1, shrinkage=(0.1, 0.1)):
|
||||
def draw_border(image: Image, xy: Tuple[int, int], size: Tuple[int, int], radius: int = 5, thickness: int = 1,
|
||||
shrinkage: Tuple[int, int] = (0.1, 0.1)) -> None:
|
||||
"""Draws a border at given coordinates.
|
||||
|
||||
Args:
|
||||
- image: The image on which the border should be drawn (usually im_black or
|
||||
im_colour.
|
||||
im_colour).
|
||||
|
||||
- xy: Tuple representing the top-left corner of the border e.g. (32, 100)
|
||||
where 32 is the x co-ordinate and 100 is the y-coordinate.
|
||||
where 32 is the x-coordinate and 100 is the y-coordinate.
|
||||
|
||||
- size: Size of the border as a tuple -> (width, height).
|
||||
|
||||
@ -320,6 +324,7 @@ def draw_border(image, xy, size, radius=5, thickness=1, shrinkage=(0.1, 0.1)):
|
||||
c5, c6 = ((x + width) - diameter, (y + height) - diameter), (x + width, y + height)
|
||||
c7, c8 = (x, (y + height) - diameter), (x + diameter, y + height)
|
||||
|
||||
|
||||
# Draw lines and arcs, creating a square with round corners
|
||||
draw = ImageDraw.Draw(image)
|
||||
draw.line((p1, p2), fill=colour, width=thickness)
|
||||
@ -334,7 +339,7 @@ def draw_border(image, xy, size, radius=5, thickness=1, shrinkage=(0.1, 0.1)):
|
||||
draw.arc((c7, c8), 90, 180, fill=colour, width=thickness)
|
||||
|
||||
|
||||
def draw_border_2(im: PIL.Image, xy: tuple, size: tuple, radius: int):
|
||||
def draw_border_2(im: Image, xy: Tuple[int, int], size: Tuple[int, int], radius: int):
|
||||
draw = ImageDraw.Draw(im)
|
||||
|
||||
x, y = xy
|
||||
|
@ -41,18 +41,9 @@ def get_json_from_url(request_url):
|
||||
|
||||
|
||||
class OpenWeatherMap:
|
||||
def __init__(
|
||||
self,
|
||||
api_key: str,
|
||||
city_id: int = None,
|
||||
lat: float = None,
|
||||
lon: float = None,
|
||||
api_version: API_VERSIONS = "2.5",
|
||||
temp_unit: TEMP_UNITS = "celsius",
|
||||
wind_unit: WIND_UNITS = "meters_sec",
|
||||
language: str = "en",
|
||||
tz_name: str = "UTC",
|
||||
) -> None:
|
||||
def __init__(self, api_key: str, city_id: int = None, lat: float = None, lon: float = None,
|
||||
api_version: API_VERSIONS = "2.5", temp_unit: TEMP_UNITS = "celsius",
|
||||
wind_unit: WIND_UNITS = "meters_sec", language: str = "en", tz_name: str = "UTC") -> None:
|
||||
self.api_key = api_key
|
||||
self.temp_unit = temp_unit
|
||||
self.wind_unit = wind_unit
|
||||
@ -187,7 +178,7 @@ class OpenWeatherMap:
|
||||
:return:
|
||||
Forecast dictionary
|
||||
"""
|
||||
# Make sure hourly forecasts are up to date
|
||||
# Make sure hourly forecasts are up-to-date
|
||||
_ = self.get_weather_forecast()
|
||||
|
||||
# Calculate the start and end times for the specified number of days from now
|
||||
@ -207,7 +198,7 @@ class OpenWeatherMap:
|
||||
]
|
||||
|
||||
# In case the next available forecast is already for the next day, use that one for the less than 3 remaining hours of today
|
||||
if forecasts == []:
|
||||
if not forecasts:
|
||||
forecasts.append(self.hourly_forecasts[0])
|
||||
|
||||
# Get rain and temperatures for that day
|
||||
|
@ -75,6 +75,8 @@ class Agenda(inkycal_module):
|
||||
# Additional config
|
||||
self.timezone = get_system_tz()
|
||||
|
||||
self.icon_font = ImageFont.truetype(fonts['MaterialIcons'], size=self.fontsize)
|
||||
|
||||
# give an OK message
|
||||
logger.debug(f'{__name__} loaded')
|
||||
|
||||
@ -201,10 +203,10 @@ class Agenda(inkycal_module):
|
||||
write(im_black, (x_time, line_pos[cursor][1]),
|
||||
(time_width, line_height), time,
|
||||
font=self.font, alignment='right')
|
||||
if parser.all_day(_):
|
||||
else:
|
||||
write(im_black, (x_time, line_pos[cursor][1]),
|
||||
(time_width, line_height), "all day",
|
||||
font=self.font, alignment='right')
|
||||
(time_width, line_height), "\ue878",
|
||||
font=self.icon_font, alignment='right')
|
||||
|
||||
write(im_black, (x_event, line_pos[cursor][1]),
|
||||
(event_width, line_height),
|
||||
|
@ -6,16 +6,16 @@ Copyright by aceinnolab
|
||||
# pylint: disable=logging-fstring-interpolation
|
||||
|
||||
import calendar as cal
|
||||
import arrow
|
||||
from inkycal.modules.template import inkycal_module
|
||||
|
||||
from inkycal.custom import *
|
||||
from inkycal.modules.template import inkycal_module
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Calendar(inkycal_module):
|
||||
"""Calendar class
|
||||
Create monthly calendar and show events from given icalendars
|
||||
Create monthly calendar and show events from given iCalendars
|
||||
"""
|
||||
|
||||
name = "Calendar - Show monthly calendar with events from iCalendars"
|
||||
@ -61,7 +61,7 @@ class Calendar(inkycal_module):
|
||||
self._days_with_events = None
|
||||
|
||||
# optional parameters
|
||||
self.weekstart = config['week_starts_on']
|
||||
self.week_start = config['week_starts_on']
|
||||
self.show_events = config['show_events']
|
||||
self.date_format = config["date_format"]
|
||||
self.time_format = config['time_format']
|
||||
@ -109,7 +109,7 @@ class Calendar(inkycal_module):
|
||||
# Allocate space for month-names, weekdays etc.
|
||||
month_name_height = int(im_height * 0.10)
|
||||
text_bbox_height = self.font.getbbox("hg")
|
||||
weekdays_height = int((text_bbox_height[3] - text_bbox_height[1]) * 1.25)
|
||||
weekdays_height = int((abs(text_bbox_height[3]) + abs(text_bbox_height[1])) * 1.25)
|
||||
logger.debug(f"month_name_height: {month_name_height}")
|
||||
logger.debug(f"weekdays_height: {weekdays_height}")
|
||||
|
||||
@ -156,13 +156,13 @@ class Calendar(inkycal_module):
|
||||
|
||||
now = arrow.now(tz=self.timezone)
|
||||
|
||||
# Set weekstart of calendar to specified weekstart
|
||||
if self.weekstart == "Monday":
|
||||
# Set week-start of calendar to specified week-start
|
||||
if self.week_start == "Monday":
|
||||
cal.setfirstweekday(cal.MONDAY)
|
||||
weekstart = now.shift(days=-now.weekday())
|
||||
week_start = now.shift(days=-now.weekday())
|
||||
else:
|
||||
cal.setfirstweekday(cal.SUNDAY)
|
||||
weekstart = now.shift(days=-now.isoweekday())
|
||||
week_start = now.shift(days=-now.isoweekday())
|
||||
|
||||
# Write the name of current month
|
||||
write(
|
||||
@ -174,9 +174,9 @@ class Calendar(inkycal_module):
|
||||
autofit=True,
|
||||
)
|
||||
|
||||
# Set up weeknames in local language and add to main section
|
||||
# Set up week-names in local language and add to main section
|
||||
weekday_names = [
|
||||
weekstart.shift(days=+_).format('ddd', locale=self.language)
|
||||
week_start.shift(days=+_).format('ddd', locale=self.language)
|
||||
for _ in range(7)
|
||||
]
|
||||
logger.debug(f'weekday names: {weekday_names}')
|
||||
@ -192,7 +192,7 @@ class Calendar(inkycal_module):
|
||||
fill_height=0.9,
|
||||
)
|
||||
|
||||
# Create a calendar template and flatten (remove nestings)
|
||||
# Create a calendar template and flatten (remove nesting)
|
||||
calendar_flat = self.flatten(cal.monthcalendar(now.year, now.month))
|
||||
# logger.debug(f" calendar_flat: {calendar_flat}")
|
||||
|
||||
@ -281,7 +281,7 @@ class Calendar(inkycal_module):
|
||||
month_start = arrow.get(now.floor('month'))
|
||||
month_end = arrow.get(now.ceil('month'))
|
||||
|
||||
# fetch events from given icalendars
|
||||
# fetch events from given iCalendars
|
||||
self.ical = iCalendar()
|
||||
parser = self.ical
|
||||
|
||||
@ -300,8 +300,6 @@ class Calendar(inkycal_module):
|
||||
|
||||
# Handle multi-day events by adding all days between start and end
|
||||
for event in month_events:
|
||||
start_date = event['begin'].date()
|
||||
end_date = event['end'].date()
|
||||
|
||||
# Convert start and end dates to arrow objects with timezone
|
||||
start = arrow.get(event['begin'].date(), tzinfo=self.timezone)
|
||||
@ -324,9 +322,7 @@ class Calendar(inkycal_module):
|
||||
im_colour,
|
||||
grid[days],
|
||||
(icon_width, icon_height),
|
||||
radius=6,
|
||||
thickness=1,
|
||||
shrinkage=(0, 0),
|
||||
radius=6
|
||||
)
|
||||
|
||||
# Filter upcoming events until 4 weeks in the future
|
||||
@ -369,7 +365,8 @@ class Calendar(inkycal_module):
|
||||
event_duration = (event['end'] - event['begin']).days
|
||||
if event_duration > 1:
|
||||
# Format the duration using Arrow's localization
|
||||
days_translation = arrow.get().shift(days=event_duration).humanize(only_distance=True, locale=lang)
|
||||
days_translation = arrow.get().shift(days=event_duration).humanize(only_distance=True,
|
||||
locale=lang)
|
||||
the_name = f"{event['title']} ({days_translation})"
|
||||
else:
|
||||
the_name = event['title']
|
||||
|
@ -2,12 +2,12 @@
|
||||
Inkycal weather module
|
||||
Copyright by aceinnolab
|
||||
"""
|
||||
|
||||
import arrow
|
||||
import decimal
|
||||
import logging
|
||||
import math
|
||||
from typing import Tuple
|
||||
|
||||
import arrow
|
||||
from PIL import Image
|
||||
from PIL import ImageDraw
|
||||
from PIL import ImageFont
|
||||
@ -51,7 +51,7 @@ class Weather(inkycal_module):
|
||||
"options": [True, False],
|
||||
},
|
||||
|
||||
"round_windspeed": {
|
||||
"round_wind_speed": {
|
||||
"label": "Round windspeed?",
|
||||
"options": [True, False],
|
||||
},
|
||||
@ -89,7 +89,7 @@ class Weather(inkycal_module):
|
||||
|
||||
# Check if all required parameters are present
|
||||
for param in self.requires:
|
||||
if not param in config:
|
||||
if param not in config:
|
||||
raise Exception(f'config is missing {param}')
|
||||
|
||||
# required parameters
|
||||
@ -98,7 +98,7 @@ class Weather(inkycal_module):
|
||||
|
||||
# optional parameters
|
||||
self.round_temperature = config['round_temperature']
|
||||
self.round_windspeed = config['round_windspeed']
|
||||
self.round_wind_speed = config['round_windspeed']
|
||||
self.forecast_interval = config['forecast_interval']
|
||||
self.hour_format = int(config['hour_format'])
|
||||
if config['units'] == "imperial":
|
||||
@ -106,7 +106,7 @@ class Weather(inkycal_module):
|
||||
else:
|
||||
self.temp_unit = "celsius"
|
||||
|
||||
if config['use_beaufort'] == True:
|
||||
if config['use_beaufort']:
|
||||
self.wind_unit = "beaufort"
|
||||
elif config['units'] == "imperial":
|
||||
self.wind_unit = "miles_hour"
|
||||
@ -145,8 +145,6 @@ class Weather(inkycal_module):
|
||||
# give an OK message
|
||||
logger.debug(f"{__name__} loaded")
|
||||
|
||||
|
||||
|
||||
def generate_image(self):
|
||||
"""Generate image for this module"""
|
||||
|
||||
@ -224,12 +222,19 @@ class Weather(inkycal_module):
|
||||
'50n': '\uf023'
|
||||
}
|
||||
|
||||
def draw_icon(image, xy, box_size, icon, rotation=None):
|
||||
"""Custom function to add icons of weather font on image
|
||||
image = on which image should the text be added?
|
||||
xy = xy-coordinates as tuple -> (x,y)
|
||||
box_size = size of text-box -> (width,height)
|
||||
icon = icon-unicode, looks this up in weathericons dictionary
|
||||
def draw_icon(image: Image, xy: Tuple[int, int], box_size: Tuple[int, int], icon: str, rotation=None):
|
||||
"""Custom function to add icons of weather font on the image.
|
||||
|
||||
Args:
|
||||
- image:
|
||||
the image on which image should the text be added
|
||||
- xy:
|
||||
coordinates as tuple -> (x,y)
|
||||
- box_size:
|
||||
size of text-box -> (width,height)
|
||||
- icon:
|
||||
icon-unicode, looks this up in weather-icons dictionary
|
||||
|
||||
"""
|
||||
|
||||
icon_size_correction = {
|
||||
@ -264,7 +269,6 @@ class Weather(inkycal_module):
|
||||
'\uf0a0': 0,
|
||||
'\uf0a3': 0,
|
||||
'\uf0a7': 0,
|
||||
'\uf0aa': 0,
|
||||
'\uf0ae': 0
|
||||
}
|
||||
|
||||
@ -278,8 +282,7 @@ class Weather(inkycal_module):
|
||||
font = ImageFont.truetype(font.path, size)
|
||||
text_width, text_height = font.getbbox(text)[2:]
|
||||
|
||||
while (text_width < int(box_width * 0.9) and
|
||||
text_height < int(box_height * 0.9)):
|
||||
while text_width < int(box_width * 0.9) and text_height < int(box_height * 0.9):
|
||||
size += 1
|
||||
font = ImageFont.truetype(font.path, size)
|
||||
text_width, text_height = font.getbbox(text)[2:]
|
||||
@ -290,8 +293,6 @@ class Weather(inkycal_module):
|
||||
x = int((box_width / 2) - (text_width / 2))
|
||||
y = int((box_height / 2) - (text_height / 2))
|
||||
|
||||
# Draw the text in the text-box
|
||||
draw = ImageDraw.Draw(image)
|
||||
space = Image.new('RGBA', (box_width, box_height))
|
||||
ImageDraw.Draw(space).text((x, y), text, fill='black', font=font)
|
||||
|
||||
@ -350,17 +351,17 @@ class Weather(inkycal_module):
|
||||
row3 = row2 + line_gap + row_height
|
||||
|
||||
# Draw lines on each row and border
|
||||
############################################################################
|
||||
## draw = ImageDraw.Draw(im_black)
|
||||
## draw.line((0, 0, im_width, 0), fill='red')
|
||||
## draw.line((0, im_height-1, im_width, im_height-1), fill='red')
|
||||
## draw.line((0, row1, im_width, row1), fill='black')
|
||||
## draw.line((0, row1+row_height, im_width, row1+row_height), fill='black')
|
||||
## draw.line((0, row2, im_width, row2), fill='black')
|
||||
## draw.line((0, row2+row_height, im_width, row2+row_height), fill='black')
|
||||
## draw.line((0, row3, im_width, row3), fill='black')
|
||||
## draw.line((0, row3+row_height, im_width, row3+row_height), fill='black')
|
||||
############################################################################
|
||||
###########################################################################
|
||||
# draw = ImageDraw.Draw(im_black)
|
||||
# draw.line((0, 0, im_width, 0), fill='red')
|
||||
# draw.line((0, im_height-1, im_width, im_height-1), fill='red')
|
||||
# draw.line((0, row1, im_width, row1), fill='black')
|
||||
# draw.line((0, row1+row_height, im_width, row1+row_height), fill='black')
|
||||
# draw.line((0, row2, im_width, row2), fill='black')
|
||||
# draw.line((0, row2+row_height, im_width, row2+row_height), fill='black')
|
||||
# draw.line((0, row3, im_width, row3), fill='black')
|
||||
# draw.line((0, row3+row_height, im_width, row3+row_height), fill='black')
|
||||
###########################################################################
|
||||
|
||||
# Positions for current weather details
|
||||
weather_icon_pos = (col1, 0)
|
||||
@ -379,24 +380,24 @@ class Weather(inkycal_module):
|
||||
sunset_time_pos = (col3 + icon_small, row3)
|
||||
|
||||
# Positions for forecast 1
|
||||
stamp_fc1 = (col4, row1)
|
||||
icon_fc1 = (col4, row1 + row_height)
|
||||
temp_fc1 = (col4, row3)
|
||||
stamp_fc1 = (col4, row1) # noqa
|
||||
icon_fc1 = (col4, row1 + row_height) # noqa
|
||||
temp_fc1 = (col4, row3) # noqa
|
||||
|
||||
# Positions for forecast 2
|
||||
stamp_fc2 = (col5, row1)
|
||||
icon_fc2 = (col5, row1 + row_height)
|
||||
temp_fc2 = (col5, row3)
|
||||
stamp_fc2 = (col5, row1) # noqa
|
||||
icon_fc2 = (col5, row1 + row_height) # noqa
|
||||
temp_fc2 = (col5, row3) # noqa
|
||||
|
||||
# Positions for forecast 3
|
||||
stamp_fc3 = (col6, row1)
|
||||
icon_fc3 = (col6, row1 + row_height)
|
||||
temp_fc3 = (col6, row3)
|
||||
stamp_fc3 = (col6, row1) # noqa
|
||||
icon_fc3 = (col6, row1 + row_height) # noqa
|
||||
temp_fc3 = (col6, row3) # noqa
|
||||
|
||||
# Positions for forecast 4
|
||||
stamp_fc4 = (col7, row1)
|
||||
icon_fc4 = (col7, row1 + row_height)
|
||||
temp_fc4 = (col7, row3)
|
||||
stamp_fc4 = (col7, row1) # noqa
|
||||
icon_fc4 = (col7, row1 + row_height) # noqa
|
||||
temp_fc4 = (col7, row3) # noqa
|
||||
|
||||
# Create current-weather and weather-forecast objects
|
||||
logging.debug('looking up location by ID')
|
||||
@ -405,7 +406,7 @@ class Weather(inkycal_module):
|
||||
|
||||
# Set decimals
|
||||
dec_temp = 0 if self.round_temperature == True else 1
|
||||
dec_wind = 0 if self.round_windspeed == True else 1
|
||||
dec_wind = 0 if self.round_wind_speed == True else 1
|
||||
|
||||
logging.debug(f'temperature unit: {self.temp_unit}')
|
||||
logging.debug(f'decimals temperature: {dec_temp} | decimals wind: {dec_wind}')
|
||||
@ -425,7 +426,8 @@ class Weather(inkycal_module):
|
||||
fc_data['fc' + str(index + 1)] = {
|
||||
'temp': f"{forecast['temp']:.{dec_temp}f}{self.tempDispUnit}",
|
||||
'icon': forecast["icon"],
|
||||
'stamp': forecast["datetime"].strftime("%I %p" if self.hour_format == 12 else "%H:%M")}
|
||||
'stamp': forecast["datetime"].strftime("%I %p" if self.hour_format == 12 else "%H:%M")
|
||||
}
|
||||
|
||||
elif self.forecast_interval == 'daily':
|
||||
|
||||
@ -514,6 +516,9 @@ class Weather(inkycal_module):
|
||||
# Add the forecast data to the correct places
|
||||
for pos in range(1, len(fc_data) + 1):
|
||||
stamp = fc_data[f'fc{pos}']['stamp']
|
||||
# check if we're using daily forecasts
|
||||
if "day" in stamp:
|
||||
stamp = arrow.get(fc_data[f'fc{pos}']['stamp'], "dddd").format("dddd", locale="de")
|
||||
|
||||
icon = weather_icons[fc_data[f'fc{pos}']['icon']]
|
||||
temp = fc_data[f'fc{pos}']['temp']
|
||||
|
@ -41,7 +41,10 @@ class Webshot(inkycal_module):
|
||||
},
|
||||
"crop_h": {
|
||||
"label": "Please enter the crop height",
|
||||
}
|
||||
},
|
||||
"rotation": {
|
||||
"label": "Please enter the rotation. Must be either 0, 90, 180 or 270",
|
||||
},
|
||||
}
|
||||
|
||||
def __init__(self, config):
|
||||
@ -73,6 +76,12 @@ class Webshot(inkycal_module):
|
||||
else:
|
||||
self.crop_y = 0
|
||||
|
||||
self.rotation = 0
|
||||
if "rotation" in config:
|
||||
self.rotation = int(config["rotation"])
|
||||
if self.rotation not in [0, 90, 180, 270]:
|
||||
raise Exception("Rotation must be either 0, 90, 180 or 270")
|
||||
|
||||
# give an OK message
|
||||
logger.debug(f'Inkycal webshot loaded')
|
||||
|
||||
@ -106,7 +115,7 @@ class Webshot(inkycal_module):
|
||||
logger.info(
|
||||
f'preparing webshot from {self.url}... cropH{self.crop_h} cropW{self.crop_w} cropX{self.crop_x} cropY{self.crop_y}')
|
||||
|
||||
shot = WebShot()
|
||||
shot = WebShot(size=(im_height, im_width))
|
||||
|
||||
shot.params = {
|
||||
"--crop-x": self.crop_x,
|
||||
@ -152,6 +161,16 @@ class Webshot(inkycal_module):
|
||||
|
||||
centerPosX = int((im_width / 2) - (im.image.width / 2))
|
||||
|
||||
|
||||
if self.rotation != 0:
|
||||
webshotSpaceBlack.paste(im_webshot_black, (centerPosX, webshotCenterPosY))
|
||||
im_black.paste(webshotSpaceBlack)
|
||||
im_black = im_black.rotate(self.rotation, expand=True)
|
||||
|
||||
webshotSpaceColour.paste(im_webshot_colour, (centerPosX, webshotCenterPosY))
|
||||
im_colour.paste(webshotSpaceColour)
|
||||
im_colour = im_colour.rotate(self.rotation, expand=True)
|
||||
else:
|
||||
webshotSpaceBlack.paste(im_webshot_black, (centerPosX, webshotCenterPosY))
|
||||
im_black.paste(webshotSpaceBlack)
|
||||
|
||||
|
@ -37,7 +37,7 @@ python-dotenv==1.0.1
|
||||
pytz==2024.1
|
||||
PyYAML==6.0.1
|
||||
recurring-ical-events==2.1.2
|
||||
requests==2.31.0
|
||||
requests==2.32.0
|
||||
sgmllib3k==1.0.0
|
||||
six==1.16.0
|
||||
soupsieve==2.5
|
||||
@ -46,7 +46,7 @@ types-python-dateutil==2.8.19.20240106
|
||||
typing_extensions==4.9.0
|
||||
tzdata==2024.1
|
||||
tzlocal==5.2
|
||||
urllib3==2.2.0
|
||||
urllib3==2.2.2
|
||||
virtualenv==20.25.0
|
||||
webencodings==0.5.1
|
||||
x-wr-timezone==0.0.6
|
||||
|
@ -1,12 +1,22 @@
|
||||
"""
|
||||
Test the functions in the functions module.
|
||||
"""
|
||||
import unittest
|
||||
|
||||
from PIL import Image, ImageFont
|
||||
from inkycal.custom import write, fonts
|
||||
|
||||
from inkycal.custom import write, fonts, get_system_tz
|
||||
|
||||
|
||||
def test_write():
|
||||
class TestIcalendar(unittest.TestCase):
|
||||
|
||||
def test_write(self):
|
||||
im = Image.new("RGB", (500, 200), "white")
|
||||
font = ImageFont.truetype(fonts['NotoSans-SemiCondensed'], size=40)
|
||||
write(im, (125, 75), (250, 50), "Hello World", font)
|
||||
# im.show()
|
||||
|
||||
def test_get_system_tz(self):
|
||||
tz = get_system_tz()
|
||||
assert isinstance(tz, str)
|
||||
|
||||
|
@ -37,7 +37,7 @@ tests = [
|
||||
"size": [500, 800],
|
||||
"ical_urls": sample_url,
|
||||
"ical_files": None,
|
||||
"date_format": "ddd D MMM",
|
||||
"date_format": "DD.MMMM YYYY",
|
||||
"time_format": "HH:mm",
|
||||
"padding_x": 10,
|
||||
"padding_y": 10,
|
||||
|
@ -20,7 +20,7 @@ tests = [
|
||||
{
|
||||
"name": "Calendar",
|
||||
"config": {
|
||||
"size": [500, 500],
|
||||
"size": [500, 600],
|
||||
"week_starts_on": "Monday",
|
||||
"show_events": True,
|
||||
"ical_urls": sample_url,
|
||||
|
@ -34,7 +34,7 @@ tests = [
|
||||
"padding_x": 10,
|
||||
"padding_y": 10,
|
||||
"fontsize": 12,
|
||||
"language": "en"
|
||||
"language": "de"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -16,33 +16,13 @@ preview = Inkyimage.preview
|
||||
merge = Inkyimage.merge
|
||||
|
||||
tests = [
|
||||
{
|
||||
"position": 1,
|
||||
"name": "Webshot",
|
||||
"config": {
|
||||
"size": [400, 100],
|
||||
"url": "https://github.com",
|
||||
"palette": "bwr",
|
||||
"padding_x": 10, "padding_y": 10, "fontsize": 12, "language": "en"
|
||||
}
|
||||
},
|
||||
{
|
||||
"position": 1,
|
||||
"name": "Webshot",
|
||||
"config": {
|
||||
"size": [400, 200],
|
||||
"url": "https://github.com",
|
||||
"palette": "bwy",
|
||||
"padding_x": 10, "padding_y": 10, "fontsize": 12, "language": "en"
|
||||
}
|
||||
},
|
||||
{
|
||||
"position": 1,
|
||||
"name": "Webshot",
|
||||
"config": {
|
||||
"size": [400, 300],
|
||||
"url": "https://github.com",
|
||||
"palette": "bw",
|
||||
"url": "https://aceinnolab.com",
|
||||
"palette": "bwr",
|
||||
"padding_x": 10, "padding_y": 10, "fontsize": 12, "language": "en"
|
||||
}
|
||||
},
|
||||
@ -51,8 +31,31 @@ tests = [
|
||||
"name": "Webshot",
|
||||
"config": {
|
||||
"size": [400, 400],
|
||||
"url": "https://github.com",
|
||||
"url": "https://aceinnolab.com",
|
||||
"palette": "bwy",
|
||||
"rotation": 0,
|
||||
"padding_x": 10, "padding_y": 10, "fontsize": 12, "language": "en"
|
||||
}
|
||||
},
|
||||
{
|
||||
"position": 1,
|
||||
"name": "Webshot",
|
||||
"config": {
|
||||
"size": [400, 600],
|
||||
"url": "https://aceinnolab.com",
|
||||
"palette": "bw",
|
||||
"rotation": 90,
|
||||
"padding_x": 10, "padding_y": 10, "fontsize": 12, "language": "en"
|
||||
}
|
||||
},
|
||||
{
|
||||
"position": 1,
|
||||
"name": "Webshot",
|
||||
"config": {
|
||||
"size": [400, 800],
|
||||
"url": "https://aceinnolab.com",
|
||||
"palette": "bwr",
|
||||
"rotation": 180,
|
||||
"padding_x": 10, "padding_y": 10, "fontsize": 12, "language": "en"
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user