kopia lustrzana https://github.com/peterhinch/micropython-micro-gui
README changes. Add new demos.
rodzic
97300f86ad
commit
dc51cf61cf
66
README.md
66
README.md
|
@ -48,9 +48,10 @@ target and a C device driver (unless you can acquire a suitable binary).
|
||||||
|
|
||||||
# Project status
|
# Project status
|
||||||
|
|
||||||
Code has been tested on ESP32 and Pi Pico. The API shuld be stable. I'm not
|
Code has been tested on ESP32, Pi Pico and Pyboard. The API shuld be stable.
|
||||||
aware of any bugs but code is new and issues are likely. This document is
|
Code is new and issues are likely: please report any found. This document is
|
||||||
likely to have errors, typos and omissions. It is under review.
|
under review. I plan to add further demos and to upgrade the performance of
|
||||||
|
some display drivers.
|
||||||
|
|
||||||
# 0. Contents
|
# 0. Contents
|
||||||
|
|
||||||
|
@ -160,16 +161,20 @@ The currently selected `Widget` is identified by a white border: the `focus`
|
||||||
moves between widgets via `Next` and `Prev`. Only `active` `Widget` instances
|
moves between widgets via `Next` and `Prev`. Only `active` `Widget` instances
|
||||||
(those that can accept input) can receive the `focus`. Widgets are defined as
|
(those that can accept input) can receive the `focus`. Widgets are defined as
|
||||||
`active` or `passive` in the constructor, and this status cannot be changed. In
|
`active` or `passive` in the constructor, and this status cannot be changed. In
|
||||||
some cases the state can be specified as a constructor arg. An `active` widget
|
some cases the state can be specified as a constructor arg, but other widgets
|
||||||
can be disabled and re-enabled at runtime. A disabled `active` widget is shown
|
have a predefined state. An `active` widget can be disabled and re-enabled at
|
||||||
"greyed-out" and, until re-enabled, cannot accept the focus.
|
runtime. A disabled `active` widget is shown "greyed-out" and, until
|
||||||
|
re-enabled, cannot accept the `focus`.
|
||||||
|
|
||||||
## 1.5 Hardware definition
|
## 1.5 Hardware definition
|
||||||
|
|
||||||
A file `hardware_setup.py` must exist in the GUI root directory. This defines
|
A file `hardware_setup.py` must exist in the GUI root directory. This defines
|
||||||
the connections to the display, the display driver, and pins used for the
|
the connections to the display, the display driver, and pins used for the
|
||||||
pushbuttons. Example files may be found in the `setup_examples` directory. The
|
pushbuttons. Example files may be found in the `setup_examples` directory.
|
||||||
following is a typical example for a Raspberry Pi Pico driving an ILI9341
|
Further examples (without pin definitions) are in this
|
||||||
|
[nano-gui directory](https://github.com/peterhinch/micropython-nano-gui/tree/master/setup_examples).
|
||||||
|
|
||||||
|
The following is a typical example for a Raspberry Pi Pico driving an ILI9341
|
||||||
display:
|
display:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
@ -242,8 +247,8 @@ If installing to the device's filesystem it is necessary to maintain the
|
||||||
directory structure. The `drivers` and `gui` directories (with subdirectories
|
directory structure. The `drivers` and `gui` directories (with subdirectories
|
||||||
and contents) should be copied, along with `hardware_setup.py`. Filesystem
|
and contents) should be copied, along with `hardware_setup.py`. Filesystem
|
||||||
space may be conserved by copying only the display driver in use. Unused
|
space may be conserved by copying only the display driver in use. Unused
|
||||||
widgets, fonts and demos can also be trimmed, but directory structure must be
|
widgets, fonts and demos can also be trimmed, but the directory structure must
|
||||||
kept.
|
be kept.
|
||||||
|
|
||||||
There is scope for speeding loading and saving RAM by using frozen bytecode.
|
There is scope for speeding loading and saving RAM by using frozen bytecode.
|
||||||
Once again, directory structure must be maintained.
|
Once again, directory structure must be maintained.
|
||||||
|
@ -326,20 +331,36 @@ require a large (320x240) display. Demos are run by issuing (for example):
|
||||||
```python
|
```python
|
||||||
>>> import gui.demos.simple
|
>>> import gui.demos.simple
|
||||||
```
|
```
|
||||||
* `simple.py` Minimal demo discussed below.
|
#### Demos
|
||||||
* `active.py` Demonstrates `active` controls providing floating point input.
|
|
||||||
* `plot.py` Graph plotting.
|
These will run on screens of 128x128 pixels or above. The initial ones are
|
||||||
* `screens.py` Listbox, dropdown and dialog boxes.
|
minimal and aim to demonstrate a single technique.
|
||||||
|
* `simple.py` Minimal demo discussed below. `Button` presses print to REPL.
|
||||||
|
* `checkbox` A `Checkbox` controlling an `LED`.
|
||||||
|
* `slider.py` A `Slider` whose color varies with its value.
|
||||||
|
* `slider_label.py` A `Slider` updating a `Label`. Good for trying precision
|
||||||
|
mode.
|
||||||
|
* `screen_change.py` A `Pushbutton` causing a screen change.
|
||||||
* `tbox.py` Text boxes and user-controlled scrolling.
|
* `tbox.py` Text boxes and user-controlled scrolling.
|
||||||
* `various.py` Assorted widgets including the different types of pushbutton.
|
|
||||||
* `vtest.py` Clock and compass styles of vector display.
|
#### Test scripts
|
||||||
|
|
||||||
|
Some of these require larger screens. Required sizes are specified as
|
||||||
|
(height x width).
|
||||||
|
* `active.py` Demonstrates `active` controls providing floating point input
|
||||||
|
(240x320).
|
||||||
|
* `plot.py` Graph plotting (128x200).
|
||||||
|
* `screens.py` Listbox, dropdown and dialog boxes (128x240).
|
||||||
|
* `various.py` Assorted widgets including the different types of pushbutton
|
||||||
|
(240x320).
|
||||||
|
* `vtest.py` Clock and compass styles of vector display (240x320).
|
||||||
|
|
||||||
## 1.12 Floating Point Widgets
|
## 1.12 Floating Point Widgets
|
||||||
|
|
||||||
The challenge is to devise a way, with just two pushbuttons, of adjusting a
|
The challenge is to devise a way, with just two pushbuttons, of adjusting a
|
||||||
data value which may have an extremely large dynamic range. This is the ratio
|
data value which may have an extremely large dynamic range. This is the ratio
|
||||||
of the data value's total range to the smallest adjustment that can be made.
|
of the data value's total range to the smallest adjustment that can be made.
|
||||||
The mechanism as currently implemented enables a precision of 0.05%.
|
The mechanism currently implemented enables a precision of 0.05%.
|
||||||
|
|
||||||
Floating point widgets respond to a brief press of the `increase` or `decrease`
|
Floating point widgets respond to a brief press of the `increase` or `decrease`
|
||||||
buttons by adjusting the value by a small amount. A continued press causes the
|
buttons by adjusting the value by a small amount. A continued press causes the
|
||||||
|
@ -359,6 +380,10 @@ of the visual appearance of the widget: fine changes can be too small to see.
|
||||||
Options are to use the [Scale widget](./README.md#18-scale-widget) or to have a
|
Options are to use the [Scale widget](./README.md#18-scale-widget) or to have a
|
||||||
linked `Label` showing the widget's exact value.
|
linked `Label` showing the widget's exact value.
|
||||||
|
|
||||||
|
The callback runs whenever the widget's value changes. This causes the callback
|
||||||
|
to run repeatedly while the user adjusts the widget. This is required if there
|
||||||
|
is a linked `Label` to update.
|
||||||
|
|
||||||
###### [Contents](./README.md#0-contents)
|
###### [Contents](./README.md#0-contents)
|
||||||
|
|
||||||
# 2. Usage
|
# 2. Usage
|
||||||
|
@ -479,9 +504,10 @@ PALE_YELLOW = create_color(12, 150, 150, 0) # index, r, g, b
|
||||||
If a 4-bit driver is in use, the color `rgb(150, 150, 0)` will be assigned to
|
If a 4-bit driver is in use, the color `rgb(150, 150, 0)` will be assigned to
|
||||||
"spare" color number 12. Any color number in range `0 <= n <= 15` may be
|
"spare" color number 12. Any color number in range `0 <= n <= 15` may be
|
||||||
used, implying that predefined colors may be reassigned. It is recommended
|
used, implying that predefined colors may be reassigned. It is recommended
|
||||||
that `BLACK` (0) and `WHITE` (15) are not changed. If an 8-bit or larger driver
|
that `BLACK` (0) and `WHITE` (15) are not changed; `GREY` (6) and `YELLOW` (5)
|
||||||
is in use, the first `index` arg is ignored and there is no restriction on the
|
are also GUI defaults. If an 8-bit or larger driver is in use, the first
|
||||||
number of colors that may be created.
|
`index` arg is ignored and there is no restriction on the number of colors that
|
||||||
|
may be created.
|
||||||
|
|
||||||
Regardless of the display driver the `PALE_YELLOW` variable may be used to
|
Regardless of the display driver the `PALE_YELLOW` variable may be used to
|
||||||
refer to the color. An example of custom color definition may be found in
|
refer to the color. An example of custom color definition may be found in
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
# checkbox.py Minimal micro-gui demo showing a Checkbox updating an LED.
|
||||||
|
|
||||||
|
# hardware_setup must be imported before other modules because of RAM use.
|
||||||
|
import hardware_setup # Create a display instance
|
||||||
|
from gui.core.ugui import Screen, ssd
|
||||||
|
|
||||||
|
from gui.widgets.buttons import CloseButton
|
||||||
|
from gui.widgets.checkbox import Checkbox
|
||||||
|
from gui.widgets.led import LED
|
||||||
|
from gui.core.writer import CWriter
|
||||||
|
|
||||||
|
# Font for CWriter
|
||||||
|
import gui.fonts.arial10 as arial10
|
||||||
|
from gui.core.colors import *
|
||||||
|
|
||||||
|
|
||||||
|
class BaseScreen(Screen):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
wri = CWriter(ssd, arial10, GREEN, BLACK, verbose=False)
|
||||||
|
col = 2
|
||||||
|
row = 2
|
||||||
|
self.cb = Checkbox(wri, row, col, callback=self.cbcb)
|
||||||
|
col+= 40
|
||||||
|
self.led = LED(wri, row, col, color=YELLOW, bdcolor=GREEN)
|
||||||
|
CloseButton(wri)
|
||||||
|
|
||||||
|
def cbcb(self, cb):
|
||||||
|
self.led.value(cb.value())
|
||||||
|
|
||||||
|
def test():
|
||||||
|
print('Checkbox demo.')
|
||||||
|
Screen.change(BaseScreen)
|
||||||
|
|
||||||
|
test()
|
|
@ -0,0 +1,48 @@
|
||||||
|
# linked_sliders.py Minimal micro-gui demo one Slider controlling two others.
|
||||||
|
|
||||||
|
# hardware_setup must be imported before other modules because of RAM use.
|
||||||
|
import hardware_setup # Create a display instance
|
||||||
|
from gui.core.ugui import Screen, ssd
|
||||||
|
|
||||||
|
from gui.widgets.buttons import CloseButton
|
||||||
|
from gui.widgets.sliders import Slider
|
||||||
|
from gui.core.writer import CWriter
|
||||||
|
|
||||||
|
# Font for CWriter
|
||||||
|
import gui.fonts.arial10 as arial10
|
||||||
|
from gui.core.colors import *
|
||||||
|
|
||||||
|
|
||||||
|
class BaseScreen(Screen):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
args = {
|
||||||
|
'bdcolor' : RED,
|
||||||
|
'slotcolor' : BLUE,
|
||||||
|
'legends' : ('0.0', '0.5', '1.0'),
|
||||||
|
'value' : 0.5,
|
||||||
|
}
|
||||||
|
super().__init__()
|
||||||
|
wri = CWriter(ssd, arial10, GREEN, BLACK, verbose=False)
|
||||||
|
col = 2
|
||||||
|
row = 2
|
||||||
|
dc = 45
|
||||||
|
# Note: callback runs now, but other sliders have not yet been instantiated.
|
||||||
|
self.s0 = Slider(wri, row, col, callback=self.slider_cb, **args)
|
||||||
|
col += dc
|
||||||
|
self.s1 = Slider(wri, row, col, **args)
|
||||||
|
col += dc
|
||||||
|
self.s2 = Slider(wri, row, col, **args)
|
||||||
|
CloseButton(wri)
|
||||||
|
|
||||||
|
def slider_cb(self, s):
|
||||||
|
v = s.value()
|
||||||
|
if hasattr(self, 's1'): # If s1 & s2 have been instantiated
|
||||||
|
self.s1.value(v)
|
||||||
|
self.s2.value(v)
|
||||||
|
|
||||||
|
def test():
|
||||||
|
print('Linked sliders. Leftmost one controls others.')
|
||||||
|
Screen.change(BaseScreen)
|
||||||
|
|
||||||
|
test()
|
|
@ -0,0 +1,51 @@
|
||||||
|
# screen_change.py Minimal micro-gui demo showing a Button causing a screen change.
|
||||||
|
|
||||||
|
# hardware_setup must be imported before other modules because of RAM use.
|
||||||
|
import hardware_setup # Create a display instance
|
||||||
|
from gui.core.ugui import Screen, ssd
|
||||||
|
|
||||||
|
from gui.widgets.buttons import Button, CloseButton
|
||||||
|
from gui.widgets.label import Label
|
||||||
|
from gui.core.writer import CWriter
|
||||||
|
|
||||||
|
# Font for CWriter
|
||||||
|
import gui.fonts.font10 as font10
|
||||||
|
from gui.core.colors import *
|
||||||
|
|
||||||
|
# Defining a button in this way enables it to be re-used on
|
||||||
|
# multiple Screen instances. Note that a Screen class is
|
||||||
|
# passed, not an instance.
|
||||||
|
def fwdbutton(wri, row, col, cls_screen, text='Next'):
|
||||||
|
def fwd(button):
|
||||||
|
Screen.change(cls_screen) # Callback
|
||||||
|
|
||||||
|
Button(wri, row, col, height = 30, callback = fwd,
|
||||||
|
fgcolor = BLACK, bgcolor = GREEN,
|
||||||
|
text = text, shape = RECTANGLE, width = 100)
|
||||||
|
|
||||||
|
wri = CWriter(ssd, font10, GREEN, BLACK, verbose=False)
|
||||||
|
|
||||||
|
# This screen overlays BaseScreen.
|
||||||
|
class BackScreen(Screen):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
Label(wri, 2, 2, 'New screen.')
|
||||||
|
CloseButton(wri)
|
||||||
|
|
||||||
|
|
||||||
|
class BaseScreen(Screen):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
Label(wri, 2, 2, 'Base screen.')
|
||||||
|
fwdbutton(wri, 40, 2, BackScreen)
|
||||||
|
CloseButton(wri)
|
||||||
|
|
||||||
|
|
||||||
|
def test():
|
||||||
|
print('Screen change demo.')
|
||||||
|
Screen.change(BaseScreen) # Pass class, not instance!
|
||||||
|
|
||||||
|
test()
|
|
@ -1,6 +1,6 @@
|
||||||
# screens.py micro-gui demo of multiple screens, dropdowns etc
|
# screens.py micro-gui demo of multiple screens, dropdowns etc
|
||||||
|
|
||||||
# Create SSD instance. Must be done first 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
|
||||||
from gui.core.ugui import Screen, Window, ssd
|
from gui.core.ugui import Screen, Window, ssd
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# simple.py Minimal micro-gui demo.
|
# simple.py Minimal micro-gui demo.
|
||||||
# Initialise hardware and framebuf before importing modules.
|
|
||||||
# Import SSD and Display instances. Must be done first 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
|
||||||
from gui.core.ugui import Screen, ssd
|
from gui.core.ugui import Screen, ssd
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ class BaseScreen(Screen):
|
||||||
CloseButton(wri) # Quit the application
|
CloseButton(wri) # Quit the application
|
||||||
|
|
||||||
def test():
|
def test():
|
||||||
print('Testing micro-gui...')
|
print('Simple demo: button presses print to REPL.')
|
||||||
Screen.change(BaseScreen)
|
Screen.change(BaseScreen) # A class is passed here, not an instance.
|
||||||
|
|
||||||
test()
|
test()
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
# slider.py Minimal micro-gui demo showing a Slider with variable color.
|
||||||
|
|
||||||
|
# hardware_setup must be imported before other modules because of RAM use.
|
||||||
|
import hardware_setup
|
||||||
|
from gui.core.ugui import Screen, ssd
|
||||||
|
|
||||||
|
from gui.widgets.buttons import CloseButton
|
||||||
|
from gui.widgets.sliders import Slider
|
||||||
|
from gui.core.writer import CWriter
|
||||||
|
|
||||||
|
# Font for CWriter
|
||||||
|
import gui.fonts.arial10 as arial10
|
||||||
|
from gui.core.colors import *
|
||||||
|
|
||||||
|
|
||||||
|
class BaseScreen(Screen):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
wri = CWriter(ssd, arial10, GREEN, BLACK, verbose=False)
|
||||||
|
col = 2
|
||||||
|
row = 2
|
||||||
|
Slider(wri, row, col, callback=self.slider_cb,
|
||||||
|
bdcolor=RED, slotcolor=BLUE,
|
||||||
|
legends=('0.0', '0.5', '1.0'), value=0.5)
|
||||||
|
CloseButton(wri)
|
||||||
|
|
||||||
|
def slider_cb(self, s):
|
||||||
|
v = s.value()
|
||||||
|
if v < 0.2:
|
||||||
|
s.color(BLUE)
|
||||||
|
elif v > 0.8:
|
||||||
|
s.color(RED)
|
||||||
|
else:
|
||||||
|
s.color(GREEN)
|
||||||
|
|
||||||
|
def test():
|
||||||
|
print('Slider demo.')
|
||||||
|
Screen.change(BaseScreen)
|
||||||
|
|
||||||
|
test()
|
|
@ -0,0 +1,41 @@
|
||||||
|
# slider_label.py Minimal micro-gui demo showing a Slider controlling a Label.
|
||||||
|
|
||||||
|
# hardware_setup must be imported before other modules because of RAM use.
|
||||||
|
import hardware_setup # Create a display instance
|
||||||
|
from gui.core.ugui import Screen, ssd
|
||||||
|
|
||||||
|
from gui.widgets.buttons import CloseButton
|
||||||
|
from gui.widgets.sliders import Slider
|
||||||
|
from gui.widgets.label import Label
|
||||||
|
from gui.core.writer import CWriter
|
||||||
|
|
||||||
|
# Font for CWriter
|
||||||
|
import gui.fonts.arial10 as arial10
|
||||||
|
from gui.core.colors import *
|
||||||
|
|
||||||
|
|
||||||
|
class BaseScreen(Screen):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
wri = CWriter(ssd, arial10, GREEN, BLACK, verbose=False)
|
||||||
|
col = 2
|
||||||
|
row = 2
|
||||||
|
self.lbl = Label(wri, row + 45, col + 50, 35, bdcolor=RED, bgcolor=DARKGREEN)
|
||||||
|
# Instntiate Label first, because Slider callback will run now.
|
||||||
|
# See linked_sliders.py for another approach.
|
||||||
|
Slider(wri, row, col, callback=self.slider_cb,
|
||||||
|
bdcolor=RED, slotcolor=BLUE,
|
||||||
|
legends=('0.0', '0.5', '1.0'), value=0.5)
|
||||||
|
CloseButton(wri)
|
||||||
|
|
||||||
|
def slider_cb(self, s):
|
||||||
|
v = s.value()
|
||||||
|
self.lbl.value('{:5.3f}'.format(v))
|
||||||
|
|
||||||
|
def test():
|
||||||
|
print('Slider Label demo. Long press select for precision mode.')
|
||||||
|
Screen.change(BaseScreen)
|
||||||
|
|
||||||
|
test()
|
|
@ -114,7 +114,7 @@ def test():
|
||||||
if ssd.height < 128 or ssd.width < 128:
|
if ssd.height < 128 or ssd.width < 128:
|
||||||
print(' This test requires a display of at least 128x128 pixels.')
|
print(' This test requires a display of at least 128x128 pixels.')
|
||||||
else:
|
else:
|
||||||
print('Testing micro-gui...')
|
print('Textbox demo.')
|
||||||
Screen.change(MainScreen)
|
Screen.change(MainScreen)
|
||||||
|
|
||||||
test()
|
test()
|
||||||
|
|
|
@ -1,107 +0,0 @@
|
||||||
# pushbutton.py
|
|
||||||
|
|
||||||
# Copyright (c) 2018-2021 Peter Hinch
|
|
||||||
# Released under the MIT License (MIT) - see LICENSE file
|
|
||||||
|
|
||||||
import uasyncio as asyncio
|
|
||||||
import utime as time
|
|
||||||
from . import launch
|
|
||||||
from gui.primitives.delay_ms import Delay_ms
|
|
||||||
|
|
||||||
|
|
||||||
# An alternative Pushbutton solution with lower RAM use is available here
|
|
||||||
# https://github.com/kevinkk525/pysmartnode/blob/dev/pysmartnode/utils/abutton.py
|
|
||||||
class Pushbutton:
|
|
||||||
debounce_ms = 50
|
|
||||||
long_press_ms = 1000
|
|
||||||
double_click_ms = 400
|
|
||||||
def __init__(self, pin, suppress=False, sense=None):
|
|
||||||
self.pin = pin # Initialise for input
|
|
||||||
self._supp = suppress
|
|
||||||
self._dblpend = False # Doubleclick waiting for 2nd click
|
|
||||||
self._dblran = False # Doubleclick executed user function
|
|
||||||
self._tf = False
|
|
||||||
self._ff = False
|
|
||||||
self._df = False
|
|
||||||
self._ld = False # Delay_ms instance for long press
|
|
||||||
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
|
|
||||||
asyncio.create_task(self.buttoncheck()) # Thread runs forever
|
|
||||||
|
|
||||||
def press_func(self, func=False, args=()):
|
|
||||||
self._tf = func
|
|
||||||
self._ta = args
|
|
||||||
|
|
||||||
def release_func(self, func=False, args=()):
|
|
||||||
self._ff = func
|
|
||||||
self._fa = args
|
|
||||||
|
|
||||||
def double_func(self, func=False, args=()):
|
|
||||||
self._df = func
|
|
||||||
self._da = args
|
|
||||||
if func: # If double timer already in place, leave it
|
|
||||||
if not self._dd:
|
|
||||||
self._dd = Delay_ms(self._ddto)
|
|
||||||
else:
|
|
||||||
self._dd = False # Clearing down double func
|
|
||||||
|
|
||||||
def long_func(self, func=False, args=()):
|
|
||||||
if func:
|
|
||||||
if self._ld:
|
|
||||||
self._ld.callback(func, args)
|
|
||||||
else:
|
|
||||||
self._ld = Delay_ms(func, args)
|
|
||||||
else:
|
|
||||||
self._ld = False
|
|
||||||
|
|
||||||
# Current non-debounced logical button state: True == pressed
|
|
||||||
def rawstate(self):
|
|
||||||
return bool(self.pin.value() ^ self.sense)
|
|
||||||
|
|
||||||
# Current debounced state of button (True == pressed)
|
|
||||||
def __call__(self):
|
|
||||||
return self.state
|
|
||||||
|
|
||||||
def _ddto(self): # Doubleclick timeout: no doubleclick occurred
|
|
||||||
self._dblpend = False
|
|
||||||
if self._supp and not self.state:
|
|
||||||
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:
|
|
||||||
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
|
|
||||||
await asyncio.sleep_ms(Pushbutton.debounce_ms)
|
|
Ładowanie…
Reference in New Issue