kopia lustrzana https://github.com/peterhinch/micropython-micro-gui
demos/calendar.py: Add date module, fix bugs.
rodzic
a940c20fd2
commit
b40d698fbc
|
@ -1,9 +1,7 @@
|
||||||
# calendar.py Test Grid class.
|
# calendar.py Test Grid class. Requires date.py.
|
||||||
|
|
||||||
# Released under the MIT License (MIT). See LICENSE.
|
# Released under the MIT License (MIT). See LICENSE.
|
||||||
# Copyright (c) 2023 Peter Hinch
|
# Copyright (c) 2023 Peter Hinch
|
||||||
# As written requires a 240*320 pixel display, but could easily be reduced
|
|
||||||
# with smaller fonts.
|
|
||||||
|
|
||||||
# hardware_setup must be imported before other modules because of RAM use.
|
# hardware_setup must be imported before other modules because of RAM use.
|
||||||
import hardware_setup # Create a display instance
|
import hardware_setup # Create a display instance
|
||||||
|
@ -16,67 +14,14 @@ from gui.core.writer import CWriter
|
||||||
import gui.fonts.font10 as font
|
import gui.fonts.font10 as font
|
||||||
import gui.fonts.font14 as font1
|
import gui.fonts.font14 as font1
|
||||||
from gui.core.colors import *
|
from gui.core.colors import *
|
||||||
|
from .date import DateCal
|
||||||
from time import mktime, localtime
|
|
||||||
SECS_PER_DAY = const(86400)
|
|
||||||
|
|
||||||
class Date:
|
|
||||||
|
|
||||||
days = ('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday')
|
|
||||||
months = ('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August',
|
|
||||||
'September', 'October', 'November', 'December')
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.now()
|
|
||||||
|
|
||||||
def now(self):
|
|
||||||
lt = list(localtime())
|
|
||||||
lt[3] = 6 # Disambiguate midnight
|
|
||||||
self.cur = mktime(lt) // SECS_PER_DAY
|
|
||||||
self.update()
|
|
||||||
|
|
||||||
def update(self, lt=None):
|
|
||||||
if lt is not None:
|
|
||||||
lt[3] = 6
|
|
||||||
self.cur = mktime(lt) // SECS_PER_DAY
|
|
||||||
lt = localtime(self.cur * SECS_PER_DAY)
|
|
||||||
self.year = lt[0]
|
|
||||||
self.month = lt[1]
|
|
||||||
self.mday = lt[2]
|
|
||||||
self.wday = lt[6]
|
|
||||||
ml = self.mlen(self.month)
|
|
||||||
self.month_length = ml
|
|
||||||
self.wday1 = (self.wday - self.mday + 1) % 7 # Weekday of 1st of month
|
|
||||||
# Commented out code provides support for UK DST calculation
|
|
||||||
#wdayld = (self.wday1 + ml -1) % 7 # Weekday of last day of month
|
|
||||||
#self.mday_sun = ml - (wdayld + 1) % 7 # Day of month of last Sunday
|
|
||||||
|
|
||||||
def add_days(self, n):
|
|
||||||
self.cur += n
|
|
||||||
self.update()
|
|
||||||
|
|
||||||
def add_months(self, n): # Crude algorithm for small n
|
|
||||||
for _ in range(abs(n)):
|
|
||||||
self.cur += self.month_length if n > 0 else -self.mlen(self.month - 1)
|
|
||||||
self.update()
|
|
||||||
|
|
||||||
def add_years(self, n):
|
|
||||||
lt = list(localtime(self.cur * SECS_PER_DAY))
|
|
||||||
lt[0] += n
|
|
||||||
self.update(lt)
|
|
||||||
|
|
||||||
def mlen(self, month):
|
|
||||||
days = (31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)[month - 1]
|
|
||||||
year = self.year
|
|
||||||
return days if days else (28 if year % 4 else (29 if year % 100 else 28))
|
|
||||||
|
|
||||||
|
|
||||||
class BaseScreen(Screen):
|
class BaseScreen(Screen):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.date = Date()
|
self.date = DateCal()
|
||||||
wri = CWriter(ssd, font, GREEN, BLACK, False)
|
wri = CWriter(ssd, font, GREEN, BLACK, False)
|
||||||
wri1 = CWriter(ssd, font1, WHITE, BLACK, False)
|
wri1 = CWriter(ssd, font1, WHITE, BLACK, False)
|
||||||
col = 2
|
col = 2
|
||||||
|
@ -87,28 +32,28 @@ class BaseScreen(Screen):
|
||||||
self.lbl = Label(wri, row, col, text = (colwidth + 4) * cols, justify=Label.CENTRE)
|
self.lbl = Label(wri, row, col, text = (colwidth + 4) * cols, justify=Label.CENTRE)
|
||||||
row = self.lbl.mrow
|
row = self.lbl.mrow
|
||||||
self.grid = Grid(wri, row, col, colwidth, rows, cols, justify=Label.CENTRE)
|
self.grid = Grid(wri, row, col, colwidth, rows, cols, justify=Label.CENTRE)
|
||||||
for n, day in enumerate(Date.days):
|
for n, day in enumerate(DateCal.days):
|
||||||
self.grid[[0, n]] = day[:3]
|
self.grid[[0, n]] = day[:3]
|
||||||
|
|
||||||
row = self.grid.mrow + 4
|
row = self.grid.mrow + 4
|
||||||
ht = 30
|
ht = 30
|
||||||
b = Button(wri, row, col, height=ht, shape=CIRCLE, text="y-", callback=self.adjust, args=(self.date.add_years, -1))
|
b = Button(wri, row, col, height=ht, shape=CIRCLE, text="y-", callback=self.adjust, args=("y", -1))
|
||||||
col = b.mcol + 2
|
col = b.mcol + 2
|
||||||
b = Button(wri, row, col, height=ht, shape=CIRCLE, text="y+", callback=self.adjust, args=(self.date.add_years, 1))
|
b = Button(wri, row, col, height=ht, shape=CIRCLE, text="y+", callback=self.adjust, args=("y", 1))
|
||||||
col = b.mcol + 5
|
col = b.mcol + 5
|
||||||
b = Button(wri, row, col, height=ht, shape=CIRCLE, text="m-", callback=self.adjust, args=(self.date.add_months, -1))
|
b = Button(wri, row, col, height=ht, shape=CIRCLE, text="m-", callback=self.adjust, args=("m", -1))
|
||||||
col = b.mcol + 2
|
col = b.mcol + 2
|
||||||
b = Button(wri, row, col, height=ht, shape=CIRCLE, text="m+", callback=self.adjust, args=(self.date.add_months, 1))
|
b = Button(wri, row, col, height=ht, shape=CIRCLE, text="m+", callback=self.adjust, args=("m", 1))
|
||||||
col = b.mcol + 5
|
col = b.mcol + 5
|
||||||
b = Button(wri, row, col, height=ht, shape=CIRCLE, text="w-", callback=self.adjust, args=(self.date.add_days, -7))
|
b = Button(wri, row, col, height=ht, shape=CIRCLE, text="w-", callback=self.adjust, args=("d", -7))
|
||||||
col = b.mcol + 2
|
col = b.mcol + 2
|
||||||
b = Button(wri, row, col, height=ht, shape=CIRCLE, text="w+", callback=self.adjust, args=(self.date.add_days, 7))
|
b = Button(wri, row, col, height=ht, shape=CIRCLE, text="w+", callback=self.adjust, args=("d", 7))
|
||||||
col = b.mcol + 5
|
col = b.mcol + 5
|
||||||
b = Button(wri, row, col, height=ht, shape=CIRCLE, text="d-", callback=self.adjust, args=(self.date.add_days, -1))
|
b = Button(wri, row, col, height=ht, shape=CIRCLE, text="d-", callback=self.adjust, args=("d", -1))
|
||||||
col = b.mcol + 2
|
col = b.mcol + 2
|
||||||
b = Button(wri, row, col, height=ht, shape=CIRCLE, text="d+", callback=self.adjust, args=(self.date.add_days, 1))
|
b = Button(wri, row, col, height=ht, shape=CIRCLE, text="d+", callback=self.adjust, args=("d", 1))
|
||||||
col = b.mcol + 5
|
col = b.mcol + 5
|
||||||
b = Button(wri, row, col, height=ht, shape=CIRCLE, fgcolor=BLUE, text="H", callback=self.adjust, args=(self.date.now,))
|
b = Button(wri, row, col, height=ht, shape=CIRCLE, fgcolor=BLUE, text="H", callback=self.adjust, args=("h",))
|
||||||
#row = b.mrow + 10
|
#row = b.mrow + 10
|
||||||
col = 2
|
col = 2
|
||||||
row = ssd.height - (wri1.height + 2)
|
row = ssd.height - (wri1.height + 2)
|
||||||
|
@ -117,33 +62,42 @@ class BaseScreen(Screen):
|
||||||
CloseButton(wri) # Quit the application
|
CloseButton(wri) # Quit the application
|
||||||
|
|
||||||
def adjust(self, _, f, n=0):
|
def adjust(self, _, f, n=0):
|
||||||
f(n) if n else f()
|
d = self.date
|
||||||
|
if f == "y":
|
||||||
|
d.year += n
|
||||||
|
elif f == "m":
|
||||||
|
d.month += n
|
||||||
|
elif f == "d":
|
||||||
|
d.day += n
|
||||||
|
elif f =="h":
|
||||||
|
d.now()
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
def cell():
|
def cell():
|
||||||
#d.clear()
|
|
||||||
if cur.year == today.year and cur.month == today.month and mday == today.mday: # Today
|
if cur.year == today.year and cur.month == today.month and mday == today.mday: # Today
|
||||||
d["fgcolor"] = RED
|
d["fgcolor"] = RED
|
||||||
elif mday == cur.mday: # Currency
|
elif mday == cur.mday: # Currency
|
||||||
d["fgcolor"] = YELLOW
|
d["fgcolor"] = YELLOW
|
||||||
|
elif mday in sundays:
|
||||||
|
d["fgcolor"] = BLUE
|
||||||
else:
|
else:
|
||||||
d["fgcolor"] = GREEN
|
d["fgcolor"] = GREEN
|
||||||
d["text"] = str(mday)
|
d["text"] = str(mday)
|
||||||
self.grid[idx] = d
|
self.grid[idx] = d
|
||||||
|
|
||||||
today = Date()
|
today = DateCal()
|
||||||
cur = self.date # Currency
|
cur = self.date # Currency
|
||||||
self.lbl.value(f"{Date.months[cur.month - 1]} {cur.year}")
|
self.lbl.value(f"{DateCal.months[cur.month - 1]} {cur.year}")
|
||||||
d = {} # Args for Label.value
|
d = {} # Args for Label.value
|
||||||
wday = 0
|
wday = 0
|
||||||
|
wday_1 = cur.wday_n(1) # Weekday of 1st of month
|
||||||
mday = 1
|
mday = 1
|
||||||
seek = True
|
seek = True
|
||||||
for idx in range(7, self.grid.ncells):
|
sundays = cur.mday_list(6)
|
||||||
self.grid[idx] = ""
|
|
||||||
for idx in range(7, self.grid.ncells):
|
for idx in range(7, self.grid.ncells):
|
||||||
if seek: # Find column for 1st of month
|
if seek: # Find column for 1st of month
|
||||||
if wday < cur.wday1:
|
if wday < wday_1:
|
||||||
self.grid[idx] = ""
|
self.grid[idx] = ""
|
||||||
wday += 1
|
wday += 1
|
||||||
else:
|
else:
|
||||||
|
@ -154,14 +108,12 @@ class BaseScreen(Screen):
|
||||||
mday += 1
|
mday += 1
|
||||||
else:
|
else:
|
||||||
self.grid[idx] = ""
|
self.grid[idx] = ""
|
||||||
idx = 7
|
idx = 7 # Where another row would be needed, roll over to top few cells.
|
||||||
while mday <= cur.month_length:
|
while mday <= cur.month_length:
|
||||||
cell()
|
cell()
|
||||||
idx += 1
|
idx += 1
|
||||||
mday += 1
|
mday += 1
|
||||||
day = Date.days[today.wday]
|
self.lblnow.value(f"{today.day_str} {today.mday} {today.month_str} {today.year}")
|
||||||
month = Date.months[today.month - 1]
|
|
||||||
self.lblnow.value(f"{day} {today.mday} {month} {today.year}")
|
|
||||||
|
|
||||||
|
|
||||||
def test():
|
def test():
|
||||||
|
|
|
@ -0,0 +1,160 @@
|
||||||
|
# date.py Minimal Date class for micropython
|
||||||
|
|
||||||
|
# Released under the MIT License (MIT). See LICENSE.
|
||||||
|
# Copyright (c) 2023 Peter Hinch
|
||||||
|
|
||||||
|
from time import mktime, localtime
|
||||||
|
|
||||||
|
_SECS_PER_DAY = const(86400)
|
||||||
|
def leap(year):
|
||||||
|
return bool((not year % 4) ^ (not year % 100))
|
||||||
|
|
||||||
|
class Date:
|
||||||
|
|
||||||
|
def __init__(self, lt=None):
|
||||||
|
self.now(lt)
|
||||||
|
|
||||||
|
def now(self, lt=None):
|
||||||
|
self._lt = list(localtime()) if lt is None else list(lt)
|
||||||
|
self._update()
|
||||||
|
|
||||||
|
def _update(self, ltmod=True): # If ltmod is False ._cur has been changed
|
||||||
|
if ltmod: # Otherwise ._lt has been modified
|
||||||
|
self._lt[3] = 6
|
||||||
|
self._cur = mktime(self._lt) // _SECS_PER_DAY
|
||||||
|
self._lt = list(localtime(self._cur * _SECS_PER_DAY))
|
||||||
|
|
||||||
|
def _mlen(self, d=bytearray((31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31))):
|
||||||
|
days = d[self._lt[1] - 1]
|
||||||
|
return days if days else (29 if leap(self._lt[0]) else 28)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def year(self):
|
||||||
|
return self._lt[0]
|
||||||
|
|
||||||
|
@year.setter
|
||||||
|
def year(self, v):
|
||||||
|
if self.mday == 29 and self.month == 2 and not leap(v):
|
||||||
|
self.mday = 28 # Ensure it doesn't skip a month
|
||||||
|
self._lt[0] = v
|
||||||
|
self._update()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def month(self):
|
||||||
|
return self._lt[1]
|
||||||
|
|
||||||
|
# Can write d.month = 4 or d.month += 15
|
||||||
|
@month.setter
|
||||||
|
def month(self, v):
|
||||||
|
y, m = divmod(v - 1, 12)
|
||||||
|
self._lt[0] += y
|
||||||
|
self._lt[1] = m + 1
|
||||||
|
self._lt[2] = min(self._lt[2], self._mlen())
|
||||||
|
self._update()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mday(self):
|
||||||
|
return self._lt[2]
|
||||||
|
|
||||||
|
@mday.setter
|
||||||
|
def mday(self, v):
|
||||||
|
if not 0 < v <= self._mlen():
|
||||||
|
raise ValueError(f"mday {v} is out of range")
|
||||||
|
self._lt[2] = v
|
||||||
|
self._update()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def day(self): # Days since epoch.
|
||||||
|
return self._cur
|
||||||
|
|
||||||
|
@day.setter
|
||||||
|
def day(self, v): # Usuge: d.day += 7 or date_1.day = d.day.
|
||||||
|
self._cur = v
|
||||||
|
self._update(False) # Flag _cur change
|
||||||
|
|
||||||
|
# Read-only properties
|
||||||
|
|
||||||
|
@property
|
||||||
|
def wday(self):
|
||||||
|
return self._lt[6]
|
||||||
|
|
||||||
|
# Date comparisons
|
||||||
|
|
||||||
|
def __lt__(self, other):
|
||||||
|
return self.day < other.day
|
||||||
|
|
||||||
|
def __le__(self, other):
|
||||||
|
return self.day <= other.day
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.day == other.day
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return self.day != other.day
|
||||||
|
|
||||||
|
def __gt__(self, other):
|
||||||
|
return self.day > other.day
|
||||||
|
|
||||||
|
def __ge__(self, other):
|
||||||
|
return self.day >= other.day
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.year}/{self.month}/{self.mday}"
|
||||||
|
|
||||||
|
|
||||||
|
class DateCal(Date):
|
||||||
|
days = ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
|
||||||
|
months = (
|
||||||
|
"January",
|
||||||
|
"February",
|
||||||
|
"March",
|
||||||
|
"April",
|
||||||
|
"May",
|
||||||
|
"June",
|
||||||
|
"July",
|
||||||
|
"August",
|
||||||
|
"September",
|
||||||
|
"October",
|
||||||
|
"November",
|
||||||
|
"December",
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, lt=None):
|
||||||
|
super().__init__(lt)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def month_length(self):
|
||||||
|
return self._mlen()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def day_str(self):
|
||||||
|
return self.days[self.wday]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def month_str(self):
|
||||||
|
return self.months[self.month - 1]
|
||||||
|
|
||||||
|
def wday_n(self, mday=1):
|
||||||
|
return (self._lt[6] - self._lt[2] + mday) % 7
|
||||||
|
|
||||||
|
def mday_list(self, wday):
|
||||||
|
ml = self._mlen() # 1 + ((wday - wday1) % 7)
|
||||||
|
d0 = 1 + ((wday - (self._lt[6] - self._lt[2] + 1)) % 7)
|
||||||
|
return [d for d in range(d0, ml + 1, 7)]
|
||||||
|
|
||||||
|
# Optional: return UK DST offset in hours. Can pass hr to ensure that time change occurs
|
||||||
|
# at 1am UTC otherwise it occurs on date change (0:0 UTC)
|
||||||
|
# offs is offset by month
|
||||||
|
def time_offset(self, hr=6, offs=bytearray((0, 0, 3, 1, 1, 1, 1, 1, 1, 10, 0, 0))):
|
||||||
|
ml = self._mlen()
|
||||||
|
wdayld = self.wday_n(ml) # Weekday of last day of month
|
||||||
|
mday_sun = self.mday_list(6)[-1] # Month day of last Sunday
|
||||||
|
m = offs[self._lt[1] - 1]
|
||||||
|
if m < 3:
|
||||||
|
return m # Deduce time offset from month alone
|
||||||
|
return int(
|
||||||
|
((self._lt[2] < mday_sun) or (self._lt[2] == mday_sun and hr <= 1)) ^ (m == 3)
|
||||||
|
) # Months where offset changes
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.day_str} {self.mday} {self.month_str} {self.year}"
|
Ładowanie…
Reference in New Issue