kopia lustrzana https://github.com/peterhinch/micropython-micro-gui
Listbox and Dropdown support dynamic element lists.
rodzic
1ac1fd0976
commit
2f9c0ee474
|
@ -65,7 +65,7 @@ target and a C device driver (unless you can acquire a suitable binary).
|
|||
|
||||
# Project status
|
||||
|
||||
Sept 2024: Dropdown and Listbox support dynamically variable lists of elements.
|
||||
Sept 2024: Dropdown and Listbox widgets support dynamically variable lists of elements.
|
||||
April 2024: Add screen replace feature for non-tree navigation.
|
||||
Sept 2023: Add "encoder only" mode suggested by @eudoxos.
|
||||
April 2023: Add limited ePaper support, grid widget, calendar and epaper demos.
|
||||
|
@ -689,6 +689,7 @@ Some of these require larger screens. Required sizes are specified as
|
|||
* `calendar.py` Demo of grid control (240x320 - but could be reduced).
|
||||
* `listbox_var.py` Listbox with dynamically variable elements.
|
||||
* `dropdown_var.py` Dropdown with dynamically variable elements.
|
||||
* `dropdown_var_tuple.py ` Dropdown with dynamically variable tuple elements.
|
||||
|
||||
###### [Contents](./README.md#0-contents)
|
||||
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
# dropdown_var_tuple.py micro-gui demo of Dropdown widget with changeable elements
|
||||
|
||||
# Released under the MIT License (MIT). See LICENSE.
|
||||
# Copyright (c) 2024 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, Window, ssd
|
||||
|
||||
from gui.widgets import Label, Button, CloseButton, Dropdown
|
||||
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, verbose=False)
|
||||
|
||||
# Create new dropdown entries
|
||||
tuples = (
|
||||
("Iron", self.mcb, ("Fe",)),
|
||||
("Copper", self.mcb, ("Cu",)),
|
||||
("Lead", self.mcb, ("Pb",)),
|
||||
("Zinc", self.mcb, ("Zn",)),
|
||||
)
|
||||
|
||||
def newtup():
|
||||
n = 0
|
||||
while True:
|
||||
yield tuples[n]
|
||||
n = (n + 1) % len(tuples)
|
||||
|
||||
self.ntup = newtup() # Instantiate the generator
|
||||
|
||||
col = 2
|
||||
row = 2
|
||||
self.els = [
|
||||
("Hydrogen", self.cb, ("H",)),
|
||||
("Helium", self.cb, ("He",)),
|
||||
("Neon", self.cb, ("Ne",)),
|
||||
("Xenon", self.cb, ("Xe",)),
|
||||
("Radon", self.cb_radon, ("Ra",)),
|
||||
("Uranium", self.cb_radon, ("U",)),
|
||||
("Plutonium", self.cb_radon, ("Pu",)),
|
||||
("Actinium", self.cb_radon, ("Ac",)),
|
||||
]
|
||||
self.dd = Dropdown(
|
||||
wri,
|
||||
row,
|
||||
col,
|
||||
elements=self.els,
|
||||
dlines=5, # Show 5 lines
|
||||
bdcolor=GREEN,
|
||||
)
|
||||
row += 30
|
||||
self.lbl = Label(wri, row, col, self.dd.width, bdcolor=RED)
|
||||
b = Button(wri, 2, 120, text="del", callback=self.delcb)
|
||||
b = Button(wri, b.mrow + 2, 120, text="add", callback=self.addcb)
|
||||
b = Button(wri, b.mrow + 10, 120, text="h2", callback=self.gocb, args=("Hydrogen",))
|
||||
b = Button(wri, b.mrow + 2, 120, text="fe", callback=self.gocb, args=("Iron",))
|
||||
CloseButton(wri)
|
||||
|
||||
def gocb(self, _, txt): # Go button callback: Move currency to specified entry
|
||||
self.dd.textvalue(txt)
|
||||
|
||||
def addcb(self, _): # Add button callback
|
||||
self.els.append(next(self.ntup)) # Append a new entry
|
||||
self.dd.update()
|
||||
|
||||
def delcb(self, _): # Delete button callback
|
||||
del self.els[self.dd.value()] # Delete current entry
|
||||
self.dd.update()
|
||||
self.lbl.value(self.dd.textvalue())
|
||||
|
||||
def cb(self, dd, s):
|
||||
self.lbl.value(self.dd.textvalue())
|
||||
print("Gas", s)
|
||||
|
||||
def mcb(self, dd, s):
|
||||
self.lbl.value(self.dd.textvalue())
|
||||
print("Metal", s)
|
||||
|
||||
def cb_radon(self, dd, s): # Yeah, Radon is a gas too...
|
||||
self.lbl.value(self.dd.textvalue())
|
||||
print("Radioactive", s)
|
||||
|
||||
def after_open(self):
|
||||
self.lbl.value(self.dd.textvalue())
|
||||
|
||||
|
||||
def test():
|
||||
print("Dropdown demo.")
|
||||
Screen.change(BaseScreen)
|
||||
|
||||
|
||||
test()
|
|
@ -1,8 +1,9 @@
|
|||
# dropdown.py Extension to ugui providing the Dropdown class
|
||||
|
||||
# Released under the MIT License (MIT). See LICENSE.
|
||||
# Copyright (c) 2021 Peter Hinch
|
||||
# Copyright (c) 2021-2024 Peter Hinch
|
||||
|
||||
# 13 Sep 24 Support dynamic elements list.
|
||||
# 12 Sep 21 Support for scrolling.
|
||||
|
||||
from gui.core.ugui import Widget, display, Window, Screen
|
||||
|
@ -14,11 +15,14 @@ dolittle = lambda *_: None
|
|||
|
||||
# Next and Prev close the listbox without updating the Dropdown. This is
|
||||
# handled by Screen .move bound method
|
||||
# Callback where elements are tuples: the listbox calls its local _ListDialog callback.
|
||||
# This changes the DialogBox value, so its callback runs. This is the ._despatch method
|
||||
# which runs the callback of the currently selected element.
|
||||
class _ListDialog(Window):
|
||||
def __init__(self, writer, row, col, dd): # dd is parent dropdown
|
||||
def __init__(self, writer, row, col, dd, dlines, els): # dd is parent dropdown
|
||||
# Need to determine Window dimensions from size of Listbox, which
|
||||
# depends on number and length of elements.
|
||||
_, lb_height, dlines, tw = Listbox.dimensions(writer, dd.elements, dd.dlines)
|
||||
_, lb_height, dlines, tw = Listbox.dimensions(writer, els, dlines)
|
||||
lb_width = tw + 2 # Text width + 2
|
||||
# Calculate Window dimensions
|
||||
ap_height = lb_height + 6 # Allow for listbox border
|
||||
|
@ -28,7 +32,7 @@ class _ListDialog(Window):
|
|||
writer,
|
||||
row + 3,
|
||||
col + 3,
|
||||
elements=dd.elements,
|
||||
elements=els,
|
||||
dlines=dlines,
|
||||
width=lb_width,
|
||||
fgcolor=dd.fgcolor,
|
||||
|
@ -38,6 +42,7 @@ class _ListDialog(Window):
|
|||
select_color=dd.select_color,
|
||||
value=dd.value(),
|
||||
callback=self.callback,
|
||||
also=Listbox.NOCB, # Force passed callback even if elements are tuples
|
||||
)
|
||||
self.dd = dd
|
||||
|
||||
|
@ -70,15 +75,16 @@ class Dropdown(Widget):
|
|||
self.entry_height = writer.height + 2 # Allow a pixel above and below text
|
||||
height = self.entry_height
|
||||
self.select_color = select_color
|
||||
self.dlines = dlines # Referenced by _ListDialog
|
||||
self.dlines = dlines # Passed to _ListDialog
|
||||
|
||||
# Check whether elements specified as (str, str,...) or ([str, callback, args], [...)
|
||||
self.simple = isinstance(elements[0], str)
|
||||
self.els = elements # Retain original
|
||||
# Listbox works with text component only because it has a single callback.
|
||||
self.elements = elements if self.simple else [x[0] for x in elements]
|
||||
if width is None: # Allow for square at end for arrow
|
||||
if self.simple:
|
||||
self.textwidth = max(writer.stringlen(s) for s in elements)
|
||||
else:
|
||||
self.textwidth = max(writer.stringlen(s[0]) for s in elements)
|
||||
width = self.textwidth + 2 + height
|
||||
else:
|
||||
self.textwidth = width
|
||||
|
@ -91,7 +97,6 @@ class Dropdown(Widget):
|
|||
super()._set_callbacks(callback, args) # Callback runs on value change
|
||||
|
||||
def update(self): # Elements list has changed. Extract text component for dropdown.
|
||||
self.elements = self.els if self.simple else [x[0] for x in self.els]
|
||||
# Ensure sensible _value if list size is reduced.
|
||||
self._value = min(self._value, len(self.els) - 1)
|
||||
self.show()
|
||||
|
@ -101,15 +106,23 @@ class Dropdown(Widget):
|
|||
return
|
||||
self._draw(x := self.col, y := self.row)
|
||||
if self._value is not None:
|
||||
display.print_left(self.writer, x, y + 1, self.elements[self._value], self.fontcolor)
|
||||
s = self.els[self._value] if self.simple else self.els[self._value][0]
|
||||
display.print_left(self.writer, x, y + 1, s, self.fontcolor)
|
||||
|
||||
def textvalue(self, text=None): # if no arg return current text
|
||||
if text is None:
|
||||
return self.elements[self._value]
|
||||
r = self.els[self._value]
|
||||
return r if self.simple else r[0]
|
||||
else: # set value by text
|
||||
try:
|
||||
v = self.elements.index(text)
|
||||
except ValueError:
|
||||
if self.simple:
|
||||
v = self.els.index(text)
|
||||
else: # More RAM-efficient than converting to list and using .index
|
||||
q = (p[0] for p in self.els)
|
||||
v = 0
|
||||
while next(q) != text:
|
||||
v += 1
|
||||
except (ValueError, StopIteration):
|
||||
v = None
|
||||
else:
|
||||
if v != self._value:
|
||||
|
@ -141,8 +154,8 @@ class Dropdown(Widget):
|
|||
)
|
||||
|
||||
def do_sel(self): # Select was pushed
|
||||
if len(self.elements) > 1:
|
||||
args = (self.writer, self.row - 2, self.col - 2, self)
|
||||
if len(self.els) > 1:
|
||||
args = (self.writer, self.row - 2, self.col - 2, self, self.dlines, self.els)
|
||||
Screen.change(_ListDialog, args=args)
|
||||
display.ipdev.adj_mode(True) # If in 3-button mode, go into adjust mode
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# Released under the MIT License (MIT). See LICENSE.
|
||||
# Copyright (c) 2021-2024 Peter Hinch
|
||||
|
||||
# 11 Sep 24 Support variable list contents.
|
||||
# 13 Sep 24 Support dynamic elements list.
|
||||
# 12 Sep 21 Support for scrolling.
|
||||
|
||||
from gui.core.ugui import Widget, display
|
||||
|
@ -19,6 +19,7 @@ dolittle = lambda *_: None
|
|||
class Listbox(Widget):
|
||||
ON_MOVE = 1 # Also run whenever the currency moves.
|
||||
ON_LEAVE = 2 # Also run on exit from the control.
|
||||
NOCB = 4 # When used in a dropdown, force passed callback.
|
||||
|
||||
# This is used by dropdown.py and menu.py
|
||||
@staticmethod
|
||||
|
@ -57,8 +58,8 @@ class Listbox(Widget):
|
|||
self.els = elements
|
||||
# Check whether elements specified as (str, str,...) or ([str, callback, args], [...)
|
||||
self.simple = isinstance(self.els[0], str)
|
||||
self.cb = callback if self.simple else self.despatch
|
||||
if not self.simple and callback is not dolittle:
|
||||
self.cb = callback if (self.simple or also == 4) else self.despatch
|
||||
if not (self.simple or also == 4) and callback is not dolittle:
|
||||
raise ValueError("Cannot specify callback.")
|
||||
# Iterate text values
|
||||
q = (p for p in self.els) if self.simple else (p[0] for p in self.els)
|
||||
|
@ -138,8 +139,6 @@ class Listbox(Widget):
|
|||
return r if self.simple else r[0]
|
||||
else: # set value by text
|
||||
try:
|
||||
# print(text)
|
||||
# print(self.els.index(text))
|
||||
if self.simple:
|
||||
v = self.els.index(text)
|
||||
else: # More RAM-efficient than converting to list and using .index
|
||||
|
@ -147,7 +146,7 @@ class Listbox(Widget):
|
|||
v = 0
|
||||
while next(q) != text:
|
||||
v += 1
|
||||
except ValueError:
|
||||
except (ValueError, StopIteration):
|
||||
v = None
|
||||
else:
|
||||
if v != self._value:
|
||||
|
|
Ładowanie…
Reference in New Issue