micropython-micro-gui/gui/widgets/tstat.py

125 wiersze
4.2 KiB
Python

# tstat.py Extension to nanogui providing the Tstat class
# Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2021 Peter Hinch
# Usage:
# from gui.widgets.tstat import Tstat
from gui.core.ugui import display
from gui.core.colors import *
from gui.widgets.meter import Meter
class Region:
# Callback reasons
EX_WB_IA = 1 # Exit region. Was below. Is above.
EX_WB_IB = 2 # Exit, was below, is below
EX_WA_IA = 4 # Exit, was above, is above.
EX_WA_IB = 8 # Exit, was above, is below
T_IA = 16 # Transit, is above
T_IB = 32 # Transit, is below
EN_WA = 64 # Entry, was above
EN_WB = 128 # Entry, was below
def __init__(self, tstat, vlo, vhi, color, callback, args):
self.tstat = tstat
if vlo >= vhi:
raise ValueError('TStat Region: vlo must be <= vhi')
self.vlo = vlo
self.vhi = vhi
self.color = color
self.cb = callback
self.args = args
self.is_in = False # Value is in region
self.fa = False # Entered from above
self.vprev = self.tstat.value()
def do_check(self, v):
cb = self.cb
args = self.args
if v < self.vlo:
if not self.is_in:
if self.vprev > self.vhi: # Low going transit
cb(self, self.T_IB, *args)
return # Was and is outside: no action.
# Was in the region, find direction of exit
self.is_in = False
reason = self.EX_WA_IB if self.fa else self.EX_WB_IB
cb(self, reason, *args)
elif v > self.vhi:
if not self.is_in:
if self.vprev < self.vlo:
cb(self, self.T_IA, *args)
return
# Was already in region
self.is_in = False
reason = self.EX_WA_IA if self.fa else self.EX_WB_IA
cb(self, reason, *args)
else: # v is in range
if self.is_in:
return # Nothing to do
self.is_in = True
if self.vprev > self.vhi:
self.fa = True # Save entry direction
cb(self, self.EN_WA, *args)
elif self.vprev < self.vlo:
self.fa = False
cb(self, self.EN_WB, *args)
def check(self, v):
self.do_check(v)
self.vprev = v # do_check gets value at prior check
def adjust(self, vlo, vhi):
old_vlo = self.vlo
old_vhi = self.vhi
self.vlo = vlo
self.vhi = vhi
vc = self.tstat.value()
self.tstat.draw = True
# Despatch cases where there is nothing to do.
# Outside both regions on same side
if vc < vlo and vc < old_vlo:
return
if vc > vhi and vc > old_vhi:
return
is_in = vlo <= vc <= vhi # Currently inside
if is_in and self.is_in: # Regions overlapped
return # Still inside so no action
if is_in: # Inside new region but not in old
self.check(vc) # Treat as if entering new region from previous value
else: # Outside new region
if not self.is_in: # Also outside old region
# Lay between old and new regions. Force
# a traverse of new
self.vprev = vlo - 0.1 if vc > vhi else vhi + 0.1
# If it was in old region treat as if leaving it
self.check(vc)
class Tstat(Meter):
def __init__(self, *args, **kwargs):
self.regions = set()
super().__init__(*args, **kwargs)
def add_region(self, vlo, vhi, color, callback, args=()):
reg = Region(self, vlo, vhi, color, callback, args)
self.regions.add(reg)
return reg
# Called by subclass prior to drawing scale and data
def preshow(self, x, y, width, height):
for r in self.regions:
ht = round(height * (r.vhi - r.vlo))
y1 = y - round(height * r.vhi)
display.fill_rect(x, y1, width, ht, r.color)
def value(self, n=None, color=None):
if n is None:
return super().value()
v = super().value(n, color)
for r in self.regions:
r.check(v)
return v