diff --git a/gui/core/ugui.py b/gui/core/ugui.py index 9effe7f..f099f93 100644 --- a/gui/core/ugui.py +++ b/gui/core/ugui.py @@ -74,7 +74,7 @@ class Input: _vb and print("Using encoder.") if incr is None or decr is None: raise ValueError("Must specify pins for encoder.") - from gui.primitives.encoder import Encoder + from gui.primitives import Encoder self._enc = Encoder(incr, decr, div=encoder, callback=Screen.adjust) else: diff --git a/gui/primitives/__init__.py b/gui/primitives/__init__.py index 3aac9f3..ef1604f 100644 --- a/gui/primitives/__init__.py +++ b/gui/primitives/__init__.py @@ -1,26 +1,7 @@ -# __init__.py Common functions for uasyncio primitives +# __init__.py Common functions for uasyncio primitives used bu ugui -# Copyright (c) 2018-2020 Peter Hinch +# Copyright (c) 2018-2022 Peter Hinch # Released under the MIT License (MIT) - see LICENSE file -_attrs = { - "Delay_ms": "delay_ms", - "Switch": "switch", - "Pushbutton": "pushbutton", - "ESP32Touch": "pushbutton", - } - -# Lazy loader, effectively does: -# global attr -# from .mod import attr -# Filched from uasyncio.__init__.py - -def __getattr__(attr): - mod = _attrs.get(attr, None) - if mod is None: - raise AttributeError(attr) - value = getattr(__import__(mod, None, None, True, 1), attr) - globals()[attr] = value - return value try: import uasyncio as asyncio @@ -41,10 +22,22 @@ def launch(func, tup_args): res = asyncio.create_task(res) return res -def set_global_exception(): - def _handle_exception(loop, context): - import sys - sys.print_exception(context["exception"]) - sys.exit() - loop = asyncio.get_event_loop() - loop.set_exception_handler(_handle_exception) +_attrs = { + "Delay_ms": "delay_ms", + "Encoder": "encoder", + "Pushbutton": "pushbutton", + "ESP32Touch": "pushbutton", + "Switch": "switch", +} + +# Copied from uasyncio.__init__.py +# Lazy loader, effectively does: +# global attr +# from .mod import attr +def __getattr__(attr): + mod = _attrs.get(attr, None) + if mod is None: + raise AttributeError(attr) + value = getattr(__import__(mod, None, None, True, 1), attr) + globals()[attr] = value + return value diff --git a/gui/primitives/delay_ms.py b/gui/primitives/delay_ms.py index 4cc53a7..d5306ba 100644 --- a/gui/primitives/delay_ms.py +++ b/gui/primitives/delay_ms.py @@ -26,6 +26,7 @@ class Delay_ms: self._trig = asyncio.ThreadSafeFlag() self._tout = asyncio.Event() # Timeout event self.wait = self._tout.wait # Allow: await wait_ms.wait() + self.clear = self._tout.clear self._ttask = self._fake # Timer task self._mtask = asyncio.create_task(self._run()) #Main task @@ -40,7 +41,6 @@ class Delay_ms: async def _timer(self, dt): await asyncio.sleep_ms(dt) self._tout.set() # Only gets here if not cancelled. - self._tout.clear() self._busy = False if self._func is not None: self._retn = launch(self._func, self._args) diff --git a/gui/primitives/pushbutton.py b/gui/primitives/pushbutton.py index 008a0c4..bc187b2 100644 --- a/gui/primitives/pushbutton.py +++ b/gui/primitives/pushbutton.py @@ -27,17 +27,24 @@ class Pushbutton: self._dd = False # Ditto for doubleclick self.sense = pin.value() if sense is None else sense # Convert from electrical to logical value self.state = self.rawstate() # Initial state - self._run = asyncio.create_task(self.buttoncheck()) # Thread runs forever + self._run = asyncio.create_task(self._go()) # Thread runs forever def press_func(self, func=False, args=()): - self._tf = func + if func is None: + self.press = asyncio.Event() + self._tf = self.press.set if func is None else func self._ta = args def release_func(self, func=False, args=()): - self._ff = func + if func is None: + self.release = asyncio.Event() + self._ff = self.release.set if func is None else func self._fa = args def double_func(self, func=False, args=()): + if func is None: + self.double = asyncio.Event() + func = self.double.set self._df = func self._da = args if func: # If double timer already in place, leave it @@ -47,6 +54,9 @@ class Pushbutton: self._dd = False # Clearing down double func def long_func(self, func=False, args=()): + if func is None: + self.long = asyncio.Event() + func = self.long.set if func: if self._ld: self._ld.callback(func, args) @@ -69,41 +79,44 @@ class Pushbutton: if not self._ld or (self._ld and not self._ld()): launch(self._ff, self._fa) - async def buttoncheck(self): - while True: - state = self.rawstate() - # State has changed: act on it now. - if state != self.state: - self.state = state - if state: # Button pressed: launch pressed func - if self._tf: - launch(self._tf, self._ta) - if self._ld: # There's a long func: start long press delay - self._ld.trigger(Pushbutton.long_press_ms) - if self._df: - if self._dd(): # Second click: timer running - self._dd.stop() - self._dblpend = False - self._dblran = True # Prevent suppressed launch on release - launch(self._df, self._da) - else: - # First click: start doubleclick timer - self._dd.trigger(Pushbutton.double_click_ms) - self._dblpend = True # Prevent suppressed launch on release - else: # Button release. Is there a release func? - if self._ff: - if self._supp: - d = self._ld - # If long delay exists, is running and doubleclick status is OK - if not self._dblpend and not self._dblran: - if (d and d()) or not d: - launch(self._ff, self._fa) - else: + def _check(self, state): + if state == self.state: + return + # State has changed: act on it now. + self.state = state + if state: # Button pressed: launch pressed func + if self._tf: + launch(self._tf, self._ta) + if self._ld: # There's a long func: start long press delay + self._ld.trigger(Pushbutton.long_press_ms) + if self._df: + if self._dd(): # Second click: timer running + self._dd.stop() + self._dblpend = False + self._dblran = True # Prevent suppressed launch on release + launch(self._df, self._da) + else: + # First click: start doubleclick timer + self._dd.trigger(Pushbutton.double_click_ms) + self._dblpend = True # Prevent suppressed launch on release + else: # Button release. Is there a release func? + if self._ff: + if self._supp: + d = self._ld + # If long delay exists, is running and doubleclick status is OK + if not self._dblpend and not self._dblran: + if (d and d()) or not d: launch(self._ff, self._fa) - if self._ld: - self._ld.stop() # Avoid interpreting a second click as a long push - self._dblran = False - # Ignore state changes until switch has settled + else: + launch(self._ff, self._fa) + if self._ld: + self._ld.stop() # Avoid interpreting a second click as a long push + self._dblran = False + + async def _go(self): + while True: + self._check(self.rawstate()) + # Ignore state changes until switch has settled. Also avoid hogging CPU. # See https://github.com/peterhinch/micropython-async/issues/69 await asyncio.sleep_ms(Pushbutton.debounce_ms) diff --git a/setup_examples/st7789_ttgo.py b/setup_examples/st7789_ttgo.py index 602956e..17c63f2 100644 --- a/setup_examples/st7789_ttgo.py +++ b/setup_examples/st7789_ttgo.py @@ -1,4 +1,4 @@ -# color_setup.py Customise for your hardware config +# hardware_setup.py Customise for your hardware config # Released under the MIT License (MIT). See LICENSE. # Copyright (c) 2021 Peter Hinch, Ihor Nehrutsa diff --git a/setup_examples/st7789_ttgo_touch.py b/setup_examples/st7789_ttgo_touch.py index c5ad366..be48e4b 100644 --- a/setup_examples/st7789_ttgo_touch.py +++ b/setup_examples/st7789_ttgo_touch.py @@ -1,8 +1,17 @@ -# color_setup.py Customise for your hardware config +# hardware_setup.py Customise for your hardware config # Released under the MIT License (MIT). See LICENSE. # Copyright (c) 2021 Peter Hinch, Ihor Nehrutsa +# TTGO T-Display 1.14" 135*240(Pixel) based on ST7789V +# http://www.lilygo.cn/claprod_view.aspx?TypeId=62&Id=1274 +# http://www.lilygo.cn/prod_view.aspx?TypeId=50044&Id=1126 +# https://github.com/Xinyuan-LilyGO/TTGO-T-Display +# https://github.com/Xinyuan-LilyGO/TTGO-T-Display/blob/master/image/pinmap.jpg +# https://github.com/Xinyuan-LilyGO/TTGO-T-Display/blob/master/schematic/ESP32-TFT(6-26).pdf + +# This version is based on a touch button interface. + from machine import Pin, SPI, ADC, freq import gc