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
|
||||
drivers subclassed from `framebuf`. Written in Python it runs under a standard
|
||||
MicroPython firmware build. Options for data input comprise:
|
||||
* Via from two to five pushbuttons depending on the application.
|
||||
* Via a switch-based navigation joystick.
|
||||
* Two pushbuttons: limited capabilities with some widgets unusable for input.
|
||||
* 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
|
||||
[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 enables switching between screens and launching modal windows. In addition
|
||||
to `nano-gui` widgets it supports listboxes, dropdown lists, various means of
|
||||
entering or displaying floating point values, and other widgets.
|
||||
to `nano-gui` widgets it supports menus, listboxes, dropdown lists, various
|
||||
means of entering or displaying floating point values, and other widgets.
|
||||
|
||||
It is compatible with all display drivers for
|
||||
[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
|
||||
|
||||
Code has been tested on ESP32, Pi Pico and Pyboard. The API shuld be stable.
|
||||
Code is new and issues are likely: please report any found. The project is
|
||||
under development so check for updates.
|
||||
February 2022: This has had a significant upgrade to support use with only
|
||||
three buttons as devised by Bart Cerneels. Simplified widget import. Existing
|
||||
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
|
||||
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
|
||||
from gui.core.ugui import Screen, ssd
|
||||
|
||||
from gui.widgets.label import Label
|
||||
from gui.widgets.buttons import Button, CloseButton
|
||||
from gui.widgets import Label, Button, CloseButton
|
||||
from gui.core.writer import CWriter
|
||||
|
||||
# Font for CWriter
|
||||
import gui.fonts.arial10 as arial10
|
||||
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
|
||||
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
|
||||
`Select` button.
|
||||
`Select` button. This provides the most intuitive operation.
|
||||
|
||||
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
|
||||
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
|
||||
floating point data entry require the `Increase` and `Decrease` buttons (or an
|
||||
encoder) to select a data item or to adjust the linear value. This is discussed
|
||||
in [Floating Point Widgets](./README.md#112-floating-point-widgets).
|
||||
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. If three buttons
|
||||
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`
|
||||
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
|
||||
`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
|
||||
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
|
||||
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)
|
||||
from gui.core.ugui import Screen, ssd
|
||||
|
||||
from gui.widgets.label import Label
|
||||
from gui.widgets.buttons import Button, CloseButton
|
||||
from gui.widgets import Label, Button, CloseButton
|
||||
from gui.core.writer import CWriter
|
||||
|
||||
# Font for CWriter
|
||||
|
@ -940,7 +947,7 @@ constructor and is closed by issuing the `close()` static method.
|
|||
# 6. Label widget
|
||||
|
||||
```python
|
||||
from gui.widgets.label import Label
|
||||
from gui.widgets import Label
|
||||
```
|
||||
![Image](./images/label.JPG)
|
||||
|
||||
|
@ -1001,8 +1008,7 @@ from gui.core.ugui import Screen
|
|||
from gui.core.writer import CWriter
|
||||
from gui.core.colors import *
|
||||
|
||||
from gui.widgets.label import Label
|
||||
from gui.widgets.buttons import CloseButton
|
||||
from gui.widgets import Label, CloseButton
|
||||
import gui.fonts.freesans20 as freesans20
|
||||
|
||||
|
||||
|
@ -1022,7 +1028,7 @@ Screen.change(BaseScreen)
|
|||
# 7. LED widget
|
||||
|
||||
```python
|
||||
from gui.widgets.led import LED
|
||||
from gui.widgets import LED
|
||||
```
|
||||
![Image](./images/led.JPG)
|
||||
|
||||
|
@ -1061,7 +1067,7 @@ controlled with `led(True)` or `led(False)`.
|
|||
# 8. Checkbox widget
|
||||
|
||||
```python
|
||||
from gui.widgets.checkbox import Checkbox
|
||||
from gui.widgets import Checkbox
|
||||
```
|
||||
![Image](./images/checkbox.JPG)
|
||||
This provides for Boolean data entry and display. In the `True` state the
|
||||
|
@ -1103,7 +1109,7 @@ Methods:
|
|||
|
||||
```python
|
||||
from gui.core.colors import * # Colors and shapes
|
||||
from gui.widgets.buttons import Button
|
||||
from gui.widgets import Button
|
||||
```
|
||||
![Image](./images/pushbuttons.JPG)
|
||||
|
||||
|
@ -1145,8 +1151,6 @@ Optional keyword only arguments:
|
|||
for example media playback symbols.
|
||||
* `callback=dolittle` Callback function which runs when button is pressed.
|
||||
* `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:
|
||||
* `greyed_out` Optional Boolean argument `val=None`. If `None` returns the
|
||||
|
@ -1182,7 +1186,7 @@ Optional keyword only arguments:
|
|||
|
||||
```python
|
||||
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
|
||||
|
@ -1241,7 +1245,7 @@ for t in table: # Buttons overlay each other at same location
|
|||
|
||||
```python
|
||||
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)
|
||||
|
||||
|
@ -1288,7 +1292,7 @@ for t in table:
|
|||
# 12. Listbox widget
|
||||
|
||||
```python
|
||||
from gui.widgets.listbox import Listbox
|
||||
from gui.widgets import Listbox
|
||||
```
|
||||
![Image](./images/listbox.JPG)
|
||||
|
||||
|
@ -1397,7 +1401,7 @@ Screen.change(BaseScreen)
|
|||
# 13. Dropdown widget
|
||||
|
||||
```python
|
||||
from gui.widgets.dropdown import Dropdown
|
||||
from gui.widgets import Dropdown
|
||||
```
|
||||
|
||||
![Image](./images/dd_closed.JPG)
|
||||
|
@ -1512,7 +1516,7 @@ Screen.change(BaseScreen)
|
|||
# 14. DialogBox class
|
||||
|
||||
```python
|
||||
from gui.widgets.dialog import DialogBox
|
||||
from gui.widgets import DialogBox
|
||||
```
|
||||
![Image](./images/dialog.JPG)
|
||||
|
||||
|
@ -1566,7 +1570,7 @@ in `gui/demos/screens.py`.
|
|||
# 15. Textbox widget
|
||||
|
||||
```python
|
||||
from gui.widgets.textbox import Textbox
|
||||
from gui.widgets import Textbox
|
||||
```
|
||||
![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
|
||||
linear scale. Optionally it can support data dependent callbacks.
|
||||
```python
|
||||
from gui.widgets.meter import Meter
|
||||
from gui.widgets import Meter
|
||||
```
|
||||
![Image](./images/meter.JPG)
|
||||
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
|
||||
|
||||
```python
|
||||
from gui.widgets.region import Region
|
||||
from gui.widgets import Region
|
||||
```
|
||||
Instantiating a `Region` associates it with a supporting widget (currently only
|
||||
a `Meter`). Constructor positional args are as follows:
|
||||
|
@ -1787,7 +1791,7 @@ callbacks to run as appropriate.
|
|||
# 17. Slider and HorizSlider widgets
|
||||
|
||||
```python
|
||||
from gui.widgets.sliders import Slider, HorizSlider
|
||||
from gui.widgets import Slider, HorizSlider
|
||||
```
|
||||
![Image](./images/sliders.JPG)
|
||||
|
||||
|
@ -1865,7 +1869,7 @@ around sliders to display all legends.
|
|||
# 18. Scale widget
|
||||
|
||||
```python
|
||||
from gui.widgets.scale import Scale
|
||||
from gui.widgets import Scale
|
||||
```
|
||||
![Image](./images/scale.JPG)
|
||||
|
||||
|
@ -2002,7 +2006,7 @@ precision. Each visible division on the control represents 10 integer units.
|
|||
# 19. ScaleLog widget
|
||||
|
||||
```python
|
||||
from gui.widgets.scale_log import ScaleLog
|
||||
from gui.widgets import ScaleLog
|
||||
```
|
||||
![Image](./images/log_scale.JPG)
|
||||
|
||||
|
@ -2142,7 +2146,7 @@ def tickcb(f, c):
|
|||
# 20. Dial widget
|
||||
|
||||
```python
|
||||
from gui.widgets.dial import Dial, Pointer
|
||||
from gui.widgets import Dial, Pointer
|
||||
```
|
||||
![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.colors import *
|
||||
|
||||
from gui.widgets.dial import Dial, Pointer
|
||||
from gui.widgets.buttons import CloseButton
|
||||
from gui.widgets import Dial, Pointer, CloseButton
|
||||
import gui.fonts.freesans20 as freesans20
|
||||
|
||||
async def run(dial):
|
||||
|
@ -2259,7 +2262,7 @@ Screen.change(BaseScreen)
|
|||
# 21. Knob widget
|
||||
|
||||
```python
|
||||
from gui.widgets.knob import Knob
|
||||
from gui.widgets import Knob
|
||||
```
|
||||
![Image](./images/knob.JPG)
|
||||
|
||||
|
@ -2317,7 +2320,7 @@ value changes. This enables dynamic color change.
|
|||
# 22. Adjuster widget
|
||||
|
||||
```python
|
||||
from gui.widgets.adjuster import Adjuster
|
||||
from gui.widgets import Adjuster
|
||||
```
|
||||
![Image](./images/adjusters.jpg) ![Image](./images/adj_vector.jpg)
|
||||
|
||||
|
@ -2383,7 +2386,7 @@ basis. See code comments for further details.
|
|||
# 23 Menu class
|
||||
|
||||
```python
|
||||
from gui.widgets.menu import Menu
|
||||
from gui.widgets import Menu
|
||||
```
|
||||
![Image](./images/menu.JPG)
|
||||
|
||||
|
|
|
@ -55,4 +55,5 @@ PRECISION = 1
|
|||
FG = 2
|
||||
BG = 3
|
||||
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.
|
||||
# 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
|
||||
from uasyncio import Event
|
||||
|
@ -13,15 +13,14 @@ import gc
|
|||
from gui.core.colors import *
|
||||
from hardware_setup import ssd
|
||||
|
||||
from gui.primitives.delay_ms import Delay_ms
|
||||
from gui.primitives.switch import Switch
|
||||
from gui.primitives import Pushbutton
|
||||
|
||||
# Globally available singleton objects
|
||||
display = None # Singleton instance
|
||||
ssd = None
|
||||
|
||||
gc.collect()
|
||||
__version__ = (0, 1, 4)
|
||||
__version__ = (0, 1, 5)
|
||||
|
||||
# Null function
|
||||
dolittle = lambda *_ : None
|
||||
|
@ -30,60 +29,97 @@ async def _g():
|
|||
pass
|
||||
type_coro = type(_g())
|
||||
|
||||
# Navigation destinations
|
||||
_FIRST = const(0)
|
||||
_NEXT = const(1)
|
||||
_PREV = const(2)
|
||||
_LAST = const(3)
|
||||
|
||||
# Wrapper for ssd providing buttons and framebuf compatible methods
|
||||
class Display:
|
||||
verbose = True
|
||||
# Input abstracts input from 2-5 pushbuttons or 3 buttons + encoder. Handles
|
||||
# transitions between modes (normal, precision, adjustment)
|
||||
class Input:
|
||||
|
||||
def __init__(self, objssd, nxt, sel, prev=None, incr=None, decr=None, encoder=False):
|
||||
global display, ssd
|
||||
self._next = Switch(nxt)
|
||||
self._sel = Switch(sel)
|
||||
self._last = None # Last switch pressed.
|
||||
def __init__(self, nxt, sel, prev, incr, decr, encoder):
|
||||
verbose = True
|
||||
self._encoder = encoder # Encoder in use
|
||||
self._precision = False # Precision mode
|
||||
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
|
||||
self._next = Pushbutton(nxt)
|
||||
self._sel = Pushbutton(sel, suppress=True)
|
||||
# Call current screen bound method
|
||||
self._next.close_func(self._closure, (self._next, Screen.ctrl_move, _NEXT))
|
||||
self._sel.close_func(self._closure, (self._sel, Screen.sel_ctrl, 0))
|
||||
self._sel.open_func(Screen.unsel)
|
||||
|
||||
self.height = objssd.height
|
||||
self.width = objssd.width
|
||||
|
||||
self._next.press_func(Screen.ctrl_move, (_NEXT,))
|
||||
self._sel.release_func(Screen.sel_ctrl)
|
||||
if encoder or (self._nb > 2): # Can use precision mode when in adjust mode
|
||||
self._sel.long_func(self.precision, (True,))
|
||||
if self._nb == 3: # Special case of 3-button interface
|
||||
self._sel.double_func(self.adj_mode) # Double click toggles adjust
|
||||
# Optional buttons
|
||||
self._prev = None
|
||||
if prev is not None:
|
||||
self._prev = Switch(prev)
|
||||
self._prev.close_func(self._closure, (self._prev, Screen.ctrl_move, _PREV))
|
||||
self._prev = Pushbutton(prev)
|
||||
self._prev.press_func(Screen.ctrl_move, (_PREV,))
|
||||
if encoder:
|
||||
self.verbose and print('Using encoder.')
|
||||
verbose and print('Using encoder.')
|
||||
if incr is None or decr is None:
|
||||
raise ValueError('Must specify pins for encoder.')
|
||||
from gui.primitives.encoder import Encoder
|
||||
self._enc = Encoder(incr, decr, div=encoder, callback=Screen.adjust)
|
||||
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.
|
||||
if incr is not None:
|
||||
sup = Switch(incr)
|
||||
sup.close_func(self._closure, (sup, Screen.adjust, 1))
|
||||
sup = Pushbutton(incr)
|
||||
sup.press_func(Screen.adjust, (sup, 1))
|
||||
if decr is not None:
|
||||
sdn = Switch(decr)
|
||||
sdn.close_func(self._closure, (sdn, Screen.adjust, -1))
|
||||
sdn = Pushbutton(decr)
|
||||
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
|
||||
display = self # Populate globals
|
||||
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):
|
||||
sl = writer.stringlen(text)
|
||||
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_done = Event() # Flag a user task that a refresh was done.
|
||||
|
||||
@classmethod
|
||||
def ctrl_move(cls, _, v):
|
||||
@classmethod # Called by Input when status change needs redraw of current obj
|
||||
def redraw_co(cls):
|
||||
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)
|
||||
|
||||
@classmethod
|
||||
def sel_ctrl(cls, b, _):
|
||||
def sel_ctrl(cls):
|
||||
if cls.current_screen is not None:
|
||||
display.ipdev.precision(False) # Cancel precision mode
|
||||
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
|
||||
# is an int (discarded), val is the delta. If using buttons, 1st
|
||||
# arg is the button, delta is +1 or -1
|
||||
|
@ -269,6 +309,7 @@ class Screen:
|
|||
cs_new = new_screen
|
||||
else:
|
||||
cs_new = cls_new_screen # An object, not a class
|
||||
display.ipdev.adj_mode(False) # Ensure normal mode
|
||||
cls.current_screen = cs_new
|
||||
cs_new.on_open() # Optional subclass method
|
||||
cs_new._do_open(cs_old) # Clear and redraw
|
||||
|
@ -413,10 +454,6 @@ class Screen:
|
|||
lo.show() # Re-display with new status
|
||||
co.enter() # Tell object it has currency
|
||||
co.show()
|
||||
#elif isinstance(self, Window):
|
||||
# Special case of Window with one object: leave
|
||||
# without making changes (Dropdown in particular)
|
||||
#Screen.back()
|
||||
done = True
|
||||
|
||||
# Move currency to a specific control.
|
||||
|
@ -439,11 +476,6 @@ class Screen:
|
|||
if co is not None:
|
||||
co.do_sel()
|
||||
|
||||
def unsel_i(self):
|
||||
co = self.get_obj()
|
||||
if co is not None:
|
||||
co.unsel()
|
||||
|
||||
def do_adj(self, button, val):
|
||||
co = self.get_obj()
|
||||
if co is not None and hasattr(co, 'do_adj'):
|
||||
|
@ -475,7 +507,7 @@ class Screen:
|
|||
await asyncio.sleep_ms(500)
|
||||
gc.collect()
|
||||
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
|
||||
# content may be drawn.
|
||||
|
@ -570,7 +602,7 @@ class Widget:
|
|||
self.bgcolor = bgcolor
|
||||
# bdcolor is False if no border is to be drawn
|
||||
self.bdcolor = bdcolor
|
||||
# Default colors allow restoration after dynamic change
|
||||
# Default colors allow restoration after dynamic change (Label)
|
||||
self.def_fgcolor = fgcolor
|
||||
self.def_bgcolor = bgcolor
|
||||
self.def_bdcolor = bdcolor
|
||||
|
@ -607,7 +639,6 @@ class Widget:
|
|||
if self.screen != Screen.current_screen:
|
||||
# Can occur if a control's action is to change screen.
|
||||
return False # Subclass abandons
|
||||
|
||||
self.draw = False
|
||||
self.draw_border()
|
||||
# Blank controls' space
|
||||
|
@ -629,8 +660,11 @@ class Widget:
|
|||
h = self.height + 4
|
||||
if self.has_focus() and not isinstance(self, DummyWidget):
|
||||
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
|
||||
elif display.ipdev.is_adjust():
|
||||
color = color_map[ADJUSTING]
|
||||
dev.rect(x, y, w, h, color)
|
||||
self.has_border = True
|
||||
else:
|
||||
|
@ -676,9 +710,6 @@ class Widget:
|
|||
def do_sel(self): # Select button was pushed
|
||||
pass
|
||||
|
||||
def unsel(self): # Select button was released
|
||||
pass
|
||||
|
||||
def enter(self): # Control has acquired focus
|
||||
pass
|
||||
|
||||
|
@ -695,6 +726,7 @@ class Widget:
|
|||
# have do_up and do_down methods which adjust the control's value in a
|
||||
# time-dependent manner.
|
||||
class LinearIO(Widget):
|
||||
|
||||
def __init__(self, writer, row, col, height, width,
|
||||
fgcolor, bgcolor, bdcolor,
|
||||
value=None, active=True, prcolor=False,
|
||||
|
@ -704,53 +736,31 @@ class LinearIO(Widget):
|
|||
super().__init__(writer, row, col, height, width,
|
||||
fgcolor, bgcolor, bdcolor,
|
||||
value, active)
|
||||
# Handle variable precision. Start normal
|
||||
self.precision = False
|
||||
self.do_precision = prcolor is not False
|
||||
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
|
||||
|
||||
# Adjust widget's value. Args: button pressed, amount of increment
|
||||
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)
|
||||
if not encoder:
|
||||
if not display.ipdev.encoder():
|
||||
asyncio.create_task(self.btnhan(button, val, d))
|
||||
|
||||
# Handle increase and decrease buttons. Redefined by textbox.py, scale_log.py
|
||||
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()
|
||||
while not button():
|
||||
while button():
|
||||
await asyncio.sleep_ms(0) # Quit fast on button release
|
||||
if ticks_diff(ticks_ms(), t) > 500: # Button was held down
|
||||
d = min(maxd, d * 2)
|
||||
self.value(self.value() + up * d)
|
||||
t = ticks_ms()
|
||||
|
||||
def precise(self, v): # Timed out while button pressed
|
||||
self.precision = v
|
||||
self.draw = True
|
||||
|
||||
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)
|
||||
# Get current status (also used by scale_log widget)
|
||||
def precision(self):
|
||||
return self.do_precision and display.ipdev.is_precision()
|
||||
|
||||
# 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
|
||||
|
|
|
@ -6,9 +6,7 @@
|
|||
# Initialise hardware and framebuf before importing modules.
|
||||
import hardware_setup # Create a display instance
|
||||
from gui.core.ugui import Screen, ssd
|
||||
from gui.widgets.label import Label
|
||||
from gui.widgets.dial import Dial, Pointer
|
||||
from gui.widgets.buttons import CloseButton
|
||||
from gui.widgets import Label, Dial, Pointer, CloseButton
|
||||
|
||||
# Now import other modules
|
||||
from cmath import rect, pi
|
||||
|
|
|
@ -10,13 +10,7 @@ from gui.core.writer import CWriter
|
|||
import gui.fonts.arial10 as arial10 # Font for CWriter
|
||||
from gui.core.colors import *
|
||||
# Widgets
|
||||
from gui.widgets.label import Label
|
||||
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
|
||||
from gui.widgets import Label, Scale, ScaleLog, Button, CloseButton, Slider, HorizSlider, Knob, Checkbox
|
||||
|
||||
|
||||
class BaseScreen(Screen):
|
||||
|
|
|
@ -8,10 +8,7 @@ import hardware_setup # Create a display instance
|
|||
import cmath
|
||||
from gui.core.ugui import Screen, ssd
|
||||
|
||||
from gui.widgets.label import Label
|
||||
from gui.widgets.buttons import CloseButton
|
||||
from gui.widgets.adjuster import Adjuster
|
||||
from gui.widgets.dial import Dial, Pointer
|
||||
from gui.widgets import Label, CloseButton, Adjuster, Dial, Pointer
|
||||
from gui.core.writer import CWriter
|
||||
|
||||
# Font for CWriter
|
||||
|
|
|
@ -7,9 +7,7 @@
|
|||
import hardware_setup # Create a display instance
|
||||
from gui.core.ugui import Screen, ssd
|
||||
|
||||
from gui.widgets.label import Label
|
||||
from gui.widgets.buttons import CloseButton
|
||||
from gui.widgets.adjuster import Adjuster, FloatAdj
|
||||
from gui.widgets import Label, CloseButton, Adjuster, FloatAdj
|
||||
from gui.core.writer import CWriter
|
||||
|
||||
# Font for CWriter
|
||||
|
|
|
@ -44,9 +44,7 @@ audio_out = I2S(I2S_ID, **config)
|
|||
# ======= GUI =======
|
||||
|
||||
from gui.widgets.label import Label
|
||||
from gui.widgets.buttons import Button, CloseButton, CIRCLE
|
||||
from gui.widgets.sliders import HorizSlider
|
||||
from gui.widgets.listbox import Listbox
|
||||
from gui.widgets import Button, CloseButton, HorizSlider, Listbox
|
||||
from gui.core.writer import CWriter
|
||||
|
||||
# Font for CWriter
|
||||
|
@ -59,7 +57,7 @@ import gc
|
|||
import uasyncio as asyncio
|
||||
import sys
|
||||
|
||||
# Initial check on ilesystem
|
||||
# Initial check on filesystem
|
||||
try:
|
||||
subdirs = [x[0] for x in os.ilistdir(root) if x[1] == 0x4000]
|
||||
if len(subdirs):
|
||||
|
|
|
@ -7,9 +7,7 @@
|
|||
import hardware_setup # Create a display instance
|
||||
from gui.core.ugui import Screen, ssd
|
||||
|
||||
from gui.widgets.buttons import CloseButton
|
||||
from gui.widgets.checkbox import Checkbox
|
||||
from gui.widgets.led import LED
|
||||
from gui.widgets import CloseButton, Checkbox, LED
|
||||
from gui.core.writer import CWriter
|
||||
|
||||
# Font for CWriter
|
||||
|
|
|
@ -7,9 +7,7 @@
|
|||
import hardware_setup # Create a display instance
|
||||
from gui.core.ugui import Screen, Window, ssd
|
||||
|
||||
from gui.widgets.label import Label
|
||||
from gui.widgets.buttons import Button, CloseButton
|
||||
from gui.widgets.dialog import DialogBox
|
||||
from gui.widgets import Label, Button, CloseButton, DialogBox
|
||||
from gui.core.writer import CWriter
|
||||
|
||||
# Font for CWriter
|
||||
|
|
|
@ -7,9 +7,7 @@
|
|||
import hardware_setup # Create a display instance
|
||||
from gui.core.ugui import Screen, Window, ssd
|
||||
|
||||
from gui.widgets.label import Label
|
||||
from gui.widgets.buttons import CloseButton
|
||||
from gui.widgets.dropdown import Dropdown
|
||||
from gui.widgets import Label, CloseButton, Dropdown
|
||||
from gui.core.writer import CWriter
|
||||
|
||||
# Font for CWriter
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
import hardware_setup # Create a display instance
|
||||
from gui.core.ugui import Screen, ssd
|
||||
|
||||
from gui.widgets.buttons import CloseButton
|
||||
from gui.widgets.sliders import Slider
|
||||
from gui.widgets import CloseButton, Slider
|
||||
from gui.core.writer import CWriter
|
||||
|
||||
# Font for CWriter
|
||||
|
|
|
@ -9,8 +9,7 @@ from gui.core.ugui import Screen
|
|||
from gui.core.writer import CWriter
|
||||
from gui.core.colors import *
|
||||
|
||||
from gui.widgets.listbox import Listbox
|
||||
from gui.widgets.buttons import CloseButton
|
||||
from gui.widgets import Listbox, CloseButton
|
||||
import gui.fonts.freesans20 as font
|
||||
|
||||
|
||||
|
|
|
@ -8,8 +8,7 @@ from gui.core.ugui import Screen, ssd
|
|||
import gui.fonts.freesans20 as font
|
||||
from gui.core.writer import CWriter
|
||||
|
||||
from gui.widgets.menu import Menu
|
||||
from gui.widgets.buttons import CloseButton
|
||||
from gui.widgets import Menu, CloseButton
|
||||
from gui.core.colors import *
|
||||
|
||||
class BaseScreen(Screen):
|
||||
|
|
|
@ -18,9 +18,7 @@ from collections import OrderedDict
|
|||
from gui.core.writer import Writer, CWriter
|
||||
from gui.core.ugui import Screen, ssd
|
||||
from gui.widgets.graph import PolarGraph, PolarCurve, CartesianGraph, Curve, TSequence
|
||||
from gui.widgets.label import Label
|
||||
from gui.widgets.buttons import Button, CloseButton
|
||||
from gui.widgets.listbox import Listbox
|
||||
from gui.widgets import Label, Button, CloseButton, Listbox
|
||||
|
||||
# Fonts & colors
|
||||
import gui.fonts.arial10 as arial10
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
import hardware_setup # Create a display instance
|
||||
from gui.core.ugui import Screen, Window, ssd, display
|
||||
|
||||
from gui.widgets.label import Label
|
||||
from gui.widgets.buttons import CloseButton
|
||||
from gui.widgets import Label, CloseButton
|
||||
from gui.core.writer import CWriter
|
||||
|
||||
# Font for CWriter
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
import hardware_setup # Create a display instance
|
||||
from gui.core.ugui import Screen, ssd
|
||||
|
||||
from gui.widgets.buttons import Button, CloseButton
|
||||
from gui.widgets.label import Label
|
||||
from gui.widgets import Button, CloseButton, Label
|
||||
from gui.core.writer import 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.widgets.label import Label
|
||||
from gui.widgets.buttons import Button, RadioButtons, CloseButton
|
||||
from gui.widgets.listbox import Listbox
|
||||
from gui.widgets.dropdown import Dropdown
|
||||
from gui.widgets.dialog import DialogBox
|
||||
from gui.widgets import Button, RadioButtons, CloseButton, Listbox, Dropdown, DialogBox
|
||||
from gui.core.writer import CWriter
|
||||
|
||||
# Font for CWriter
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
import hardware_setup # Create a display instance
|
||||
from gui.core.ugui import Screen, ssd
|
||||
|
||||
from gui.widgets.label import Label
|
||||
from gui.widgets.buttons import Button, CloseButton
|
||||
from gui.widgets import Label, Button, CloseButton
|
||||
from gui.core.writer import CWriter
|
||||
|
||||
# Font for CWriter
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
import hardware_setup
|
||||
from gui.core.ugui import Screen, ssd
|
||||
|
||||
from gui.widgets.buttons import CloseButton
|
||||
from gui.widgets.sliders import Slider
|
||||
from gui.widgets import CloseButton, Slider
|
||||
from gui.core.writer import CWriter
|
||||
|
||||
# Font for CWriter
|
||||
|
|
|
@ -7,9 +7,7 @@
|
|||
import hardware_setup # Create a display instance
|
||||
from gui.core.ugui import Screen, ssd
|
||||
|
||||
from gui.widgets.buttons import CloseButton
|
||||
from gui.widgets.sliders import Slider
|
||||
from gui.widgets.label import Label
|
||||
from gui.widgets import CloseButton, Slider, Label
|
||||
from gui.core.writer import CWriter
|
||||
|
||||
# Font for CWriter
|
||||
|
|
|
@ -15,9 +15,7 @@ from gui.core.writer import CWriter
|
|||
import uasyncio as asyncio
|
||||
from gui.core.colors import *
|
||||
import gui.fonts.arial10 as arial10
|
||||
from gui.widgets.label import Label
|
||||
from gui.widgets.textbox import Textbox
|
||||
from gui.widgets.buttons import Button, CloseButton
|
||||
from gui.widgets import Label, Textbox, Button, CloseButton
|
||||
|
||||
wri = CWriter(ssd, arial10) # verbose = True
|
||||
|
||||
|
|
|
@ -10,12 +10,7 @@
|
|||
import hardware_setup # Create a display instance
|
||||
from gui.core.ugui import Screen, ssd
|
||||
|
||||
from gui.widgets.buttons import Button, CloseButton
|
||||
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.widgets import Button, CloseButton, Slider, Label, Meter, Region, LED
|
||||
from gui.core.writer import CWriter
|
||||
|
||||
# Font for CWriter
|
||||
|
|
|
@ -16,9 +16,7 @@ from gui.core.writer import CWriter
|
|||
from gui.fonts import font10
|
||||
from gui.core.colors import *
|
||||
# Widgets
|
||||
from gui.widgets.label import Label
|
||||
from gui.widgets.buttons import Button, CloseButton
|
||||
from gui.widgets.dial import Pointer, Dial
|
||||
from gui.widgets import Label, Button, CloseButton, Pointer, Dial
|
||||
|
||||
|
||||
def fwdbutton(wri, row, col, cls_screen, text='Next'):
|
||||
|
|
|
@ -2,6 +2,24 @@
|
|||
|
||||
# Copyright (c) 2018-2020 Peter Hinch
|
||||
# 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:
|
||||
import uasyncio as asyncio
|
||||
|
|
|
@ -27,7 +27,7 @@ class Delay_ms:
|
|||
self._tout = asyncio.Event() # Timeout event
|
||||
self.wait = self._tout.wait # Allow: await wait_ms.wait()
|
||||
self._ttask = self._fake # Timer task
|
||||
asyncio.create_task(self._run())
|
||||
self._mtask = asyncio.create_task(self._run()) #Main task
|
||||
|
||||
async def _run(self):
|
||||
while True:
|
||||
|
@ -48,6 +48,8 @@ class Delay_ms:
|
|||
# API
|
||||
# trigger may be called from hard ISR.
|
||||
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._retn = None # Default in case cancelled.
|
||||
self._busy = True
|
||||
|
@ -69,3 +71,8 @@ class Delay_ms:
|
|||
def callback(self, func=None, args=()):
|
||||
self._func = func
|
||||
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
|
||||
def __init__(self, writer, row, col, *, shape=RECTANGLE, height=20, width=50,
|
||||
fgcolor=None, bgcolor=None, bdcolor=False, textcolor=None, litcolor=None, text='',
|
||||
callback=dolittle, args=[], onrelease=False):
|
||||
callback=dolittle, args=[]):
|
||||
sl = writer.stringlen(text)
|
||||
if shape == CIRCLE: # Only height need be specified
|
||||
width = max(sl, height)
|
||||
|
@ -30,7 +30,6 @@ class Button(Widget):
|
|||
self.text = text
|
||||
self.callback = callback
|
||||
self.callback_args = args
|
||||
self.onrelease = onrelease
|
||||
if self.litcolor is not None:
|
||||
self.delay = Delay_ms(self.shownormal)
|
||||
|
||||
|
@ -77,17 +76,12 @@ class Button(Widget):
|
|||
self.draw = True # Redisplay
|
||||
|
||||
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
|
||||
self.bgcolor = self.litcolor
|
||||
self.draw = True # Redisplay
|
||||
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.
|
||||
# Note that if the bottom screen is closed, the application terminates.
|
||||
class CloseButton(Button):
|
||||
|
|
|
@ -24,7 +24,7 @@ class _ListDialog(Window):
|
|||
# Calculate Window dimensions
|
||||
ap_height = lb_height + 6 # Allow for listbox border
|
||||
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,
|
||||
elements = dd.elements,
|
||||
dlines = dlines, width = lb_width,
|
||||
|
@ -35,6 +35,7 @@ class _ListDialog(Window):
|
|||
self.dd = dd
|
||||
|
||||
def callback(self, obj_listbox):
|
||||
display.ipdev.adj_mode(False) # If in 3-button mode, leave adjust mode
|
||||
Screen.back()
|
||||
self.dd.value(obj_listbox.value()) # Update it
|
||||
|
||||
|
@ -106,6 +107,7 @@ class Dropdown(Widget):
|
|||
if len(self.elements) > 1:
|
||||
args = (self.writer, self.row - 2, self.col - 2, self)
|
||||
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
|
||||
x = self.els[self()]
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
# Usage:
|
||||
# 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.listbox import Listbox
|
||||
from gui.core.colors import *
|
||||
|
@ -31,18 +31,20 @@ class SubMenu(Window):
|
|||
# Calculate Window dimensions
|
||||
ap_height = lb_height + 6 # Allow for listbox border
|
||||
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,
|
||||
fgcolor = button.fgcolor, bgcolor = button.bgcolor, bdcolor=False,
|
||||
fontcolor = button.textcolor, select_color = menu.select_color,
|
||||
callback = self.callback)
|
||||
|
||||
def callback(self, lbox):
|
||||
display.ipdev.adj_mode(False) # If in 3-button mode, leave adjust mode
|
||||
Screen.back()
|
||||
el = self.elements[lbox.value()] # (text, cb, args)
|
||||
if len(el) == 2: # Recurse into submenu
|
||||
args = (self.menu, self.button, el[1])
|
||||
Screen.change(SubMenu, args = args)
|
||||
display.ipdev.adj_mode(True) # If in 3-button mode, go into adjust mode
|
||||
else:
|
||||
el[1](lbox, *el[2])
|
||||
|
||||
|
@ -77,3 +79,4 @@ class Menu:
|
|||
def cb(self, button, txt, elements): # Button pushed which calls submenu
|
||||
args = (self, button, elements)
|
||||
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
|
||||
def do_adj(self, button, val):
|
||||
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)
|
||||
else: # val == 1 or -1
|
||||
asyncio.create_task(self.btnhan(button, val))
|
||||
|
||||
async def btnhan(self, button, up):
|
||||
up = up == 1
|
||||
if self.precision:
|
||||
if self.precision():
|
||||
delta = self.delta * 0.1
|
||||
maxdelta = self.delta
|
||||
else:
|
||||
|
@ -161,7 +161,7 @@ class ScaleLog(LinearIO):
|
|||
smul= (1 + delta) if up else (1 / (1 + delta))
|
||||
self.value(self.value() * smul)
|
||||
t = ticks_ms()
|
||||
while not button():
|
||||
while button():
|
||||
await asyncio.sleep_ms(0) # Quit fast on button release
|
||||
if ticks_diff(ticks_ms(), t) > 500: # Button was held down
|
||||
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
|
||||
increase = Pin(20, Pin.IN, Pin.PULL_UP) # Increase 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