kopia lustrzana https://github.com/peterhinch/micropython-micro-gui
Fix widget metrics.
rodzic
3b26ab9528
commit
3f37900d12
33
README.md
33
README.md
|
@ -49,13 +49,9 @@ 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. This document is
|
||||
under review. I plan to add further demos and to upgrade the performance of
|
||||
some display drivers.
|
||||
|
||||
An issue under investigation is that a soft reset is required after a GUI
|
||||
application is run and before running another. Otherwise the second application
|
||||
displays correctly but is unresponsive.
|
||||
Code is new and issues are likely: please report any found. The project is
|
||||
under development so check for updates. I also plan to upgrade the performance
|
||||
of some display drivers.
|
||||
|
||||
# 0. Contents
|
||||
|
||||
|
@ -392,6 +388,11 @@ Demos are run by issuing (for example):
|
|||
```python
|
||||
>>> import gui.demos.simple
|
||||
```
|
||||
If shut down cleanly with the "close" button a demo can be re-run with (e.g.):
|
||||
```python
|
||||
gui.demos.simple.test()
|
||||
```
|
||||
Before running a different demo the host should be reset (ctrl-d) to clear RAM.
|
||||
|
||||
These will run on screens of 128x128 pixels or above. The initial ones are
|
||||
minimal and aim to demonstrate a single technique.
|
||||
|
@ -2170,7 +2171,10 @@ class TSeq(Screen):
|
|||
|
||||
The "tab order" of widgets on a `Screen` is the order with which they acquire
|
||||
focus with successive presses of the `Next` button. It is determined by the
|
||||
order in which they are instantiated.
|
||||
order in which they are instantiated. Tab order is important for usability but
|
||||
instantiating in the best order can conflict with program logic. This happens
|
||||
if a widget's callback refers to others not yet instantiated. See demos
|
||||
`dropdown.py` and `linked_sliders.py` for one solution.
|
||||
|
||||
The obvious layout for the physical buttons is as per a joystick:
|
||||
|
||||
|
@ -2201,13 +2205,12 @@ have the following bound variables, which should be considered read-only:
|
|||
|
||||
* `height` As specified. Does not include border.
|
||||
* `width` Ditto.
|
||||
* `rows` Height including borders.
|
||||
* `cols` Width with borders.
|
||||
* `mrow` Maximum absolute row occupied by the widget.
|
||||
* `mcol` Maximum absolute col occupied by the widget.
|
||||
|
||||
This support is fairly "micro" and does not take account of labels and legends.
|
||||
This means that `rows` and `cols` for `Dial`, `Meter`, `Slider` and
|
||||
`HorizSlider` do not necessarily reflect the full amount of space used by the
|
||||
control.
|
||||
The `mrow` and `mcol` values enable other widgets to be positioned relative to
|
||||
the one previously instantiated. In the cases of sliders, `Dial` and `Meter`
|
||||
widgets these take account of space ocupied by legends or labels.
|
||||
|
||||
The `aclock.py` demo provides a simple example of this approach.
|
||||
|
||||
|
@ -2254,4 +2257,6 @@ Hopefully these are self explanatory. The `Display` methods use the `framebuf`
|
|||
convention of `x, y` coordinates rather than the `row, col` system used by
|
||||
micro-gui.
|
||||
|
||||
The `primitives.py` demo provides a simple example.
|
||||
|
||||
###### [Contents](./README.md#0-contents)
|
||||
|
|
|
@ -274,10 +274,9 @@ class Screen:
|
|||
cs_new._do_open(cs_old) # Clear and redraw
|
||||
cs_new.after_open() # Optional subclass method
|
||||
if cs_old is None: # Initialising
|
||||
try:
|
||||
asyncio.run(Screen.monitor()) # Starts and ends uasyncio
|
||||
finally:
|
||||
asyncio.new_event_loop()
|
||||
asyncio.run(Screen.monitor()) # Starts and ends uasyncio
|
||||
# Don't do asyncio.new_event_loop() as it prevents re-running
|
||||
# the same app.
|
||||
|
||||
@classmethod
|
||||
async def monitor(cls):
|
||||
|
@ -550,8 +549,9 @@ class Widget:
|
|||
self.col = col
|
||||
self.height = height
|
||||
self.width = width
|
||||
self.rows = height + 4 # For metrics. Default: allow for border.
|
||||
self.cols = width + 4
|
||||
# Maximum row and col. Defaults for user metrics. May be overridden
|
||||
self.mrow = row + height + 2 # in subclass. Allow for border.
|
||||
self.mcol = col + width + 2
|
||||
self.visible = True # Used by ButtonList class for invisible buttons
|
||||
self.draw = True # Signals that obect must be redrawn
|
||||
self._value = value
|
||||
|
|
|
@ -25,9 +25,9 @@ from gui.core.colors import *
|
|||
async def aclock(dial, lbldate, lbltim):
|
||||
# Return a unit vector of phase phi. Multiplying by this will
|
||||
# rotate a vector anticlockwise which is mathematically correct.
|
||||
# Alas clocks modelled on sundials were invented in the northern
|
||||
# hemisphere. Otherwise they would have rotated widdershins
|
||||
# in accordance with maths. Hence negative sign when called.
|
||||
# Alas clocks, modelled on sundials, were invented in the northern
|
||||
# hemisphere. Otherwise they would have rotated widdershins like
|
||||
# the maths. Hence negative sign when called.
|
||||
def uv(phi):
|
||||
return rect(1, phi)
|
||||
|
||||
|
@ -70,13 +70,14 @@ class BaseScreen(Screen):
|
|||
}
|
||||
|
||||
wri = CWriter(ssd, font, GREEN, BLACK, verbose=False)
|
||||
gap = 4 # Vertical gap between widgets
|
||||
dial = Dial(wri, 2, 2, height = 70, ticks = 12,
|
||||
fgcolor = GREEN, pip = GREEN)
|
||||
# Set up clock display: instantiate labels
|
||||
row = dial.rows + gap
|
||||
# Demo of relative positioning.
|
||||
gap = 4 # Vertical gap between widgets
|
||||
row = dial.mrow + gap
|
||||
lbldate = Label(wri, row, 2, 100, **labels)
|
||||
row += lbldate.rows + gap
|
||||
row = lbldate.mrow + gap
|
||||
lbltim = Label(wri, row, 2, '00.00.00', **labels)
|
||||
self.reg_task(aclock(dial, lbldate, lbltim))
|
||||
CloseButton(wri)
|
||||
|
|
|
@ -47,12 +47,13 @@ class BaseScreen(Screen):
|
|||
self.vslider = Slider(wri, 2, 2, callback=self.slider_cb,
|
||||
bdcolor=RED, slotcolor=BLUE,
|
||||
legends=('0.0', '0.5', '1.0'), value=0.5)
|
||||
|
||||
#Label(wri, 2, self.vslider.mcol, 'FF')
|
||||
col = 80
|
||||
row = 15
|
||||
self.hslider = HorizSlider(wri, row, col, callback=self.slider_cb,
|
||||
bdcolor=GREEN, slotcolor=BLUE,
|
||||
legends=('0.0', '0.5', '1.0'), value=0.7)
|
||||
Label(wri, self.hslider.mrow, self.hslider.mcol, 'FF')
|
||||
row += 30
|
||||
self.scale = Scale(wri, row, col, width = 150, tickcb = tickcb,
|
||||
pointercolor=RED, fontcolor=YELLOW, bdcolor=CYAN,
|
||||
|
|
|
@ -61,8 +61,11 @@ class FooScreen(Screen):
|
|||
|
||||
m0 = Meter(wri, 10, 240, divisions = 4, ptcolor=YELLOW, height=80, width=15,
|
||||
label='Meter example', style=Meter.BAR, legends=('0.0', '0.5', '1.0'))
|
||||
#Label(wri, 2, m0.mcol, 'FF')
|
||||
# Instantiate displayable objects. bgcolor forces complete redraw.
|
||||
dial = Dial(wri, 2, 2, height = 75, ticks = 12, bgcolor=BLACK, bdcolor=None, label=120) # Border in fg color
|
||||
#Label(wri, dial.mrow, 2, 'FF')
|
||||
#Label(wri, dial.mrow, dial.mcol, 'FF')
|
||||
scale = Scale(wri, 2, 100, width = 124, tickcb = tickcb,
|
||||
pointercolor=RED, fontcolor=YELLOW, bdcolor=CYAN)
|
||||
|
||||
|
|
|
@ -41,7 +41,10 @@ async def ptr_test(dial):
|
|||
ptr = Pointer(dial)
|
||||
v = 0j
|
||||
steps = 20 # No. of interpolation steps
|
||||
grv = lambda : urandom.getrandbits(16) / 2**15 - 1 # Random: range -1.0 to +1.0
|
||||
# BUG getting a weird visual flicker on occasion, with yellow
|
||||
# being briefly displayed. Where is that coming from?
|
||||
# Does not seem to be affected by max value. TODO
|
||||
grv = lambda : urandom.getrandbits(16) / 2**15 - 1 # Random: range -1.0 to +0.999
|
||||
while True:
|
||||
v1 = grv() + 1j * grv() # Random vector
|
||||
dv = (v1 - v) / steps # Interpolation vector
|
||||
|
|
|
@ -67,8 +67,9 @@ class Dial(Widget):
|
|||
self.pip = self.fgcolor if pip is None else pip
|
||||
if label is not None:
|
||||
self.label = Label(writer, row + height + 3, col, label)
|
||||
#self.cols = max(self.cols, self.label.cols)
|
||||
#self.rows += 3 + self.label.rows
|
||||
# Adjust metrics
|
||||
self.mrow = self.label.mrow - 2 # Label never has border
|
||||
self.mcol = max(self.mcol, self.label.mcol - 2)
|
||||
radius = int(height / 2)
|
||||
self.radius = radius
|
||||
self.ticks = ticks
|
||||
|
|
|
@ -24,6 +24,7 @@ class Meter(Widget):
|
|||
self.style = style
|
||||
self.ptcolor = ptcolor if ptcolor is not None else self.fgcolor
|
||||
if legends is not None: # Legends are static
|
||||
mcol = 0
|
||||
x = col + width + 4
|
||||
y = row + height
|
||||
dy = 0 if len(legends) <= 1 else height / (len(legends) -1)
|
||||
|
@ -31,6 +32,8 @@ class Meter(Widget):
|
|||
for legend in legends:
|
||||
l = Label(writer, round(yl), x, legend)
|
||||
yl -= dy
|
||||
mcol = max(mcol, l.mcol)
|
||||
self.mcol = mcol - 2 # For metrics. Legends never have border.
|
||||
self.value(value)
|
||||
|
||||
def value(self, n=None, color=None):
|
||||
|
|
|
@ -28,6 +28,9 @@ class Slider(LinearIO):
|
|||
super()._set_callbacks(callback, args)
|
||||
self.divisions = divisions
|
||||
self.legends = legends
|
||||
if legends is not None: # Adjust column metric
|
||||
ml = max((writer.stringlen(l) for l in legends))
|
||||
self.mcol += ml + 2 # Strings are rendered 2 pixels right of border
|
||||
self.fontcolor = self.fgcolor if fontcolor is None else fontcolor
|
||||
self.slotcolor = self.bgcolor if slotcolor is None else slotcolor
|
||||
# Define slider
|
||||
|
|
Ładowanie…
Reference in New Issue