kopia lustrzana https://github.com/peterhinch/micropython-micro-gui
Add Adjuster widget and demos.
rodzic
c2b858e18e
commit
35064c3501
118
README.md
118
README.md
|
@ -117,21 +117,22 @@ there is a workround if it's impossible to upgrade. See
|
|||
19. [ScaleLog widget](./README.md#19-scalelog-widget) Wide dynamic range float entry and display.
|
||||
20. [Dial widget](./README.md#20-dial-widget) Display multiple vectors.
|
||||
21. [Knob widget](./README.md#21-knob-widget) Rotary potentiometer float entry.
|
||||
22. [Menu class](./README.md#22-menu-class)
|
||||
23. [Graph plotting](./README.md#22-graph-plotting) Widgets for Cartesian and polar graphs.
|
||||
23.1 [Concepts](./README.md#231-concepts)
|
||||
23.1.1 [Graph classes](./README.md#2311-graph-classes)
|
||||
23.1.2 [Curve classes](./README.md#2312-curve-classes)
|
||||
23.1.3 [Coordinates](./README.md#2313-coordinates)
|
||||
23.2 [Graph classes](./README.md#231-graph-classes)
|
||||
23.2.1 [Class CartesianGraph](./README.md#2321-class-cartesiangraph)
|
||||
23.2.2 [Class PolarGraph](./README.md#2322-class-polargraph)
|
||||
23.3 [Curve classes](./README.md#233-curve-classes)
|
||||
23.3.1 [Class Curve](./README.md#2331-class-curve)
|
||||
23.3.2 [Class PolarCurve](./README.md#2332-class-polarcurve)
|
||||
23.4 [Class TSequence](./README.md#234-class-tsequence) Plotting realtime, time sequential data.
|
||||
24. [Old firmware](./README.md#24-old-firmware) For users of color displays who can't run current firmware.
|
||||
25. [Realtime applications](./README.md#25-realtime-applications) Accommodating tasks requiring fast RT performance.
|
||||
22. [Adjuster widget](./README.md#22-adjuster-widget) Space saving way to enter floats.
|
||||
23. [Menu class](./README.md#23-menu-class)
|
||||
24. [Graph plotting](./README.md#24-graph-plotting) Widgets for Cartesian and polar graphs.
|
||||
24.1 [Concepts](./README.md#241-concepts)
|
||||
24.1.1 [Graph classes](./README.md#2411-graph-classes)
|
||||
24.1.2 [Curve classes](./README.md#2412-curve-classes)
|
||||
24.1.3 [Coordinates](./README.md#2413-coordinates)
|
||||
24.2 [Graph classes](./README.md#242-graph-classes)
|
||||
24.2.1 [Class CartesianGraph](./README.md#2421-class-cartesiangraph)
|
||||
24.2.2 [Class PolarGraph](./README.md#2422-class-polargraph)
|
||||
24.3 [Curve classes](./README.md#243-curve-classes)
|
||||
24.3.1 [Class Curve](./README.md#2431-class-curve)
|
||||
24.3.2 [Class PolarCurve](./README.md#2432-class-polarcurve)
|
||||
24.4 [Class TSequence](./README.md#244-class-tsequence) Plotting realtime, time sequential data.
|
||||
25. [Old firmware](./README.md#25-old-firmware) For users of color displays who can't run current firmware.
|
||||
26. [Realtime applications](./README.md#26-realtime-applications) Accommodating tasks requiring fast RT performance.
|
||||
[Appendix 1 Application design](./README.md#appendix-1-application-design) Tab order, button layout, encoder interface, use of graphics primitives
|
||||
|
||||
# 1. Basic concepts
|
||||
|
@ -520,6 +521,8 @@ minimal and aim to demonstrate a single technique.
|
|||
* `tbox.py` Text boxes and user-controlled scrolling.
|
||||
* `tstat.py` A demo of the `Meter` class with data sensitive regions.
|
||||
* `menu.py` A multi-level menu.
|
||||
* `adjuster.py` Simple demo of the `Adjuster` control.
|
||||
* `adjust_vec.py` A pair of `Adjuster`s vary a vector.
|
||||
|
||||
### 1.11.2 Test scripts
|
||||
|
||||
|
@ -2272,8 +2275,8 @@ Optional keyword only arguments:
|
|||
* `bdcolor=False` Color of border. If `False` no border will be drawn. If a
|
||||
color is provided, a border line will be drawn around the control.
|
||||
* `prcolor=None` If `active`, in precision mode the white focus border changes
|
||||
to yellow to for a visual indication. An alternative color can be provided.
|
||||
`WHITE` will defeat this change.
|
||||
to yellow for a visual indication. An alternative color can be provided.
|
||||
`WHITE` defeats this change; `False` disables precision mode.
|
||||
* `callback=dolittle` Callback function runs when the user moves the knob or
|
||||
the value is changed programmatically.
|
||||
* `args=[]` A list/tuple of arguments for above callback.
|
||||
|
@ -2296,7 +2299,58 @@ value changes. This enables dynamic color change.
|
|||
|
||||
###### [Contents](./README.md#0-contents)
|
||||
|
||||
# 22 Menu class
|
||||
# 22. Adjuster widget
|
||||
|
||||
```python
|
||||
from gui.widgets.adjuster import Adjuster
|
||||
```
|
||||
The `Adjuster` is a space saving version of the `Knob`. It is normally paired
|
||||
with a `Label` which provides user feedback of the value. It has a range of
|
||||
0.0 to 1.0 and a visual arc of 270°. User code can provide arbitrary scaling
|
||||
or nonlinear operation. This is demonstrated in `demos/adjuster.py`. The
|
||||
widget was inspired by discussions with the author of
|
||||
[this project](https://www.instructables.com/Poor-Mans-Waveform-Generator-Based-on-RP2040-Raspb/).
|
||||
|
||||
|
||||
Constructor mandatory positional args:
|
||||
1. `writer` The `Writer` instance. This defines the control's height.
|
||||
2. `row` Location on screen.
|
||||
3. `col`
|
||||
|
||||
Optional keyword only arguments:
|
||||
* `value=0.0` Initial value. By default the knob will be at its most
|
||||
counter-clockwise position.
|
||||
* `fgcolor=None` Color of foreground (the control itself). If `None` the
|
||||
`Writer` foreground default is used.
|
||||
* `bgcolor=None` Background color of object. If `None` the `Writer` background
|
||||
default is used.
|
||||
* `color=None` Fill color for the control knob. Default: no fill.
|
||||
* `prcolor=None` In precision mode the white focus border changes to yellow
|
||||
for a visual indication. An alternative color can be provided. `WHITE` defeats
|
||||
the change; `False` disables precision mode.
|
||||
* `callback=dolittle` Callback function runs when the user moves the knob or
|
||||
the value is changed programmatically.
|
||||
* `args=[]` A list/tuple of arguments for above callback.
|
||||
|
||||
Methods:
|
||||
* `greyed_out` Optional Boolean argument `val=None`. If `None` returns the
|
||||
current 'greyed out' status of the control. Otherwise enables or disables it,
|
||||
showing it in its new state.
|
||||
* `value` Optional argument `val`. If set, adjusts the pointer to
|
||||
correspond to the new value. The move callback will run. The method constrains
|
||||
the range to 0.0 to 1.0. Always returns the control's value.
|
||||
|
||||
### Callback
|
||||
|
||||
The callback receives an initial arg being the widget instance followed by any
|
||||
user supplied args. The callback can be a bound method, typically of a `Screen`
|
||||
subclass. The callback runs when the widget is instantiated and whenever the
|
||||
value changes. Typically the callback will adjust the text displayed on a
|
||||
linked label.
|
||||
|
||||
###### [Contents](./README.md#0-contents)
|
||||
|
||||
# 23 Menu class
|
||||
|
||||
```python
|
||||
from gui.widgets.menu import Menu
|
||||
|
@ -2404,7 +2458,7 @@ different callback if the application required it.
|
|||
|
||||
###### [Contents](./README.md#0-contents)
|
||||
|
||||
# 23. Graph Plotting
|
||||
# 24. Graph Plotting
|
||||
|
||||
```python
|
||||
from gui.widgets.graph import PolarGraph, PolarCurve, CartesianGraph, Curve, TSequence
|
||||
|
@ -2417,7 +2471,7 @@ from gui.widgets.graph import PolarGraph, PolarCurve, CartesianGraph, Curve, TSe
|
|||
|
||||
For example code see `gui/demos/plot.py`.
|
||||
|
||||
## 23.1 Concepts
|
||||
## 24.1 Concepts
|
||||
|
||||
Data for Cartesian graphs constitutes a sequence of x, y pairs, for polar
|
||||
graphs it is a sequence of complex `z` values. The module supports three
|
||||
|
@ -2427,13 +2481,13 @@ common cases:
|
|||
3. One or more `y` values arrive gradually. The `X` axis represents time. This
|
||||
is a simplifying case of 2.
|
||||
|
||||
### 23.1.1 Graph classes
|
||||
### 24.1.1 Graph classes
|
||||
|
||||
A user program first instantiates a graph object (`PolarGraph` or
|
||||
`CartesianGraph`). This creates an empty graph image upon which one or more
|
||||
curves may be plotted. Graphs are passive widgets so cannot accept user input.
|
||||
|
||||
### 23.1.2 Curve classes
|
||||
### 24.1.2 Curve classes
|
||||
|
||||
The user program then instantiates one or more curves (`Curve` or
|
||||
`PolarCurve`) as appropriate to the graph. Curves may be assigned colors to
|
||||
|
@ -2448,7 +2502,7 @@ Where it is required to plot realtime data as it arrives, this is achieved
|
|||
via calls to the curve's `point` method. If a prior point exists it causes a
|
||||
line to be drawn connecting the point to the last one drawn.
|
||||
|
||||
### 23.1.3 Coordinates
|
||||
### 24.1.3 Coordinates
|
||||
|
||||
`PolarGraph` and `CartesianGraph` objects are subclassed from `Widget` and are
|
||||
positioned accordingly by `row` and `col` with a 2-pixel outside border. The
|
||||
|
@ -2464,9 +2518,9 @@ unit circle but will be clipped to the rectangular graph boundary.
|
|||
|
||||
###### [Contents](./README.md#0-contents)
|
||||
|
||||
## 23.2 Graph classes
|
||||
## 24.2 Graph classes
|
||||
|
||||
### 23.2.1 Class CartesianGraph
|
||||
### 24.2.1 Class CartesianGraph
|
||||
|
||||
Constructor.
|
||||
Mandatory positional arguments:
|
||||
|
@ -2493,7 +2547,7 @@ Keyword only arguments (all optional):
|
|||
Method:
|
||||
* `show` No args. Redraws the empty graph. Used when plotting time sequences.
|
||||
|
||||
### 23.2.2 Class PolarGraph
|
||||
### 24.2.2 Class PolarGraph
|
||||
|
||||
Constructor.
|
||||
Mandatory positional arguments:
|
||||
|
@ -2518,9 +2572,9 @@ Method:
|
|||
|
||||
###### [Contents](./README.md#0-contents)
|
||||
|
||||
## 23.3 Curve classes
|
||||
## 24.3 Curve classes
|
||||
|
||||
### 23.3.1 Class Curve
|
||||
### 24.3.1 Class Curve
|
||||
|
||||
The Cartesian curve constructor takes the following positional arguments:
|
||||
|
||||
|
@ -2560,7 +2614,7 @@ To plot x values from 1000 to 4000 we would set the `origin` x value to 1000
|
|||
and the `excursion` x value to 3000. The `excursion` values scale the plotted
|
||||
values to fit the corresponding axis.
|
||||
|
||||
### 23.3.2 Class PolarCurve
|
||||
### 24.3.2 Class PolarCurve
|
||||
|
||||
The constructor takes the following positional arguments:
|
||||
|
||||
|
@ -2594,7 +2648,7 @@ Complex points should lie within the unit circle to be drawn within the grid.
|
|||
|
||||
###### [Contents](./README.md#0-contents)
|
||||
|
||||
## 23.4 Class TSequence
|
||||
## 24.4 Class TSequence
|
||||
|
||||
A common task is the acquisition and plotting of real time data against time,
|
||||
such as hourly temperature and air pressure readings. This class facilitates
|
||||
|
@ -2658,7 +2712,7 @@ class TSeq(Screen):
|
|||
```
|
||||
###### [Contents](./README.md#0-contents)
|
||||
|
||||
# 24. Old firmware
|
||||
# 25. Old firmware
|
||||
|
||||
Current firmware is highly recommended. For users of color displays who cannot
|
||||
run V1.17 or later it is possible to run under V1.15+. This involves copying
|
||||
|
@ -2666,7 +2720,7 @@ run V1.17 or later it is possible to run under V1.15+. This involves copying
|
|||
to `gui/core/writer.py`. This uses Python code to render text if the firmware
|
||||
or driver are unable to support fast rendering.
|
||||
|
||||
# 25. Realtime applications
|
||||
# 26. Realtime applications
|
||||
|
||||
Screen refresh is performed in a continuous loop with yields to the scheduler.
|
||||
In normal applications this works well, however a significant proportion of
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
# adjust_vec.py Demo of Adjusters linked to a dial
|
||||
|
||||
# Released under the MIT License (MIT). See LICENSE.
|
||||
# Copyright (c) 2021 Peter Hinch
|
||||
|
||||
# hardware_setup must be imported before other modules because of RAM use.
|
||||
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.core.writer import CWriter
|
||||
|
||||
# Font for CWriter
|
||||
import gui.fonts.font10 as font
|
||||
from gui.core.colors import *
|
||||
|
||||
|
||||
class BaseScreen(Screen):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
super().__init__()
|
||||
wri = CWriter(ssd, font, GREEN, BLACK)
|
||||
col = 2
|
||||
row = 2
|
||||
self.dial = Dial(wri, row, col, bdcolor=None)
|
||||
self.vec = Pointer(self.dial)
|
||||
a = Adjuster(wri, row, self.dial.mcol + 2, fgcolor=BLUE, callback=self.phi_cb)
|
||||
Label(wri, row, a.mcol + 2, "ϕ", fgcolor=BLUE)
|
||||
a = Adjuster(wri, row + 20, self.dial.mcol + 2, value=1, fgcolor=MAGENTA, callback=self.r_cb)
|
||||
Label(wri, row + 20, a.mcol + 2, "r", fgcolor=MAGENTA)
|
||||
CloseButton(wri) # Quit the application
|
||||
|
||||
def phi_cb(self, adj):
|
||||
v = adj.value()
|
||||
r, phi = cmath.polar(self.vec.value())
|
||||
self.vec.value(cmath.rect(r, v * 2 * cmath.pi))
|
||||
|
||||
def r_cb(self, adj):
|
||||
v = adj.value()
|
||||
r, phi = cmath.polar(self.vec.value())
|
||||
self.vec.value(cmath.rect(v, phi))
|
||||
|
||||
def test():
|
||||
print('Alter a vector using Adjuster control.')
|
||||
Screen.change(BaseScreen) # A class is passed here, not an instance.
|
||||
|
||||
test()
|
|
@ -0,0 +1,58 @@
|
|||
# adjuster.py Demo of Adjusters linked to Labels
|
||||
|
||||
# Released under the MIT License (MIT). See LICENSE.
|
||||
# Copyright (c) 2021 Peter Hinch
|
||||
|
||||
# hardware_setup must be imported before other modules because of RAM use.
|
||||
import hardware_setup # Create a display instance
|
||||
from gui.core.ugui import Screen, ssd
|
||||
|
||||
from gui.widgets.label import Label
|
||||
from gui.widgets.buttons import CloseButton
|
||||
from gui.widgets.adjuster import Adjuster
|
||||
from gui.core.writer import CWriter
|
||||
|
||||
# Font for CWriter
|
||||
import gui.fonts.font10 as font
|
||||
from gui.core.colors import *
|
||||
|
||||
|
||||
class BaseScreen(Screen):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
super().__init__()
|
||||
wri = CWriter(ssd, font, GREEN, BLACK)
|
||||
col = 2
|
||||
row = 2
|
||||
self.lbl1 = Label(wri, row, col, 60, bdcolor=RED)
|
||||
a = Adjuster(wri, row, self.lbl1.mcol + 2, fgcolor=RED, callback=self.adj1_callback)
|
||||
Label(wri, row, a.mcol + 2, "Simple")
|
||||
row = self.lbl1.mrow + 5
|
||||
self.lbl2 = Label(wri, row, col, 60, bdcolor=RED)
|
||||
a =Adjuster(wri, row, self.lbl2.mcol + 2, fgcolor=RED, value=0.5, callback=self.adj2_callback)
|
||||
Label(wri, row, a.mcol + 2, "Scale")
|
||||
row = self.lbl2.mrow + 5
|
||||
self.lbl3 = Label(wri, row, col, 60, bdcolor=YELLOW)
|
||||
a = Adjuster(wri, row, self.lbl3.mcol + 2, fgcolor=YELLOW, callback=self.adj3_callback)
|
||||
Label(wri, row, a.mcol + 2, "Log")
|
||||
CloseButton(wri) # Quit the application
|
||||
|
||||
def adj1_callback(self, adj):
|
||||
v = adj.value() # Typically do mapping here
|
||||
self.lbl1.value(f'{v:4.2f}')
|
||||
|
||||
def adj2_callback(self, adj):
|
||||
v = (adj.value() - 0.5) * 10 # Scale and offset
|
||||
self.lbl2.value(f'{v:4.2f}')
|
||||
|
||||
def adj3_callback(self, adj):
|
||||
v = 10 ** (3 * adj.value()) # Log 3 decades
|
||||
self.lbl3.value(f'{v:4.2f}')
|
||||
|
||||
|
||||
def test():
|
||||
print('Demo of Adjuster control.')
|
||||
Screen.change(BaseScreen) # A class is passed here, not an instance.
|
||||
|
||||
test()
|
|
@ -0,0 +1,49 @@
|
|||
# adjuster.py Tiny control knob (rotary potentiometer) widget
|
||||
|
||||
# Released under the MIT License (MIT). See LICENSE.
|
||||
# Copyright (c) 2021 Peter Hinch
|
||||
|
||||
from gui.core.ugui import LinearIO, display
|
||||
import math
|
||||
|
||||
TWOPI = 2 * math.pi
|
||||
# Null function
|
||||
dolittle = lambda *_ : None
|
||||
|
||||
# *********** CONTROL KNOB CLASS ***********
|
||||
|
||||
class Adjuster(LinearIO):
|
||||
def __init__(self, writer, row, col, *, value=0.0,
|
||||
fgcolor=None, bgcolor=None, color=None, prcolor=None,
|
||||
callback=dolittle, args=[]):
|
||||
height = writer.height # Match a user-linked Label
|
||||
super().__init__(writer, row, col, height, height, fgcolor,
|
||||
bgcolor, False, value, True, prcolor)
|
||||
super()._set_callbacks(callback, args)
|
||||
radius = height / 2
|
||||
self.arc = 1.5 * math.pi # Usable angle of control
|
||||
self.radius = radius
|
||||
self.xorigin = col + radius
|
||||
self.yorigin = row + radius
|
||||
self.color = color
|
||||
self.draw = True # Ensure a redraw on next refresh
|
||||
# Run callback (e.g. to set dynamic colors)
|
||||
self.callback(self, *self.args)
|
||||
|
||||
def show(self):
|
||||
if super().show(False): # Honour bgcolor
|
||||
arc = self.arc
|
||||
radius = self.radius
|
||||
if self.color is not None:
|
||||
display.fillcircle(self.xorigin, self.yorigin, radius, self.color)
|
||||
display.circle(self.xorigin, self.yorigin, radius, self.fgcolor)
|
||||
display.circle(self.xorigin, self.yorigin, radius, self.fgcolor)
|
||||
self._drawpointer(self._value, self.fgcolor) # draw new
|
||||
|
||||
def _drawpointer(self, value, color):
|
||||
arc = self.arc
|
||||
length = self.radius - 1
|
||||
angle = value * arc - arc / 2
|
||||
x_end = int(self.xorigin + length * math.sin(angle))
|
||||
y_end = int(self.yorigin - length * math.cos(angle))
|
||||
display.line(int(self.xorigin), int(self.yorigin), x_end, y_end, color)
|
|
@ -51,4 +51,4 @@ 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)
|
||||
display = Display(ssd, nxt, sel, prev, increase, decrease, 5)
|
||||
|
|
Ładowanie…
Reference in New Issue