kopia lustrzana https://github.com/peterhinch/micropython-micro-gui
V0.1.5 3-button adjustment mode.
rodzic
d2247be40a
commit
f7c33cb408
91
README.md
91
README.md
|
@ -3,15 +3,17 @@
|
||||||
This is a lightweight, portable, MicroPython GUI library for displays having
|
This is a lightweight, portable, MicroPython GUI library for displays having
|
||||||
drivers subclassed from `framebuf`. Written in Python it runs under a standard
|
drivers subclassed from `framebuf`. Written in Python it runs under a standard
|
||||||
MicroPython firmware build. Options for data input comprise:
|
MicroPython firmware build. Options for data input comprise:
|
||||||
* Via from two to five pushbuttons depending on the application.
|
* Two pushbuttons: limited capabilities with some widgets unusable for input.
|
||||||
* Via a switch-based navigation joystick.
|
* Three pushbuttons with full capability.
|
||||||
|
* Five pushbuttons: full capability, less "modal" interface.
|
||||||
|
* A switch-based navigation joystick: another way to implement five buttons.
|
||||||
* Via two pushbuttons and a rotary encoder such as
|
* Via two pushbuttons and a rotary encoder such as
|
||||||
[this one](https://www.adafruit.com/product/377).
|
[this one](https://www.adafruit.com/product/377). An intuitive interface.
|
||||||
|
|
||||||
It is larger and more complex than `nano-gui` owing to the support for input.
|
It is larger and more complex than `nano-gui` owing to the support for input.
|
||||||
It enables switching between screens and launching modal windows. In addition
|
It enables switching between screens and launching modal windows. In addition
|
||||||
to `nano-gui` widgets it supports listboxes, dropdown lists, various means of
|
to `nano-gui` widgets it supports menus, listboxes, dropdown lists, various
|
||||||
entering or displaying floating point values, and other widgets.
|
means of entering or displaying floating point values, and other widgets.
|
||||||
|
|
||||||
It is compatible with all display drivers for
|
It is compatible with all display drivers for
|
||||||
[nano-gui](https://github.com/peterhinch/micropython-nano-gui) so is portable
|
[nano-gui](https://github.com/peterhinch/micropython-nano-gui) so is portable
|
||||||
|
@ -56,9 +58,12 @@ target and a C device driver (unless you can acquire a suitable binary).
|
||||||
|
|
||||||
# Project status
|
# Project status
|
||||||
|
|
||||||
Code has been tested on ESP32, Pi Pico and Pyboard. The API shuld be stable.
|
February 2022: This has had a significant upgrade to support use with only
|
||||||
Code is new and issues are likely: please report any found. The project is
|
three buttons as devised by Bart Cerneels. Simplified widget import. Existing
|
||||||
under development so check for updates.
|
users should replace the entire `gui` tree.
|
||||||
|
|
||||||
|
Code has been tested on ESP32, Pi Pico and Pyboard. This is under development
|
||||||
|
so check for updates.
|
||||||
|
|
||||||
Firmware V1.17 has provided a major boost to text rendering speed on color
|
Firmware V1.17 has provided a major boost to text rendering speed on color
|
||||||
display. V1.17 or later is now a requirement for color displays, although
|
display. V1.17 or later is now a requirement for color displays, although
|
||||||
|
@ -155,10 +160,8 @@ shows a message and has "Yes" and "No" buttons which trigger a callback.
|
||||||
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
|
||||||
|
|
||||||
from gui.widgets.label import Label
|
from gui.widgets import Label, Button, CloseButton
|
||||||
from gui.widgets.buttons import Button, CloseButton
|
|
||||||
from gui.core.writer import CWriter
|
from gui.core.writer import CWriter
|
||||||
|
|
||||||
# Font for CWriter
|
# Font for CWriter
|
||||||
import gui.fonts.arial10 as arial10
|
import gui.fonts.arial10 as arial10
|
||||||
from gui.core.colors import *
|
from gui.core.colors import *
|
||||||
|
@ -256,16 +259,20 @@ The GUI requires from 2 to 5 pushbuttons for control. These are:
|
||||||
An alternative is to replace buttons 4 and 5 with a quadrature encoder knob
|
An alternative is to replace buttons 4 and 5 with a quadrature encoder knob
|
||||||
such as [this one](https://www.adafruit.com/product/377). That device has a
|
such as [this one](https://www.adafruit.com/product/377). That device has a
|
||||||
switch which operates when the knob is pressed: this may be wired for the
|
switch which operates when the knob is pressed: this may be wired for the
|
||||||
`Select` button.
|
`Select` button. This provides the most intuitive operation.
|
||||||
|
|
||||||
Many widgets such as `Pushbutton` or `Checkbox` objects require only the
|
Many widgets such as `Pushbutton` or `Checkbox` objects require only the
|
||||||
`Select` button to operate: it is possible to design an interface with a subset
|
`Select` button to operate: it is possible to design an interface with a subset
|
||||||
of `micro-gui` widgets which requires only the first two buttons.
|
of `micro-gui` widgets which requires only the first two buttons. With three
|
||||||
|
buttons all widgets may be used without restriction.
|
||||||
|
|
||||||
Widgets such as `Listbox` objects, dropdown lists (`Dropdown`), and those for
|
Widgets such as `Listbox` objects, dropdown lists (`Dropdown`), and those for
|
||||||
floating point data entry require the `Increase` and `Decrease` buttons (or an
|
floating point data entry can use the `Increase` and `Decrease` buttons (or an
|
||||||
encoder) to select a data item or to adjust the linear value. This is discussed
|
encoder) to select a data item or to adjust the linear value. If three buttons
|
||||||
in [Floating Point Widgets](./README.md#112-floating-point-widgets).
|
are provided, the GUI will enter "adjust" mode in response to a double-click
|
||||||
|
of `Select`. In this mode `Prev` and `Next` act to decrease and increase the
|
||||||
|
widget's value. A further double-click restores normal navigation. This is
|
||||||
|
discussed in [Floating Point Widgets](./README.md#112-floating-point-widgets).
|
||||||
|
|
||||||
The currently selected `Widget` is identified by a white border: the `focus`
|
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
|
||||||
|
@ -563,7 +570,8 @@ one second. The GUI will respond by changing the border color from white
|
||||||
(i.e. has focus) to yellow. In this mode a brief press of `increase` or
|
(i.e. has focus) to yellow. In this mode a brief press of `increase` or
|
||||||
`decrease` or small movement of an encoder will have a reduced effect (0.05%).
|
`decrease` or small movement of an encoder will have a reduced effect (0.05%).
|
||||||
Fine mode may be cancelled by pressing `select` or by moving the focus to
|
Fine mode may be cancelled by pressing `select` or by moving the focus to
|
||||||
another control.
|
another control. This also works in three-button mode, with `Next` and `Prev`
|
||||||
|
performing the adjustments.
|
||||||
|
|
||||||
In the case of slider and knob controls the precision of fine mode exceeds that
|
In the case of slider and knob controls the precision of fine mode exceeds that
|
||||||
of the visual appearance of the widget: fine changes can be too small to see.
|
of the visual appearance of the widget: fine changes can be too small to see.
|
||||||
|
@ -596,8 +604,7 @@ of contiguous RAM.
|
||||||
import hardware_setup # Instantiate display, setup color LUT (if present)
|
import hardware_setup # Instantiate display, setup color LUT (if present)
|
||||||
from gui.core.ugui import Screen, ssd
|
from gui.core.ugui import Screen, ssd
|
||||||
|
|
||||||
from gui.widgets.label import Label
|
from gui.widgets import Label, Button, CloseButton
|
||||||
from gui.widgets.buttons import Button, CloseButton
|
|
||||||
from gui.core.writer import CWriter
|
from gui.core.writer import CWriter
|
||||||
|
|
||||||
# Font for CWriter
|
# Font for CWriter
|
||||||
|
@ -940,7 +947,7 @@ constructor and is closed by issuing the `close()` static method.
|
||||||
# 6. Label widget
|
# 6. Label widget
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from gui.widgets.label import Label
|
from gui.widgets import Label
|
||||||
```
|
```
|
||||||

|

|
||||||
|
|
||||||
|
@ -1001,8 +1008,7 @@ from gui.core.ugui import Screen
|
||||||
from gui.core.writer import CWriter
|
from gui.core.writer import CWriter
|
||||||
from gui.core.colors import *
|
from gui.core.colors import *
|
||||||
|
|
||||||
from gui.widgets.label import Label
|
from gui.widgets import Label, CloseButton
|
||||||
from gui.widgets.buttons import CloseButton
|
|
||||||
import gui.fonts.freesans20 as freesans20
|
import gui.fonts.freesans20 as freesans20
|
||||||
|
|
||||||
|
|
||||||
|
@ -1022,7 +1028,7 @@ Screen.change(BaseScreen)
|
||||||
# 7. LED widget
|
# 7. LED widget
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from gui.widgets.led import LED
|
from gui.widgets import LED
|
||||||
```
|
```
|
||||||

|

|
||||||
|
|
||||||
|
@ -1061,7 +1067,7 @@ controlled with `led(True)` or `led(False)`.
|
||||||
# 8. Checkbox widget
|
# 8. Checkbox widget
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from gui.widgets.checkbox import Checkbox
|
from gui.widgets import Checkbox
|
||||||
```
|
```
|
||||||

|

|
||||||
This provides for Boolean data entry and display. In the `True` state the
|
This provides for Boolean data entry and display. In the `True` state the
|
||||||
|
@ -1103,7 +1109,7 @@ Methods:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from gui.core.colors import * # Colors and shapes
|
from gui.core.colors import * # Colors and shapes
|
||||||
from gui.widgets.buttons import Button
|
from gui.widgets import Button
|
||||||
```
|
```
|
||||||

|

|
||||||
|
|
||||||
|
@ -1145,8 +1151,6 @@ Optional keyword only arguments:
|
||||||
for example media playback symbols.
|
for example media playback symbols.
|
||||||
* `callback=dolittle` Callback function which runs when button is pressed.
|
* `callback=dolittle` Callback function which runs when button is pressed.
|
||||||
* `args=()` A list/tuple of arguments for the above callback.
|
* `args=()` A list/tuple of arguments for the above callback.
|
||||||
* `onrelease=False` If `True` the callback will occur when the `select`
|
|
||||||
pushbutton is released otherwise it will occur when pressed.
|
|
||||||
|
|
||||||
Method:
|
Method:
|
||||||
* `greyed_out` Optional Boolean argument `val=None`. If `None` returns the
|
* `greyed_out` Optional Boolean argument `val=None`. If `None` returns the
|
||||||
|
@ -1182,7 +1186,7 @@ Optional keyword only arguments:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from gui.core.colors import * # Colors and shapes
|
from gui.core.colors import * # Colors and shapes
|
||||||
from gui.widgets.buttons import Button, ButtonList
|
from gui.widgets import Button, ButtonList
|
||||||
```
|
```
|
||||||
|
|
||||||
A `ButtonList` groups a number of buttons together to implement a button which
|
A `ButtonList` groups a number of buttons together to implement a button which
|
||||||
|
@ -1241,7 +1245,7 @@ for t in table: # Buttons overlay each other at same location
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from gui.core.colors import * # Colors and shapes
|
from gui.core.colors import * # Colors and shapes
|
||||||
from gui.widgets.buttons import Button, RadioButtons
|
from gui.widgets import Button, RadioButtons
|
||||||
```
|
```
|
||||||

|

|
||||||
|
|
||||||
|
@ -1288,7 +1292,7 @@ for t in table:
|
||||||
# 12. Listbox widget
|
# 12. Listbox widget
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from gui.widgets.listbox import Listbox
|
from gui.widgets import Listbox
|
||||||
```
|
```
|
||||||

|

|
||||||
|
|
||||||
|
@ -1397,7 +1401,7 @@ Screen.change(BaseScreen)
|
||||||
# 13. Dropdown widget
|
# 13. Dropdown widget
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from gui.widgets.dropdown import Dropdown
|
from gui.widgets import Dropdown
|
||||||
```
|
```
|
||||||
|
|
||||||

|

|
||||||
|
@ -1512,7 +1516,7 @@ Screen.change(BaseScreen)
|
||||||
# 14. DialogBox class
|
# 14. DialogBox class
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from gui.widgets.dialog import DialogBox
|
from gui.widgets import DialogBox
|
||||||
```
|
```
|
||||||

|

|
||||||
|
|
||||||
|
@ -1566,7 +1570,7 @@ in `gui/demos/screens.py`.
|
||||||
# 15. Textbox widget
|
# 15. Textbox widget
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from gui.widgets.textbox import Textbox
|
from gui.widgets import Textbox
|
||||||
```
|
```
|
||||||

|

|
||||||
|
|
||||||
|
@ -1635,7 +1639,7 @@ the oldest (topmost) being discarded as required.
|
||||||
This `passive` widget displays a single floating point value on a vertical
|
This `passive` widget displays a single floating point value on a vertical
|
||||||
linear scale. Optionally it can support data dependent callbacks.
|
linear scale. Optionally it can support data dependent callbacks.
|
||||||
```python
|
```python
|
||||||
from gui.widgets.meter import Meter
|
from gui.widgets import Meter
|
||||||
```
|
```
|
||||||

|

|
||||||
The two styles of `meter`, both showing a value of 0.65. This `passive` widget
|
The two styles of `meter`, both showing a value of 0.65. This `passive` widget
|
||||||
|
@ -1727,7 +1731,7 @@ behaves similarly for data values between 0.9 and 1.0.
|
||||||
## 16.1 Region class
|
## 16.1 Region class
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from gui.widgets.region import Region
|
from gui.widgets import Region
|
||||||
```
|
```
|
||||||
Instantiating a `Region` associates it with a supporting widget (currently only
|
Instantiating a `Region` associates it with a supporting widget (currently only
|
||||||
a `Meter`). Constructor positional args are as follows:
|
a `Meter`). Constructor positional args are as follows:
|
||||||
|
@ -1787,7 +1791,7 @@ callbacks to run as appropriate.
|
||||||
# 17. Slider and HorizSlider widgets
|
# 17. Slider and HorizSlider widgets
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from gui.widgets.sliders import Slider, HorizSlider
|
from gui.widgets import Slider, HorizSlider
|
||||||
```
|
```
|
||||||

|

|
||||||
|
|
||||||
|
@ -1865,7 +1869,7 @@ around sliders to display all legends.
|
||||||
# 18. Scale widget
|
# 18. Scale widget
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from gui.widgets.scale import Scale
|
from gui.widgets import Scale
|
||||||
```
|
```
|
||||||

|

|
||||||
|
|
||||||
|
@ -2002,7 +2006,7 @@ precision. Each visible division on the control represents 10 integer units.
|
||||||
# 19. ScaleLog widget
|
# 19. ScaleLog widget
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from gui.widgets.scale_log import ScaleLog
|
from gui.widgets import ScaleLog
|
||||||
```
|
```
|
||||||

|

|
||||||
|
|
||||||
|
@ -2142,7 +2146,7 @@ def tickcb(f, c):
|
||||||
# 20. Dial widget
|
# 20. Dial widget
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from gui.widgets.dial import Dial, Pointer
|
from gui.widgets import Dial, Pointer
|
||||||
```
|
```
|
||||||
 
|
 
|
||||||
|
|
||||||
|
@ -2225,8 +2229,7 @@ from gui.core.ugui import Screen
|
||||||
from gui.core.writer import CWriter
|
from gui.core.writer import CWriter
|
||||||
from gui.core.colors import *
|
from gui.core.colors import *
|
||||||
|
|
||||||
from gui.widgets.dial import Dial, Pointer
|
from gui.widgets import Dial, Pointer, CloseButton
|
||||||
from gui.widgets.buttons import CloseButton
|
|
||||||
import gui.fonts.freesans20 as freesans20
|
import gui.fonts.freesans20 as freesans20
|
||||||
|
|
||||||
async def run(dial):
|
async def run(dial):
|
||||||
|
@ -2259,7 +2262,7 @@ Screen.change(BaseScreen)
|
||||||
# 21. Knob widget
|
# 21. Knob widget
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from gui.widgets.knob import Knob
|
from gui.widgets import Knob
|
||||||
```
|
```
|
||||||

|

|
||||||
|
|
||||||
|
@ -2317,7 +2320,7 @@ value changes. This enables dynamic color change.
|
||||||
# 22. Adjuster widget
|
# 22. Adjuster widget
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from gui.widgets.adjuster import Adjuster
|
from gui.widgets import Adjuster
|
||||||
```
|
```
|
||||||
 
|
 
|
||||||
|
|
||||||
|
@ -2383,7 +2386,7 @@ basis. See code comments for further details.
|
||||||
# 23 Menu class
|
# 23 Menu class
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from gui.widgets.menu import Menu
|
from gui.widgets import Menu
|
||||||
```
|
```
|
||||||

|

|
||||||
|
|
||||||
|
|
|
@ -55,4 +55,5 @@ PRECISION = 1
|
||||||
FG = 2
|
FG = 2
|
||||||
BG = 3
|
BG = 3
|
||||||
GREY_OUT = 4
|
GREY_OUT = 4
|
||||||
color_map = [WHITE, YELLOW, WHITE, BLACK, GREY]
|
ADJUSTING = 5
|
||||||
|
color_map = [WHITE, YELLOW, WHITE, BLACK, GREY, LIGHTGREEN]
|
||||||
|
|
186
gui/core/ugui.py
186
gui/core/ugui.py
|
@ -3,7 +3,7 @@
|
||||||
# Released under the MIT License (MIT). See LICENSE.
|
# Released under the MIT License (MIT). See LICENSE.
|
||||||
# Copyright (c) 2019-2022 Peter Hinch
|
# Copyright (c) 2019-2022 Peter Hinch
|
||||||
|
|
||||||
# Requires uasyncio V3
|
# Credit to Bart Cerneels for devising and prototyping the 3-button mode
|
||||||
|
|
||||||
import uasyncio as asyncio
|
import uasyncio as asyncio
|
||||||
from uasyncio import Event
|
from uasyncio import Event
|
||||||
|
@ -13,15 +13,14 @@ import gc
|
||||||
from gui.core.colors import *
|
from gui.core.colors import *
|
||||||
from hardware_setup import ssd
|
from hardware_setup import ssd
|
||||||
|
|
||||||
from gui.primitives.delay_ms import Delay_ms
|
from gui.primitives import Pushbutton
|
||||||
from gui.primitives.switch import Switch
|
|
||||||
|
|
||||||
# Globally available singleton objects
|
# Globally available singleton objects
|
||||||
display = None # Singleton instance
|
display = None # Singleton instance
|
||||||
ssd = None
|
ssd = None
|
||||||
|
|
||||||
gc.collect()
|
gc.collect()
|
||||||
__version__ = (0, 1, 4)
|
__version__ = (0, 1, 5)
|
||||||
|
|
||||||
# Null function
|
# Null function
|
||||||
dolittle = lambda *_ : None
|
dolittle = lambda *_ : None
|
||||||
|
@ -30,60 +29,97 @@ async def _g():
|
||||||
pass
|
pass
|
||||||
type_coro = type(_g())
|
type_coro = type(_g())
|
||||||
|
|
||||||
|
# Navigation destinations
|
||||||
_FIRST = const(0)
|
_FIRST = const(0)
|
||||||
_NEXT = const(1)
|
_NEXT = const(1)
|
||||||
_PREV = const(2)
|
_PREV = const(2)
|
||||||
_LAST = const(3)
|
_LAST = const(3)
|
||||||
|
|
||||||
# Wrapper for ssd providing buttons and framebuf compatible methods
|
# Input abstracts input from 2-5 pushbuttons or 3 buttons + encoder. Handles
|
||||||
class Display:
|
# transitions between modes (normal, precision, adjustment)
|
||||||
verbose = True
|
class Input:
|
||||||
|
|
||||||
def __init__(self, objssd, nxt, sel, prev=None, incr=None, decr=None, encoder=False):
|
def __init__(self, nxt, sel, prev, incr, decr, encoder):
|
||||||
global display, ssd
|
verbose = True
|
||||||
self._next = Switch(nxt)
|
self._encoder = encoder # Encoder in use
|
||||||
self._sel = Switch(sel)
|
self._precision = False # Precision mode
|
||||||
self._last = None # Last switch pressed.
|
self._adj = False # Adjustment mode
|
||||||
|
# Count buttons
|
||||||
|
self._nb = sum(1 for x in (nxt, sel, prev, incr, decr) if x is not None)
|
||||||
# Mandatory buttons
|
# Mandatory buttons
|
||||||
|
self._next = Pushbutton(nxt)
|
||||||
|
self._sel = Pushbutton(sel, suppress=True)
|
||||||
# Call current screen bound method
|
# Call current screen bound method
|
||||||
self._next.close_func(self._closure, (self._next, Screen.ctrl_move, _NEXT))
|
self._next.press_func(Screen.ctrl_move, (_NEXT,))
|
||||||
self._sel.close_func(self._closure, (self._sel, Screen.sel_ctrl, 0))
|
self._sel.release_func(Screen.sel_ctrl)
|
||||||
self._sel.open_func(Screen.unsel)
|
if encoder or (self._nb > 2): # Can use precision mode when in adjust mode
|
||||||
|
self._sel.long_func(self.precision, (True,))
|
||||||
self.height = objssd.height
|
if self._nb == 3: # Special case of 3-button interface
|
||||||
self.width = objssd.width
|
self._sel.double_func(self.adj_mode) # Double click toggles adjust
|
||||||
|
|
||||||
# Optional buttons
|
# Optional buttons
|
||||||
self._prev = None
|
|
||||||
if prev is not None:
|
if prev is not None:
|
||||||
self._prev = Switch(prev)
|
self._prev = Pushbutton(prev)
|
||||||
self._prev.close_func(self._closure, (self._prev, Screen.ctrl_move, _PREV))
|
self._prev.press_func(Screen.ctrl_move, (_PREV,))
|
||||||
if encoder:
|
if encoder:
|
||||||
self.verbose and print('Using encoder.')
|
verbose and print('Using encoder.')
|
||||||
if incr is None or decr is None:
|
if incr is None or decr is None:
|
||||||
raise ValueError('Must specify pins for encoder.')
|
raise ValueError('Must specify pins for encoder.')
|
||||||
from gui.primitives.encoder import Encoder
|
from gui.primitives.encoder import Encoder
|
||||||
self._enc = Encoder(incr, decr, div=encoder, callback=Screen.adjust)
|
self._enc = Encoder(incr, decr, div=encoder, callback=Screen.adjust)
|
||||||
else:
|
else:
|
||||||
self.verbose and print('Using switches.')
|
verbose and print('Using {:d} switches.'.format(self._nb))
|
||||||
# incr and decr methods get the button as an arg.
|
# incr and decr methods get the button as an arg.
|
||||||
if incr is not None:
|
if incr is not None:
|
||||||
sup = Switch(incr)
|
sup = Pushbutton(incr)
|
||||||
sup.close_func(self._closure, (sup, Screen.adjust, 1))
|
sup.press_func(Screen.adjust, (sup, 1))
|
||||||
if decr is not None:
|
if decr is not None:
|
||||||
sdn = Switch(decr)
|
sdn = Pushbutton(decr)
|
||||||
sdn.close_func(self._closure, (sdn, Screen.adjust, -1))
|
sdn.press_func(Screen.adjust, (sdn, -1))
|
||||||
|
|
||||||
|
def precision(self, val): # Also called by Screen.ctrl_move to cancel mode
|
||||||
|
if val:
|
||||||
|
if self._nb == 3 and not self._adj:
|
||||||
|
self.adj_mode()
|
||||||
|
self._precision = True
|
||||||
|
else:
|
||||||
|
self._precision = False
|
||||||
|
Screen.redraw_co()
|
||||||
|
|
||||||
|
def adj_mode(self, v=None): # Set, clear or toggle adjustment mode
|
||||||
|
if self._nb == 3: # Called from menu and dropdown widgets
|
||||||
|
self._adj = not self._adj if v is None else v
|
||||||
|
# Change button function
|
||||||
|
if self._adj:
|
||||||
|
self._prev.press_func(Screen.adjust, (self._prev, -1))
|
||||||
|
self._next.press_func(Screen.adjust, (self._next, 1))
|
||||||
|
else:
|
||||||
|
self._prev.press_func(Screen.ctrl_move, (_PREV,))
|
||||||
|
self._next.press_func(Screen.ctrl_move, (_NEXT,))
|
||||||
|
self._precision = False
|
||||||
|
Screen.redraw_co()
|
||||||
|
|
||||||
|
def encoder(self):
|
||||||
|
return self._encoder
|
||||||
|
|
||||||
|
def is_precision(self):
|
||||||
|
return self._precision
|
||||||
|
|
||||||
|
def is_adjust(self):
|
||||||
|
return self._adj
|
||||||
|
|
||||||
|
|
||||||
|
# Wrapper for ssd providing buttons and framebuf compatible methods
|
||||||
|
class Display:
|
||||||
|
|
||||||
|
def __init__(self, objssd, nxt, sel, prev=None, incr=None, decr=None, encoder=False):
|
||||||
|
global display, ssd
|
||||||
|
self.ipdev = Input(nxt, sel, prev, incr, decr, encoder)
|
||||||
|
self.height = objssd.height
|
||||||
|
self.width = objssd.width
|
||||||
self._is_grey = False # Not greyed-out
|
self._is_grey = False # Not greyed-out
|
||||||
display = self # Populate globals
|
display = self # Populate globals
|
||||||
ssd = objssd
|
ssd = objssd
|
||||||
|
|
||||||
# Reject button presses where a button is already pressed.
|
|
||||||
# Execute if initialising, if same switch re-pressed or if last switch released
|
|
||||||
def _closure(self, switch, func, arg):
|
|
||||||
if (self._last is None) or (self._last == switch) or self._last():
|
|
||||||
self._last = switch
|
|
||||||
func(switch, arg)
|
|
||||||
|
|
||||||
def print_centred(self, writer, x, y, text, fgcolor=None, bgcolor=None, invert=False):
|
def print_centred(self, writer, x, y, text, fgcolor=None, bgcolor=None, invert=False):
|
||||||
sl = writer.stringlen(text)
|
sl = writer.stringlen(text)
|
||||||
writer.set_textpos(ssd, y - writer.height // 2, x - sl // 2)
|
writer.set_textpos(ssd, y - writer.height // 2, x - sl // 2)
|
||||||
|
@ -208,21 +244,25 @@ class Screen:
|
||||||
rfsh_start = Event() # Refresh pauses until set (set by default).
|
rfsh_start = Event() # Refresh pauses until set (set by default).
|
||||||
rfsh_done = Event() # Flag a user task that a refresh was done.
|
rfsh_done = Event() # Flag a user task that a refresh was done.
|
||||||
|
|
||||||
@classmethod
|
@classmethod # Called by Input when status change needs redraw of current obj
|
||||||
def ctrl_move(cls, _, v):
|
def redraw_co(cls):
|
||||||
if cls.current_screen is not None:
|
if cls.current_screen is not None:
|
||||||
|
obj = cls.current_screen.get_obj()
|
||||||
|
if obj is not None:
|
||||||
|
obj.draw = True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def ctrl_move(cls, v):
|
||||||
|
if cls.current_screen is not None:
|
||||||
|
display.ipdev.precision(False) # Cancel precision mode
|
||||||
cls.current_screen.move(v)
|
cls.current_screen.move(v)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def sel_ctrl(cls, b, _):
|
def sel_ctrl(cls):
|
||||||
if cls.current_screen is not None:
|
if cls.current_screen is not None:
|
||||||
|
display.ipdev.precision(False) # Cancel precision mode
|
||||||
cls.current_screen.do_sel()
|
cls.current_screen.do_sel()
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def unsel(cls):
|
|
||||||
if cls.current_screen is not None:
|
|
||||||
cls.current_screen.unsel_i()
|
|
||||||
|
|
||||||
# Adjust the value of a widget. If an encoder is used, button arg
|
# Adjust the value of a widget. If an encoder is used, button arg
|
||||||
# is an int (discarded), val is the delta. If using buttons, 1st
|
# is an int (discarded), val is the delta. If using buttons, 1st
|
||||||
# arg is the button, delta is +1 or -1
|
# arg is the button, delta is +1 or -1
|
||||||
|
@ -269,6 +309,7 @@ class Screen:
|
||||||
cs_new = new_screen
|
cs_new = new_screen
|
||||||
else:
|
else:
|
||||||
cs_new = cls_new_screen # An object, not a class
|
cs_new = cls_new_screen # An object, not a class
|
||||||
|
display.ipdev.adj_mode(False) # Ensure normal mode
|
||||||
cls.current_screen = cs_new
|
cls.current_screen = cs_new
|
||||||
cs_new.on_open() # Optional subclass method
|
cs_new.on_open() # Optional subclass method
|
||||||
cs_new._do_open(cs_old) # Clear and redraw
|
cs_new._do_open(cs_old) # Clear and redraw
|
||||||
|
@ -413,10 +454,6 @@ class Screen:
|
||||||
lo.show() # Re-display with new status
|
lo.show() # Re-display with new status
|
||||||
co.enter() # Tell object it has currency
|
co.enter() # Tell object it has currency
|
||||||
co.show()
|
co.show()
|
||||||
#elif isinstance(self, Window):
|
|
||||||
# Special case of Window with one object: leave
|
|
||||||
# without making changes (Dropdown in particular)
|
|
||||||
#Screen.back()
|
|
||||||
done = True
|
done = True
|
||||||
|
|
||||||
# Move currency to a specific control.
|
# Move currency to a specific control.
|
||||||
|
@ -439,11 +476,6 @@ class Screen:
|
||||||
if co is not None:
|
if co is not None:
|
||||||
co.do_sel()
|
co.do_sel()
|
||||||
|
|
||||||
def unsel_i(self):
|
|
||||||
co = self.get_obj()
|
|
||||||
if co is not None:
|
|
||||||
co.unsel()
|
|
||||||
|
|
||||||
def do_adj(self, button, val):
|
def do_adj(self, button, val):
|
||||||
co = self.get_obj()
|
co = self.get_obj()
|
||||||
if co is not None and hasattr(co, 'do_adj'):
|
if co is not None and hasattr(co, 'do_adj'):
|
||||||
|
@ -475,7 +507,7 @@ class Screen:
|
||||||
await asyncio.sleep_ms(500)
|
await asyncio.sleep_ms(500)
|
||||||
gc.collect()
|
gc.collect()
|
||||||
gc.threshold(gc.mem_free() // 4 + gc.mem_alloc())
|
gc.threshold(gc.mem_free() // 4 + gc.mem_alloc())
|
||||||
#print(gc.mem_free())
|
# print(gc.mem_free())
|
||||||
|
|
||||||
# Very basic window class. Cuts a rectangular hole in a screen on which
|
# Very basic window class. Cuts a rectangular hole in a screen on which
|
||||||
# content may be drawn.
|
# content may be drawn.
|
||||||
|
@ -570,7 +602,7 @@ class Widget:
|
||||||
self.bgcolor = bgcolor
|
self.bgcolor = bgcolor
|
||||||
# bdcolor is False if no border is to be drawn
|
# bdcolor is False if no border is to be drawn
|
||||||
self.bdcolor = bdcolor
|
self.bdcolor = bdcolor
|
||||||
# Default colors allow restoration after dynamic change
|
# Default colors allow restoration after dynamic change (Label)
|
||||||
self.def_fgcolor = fgcolor
|
self.def_fgcolor = fgcolor
|
||||||
self.def_bgcolor = bgcolor
|
self.def_bgcolor = bgcolor
|
||||||
self.def_bdcolor = bdcolor
|
self.def_bdcolor = bdcolor
|
||||||
|
@ -607,7 +639,6 @@ class Widget:
|
||||||
if self.screen != Screen.current_screen:
|
if self.screen != Screen.current_screen:
|
||||||
# Can occur if a control's action is to change screen.
|
# Can occur if a control's action is to change screen.
|
||||||
return False # Subclass abandons
|
return False # Subclass abandons
|
||||||
|
|
||||||
self.draw = False
|
self.draw = False
|
||||||
self.draw_border()
|
self.draw_border()
|
||||||
# Blank controls' space
|
# Blank controls' space
|
||||||
|
@ -629,8 +660,11 @@ class Widget:
|
||||||
h = self.height + 4
|
h = self.height + 4
|
||||||
if self.has_focus() and not isinstance(self, DummyWidget):
|
if self.has_focus() and not isinstance(self, DummyWidget):
|
||||||
color = color_map[FOCUS]
|
color = color_map[FOCUS]
|
||||||
if hasattr(self, 'precision') and self.precision and self.prcolor is not None:
|
precision = hasattr(self, 'do_precision') and self.do_precision and display.ipdev.is_precision()
|
||||||
|
if precision:
|
||||||
color = self.prcolor
|
color = self.prcolor
|
||||||
|
elif display.ipdev.is_adjust():
|
||||||
|
color = color_map[ADJUSTING]
|
||||||
dev.rect(x, y, w, h, color)
|
dev.rect(x, y, w, h, color)
|
||||||
self.has_border = True
|
self.has_border = True
|
||||||
else:
|
else:
|
||||||
|
@ -676,9 +710,6 @@ class Widget:
|
||||||
def do_sel(self): # Select button was pushed
|
def do_sel(self): # Select button was pushed
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def unsel(self): # Select button was released
|
|
||||||
pass
|
|
||||||
|
|
||||||
def enter(self): # Control has acquired focus
|
def enter(self): # Control has acquired focus
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -695,6 +726,7 @@ class Widget:
|
||||||
# have do_up and do_down methods which adjust the control's value in a
|
# have do_up and do_down methods which adjust the control's value in a
|
||||||
# time-dependent manner.
|
# time-dependent manner.
|
||||||
class LinearIO(Widget):
|
class LinearIO(Widget):
|
||||||
|
|
||||||
def __init__(self, writer, row, col, height, width,
|
def __init__(self, writer, row, col, height, width,
|
||||||
fgcolor, bgcolor, bdcolor,
|
fgcolor, bgcolor, bdcolor,
|
||||||
value=None, active=True, prcolor=False,
|
value=None, active=True, prcolor=False,
|
||||||
|
@ -704,53 +736,31 @@ class LinearIO(Widget):
|
||||||
super().__init__(writer, row, col, height, width,
|
super().__init__(writer, row, col, height, width,
|
||||||
fgcolor, bgcolor, bdcolor,
|
fgcolor, bgcolor, bdcolor,
|
||||||
value, active)
|
value, active)
|
||||||
# Handle variable precision. Start normal
|
|
||||||
self.precision = False
|
|
||||||
self.do_precision = prcolor is not False
|
self.do_precision = prcolor is not False
|
||||||
if self.do_precision:
|
if self.do_precision:
|
||||||
# Subclass supports precision mode
|
|
||||||
# 1 sec long press to set precise
|
|
||||||
self.lpd = Delay_ms(self.precise, (True,))
|
|
||||||
# Precision mode can only be entered when the active control has focus.
|
|
||||||
# In this state it will have a white border. By default this turns yellow
|
|
||||||
# but subclass can be defeat this with WHITE or another color
|
|
||||||
self.prcolor = color_map[PRECISION] if prcolor is None else prcolor
|
self.prcolor = color_map[PRECISION] if prcolor is None else prcolor
|
||||||
|
|
||||||
# Adjust widget's value. Args: button pressed, amount of increment
|
# Adjust widget's value. Args: button pressed, amount of increment
|
||||||
def do_adj(self, button, val):
|
def do_adj(self, button, val):
|
||||||
encoder = isinstance(button, int)
|
d = self.min_delta * 0.1 if self.precision() else self.min_delta
|
||||||
d = self.min_delta * 0.1 if self.precision else self.min_delta
|
|
||||||
self.value(self.value() + val * d)
|
self.value(self.value() + val * d)
|
||||||
if not encoder:
|
if not display.ipdev.encoder():
|
||||||
asyncio.create_task(self.btnhan(button, val, d))
|
asyncio.create_task(self.btnhan(button, val, d))
|
||||||
|
|
||||||
# Handle increase and decrease buttons. Redefined by textbox.py, scale_log.py
|
# Handle increase and decrease buttons. Redefined by textbox.py, scale_log.py
|
||||||
async def btnhan(self, button, up, d):
|
async def btnhan(self, button, up, d):
|
||||||
maxd = self.max_delta if self.precision else d * 4 # Why move fast in precision mode?
|
maxd = self.max_delta if self.precision() else d * 4 # Why move fast in precision mode?
|
||||||
t = ticks_ms()
|
t = ticks_ms()
|
||||||
while not button():
|
while button():
|
||||||
await asyncio.sleep_ms(0) # Quit fast on button release
|
await asyncio.sleep_ms(0) # Quit fast on button release
|
||||||
if ticks_diff(ticks_ms(), t) > 500: # Button was held down
|
if ticks_diff(ticks_ms(), t) > 500: # Button was held down
|
||||||
d = min(maxd, d * 2)
|
d = min(maxd, d * 2)
|
||||||
self.value(self.value() + up * d)
|
self.value(self.value() + up * d)
|
||||||
t = ticks_ms()
|
t = ticks_ms()
|
||||||
|
|
||||||
def precise(self, v): # Timed out while button pressed
|
# Get current status (also used by scale_log widget)
|
||||||
self.precision = v
|
def precision(self):
|
||||||
self.draw = True
|
return self.do_precision and display.ipdev.is_precision()
|
||||||
|
|
||||||
def do_sel(self): # Select button was pushed
|
|
||||||
if self.do_precision: # Subclass handles precision mode
|
|
||||||
if self.precision: # Already in mode
|
|
||||||
self.precise(False)
|
|
||||||
else: # Require a long press to enter mode
|
|
||||||
self.lpd.trigger()
|
|
||||||
|
|
||||||
def unsel(self): # Select button was released
|
|
||||||
self.do_precision and self.lpd.stop()
|
|
||||||
|
|
||||||
def leave(self): # Control has lost focus
|
|
||||||
self.precise(False)
|
|
||||||
|
|
||||||
# The dummy enables popup windows by satisfying the need for at least one active
|
# The dummy enables popup windows by satisfying the need for at least one active
|
||||||
# widget on a screen. It is invisible and is drawn by Window constructor before
|
# widget on a screen. It is invisible and is drawn by Window constructor before
|
||||||
|
|
|
@ -6,9 +6,7 @@
|
||||||
# Initialise hardware and framebuf before importing modules.
|
# Initialise hardware and framebuf before importing modules.
|
||||||
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
|
||||||
from gui.widgets.label import Label
|
from gui.widgets import Label, Dial, Pointer, CloseButton
|
||||||
from gui.widgets.dial import Dial, Pointer
|
|
||||||
from gui.widgets.buttons import CloseButton
|
|
||||||
|
|
||||||
# Now import other modules
|
# Now import other modules
|
||||||
from cmath import rect, pi
|
from cmath import rect, pi
|
||||||
|
|
|
@ -10,13 +10,7 @@ from gui.core.writer import CWriter
|
||||||
import gui.fonts.arial10 as arial10 # Font for CWriter
|
import gui.fonts.arial10 as arial10 # Font for CWriter
|
||||||
from gui.core.colors import *
|
from gui.core.colors import *
|
||||||
# Widgets
|
# Widgets
|
||||||
from gui.widgets.label import Label
|
from gui.widgets import Label, Scale, ScaleLog, Button, CloseButton, Slider, HorizSlider, Knob, Checkbox
|
||||||
from gui.widgets.scale import Scale
|
|
||||||
from gui.widgets.scale_log import ScaleLog
|
|
||||||
from gui.widgets.buttons import Button, CloseButton
|
|
||||||
from gui.widgets.sliders import Slider, HorizSlider
|
|
||||||
from gui.widgets.knob import Knob
|
|
||||||
from gui.widgets.checkbox import Checkbox
|
|
||||||
|
|
||||||
|
|
||||||
class BaseScreen(Screen):
|
class BaseScreen(Screen):
|
||||||
|
|
|
@ -8,10 +8,7 @@ import hardware_setup # Create a display instance
|
||||||
import cmath
|
import cmath
|
||||||
from gui.core.ugui import Screen, ssd
|
from gui.core.ugui import Screen, ssd
|
||||||
|
|
||||||
from gui.widgets.label import Label
|
from gui.widgets import Label, CloseButton, Adjuster, Dial, Pointer
|
||||||
from gui.widgets.buttons import CloseButton
|
|
||||||
from gui.widgets.adjuster import Adjuster
|
|
||||||
from gui.widgets.dial import Dial, Pointer
|
|
||||||
from gui.core.writer import CWriter
|
from gui.core.writer import CWriter
|
||||||
|
|
||||||
# Font for CWriter
|
# Font for CWriter
|
||||||
|
|
|
@ -7,9 +7,7 @@
|
||||||
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
|
||||||
|
|
||||||
from gui.widgets.label import Label
|
from gui.widgets import Label, CloseButton, Adjuster, FloatAdj
|
||||||
from gui.widgets.buttons import CloseButton
|
|
||||||
from gui.widgets.adjuster import Adjuster, FloatAdj
|
|
||||||
from gui.core.writer import CWriter
|
from gui.core.writer import CWriter
|
||||||
|
|
||||||
# Font for CWriter
|
# Font for CWriter
|
||||||
|
|
|
@ -44,9 +44,7 @@ audio_out = I2S(I2S_ID, **config)
|
||||||
# ======= GUI =======
|
# ======= GUI =======
|
||||||
|
|
||||||
from gui.widgets.label import Label
|
from gui.widgets.label import Label
|
||||||
from gui.widgets.buttons import Button, CloseButton, CIRCLE
|
from gui.widgets import Button, CloseButton, HorizSlider, Listbox
|
||||||
from gui.widgets.sliders import HorizSlider
|
|
||||||
from gui.widgets.listbox import Listbox
|
|
||||||
from gui.core.writer import CWriter
|
from gui.core.writer import CWriter
|
||||||
|
|
||||||
# Font for CWriter
|
# Font for CWriter
|
||||||
|
@ -59,7 +57,7 @@ import gc
|
||||||
import uasyncio as asyncio
|
import uasyncio as asyncio
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
# Initial check on ilesystem
|
# Initial check on filesystem
|
||||||
try:
|
try:
|
||||||
subdirs = [x[0] for x in os.ilistdir(root) if x[1] == 0x4000]
|
subdirs = [x[0] for x in os.ilistdir(root) if x[1] == 0x4000]
|
||||||
if len(subdirs):
|
if len(subdirs):
|
||||||
|
|
|
@ -7,9 +7,7 @@
|
||||||
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
|
||||||
|
|
||||||
from gui.widgets.buttons import CloseButton
|
from gui.widgets import CloseButton, Checkbox, LED
|
||||||
from gui.widgets.checkbox import Checkbox
|
|
||||||
from gui.widgets.led import LED
|
|
||||||
from gui.core.writer import CWriter
|
from gui.core.writer import CWriter
|
||||||
|
|
||||||
# Font for CWriter
|
# Font for CWriter
|
||||||
|
|
|
@ -7,9 +7,7 @@
|
||||||
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
|
||||||
|
|
||||||
from gui.widgets.label import Label
|
from gui.widgets import Label, Button, CloseButton, DialogBox
|
||||||
from gui.widgets.buttons import Button, CloseButton
|
|
||||||
from gui.widgets.dialog import DialogBox
|
|
||||||
from gui.core.writer import CWriter
|
from gui.core.writer import CWriter
|
||||||
|
|
||||||
# Font for CWriter
|
# Font for CWriter
|
||||||
|
|
|
@ -7,9 +7,7 @@
|
||||||
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
|
||||||
|
|
||||||
from gui.widgets.label import Label
|
from gui.widgets import Label, CloseButton, Dropdown
|
||||||
from gui.widgets.buttons import CloseButton
|
|
||||||
from gui.widgets.dropdown import Dropdown
|
|
||||||
from gui.core.writer import CWriter
|
from gui.core.writer import CWriter
|
||||||
|
|
||||||
# Font for CWriter
|
# Font for CWriter
|
||||||
|
|
|
@ -7,8 +7,7 @@
|
||||||
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
|
||||||
|
|
||||||
from gui.widgets.buttons import CloseButton
|
from gui.widgets import CloseButton, Slider
|
||||||
from gui.widgets.sliders import Slider
|
|
||||||
from gui.core.writer import CWriter
|
from gui.core.writer import CWriter
|
||||||
|
|
||||||
# Font for CWriter
|
# Font for CWriter
|
||||||
|
|
|
@ -9,8 +9,7 @@ from gui.core.ugui import Screen
|
||||||
from gui.core.writer import CWriter
|
from gui.core.writer import CWriter
|
||||||
from gui.core.colors import *
|
from gui.core.colors import *
|
||||||
|
|
||||||
from gui.widgets.listbox import Listbox
|
from gui.widgets import Listbox, CloseButton
|
||||||
from gui.widgets.buttons import CloseButton
|
|
||||||
import gui.fonts.freesans20 as font
|
import gui.fonts.freesans20 as font
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,7 @@ from gui.core.ugui import Screen, ssd
|
||||||
import gui.fonts.freesans20 as font
|
import gui.fonts.freesans20 as font
|
||||||
from gui.core.writer import CWriter
|
from gui.core.writer import CWriter
|
||||||
|
|
||||||
from gui.widgets.menu import Menu
|
from gui.widgets import Menu, CloseButton
|
||||||
from gui.widgets.buttons import CloseButton
|
|
||||||
from gui.core.colors import *
|
from gui.core.colors import *
|
||||||
|
|
||||||
class BaseScreen(Screen):
|
class BaseScreen(Screen):
|
||||||
|
|
|
@ -18,9 +18,7 @@ from collections import OrderedDict
|
||||||
from gui.core.writer import Writer, CWriter
|
from gui.core.writer import Writer, CWriter
|
||||||
from gui.core.ugui import Screen, ssd
|
from gui.core.ugui import Screen, ssd
|
||||||
from gui.widgets.graph import PolarGraph, PolarCurve, CartesianGraph, Curve, TSequence
|
from gui.widgets.graph import PolarGraph, PolarCurve, CartesianGraph, Curve, TSequence
|
||||||
from gui.widgets.label import Label
|
from gui.widgets import Label, Button, CloseButton, Listbox
|
||||||
from gui.widgets.buttons import Button, CloseButton
|
|
||||||
from gui.widgets.listbox import Listbox
|
|
||||||
|
|
||||||
# Fonts & colors
|
# Fonts & colors
|
||||||
import gui.fonts.arial10 as arial10
|
import gui.fonts.arial10 as arial10
|
||||||
|
|
|
@ -7,8 +7,7 @@
|
||||||
import hardware_setup # Create a display instance
|
import hardware_setup # Create a display instance
|
||||||
from gui.core.ugui import Screen, Window, ssd, display
|
from gui.core.ugui import Screen, Window, ssd, display
|
||||||
|
|
||||||
from gui.widgets.label import Label
|
from gui.widgets import Label, CloseButton
|
||||||
from gui.widgets.buttons import CloseButton
|
|
||||||
from gui.core.writer import CWriter
|
from gui.core.writer import CWriter
|
||||||
|
|
||||||
# Font for CWriter
|
# Font for CWriter
|
||||||
|
|
|
@ -7,8 +7,7 @@
|
||||||
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
|
||||||
|
|
||||||
from gui.widgets.buttons import Button, CloseButton
|
from gui.widgets import Button, CloseButton, Label
|
||||||
from gui.widgets.label import Label
|
|
||||||
from gui.core.writer import CWriter
|
from gui.core.writer import CWriter
|
||||||
|
|
||||||
# Font for CWriter
|
# Font for CWriter
|
||||||
|
|
|
@ -8,10 +8,7 @@ import hardware_setup # Create a display instance
|
||||||
from gui.core.ugui import Screen, Window, ssd
|
from gui.core.ugui import Screen, Window, ssd
|
||||||
|
|
||||||
from gui.widgets.label import Label
|
from gui.widgets.label import Label
|
||||||
from gui.widgets.buttons import Button, RadioButtons, CloseButton
|
from gui.widgets import Button, RadioButtons, CloseButton, Listbox, Dropdown, DialogBox
|
||||||
from gui.widgets.listbox import Listbox
|
|
||||||
from gui.widgets.dropdown import Dropdown
|
|
||||||
from gui.widgets.dialog import DialogBox
|
|
||||||
from gui.core.writer import CWriter
|
from gui.core.writer import CWriter
|
||||||
|
|
||||||
# Font for CWriter
|
# Font for CWriter
|
||||||
|
|
|
@ -7,8 +7,7 @@
|
||||||
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
|
||||||
|
|
||||||
from gui.widgets.label import Label
|
from gui.widgets import Label, Button, CloseButton
|
||||||
from gui.widgets.buttons import Button, CloseButton
|
|
||||||
from gui.core.writer import CWriter
|
from gui.core.writer import CWriter
|
||||||
|
|
||||||
# Font for CWriter
|
# Font for CWriter
|
||||||
|
|
|
@ -7,8 +7,7 @@
|
||||||
import hardware_setup
|
import hardware_setup
|
||||||
from gui.core.ugui import Screen, ssd
|
from gui.core.ugui import Screen, ssd
|
||||||
|
|
||||||
from gui.widgets.buttons import CloseButton
|
from gui.widgets import CloseButton, Slider
|
||||||
from gui.widgets.sliders import Slider
|
|
||||||
from gui.core.writer import CWriter
|
from gui.core.writer import CWriter
|
||||||
|
|
||||||
# Font for CWriter
|
# Font for CWriter
|
||||||
|
|
|
@ -7,9 +7,7 @@
|
||||||
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
|
||||||
|
|
||||||
from gui.widgets.buttons import CloseButton
|
from gui.widgets import CloseButton, Slider, Label
|
||||||
from gui.widgets.sliders import Slider
|
|
||||||
from gui.widgets.label import Label
|
|
||||||
from gui.core.writer import CWriter
|
from gui.core.writer import CWriter
|
||||||
|
|
||||||
# Font for CWriter
|
# Font for CWriter
|
||||||
|
|
|
@ -15,9 +15,7 @@ from gui.core.writer import CWriter
|
||||||
import uasyncio as asyncio
|
import uasyncio as asyncio
|
||||||
from gui.core.colors import *
|
from gui.core.colors import *
|
||||||
import gui.fonts.arial10 as arial10
|
import gui.fonts.arial10 as arial10
|
||||||
from gui.widgets.label import Label
|
from gui.widgets import Label, Textbox, Button, CloseButton
|
||||||
from gui.widgets.textbox import Textbox
|
|
||||||
from gui.widgets.buttons import Button, CloseButton
|
|
||||||
|
|
||||||
wri = CWriter(ssd, arial10) # verbose = True
|
wri = CWriter(ssd, arial10) # verbose = True
|
||||||
|
|
||||||
|
|
|
@ -10,12 +10,7 @@
|
||||||
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
|
||||||
|
|
||||||
from gui.widgets.buttons import Button, CloseButton
|
from gui.widgets import Button, CloseButton, Slider, Label, Meter, Region, LED
|
||||||
from gui.widgets.sliders import Slider
|
|
||||||
from gui.widgets.label import Label
|
|
||||||
from gui.widgets.meter import Meter
|
|
||||||
from gui.widgets.region import Region
|
|
||||||
from gui.widgets.led import LED
|
|
||||||
from gui.core.writer import CWriter
|
from gui.core.writer import CWriter
|
||||||
|
|
||||||
# Font for CWriter
|
# Font for CWriter
|
||||||
|
|
|
@ -16,9 +16,7 @@ from gui.core.writer import CWriter
|
||||||
from gui.fonts import font10
|
from gui.fonts import font10
|
||||||
from gui.core.colors import *
|
from gui.core.colors import *
|
||||||
# Widgets
|
# Widgets
|
||||||
from gui.widgets.label import Label
|
from gui.widgets import Label, Button, CloseButton, Pointer, Dial
|
||||||
from gui.widgets.buttons import Button, CloseButton
|
|
||||||
from gui.widgets.dial import Pointer, Dial
|
|
||||||
|
|
||||||
|
|
||||||
def fwdbutton(wri, row, col, cls_screen, text='Next'):
|
def fwdbutton(wri, row, col, cls_screen, text='Next'):
|
||||||
|
|
|
@ -2,6 +2,24 @@
|
||||||
|
|
||||||
# Copyright (c) 2018-2020 Peter Hinch
|
# Copyright (c) 2018-2020 Peter Hinch
|
||||||
# Released under the MIT License (MIT) - see LICENSE file
|
# Released under the MIT License (MIT) - see LICENSE file
|
||||||
|
_attrs = {
|
||||||
|
"Delay_ms": "delay_ms",
|
||||||
|
"Switch": "switch",
|
||||||
|
"Pushbutton": "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:
|
try:
|
||||||
import uasyncio as asyncio
|
import uasyncio as asyncio
|
||||||
|
|
|
@ -27,7 +27,7 @@ class Delay_ms:
|
||||||
self._tout = asyncio.Event() # Timeout event
|
self._tout = asyncio.Event() # Timeout event
|
||||||
self.wait = self._tout.wait # Allow: await wait_ms.wait()
|
self.wait = self._tout.wait # Allow: await wait_ms.wait()
|
||||||
self._ttask = self._fake # Timer task
|
self._ttask = self._fake # Timer task
|
||||||
asyncio.create_task(self._run())
|
self._mtask = asyncio.create_task(self._run()) #Main task
|
||||||
|
|
||||||
async def _run(self):
|
async def _run(self):
|
||||||
while True:
|
while True:
|
||||||
|
@ -48,6 +48,8 @@ class Delay_ms:
|
||||||
# API
|
# API
|
||||||
# trigger may be called from hard ISR.
|
# trigger may be called from hard ISR.
|
||||||
def trigger(self, duration=0): # Update absolute end time, 0-> ctor default
|
def trigger(self, duration=0): # Update absolute end time, 0-> ctor default
|
||||||
|
if self._mtask is None:
|
||||||
|
raise RuntimeError("Delay_ms.deinit() has run.")
|
||||||
self._tend = ticks_add(ticks_ms(), duration if duration > 0 else self._durn)
|
self._tend = ticks_add(ticks_ms(), duration if duration > 0 else self._durn)
|
||||||
self._retn = None # Default in case cancelled.
|
self._retn = None # Default in case cancelled.
|
||||||
self._busy = True
|
self._busy = True
|
||||||
|
@ -69,3 +71,8 @@ class Delay_ms:
|
||||||
def callback(self, func=None, args=()):
|
def callback(self, func=None, args=()):
|
||||||
self._func = func
|
self._func = func
|
||||||
self._args = args
|
self._args = args
|
||||||
|
|
||||||
|
def deinit(self):
|
||||||
|
self.stop()
|
||||||
|
self._mtask.cancel()
|
||||||
|
self._mtask = None
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
# switch.py
|
|
||||||
|
|
||||||
# Copyright (c) 2018-2020 Peter Hinch
|
|
||||||
# Released under the MIT License (MIT) - see LICENSE file
|
|
||||||
|
|
||||||
import uasyncio as asyncio
|
|
||||||
import utime as time
|
|
||||||
from . import launch
|
|
||||||
|
|
||||||
class Switch:
|
|
||||||
debounce_ms = 50
|
|
||||||
def __init__(self, pin):
|
|
||||||
self.pin = pin # Should be initialised for input with pullup
|
|
||||||
self._open_func = False
|
|
||||||
self._close_func = False
|
|
||||||
self.switchstate = self.pin.value() # Get initial state
|
|
||||||
asyncio.create_task(self.switchcheck()) # Thread runs forever
|
|
||||||
|
|
||||||
def open_func(self, func, args=()):
|
|
||||||
self._open_func = func
|
|
||||||
self._open_args = args
|
|
||||||
|
|
||||||
def close_func(self, func, args=()):
|
|
||||||
self._close_func = func
|
|
||||||
self._close_args = args
|
|
||||||
|
|
||||||
# Return current state of switch (0 = pressed)
|
|
||||||
def __call__(self):
|
|
||||||
return self.switchstate
|
|
||||||
|
|
||||||
async def switchcheck(self):
|
|
||||||
while True:
|
|
||||||
state = self.pin.value()
|
|
||||||
if state != self.switchstate:
|
|
||||||
# State has changed: act on it now.
|
|
||||||
self.switchstate = state
|
|
||||||
if state == 0 and self._close_func:
|
|
||||||
launch(self._close_func, self._close_args)
|
|
||||||
elif state == 1 and self._open_func:
|
|
||||||
launch(self._open_func, self._open_args)
|
|
||||||
# Ignore further state changes until switch has settled
|
|
||||||
await asyncio.sleep_ms(Switch.debounce_ms)
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
_attrs = {
|
||||||
|
"Adjuster": "adjuster",
|
||||||
|
"FloatAdj": "adjuster",
|
||||||
|
"Button": "buttons",
|
||||||
|
"CloseButton": "buttons",
|
||||||
|
"ButtonList": "buttons",
|
||||||
|
"RadioButtons": "buttons",
|
||||||
|
"Checkbox": "checkbox",
|
||||||
|
"Dial": "dial",
|
||||||
|
"Pointer": "dial",
|
||||||
|
"DialogBox": "dialog",
|
||||||
|
"Dropdown": "dropdown",
|
||||||
|
"Knob": "knob",
|
||||||
|
"Label": "label",
|
||||||
|
"LED": "led",
|
||||||
|
"Listbox": "listbox",
|
||||||
|
"SubMenu": "menu",
|
||||||
|
"Menu": "menu",
|
||||||
|
"Meter": "meter",
|
||||||
|
"Region": "region",
|
||||||
|
"ScaleLog": "scale_log",
|
||||||
|
"Scale": "scale",
|
||||||
|
"Slider": "sliders",
|
||||||
|
"HorizSlider": "sliders",
|
||||||
|
"Textbox": "textbox",
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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
|
|
@ -15,7 +15,7 @@ class Button(Widget):
|
||||||
lit_time = 1000
|
lit_time = 1000
|
||||||
def __init__(self, writer, row, col, *, shape=RECTANGLE, height=20, width=50,
|
def __init__(self, writer, row, col, *, shape=RECTANGLE, height=20, width=50,
|
||||||
fgcolor=None, bgcolor=None, bdcolor=False, textcolor=None, litcolor=None, text='',
|
fgcolor=None, bgcolor=None, bdcolor=False, textcolor=None, litcolor=None, text='',
|
||||||
callback=dolittle, args=[], onrelease=False):
|
callback=dolittle, args=[]):
|
||||||
sl = writer.stringlen(text)
|
sl = writer.stringlen(text)
|
||||||
if shape == CIRCLE: # Only height need be specified
|
if shape == CIRCLE: # Only height need be specified
|
||||||
width = max(sl, height)
|
width = max(sl, height)
|
||||||
|
@ -30,7 +30,6 @@ class Button(Widget):
|
||||||
self.text = text
|
self.text = text
|
||||||
self.callback = callback
|
self.callback = callback
|
||||||
self.callback_args = args
|
self.callback_args = args
|
||||||
self.onrelease = onrelease
|
|
||||||
if self.litcolor is not None:
|
if self.litcolor is not None:
|
||||||
self.delay = Delay_ms(self.shownormal)
|
self.delay = Delay_ms(self.shownormal)
|
||||||
|
|
||||||
|
@ -77,17 +76,12 @@ class Button(Widget):
|
||||||
self.draw = True # Redisplay
|
self.draw = True # Redisplay
|
||||||
|
|
||||||
def do_sel(self): # Select was pushed
|
def do_sel(self): # Select was pushed
|
||||||
if not self.onrelease:
|
self.callback(self, *self.callback_args) # CB takes self as 1st arg.
|
||||||
self.callback(self, *self.callback_args) # CB takes self as 1st arg.
|
|
||||||
if self.litcolor is not None and self.has_focus(): # CB may have changed focus
|
if self.litcolor is not None and self.has_focus(): # CB may have changed focus
|
||||||
self.bgcolor = self.litcolor
|
self.bgcolor = self.litcolor
|
||||||
self.draw = True # Redisplay
|
self.draw = True # Redisplay
|
||||||
self.delay.trigger(Button.lit_time)
|
self.delay.trigger(Button.lit_time)
|
||||||
|
|
||||||
def unsel(self): # Select was released
|
|
||||||
if self.onrelease:
|
|
||||||
self.callback(self, *self.callback_args) # Callback not a bound method so pass self
|
|
||||||
|
|
||||||
# Preferred way to close a screen or dialog. Produces an X button at the top RHS.
|
# Preferred way to close a screen or dialog. Produces an X button at the top RHS.
|
||||||
# Note that if the bottom screen is closed, the application terminates.
|
# Note that if the bottom screen is closed, the application terminates.
|
||||||
class CloseButton(Button):
|
class CloseButton(Button):
|
||||||
|
|
|
@ -24,7 +24,7 @@ class _ListDialog(Window):
|
||||||
# Calculate Window dimensions
|
# Calculate Window dimensions
|
||||||
ap_height = lb_height + 6 # Allow for listbox border
|
ap_height = lb_height + 6 # Allow for listbox border
|
||||||
ap_width = lb_width + 6
|
ap_width = lb_width + 6
|
||||||
super().__init__(row, col, ap_height, ap_width)
|
super().__init__(row, col, ap_height, ap_width, draw_border=False)
|
||||||
self.listbox = Listbox(writer, row + 3, col + 3,
|
self.listbox = Listbox(writer, row + 3, col + 3,
|
||||||
elements = dd.elements,
|
elements = dd.elements,
|
||||||
dlines = dlines, width = lb_width,
|
dlines = dlines, width = lb_width,
|
||||||
|
@ -35,6 +35,7 @@ class _ListDialog(Window):
|
||||||
self.dd = dd
|
self.dd = dd
|
||||||
|
|
||||||
def callback(self, obj_listbox):
|
def callback(self, obj_listbox):
|
||||||
|
display.ipdev.adj_mode(False) # If in 3-button mode, leave adjust mode
|
||||||
Screen.back()
|
Screen.back()
|
||||||
self.dd.value(obj_listbox.value()) # Update it
|
self.dd.value(obj_listbox.value()) # Update it
|
||||||
|
|
||||||
|
@ -106,6 +107,7 @@ class Dropdown(Widget):
|
||||||
if len(self.elements) > 1:
|
if len(self.elements) > 1:
|
||||||
args = (self.writer, self.row - 2, self.col - 2, self)
|
args = (self.writer, self.row - 2, self.col - 2, self)
|
||||||
Screen.change(_ListDialog, args = args)
|
Screen.change(_ListDialog, args = args)
|
||||||
|
display.ipdev.adj_mode(True) # If in 3-button mode, go into adjust mode
|
||||||
|
|
||||||
def _despatch(self, _): # Run the callback specified in elements
|
def _despatch(self, _): # Run the callback specified in elements
|
||||||
x = self.els[self()]
|
x = self.els[self()]
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
# Usage:
|
# Usage:
|
||||||
# from gui.widgets.menu import Menu
|
# from gui.widgets.menu import Menu
|
||||||
|
|
||||||
from gui.core.ugui import Window, Screen
|
from gui.core.ugui import Window, Screen, display
|
||||||
from gui.widgets.buttons import Button
|
from gui.widgets.buttons import Button
|
||||||
from gui.widgets.listbox import Listbox
|
from gui.widgets.listbox import Listbox
|
||||||
from gui.core.colors import *
|
from gui.core.colors import *
|
||||||
|
@ -31,18 +31,20 @@ class SubMenu(Window):
|
||||||
# Calculate Window dimensions
|
# Calculate Window dimensions
|
||||||
ap_height = lb_height + 6 # Allow for listbox border
|
ap_height = lb_height + 6 # Allow for listbox border
|
||||||
ap_width = lb_width + 6
|
ap_width = lb_width + 6
|
||||||
super().__init__(row, col, ap_height, ap_width)
|
super().__init__(row, col, ap_height, ap_width, draw_border=False)
|
||||||
Listbox(wri, row + 3, col + 3, elements = te, width = lb_width,
|
Listbox(wri, row + 3, col + 3, elements = te, width = lb_width,
|
||||||
fgcolor = button.fgcolor, bgcolor = button.bgcolor, bdcolor=False,
|
fgcolor = button.fgcolor, bgcolor = button.bgcolor, bdcolor=False,
|
||||||
fontcolor = button.textcolor, select_color = menu.select_color,
|
fontcolor = button.textcolor, select_color = menu.select_color,
|
||||||
callback = self.callback)
|
callback = self.callback)
|
||||||
|
|
||||||
def callback(self, lbox):
|
def callback(self, lbox):
|
||||||
|
display.ipdev.adj_mode(False) # If in 3-button mode, leave adjust mode
|
||||||
Screen.back()
|
Screen.back()
|
||||||
el = self.elements[lbox.value()] # (text, cb, args)
|
el = self.elements[lbox.value()] # (text, cb, args)
|
||||||
if len(el) == 2: # Recurse into submenu
|
if len(el) == 2: # Recurse into submenu
|
||||||
args = (self.menu, self.button, el[1])
|
args = (self.menu, self.button, el[1])
|
||||||
Screen.change(SubMenu, args = args)
|
Screen.change(SubMenu, args = args)
|
||||||
|
display.ipdev.adj_mode(True) # If in 3-button mode, go into adjust mode
|
||||||
else:
|
else:
|
||||||
el[1](lbox, *el[2])
|
el[1](lbox, *el[2])
|
||||||
|
|
||||||
|
@ -77,3 +79,4 @@ class Menu:
|
||||||
def cb(self, button, txt, elements): # Button pushed which calls submenu
|
def cb(self, button, txt, elements): # Button pushed which calls submenu
|
||||||
args = (self, button, elements)
|
args = (self, button, elements)
|
||||||
Screen.change(SubMenu, args = args)
|
Screen.change(SubMenu, args = args)
|
||||||
|
display.ipdev.adj_mode(True) # If in 3-button mode, go into adjust mode
|
||||||
|
|
|
@ -145,14 +145,14 @@ class ScaleLog(LinearIO):
|
||||||
# Adjust widget's value. Args: button pressed, amount of increment
|
# Adjust widget's value. Args: button pressed, amount of increment
|
||||||
def do_adj(self, button, val):
|
def do_adj(self, button, val):
|
||||||
if isinstance(button, int): # Using an encoder
|
if isinstance(button, int): # Using an encoder
|
||||||
delta = self.delta * self.encoder_rate * 0.1 if self.precision else self.delta * self.encoder_rate
|
delta = self.delta * self.encoder_rate * 0.1 if self.precision() else self.delta * self.encoder_rate
|
||||||
self.value(self.value() * (1 + delta)**val)
|
self.value(self.value() * (1 + delta)**val)
|
||||||
else: # val == 1 or -1
|
else: # val == 1 or -1
|
||||||
asyncio.create_task(self.btnhan(button, val))
|
asyncio.create_task(self.btnhan(button, val))
|
||||||
|
|
||||||
async def btnhan(self, button, up):
|
async def btnhan(self, button, up):
|
||||||
up = up == 1
|
up = up == 1
|
||||||
if self.precision:
|
if self.precision():
|
||||||
delta = self.delta * 0.1
|
delta = self.delta * 0.1
|
||||||
maxdelta = self.delta
|
maxdelta = self.delta
|
||||||
else:
|
else:
|
||||||
|
@ -161,7 +161,7 @@ class ScaleLog(LinearIO):
|
||||||
smul= (1 + delta) if up else (1 / (1 + delta))
|
smul= (1 + delta) if up else (1 / (1 + delta))
|
||||||
self.value(self.value() * smul)
|
self.value(self.value() * smul)
|
||||||
t = ticks_ms()
|
t = ticks_ms()
|
||||||
while not button():
|
while button():
|
||||||
await asyncio.sleep_ms(0) # Quit fast on button release
|
await asyncio.sleep_ms(0) # Quit fast on button release
|
||||||
if ticks_diff(ticks_ms(), t) > 500: # Button was held down
|
if ticks_diff(ticks_ms(), t) > 500: # Button was held down
|
||||||
delta = min(maxdelta, delta * 2)
|
delta = min(maxdelta, delta * 2)
|
||||||
|
|
|
@ -51,4 +51,5 @@ sel = Pin(16, Pin.IN, Pin.PULL_UP) # Operate current control
|
||||||
prev = Pin(18, Pin.IN, Pin.PULL_UP) # Move to previous control
|
prev = Pin(18, Pin.IN, Pin.PULL_UP) # Move to previous control
|
||||||
increase = Pin(20, Pin.IN, Pin.PULL_UP) # Increase control's value
|
increase = Pin(20, Pin.IN, Pin.PULL_UP) # Increase control's value
|
||||||
decrease = Pin(17, Pin.IN, Pin.PULL_UP) # Decrease control's value
|
decrease = Pin(17, Pin.IN, Pin.PULL_UP) # Decrease control's value
|
||||||
display = Display(ssd, nxt, sel, prev, increase, decrease) #, 5)
|
display = Display(ssd, nxt, sel, prev) # 3-button mode
|
||||||
|
# display = Display(ssd, nxt, sel, prev, increase, decrease, 5) # Encoder mode
|
||||||
|
|
Ładowanie…
Reference in New Issue