kopia lustrzana https://github.com/peterhinch/micropython-micro-gui
Dynamic lists: Listbox and Dropdown widgets.
rodzic
f756f00f33
commit
1ac1fd0976
20
README.md
20
README.md
|
@ -65,6 +65,7 @@ target and a C device driver (unless you can acquire a suitable binary).
|
||||||
|
|
||||||
# Project status
|
# Project status
|
||||||
|
|
||||||
|
Sept 2024: Dropdown and Listbox support dynamically variable lists of elements.
|
||||||
April 2024: Add screen replace feature for non-tree navigation.
|
April 2024: Add screen replace feature for non-tree navigation.
|
||||||
Sept 2023: Add "encoder only" mode suggested by @eudoxos.
|
Sept 2023: Add "encoder only" mode suggested by @eudoxos.
|
||||||
April 2023: Add limited ePaper support, grid widget, calendar and epaper demos.
|
April 2023: Add limited ePaper support, grid widget, calendar and epaper demos.
|
||||||
|
@ -128,7 +129,9 @@ under development so check for updates.
|
||||||
6.5 [ButtonList object](./README.md#65-buttonlist-object) Pushbuttons with multiple states.
|
6.5 [ButtonList object](./README.md#65-buttonlist-object) Pushbuttons with multiple states.
|
||||||
6.6 [RadioButtons object](./README.md#66-radiobuttons-object) One-of-N pushbuttons.
|
6.6 [RadioButtons object](./README.md#66-radiobuttons-object) One-of-N pushbuttons.
|
||||||
6.7 [Listbox widget](./README.md#67-listbox-widget)
|
6.7 [Listbox widget](./README.md#67-listbox-widget)
|
||||||
|
6.7.1 [Dynamic changes](./README.md#671-dynamic-changes) Alter listbox contents at runtime.
|
||||||
6.8 [Dropdown widget](./README.md#68-dropdown-widget) Dropdown lists.
|
6.8 [Dropdown widget](./README.md#68-dropdown-widget) Dropdown lists.
|
||||||
|
6.8.1 [Dynamic changes](./README.md#681-dynamic-changes) Alter dropdown contents at runtime.
|
||||||
6.9 [DialogBox class](./README.md#69-dialogbox-class) Pop-up modal dialog boxes.
|
6.9 [DialogBox class](./README.md#69-dialogbox-class) Pop-up modal dialog boxes.
|
||||||
6.10 [Textbox widget](./README.md#610-textbox-widget) Scrolling text display.
|
6.10 [Textbox widget](./README.md#610-textbox-widget) Scrolling text display.
|
||||||
6.11 [Meter widget](./README.md#611-meter-widget) Display floats on an analog meter, with data driven callbacks.
|
6.11 [Meter widget](./README.md#611-meter-widget) Display floats on an analog meter, with data driven callbacks.
|
||||||
|
@ -684,6 +687,8 @@ Some of these require larger screens. Required sizes are specified as
|
||||||
(240x320).
|
(240x320).
|
||||||
* `vtest.py` Clock and compass styles of vector display (240x320).
|
* `vtest.py` Clock and compass styles of vector display (240x320).
|
||||||
* `calendar.py` Demo of grid control (240x320 - but could be reduced).
|
* `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.
|
||||||
|
|
||||||
###### [Contents](./README.md#0-contents)
|
###### [Contents](./README.md#0-contents)
|
||||||
|
|
||||||
|
@ -1628,6 +1633,7 @@ Methods:
|
||||||
the control's list, that item becomes current. Normally returns the current
|
the control's list, that item becomes current. Normally returns the current
|
||||||
string. If a provided arg did not match any list item, the control's state is
|
string. If a provided arg did not match any list item, the control's state is
|
||||||
not changed and `None` is returned.
|
not changed and `None` is returned.
|
||||||
|
* `update` No args. See [Dynamic changes](./README.md#671-dynamic-changes).
|
||||||
|
|
||||||
The callback's first argument is the listbox instance followed by any args
|
The callback's first argument is the listbox instance followed by any args
|
||||||
specified to the constructor. The currently selected item may be retrieved by
|
specified to the constructor. The currently selected item may be retrieved by
|
||||||
|
@ -1668,6 +1674,12 @@ class BaseScreen(Screen):
|
||||||
|
|
||||||
Screen.change(BaseScreen)
|
Screen.change(BaseScreen)
|
||||||
```
|
```
|
||||||
|
### 6.7.1 Dynamic changes
|
||||||
|
|
||||||
|
The contents of a listbox may be changed at runtime. To achieve this, elements
|
||||||
|
must be defined as a list rather than a tuple. After the application has
|
||||||
|
modified the list, it should call the `.update` method to refresh the control.
|
||||||
|
The demo script `listbox_var.py` illustrates this.
|
||||||
|
|
||||||
###### [Contents](./README.md#0-contents)
|
###### [Contents](./README.md#0-contents)
|
||||||
|
|
||||||
|
@ -1734,6 +1746,7 @@ Methods:
|
||||||
the control's list, that item becomes current. Normally returns the current
|
the control's list, that item becomes current. Normally returns the current
|
||||||
string. If a provided arg did not match any list item, the control's state is
|
string. If a provided arg did not match any list item, the control's state is
|
||||||
not changed and `None` is returned.
|
not changed and `None` is returned.
|
||||||
|
* `update` No args. See [Dynamic changes](./README.md#681-dynamic-changes).
|
||||||
|
|
||||||
If `select` is pressed when the `Dropdown` has focus, the list is displayed.
|
If `select` is pressed when the `Dropdown` has focus, the list is displayed.
|
||||||
The `increase` and `decrease` buttons move the list currency. If `select` is
|
The `increase` and `decrease` buttons move the list currency. If `select` is
|
||||||
|
@ -1784,6 +1797,13 @@ class BaseScreen(Screen):
|
||||||
|
|
||||||
Screen.change(BaseScreen)
|
Screen.change(BaseScreen)
|
||||||
```
|
```
|
||||||
|
### 6.8.1 Dynamic changes
|
||||||
|
|
||||||
|
The contents of a Dropdown may be changed at runtime. To achieve this, elements
|
||||||
|
must be defined as a list rather than a tuple. After the application has
|
||||||
|
modified the list, it should call the `.update` method to refresh the control.
|
||||||
|
The demo script `dropdown_var.py` illustrates this.
|
||||||
|
|
||||||
###### [Contents](./README.md#0-contents)
|
###### [Contents](./README.md#0-contents)
|
||||||
|
|
||||||
## 6.9 DialogBox class
|
## 6.9 DialogBox class
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
# dropdown_var.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 *
|
||||||
|
|
||||||
|
|
||||||
|
def newtext(): # Create new listbox entries
|
||||||
|
strings = ("Iron", "Copper", "Lead", "Zinc")
|
||||||
|
n = 0
|
||||||
|
while True:
|
||||||
|
yield strings[n]
|
||||||
|
n = (n + 1) % len(strings)
|
||||||
|
|
||||||
|
|
||||||
|
ntxt = newtext() # Instantiate the generator
|
||||||
|
|
||||||
|
|
||||||
|
class BaseScreen(Screen):
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
wri = CWriter(ssd, font, GREEN, BLACK, verbose=False)
|
||||||
|
|
||||||
|
col = 2
|
||||||
|
row = 2
|
||||||
|
self.els = ["Hydrogen", "Helium", "Neon", "Argon", "Krypton", "Xenon", "Radon"]
|
||||||
|
self.dd = Dropdown(
|
||||||
|
wri,
|
||||||
|
row,
|
||||||
|
col,
|
||||||
|
elements=self.els,
|
||||||
|
dlines=5, # Show 5 lines
|
||||||
|
bdcolor=GREEN,
|
||||||
|
callback=self.ddcb,
|
||||||
|
)
|
||||||
|
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(ntxt)) # 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()
|
||||||
|
|
||||||
|
def ddcb(self, dd):
|
||||||
|
if hasattr(self, "lbl"):
|
||||||
|
self.lbl.value(dd.textvalue())
|
||||||
|
|
||||||
|
def after_open(self):
|
||||||
|
self.lbl.value(self.dd.textvalue())
|
||||||
|
|
||||||
|
|
||||||
|
def test():
|
||||||
|
print("Dropdown demo.")
|
||||||
|
Screen.change(BaseScreen)
|
||||||
|
|
||||||
|
|
||||||
|
test()
|
|
@ -0,0 +1,74 @@
|
||||||
|
# listbox_var.py micro-gui demo of Listbox class with variable 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.
|
||||||
|
from hardware_setup import ssd # Create a display instance
|
||||||
|
from gui.core.ugui import Screen
|
||||||
|
from gui.core.writer import CWriter
|
||||||
|
from gui.core.colors import *
|
||||||
|
|
||||||
|
from gui.widgets import Listbox, Button, CloseButton
|
||||||
|
import gui.fonts.freesans20 as font
|
||||||
|
|
||||||
|
|
||||||
|
def newtext(): # Create new listbox entries
|
||||||
|
strings = ("Iron", "Copper", "Lead", "Zinc")
|
||||||
|
n = 0
|
||||||
|
while True:
|
||||||
|
yield strings[n]
|
||||||
|
n = (n + 1) % len(strings)
|
||||||
|
|
||||||
|
|
||||||
|
ntxt = newtext() # Instantiate the generator
|
||||||
|
|
||||||
|
|
||||||
|
class BaseScreen(Screen):
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
wri = CWriter(ssd, font, GREEN, BLACK, verbose=False)
|
||||||
|
self.els = [
|
||||||
|
"Hydrogen",
|
||||||
|
"Helium",
|
||||||
|
"Neon",
|
||||||
|
"Xenon",
|
||||||
|
"Radon",
|
||||||
|
"Uranium",
|
||||||
|
"Plutonium",
|
||||||
|
"Actinium",
|
||||||
|
]
|
||||||
|
self.lb = Listbox(
|
||||||
|
wri,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
elements=self.els,
|
||||||
|
dlines=5,
|
||||||
|
bdcolor=RED,
|
||||||
|
value=1,
|
||||||
|
callback=self.lbcb,
|
||||||
|
also=Listbox.ON_LEAVE,
|
||||||
|
)
|
||||||
|
Button(wri, 2, 120, height=25, text="del", callback=self.delcb)
|
||||||
|
Button(wri, 32, 120, height=25, text="add", callback=self.addcb)
|
||||||
|
Button(wri, 62, 120, height=25, text="h2", callback=self.gocb, args=("Hydrogen",))
|
||||||
|
Button(wri, 92, 120, height=25, text="fe", callback=self.gocb, args=("Iron",))
|
||||||
|
CloseButton(wri)
|
||||||
|
|
||||||
|
def lbcb(self, lb): # Listbox callback
|
||||||
|
print(lb.textvalue())
|
||||||
|
|
||||||
|
def gocb(self, _, txt): # Go button callback: Move currency to specified entry
|
||||||
|
self.lb.textvalue(txt)
|
||||||
|
|
||||||
|
def addcb(self, _): # Add button callback
|
||||||
|
self.els.append(next(ntxt)) # Append a new entry
|
||||||
|
self.lb.update()
|
||||||
|
|
||||||
|
def delcb(self, _): # Delete button callback
|
||||||
|
del self.els[self.lb.value()] # Delete current entry
|
||||||
|
self.lb.update()
|
||||||
|
|
||||||
|
|
||||||
|
Screen.change(BaseScreen)
|
|
@ -15,7 +15,6 @@ dolittle = lambda *_ : None
|
||||||
# Next and Prev close the listbox without updating the Dropdown. This is
|
# Next and Prev close the listbox without updating the Dropdown. This is
|
||||||
# handled by Screen .move bound method
|
# handled by Screen .move bound method
|
||||||
class _ListDialog(Window):
|
class _ListDialog(Window):
|
||||||
|
|
||||||
def __init__(self, writer, row, col, dd): # dd is parent dropdown
|
def __init__(self, writer, row, col, dd): # dd is parent dropdown
|
||||||
# Need to determine Window dimensions from size of Listbox, which
|
# Need to determine Window dimensions from size of Listbox, which
|
||||||
# depends on number and length of elements.
|
# depends on number and length of elements.
|
||||||
|
@ -25,13 +24,21 @@ class _ListDialog(Window):
|
||||||
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, draw_border=False)
|
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,
|
||||||
fgcolor = dd.fgcolor, bgcolor = dd.bgcolor,
|
width=lb_width,
|
||||||
bdcolor=False, fontcolor = dd.fontcolor,
|
fgcolor=dd.fgcolor,
|
||||||
|
bgcolor=dd.bgcolor,
|
||||||
|
bdcolor=False,
|
||||||
|
fontcolor=dd.fontcolor,
|
||||||
select_color=dd.select_color,
|
select_color=dd.select_color,
|
||||||
value = dd.value(), callback = self.callback)
|
value=dd.value(),
|
||||||
|
callback=self.callback,
|
||||||
|
)
|
||||||
self.dd = dd
|
self.dd = dd
|
||||||
|
|
||||||
def callback(self, obj_listbox):
|
def callback(self, obj_listbox):
|
||||||
|
@ -41,40 +48,58 @@ class _ListDialog(Window):
|
||||||
|
|
||||||
|
|
||||||
class Dropdown(Widget):
|
class Dropdown(Widget):
|
||||||
def __init__(self, writer, row, col, *,
|
def __init__(
|
||||||
|
self,
|
||||||
|
writer,
|
||||||
|
row,
|
||||||
|
col,
|
||||||
|
*,
|
||||||
elements,
|
elements,
|
||||||
dlines=None, width=None, value=0,
|
dlines=None,
|
||||||
fgcolor=None, bgcolor=None, bdcolor=False,
|
width=None,
|
||||||
fontcolor=None, select_color=DARKBLUE,
|
value=0,
|
||||||
callback=dolittle, args=[]):
|
fgcolor=None,
|
||||||
|
bgcolor=None,
|
||||||
|
bdcolor=False,
|
||||||
|
fontcolor=None,
|
||||||
|
select_color=DARKBLUE,
|
||||||
|
callback=dolittle,
|
||||||
|
args=[],
|
||||||
|
):
|
||||||
|
|
||||||
self.entry_height = writer.height + 2 # Allow a pixel above and below text
|
self.entry_height = writer.height + 2 # Allow a pixel above and below text
|
||||||
height = self.entry_height
|
height = self.entry_height
|
||||||
e0 = elements[0]
|
self.select_color = select_color
|
||||||
|
self.dlines = dlines # Referenced by _ListDialog
|
||||||
|
|
||||||
# Check whether elements specified as (str, str,...) or ([str, callback, args], [...)
|
# Check whether elements specified as (str, str,...) or ([str, callback, args], [...)
|
||||||
if isinstance(e0, tuple) or isinstance(e0, list):
|
self.simple = isinstance(elements[0], str)
|
||||||
te = [x[0] for x in elements] # Copy text component
|
|
||||||
self.els = elements # Retain original
|
self.els = elements # Retain original
|
||||||
elements = te
|
# Listbox works with text component only because it has a single callback.
|
||||||
if callback is not dolittle:
|
self.elements = elements if self.simple else [x[0] for x in elements]
|
||||||
raise ValueError('Cannot specify callback.')
|
|
||||||
callback = self._despatch
|
|
||||||
if width is None: # Allow for square at end for arrow
|
if width is None: # Allow for square at end for arrow
|
||||||
self.textwidth = max(writer.stringlen(s) for s in elements)
|
self.textwidth = max(writer.stringlen(s) for s in elements)
|
||||||
width = self.textwidth + 2 + height
|
width = self.textwidth + 2 + height
|
||||||
else:
|
else:
|
||||||
self.textwidth = width
|
self.textwidth = width
|
||||||
super().__init__(writer, row, col, height, width, fgcolor, bgcolor, bdcolor, value, True)
|
super().__init__(writer, row, col, height, width, fgcolor, bgcolor, bdcolor, value, True)
|
||||||
super()._set_callbacks(callback, args)
|
|
||||||
self.select_color = select_color
|
|
||||||
self.fontcolor = self.fgcolor if fontcolor is None else fontcolor
|
self.fontcolor = self.fgcolor if fontcolor is None else fontcolor
|
||||||
self.elements = elements
|
if not self.simple:
|
||||||
self.dlines = dlines
|
if callback is not dolittle:
|
||||||
|
raise ValueError("Cannot specify callback.")
|
||||||
|
callback = self._despatch # Override passed CB if each element has a CB.
|
||||||
|
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()
|
||||||
|
|
||||||
def show(self):
|
def show(self):
|
||||||
if super().show():
|
if not super().show():
|
||||||
x, y = self.col, self.row
|
return
|
||||||
self._draw(x, y)
|
self._draw(x := self.col, y := self.row)
|
||||||
if self._value is not None:
|
if self._value is not None:
|
||||||
display.print_left(self.writer, x, y + 1, self.elements[self._value], self.fontcolor)
|
display.print_left(self.writer, x, y + 1, self.elements[self._value], self.fontcolor)
|
||||||
|
|
||||||
|
@ -100,8 +125,20 @@ class Dropdown(Widget):
|
||||||
length = halflength * 2
|
length = halflength * 2
|
||||||
if length > 0:
|
if length > 0:
|
||||||
display.hline(xcentre - halflength, ycentre - halflength, length, self.fgcolor)
|
display.hline(xcentre - halflength, ycentre - halflength, length, self.fgcolor)
|
||||||
display.line(xcentre - halflength, ycentre - halflength, xcentre, ycentre + halflength, self.fgcolor)
|
display.line(
|
||||||
display.line(xcentre + halflength, ycentre - halflength, xcentre, ycentre + halflength, self.fgcolor)
|
xcentre - halflength,
|
||||||
|
ycentre - halflength,
|
||||||
|
xcentre,
|
||||||
|
ycentre + halflength,
|
||||||
|
self.fgcolor,
|
||||||
|
)
|
||||||
|
display.line(
|
||||||
|
xcentre + halflength,
|
||||||
|
ycentre - halflength,
|
||||||
|
xcentre,
|
||||||
|
ycentre + halflength,
|
||||||
|
self.fgcolor,
|
||||||
|
)
|
||||||
|
|
||||||
def do_sel(self): # Select was pushed
|
def do_sel(self): # Select was pushed
|
||||||
if len(self.elements) > 1:
|
if len(self.elements) > 1:
|
||||||
|
|
|
@ -29,7 +29,9 @@ class Listbox(Widget):
|
||||||
dlines = len(elements) if dlines is None else dlines
|
dlines = len(elements) if dlines is None else dlines
|
||||||
# Height of control
|
# Height of control
|
||||||
height = entry_height * dlines + 2
|
height = entry_height * dlines + 2
|
||||||
textwidth = max(writer.stringlen(s) for s in elements) + 4
|
simple = isinstance(elements[0], str) # list or list of lists?
|
||||||
|
q = (p for p in elements) if simple else (p[0] for p in elements)
|
||||||
|
textwidth = max(writer.stringlen(x) for x in q) + 4
|
||||||
return entry_height, height, dlines, textwidth
|
return entry_height, height, dlines, textwidth
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
@ -52,25 +54,23 @@ class Listbox(Widget):
|
||||||
also=0
|
also=0
|
||||||
):
|
):
|
||||||
|
|
||||||
e0 = elements[0]
|
self.els = elements
|
||||||
# Check whether elements specified as (str, str,...) or ([str, callback, args], [...)
|
# Check whether elements specified as (str, str,...) or ([str, callback, args], [...)
|
||||||
if isinstance(e0, tuple) or isinstance(e0, list):
|
self.simple = isinstance(self.els[0], str)
|
||||||
self.els = elements # Retain original for .despatch
|
self.cb = callback if self.simple else self.despatch
|
||||||
self.elements = [x[0] for x in elements] # Copy text component
|
if not self.simple and callback is not dolittle:
|
||||||
if callback is not dolittle:
|
|
||||||
raise ValueError("Cannot specify callback.")
|
raise ValueError("Cannot specify callback.")
|
||||||
self.cb = self.despatch
|
# Iterate text values
|
||||||
else:
|
q = (p for p in self.els) if self.simple else (p[0] for p in self.els)
|
||||||
self.cb = callback
|
if not all(isinstance(x, str) for x in q):
|
||||||
self.elements = elements
|
|
||||||
if any(not isinstance(s, str) for s in self.elements):
|
|
||||||
raise ValueError("Invalid elements arg.")
|
raise ValueError("Invalid elements arg.")
|
||||||
|
|
||||||
# Calculate dimensions
|
# Calculate dimensions
|
||||||
self.entry_height, height, self.dlines, tw = self.dimensions(writer, self.elements, dlines)
|
self.entry_height, height, self.dlines, tw = self.dimensions(writer, self.els, dlines)
|
||||||
if width is None:
|
if width is None:
|
||||||
width = tw # Text width
|
width = tw # Text width
|
||||||
|
|
||||||
self.also = also
|
self.also = also # Additioal callback events
|
||||||
self.ntop = 0 # Top visible line
|
self.ntop = 0 # Top visible line
|
||||||
if not isinstance(value, int):
|
if not isinstance(value, int):
|
||||||
value = 0 # Or ValueError?
|
value = 0 # Or ValueError?
|
||||||
|
@ -86,7 +86,7 @@ class Listbox(Widget):
|
||||||
self.ev = value # Value change detection
|
self.ev = value # Value change detection
|
||||||
|
|
||||||
def update(self): # Elements list has changed.
|
def update(self): # Elements list has changed.
|
||||||
l = len(self.elements)
|
l = len(self.els)
|
||||||
nl = self.dlines # No. of lines that can fit in window
|
nl = self.dlines # No. of lines that can fit in window
|
||||||
self.ntop = max(0, min(self.ntop, l - nl))
|
self.ntop = max(0, min(self.ntop, l - nl))
|
||||||
self._value = min(self._value, l - 1)
|
self._value = min(self._value, l - 1)
|
||||||
|
@ -99,11 +99,13 @@ class Listbox(Widget):
|
||||||
x = self.col
|
x = self.col
|
||||||
y = self.row
|
y = self.row
|
||||||
eh = self.entry_height
|
eh = self.entry_height
|
||||||
ntop = self.ntop
|
|
||||||
dlines = self.dlines
|
dlines = self.dlines
|
||||||
nlines = min(dlines, len(self.elements)) # Displayable lines
|
self.ntop = min(self.ntop, self._value) # Ensure currency is visible
|
||||||
|
self.ntop = max(self.ntop, self._value - dlines + 1)
|
||||||
|
ntop = self.ntop
|
||||||
|
nlines = min(dlines, len(self.els)) # Displayable lines
|
||||||
for n in range(ntop, ntop + nlines):
|
for n in range(ntop, ntop + nlines):
|
||||||
text = self.elements[n]
|
text = self.els[n] if self.simple else self.els[n][0]
|
||||||
if self.writer.stringlen(text) > self.width: # Clip
|
if self.writer.stringlen(text) > self.width: # Clip
|
||||||
font = self.writer.font
|
font = self.writer.font
|
||||||
pos = 0
|
pos = 0
|
||||||
|
@ -126,18 +128,25 @@ class Listbox(Widget):
|
||||||
x = self.col + self.width - 2
|
x = self.col + self.width - 2
|
||||||
if ntop:
|
if ntop:
|
||||||
display.vline(x, self.row, eh - 1, self.fgcolor)
|
display.vline(x, self.row, eh - 1, self.fgcolor)
|
||||||
if ntop + dlines < len(self.elements):
|
if ntop + dlines < len(self.els):
|
||||||
y = self.row + (dlines - 1) * eh
|
y = self.row + (dlines - 1) * eh
|
||||||
display.vline(x, y, eh - 1, self.fgcolor)
|
display.vline(x, y, eh - 1, self.fgcolor)
|
||||||
|
|
||||||
def textvalue(self, text=None): # if no arg return current text
|
def textvalue(self, text=None): # if no arg return current text
|
||||||
if text is None:
|
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
|
else: # set value by text
|
||||||
try:
|
try:
|
||||||
# print(text)
|
# print(text)
|
||||||
# print(self.elements.index(text))
|
# print(self.els.index(text))
|
||||||
v = self.elements.index(text)
|
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:
|
except ValueError:
|
||||||
v = None
|
v = None
|
||||||
else:
|
else:
|
||||||
|
@ -161,7 +170,7 @@ class Listbox(Widget):
|
||||||
if v:
|
if v:
|
||||||
self._vchange(v - 1)
|
self._vchange(v - 1)
|
||||||
elif val < 0:
|
elif val < 0:
|
||||||
if v < len(self.elements) - 1:
|
if v < len(self.els) - 1:
|
||||||
self._vchange(v + 1)
|
self._vchange(v + 1)
|
||||||
|
|
||||||
# Callback runs if select is pressed. Also (if ON_LEAVE) if user changes
|
# Callback runs if select is pressed. Also (if ON_LEAVE) if user changes
|
||||||
|
|
Ładowanie…
Reference in New Issue