V0.1.5 3-button adjustment mode.

pull/16/head
Peter Hinch 2022-02-06 12:05:38 +00:00
rodzic d2247be40a
commit f7c33cb408
33 zmienionych plików z 249 dodań i 257 usunięć

Wyświetl plik

@ -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
``` ```
![Image](./images/label.JPG) ![Image](./images/label.JPG)
@ -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
``` ```
![Image](./images/led.JPG) ![Image](./images/led.JPG)
@ -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
``` ```
![Image](./images/checkbox.JPG) ![Image](./images/checkbox.JPG)
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
``` ```
![Image](./images/pushbuttons.JPG) ![Image](./images/pushbuttons.JPG)
@ -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
``` ```
![Image](./images/radiobuttons.JPG) ![Image](./images/radiobuttons.JPG)
@ -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
``` ```
![Image](./images/listbox.JPG) ![Image](./images/listbox.JPG)
@ -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
``` ```
![Image](./images/dd_closed.JPG) ![Image](./images/dd_closed.JPG)
@ -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
``` ```
![Image](./images/dialog.JPG) ![Image](./images/dialog.JPG)
@ -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
``` ```
![Image](./images/textbox.JPG) ![Image](./images/textbox.JPG)
@ -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
``` ```
![Image](./images/meter.JPG) ![Image](./images/meter.JPG)
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
``` ```
![Image](./images/sliders.JPG) ![Image](./images/sliders.JPG)
@ -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
``` ```
![Image](./images/scale.JPG) ![Image](./images/scale.JPG)
@ -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
``` ```
![Image](./images/log_scale.JPG) ![Image](./images/log_scale.JPG)
@ -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
``` ```
![Image](./images/dial.JPG) ![Image](./images/dial1.JPG) ![Image](./images/dial.JPG) ![Image](./images/dial1.JPG)
@ -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
``` ```
![Image](./images/knob.JPG) ![Image](./images/knob.JPG)
@ -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
``` ```
![Image](./images/adjusters.jpg) ![Image](./images/adj_vector.jpg) ![Image](./images/adjusters.jpg) ![Image](./images/adj_vector.jpg)
@ -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
``` ```
![Image](./images/menu.JPG) ![Image](./images/menu.JPG)

Wyświetl plik

@ -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]

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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):

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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):

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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):

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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'):

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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)

Wyświetl plik

@ -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

Wyświetl plik

@ -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):

Wyświetl plik

@ -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()]

Wyświetl plik

@ -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

Wyświetl plik

@ -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)

Wyświetl plik

@ -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