kopia lustrzana https://github.com/peterhinch/micropython-micro-gui
V0.1.3 N-level menus. Bugfixes and doc improvements.
rodzic
70c0ea248e
commit
ba46d98216
253
README.md
253
README.md
|
@ -63,7 +63,7 @@ of some display drivers.
|
|||
|
||||
# 0. Contents
|
||||
|
||||
1. [Basic concepts](./README.md#1-basic-concepts) Including installation and test.
|
||||
1. [Basic concepts](./README.md#1-basic-concepts) Including "Hello world" script.
|
||||
1.1 [Coordinates](./README.md#11-coordinates) The GUI's coordinate system.
|
||||
1.2 [Screen Window and Widget objects](./README.md#12-Screen-window-and-widget-objects) Basic GUI classes.
|
||||
1.3 [Fonts](./README.md#13-fonts)
|
||||
|
@ -137,6 +137,47 @@ provision of extra visual information. For example the color of all or part of
|
|||
a widget may be changed programmatically, for example to highlight an overrange
|
||||
condition.
|
||||
|
||||
The following, taken from `gui.demos.simple.py`, is a complete application. It
|
||||
shows a message and has "Yes" and "No" buttons which trigger a callback.
|
||||
```python
|
||||
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.core.writer import CWriter
|
||||
|
||||
# Font for CWriter
|
||||
import gui.fonts.arial10 as arial10
|
||||
from gui.core.colors import *
|
||||
|
||||
|
||||
class BaseScreen(Screen):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
def my_callback(button, arg):
|
||||
print('Button pressed', arg)
|
||||
|
||||
super().__init__()
|
||||
wri = CWriter(ssd, arial10, GREEN, BLACK, verbose=False)
|
||||
|
||||
col = 2
|
||||
row = 2
|
||||
Label(wri, row, col, 'Simple Demo')
|
||||
row = 50
|
||||
Button(wri, row, col, text='Yes', callback=my_callback, args=('Yes',))
|
||||
col += 60
|
||||
Button(wri, row, col, text='No', callback=my_callback, args=('No',))
|
||||
CloseButton(wri) # Quit the application
|
||||
|
||||
def test():
|
||||
print('Simple demo: button presses print to REPL.')
|
||||
Screen.change(BaseScreen) # A class is passed here, not an instance.
|
||||
|
||||
test()
|
||||
```
|
||||
|
||||
## 1.1 Coordinates
|
||||
|
||||
These are defined as `row` and `col` values where `row==0` and `col==0`
|
||||
|
@ -154,7 +195,8 @@ re-displayed.
|
|||
|
||||
A `Window` is a subclass of `Screen` but is smaller, with size and location
|
||||
attributes. It can overlay part of an underlying `Screen` and is typically used
|
||||
for modal dialog boxes.
|
||||
for dialog boxes. `Window` objects are modal: a `Window` can overlay a `Screen`
|
||||
but cannot overlay another `Window`.
|
||||
|
||||
A `Widget` is an object capable of displaying data. Some are also capable of
|
||||
data input: such a widget is defined as `active`. A `passive` widget can only
|
||||
|
@ -162,9 +204,9 @@ display data. An `active` widget can acquire `focus`. The widget with `focus`
|
|||
is able to respond to user input. See [navigation](./README.md#14-navigation).
|
||||
`Widget` objects have dimensions defined as `height` and `width`. The space
|
||||
requred by them exceeds these dimensions by two pixels all round. This is
|
||||
because `micro-gui` displays a white border to show which object currently has
|
||||
`focus`. Thus to place a `Widget` at the extreme top left, `row` and `col`
|
||||
values should be 2.
|
||||
because `micro-gui` displays a surrounding white border to show which object
|
||||
currently has `focus`. Thus to place a `Widget` at the extreme top left, `row`
|
||||
and `col` values should be 2.
|
||||
|
||||
###### [Contents](./README.md#0-contents)
|
||||
|
||||
|
@ -200,7 +242,9 @@ The GUI requires from 2 to 5 pushbuttons for control. These are:
|
|||
5. `Decrease` Move within the widget.
|
||||
|
||||
An alternative is to replace buttons 4 and 5 with a quadrature encoder knob
|
||||
such as [this one](https://www.adafruit.com/product/377).
|
||||
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.
|
||||
|
||||
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
|
||||
|
@ -217,8 +261,8 @@ moves between widgets via `Next` and `Prev`. Only `active` `Widget` instances
|
|||
`active` or `passive` in the constructor, and this status cannot be changed. In
|
||||
some cases the state can be specified as a constructor arg, but other widgets
|
||||
have a predefined state. An `active` widget can be disabled and re-enabled at
|
||||
runtime. A disabled `active` widget is shown "greyed-out" and, until
|
||||
re-enabled, cannot accept the `focus`.
|
||||
runtime. A disabled `active` widget is shown "greyed-out" and cannot accept the
|
||||
`focus` until re-enabled.
|
||||
|
||||
###### [Contents](./README.md#0-contents)
|
||||
|
||||
|
@ -245,20 +289,21 @@ prst = Pin(9, Pin.OUT, value=1)
|
|||
pcs = Pin(10, Pin.OUT, value=1)
|
||||
spi = SPI(0, baudrate=30_000_000)
|
||||
gc.collect() # Precaution before instantiating framebuf
|
||||
# Instantiate display and assign to ssd. For args see display drivers doc.
|
||||
ssd = SSD(spi, pcs, pdc, prst, usd=True)
|
||||
|
||||
from gui.core.ugui import Display, setup
|
||||
# Create and export a Display instance
|
||||
# Define control buttons
|
||||
nxt = Pin(19, Pin.IN, Pin.PULL_UP) # Move to next control
|
||||
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
|
||||
# Create a Display instance and assign to display.
|
||||
display = Display(ssd, nxt, sel, prev, increase, decrease)
|
||||
```
|
||||
Where an encoder replaces the `increase` and `decrease` buttons the final line
|
||||
only needs to be changed to read:
|
||||
Where an encoder replaces the `increase` and `decrease` buttons, only the final
|
||||
line needs to be changed to provide an extra arg:
|
||||
```python
|
||||
display = Display(ssd, nxt, sel, prev, increase, decrease, 5)
|
||||
```
|
||||
|
@ -269,6 +314,9 @@ provides the highest sensitivity, being the native rate of the encoder.
|
|||
Instantiation of `SSD` and `Display` classes is detailed in
|
||||
[section 3](./README.md#3-the-ssd-and-display-objects).
|
||||
|
||||
Display drivers are
|
||||
[documented here](https://github.com/peterhinch/micropython-nano-gui/blob/master/DRIVERS.md).
|
||||
|
||||
###### [Contents](./README.md#0-contents)
|
||||
|
||||
## 1.6 Quick hardware check
|
||||
|
@ -319,14 +367,16 @@ widgets, fonts and demos can also be trimmed, but the directory structure must
|
|||
be kept.
|
||||
|
||||
There is scope for speeding loading and saving RAM by using frozen bytecode.
|
||||
Once again, directory structure must be maintained.
|
||||
Once again, directory structure must be maintained. An example directory
|
||||
structure, pruned to contain a minimum of files, may be seen
|
||||
[here](https://github.com/peterhinch/micropython-nano-gui#4-esp8266).
|
||||
|
||||
###### [Contents](./README.md#0-contents)
|
||||
|
||||
## 1.8 Performance and hardware notes
|
||||
|
||||
The largest supported display is a 320x240 ILI9341 unit. On a Pi Pico with no
|
||||
use of frozen bytecode the demos run with about 74K of free RAM. Substantial
|
||||
use of frozen bytecode the demos run with about 70K of free RAM. Substantial
|
||||
improvements could be achieved using frozen bytecode.
|
||||
|
||||
Snappy navigation benefits from several approaches:
|
||||
|
@ -340,9 +390,8 @@ This is because display update blocks for tens of milliseconds, during which
|
|||
time the pushbuttons are not polled. Blocking is much reduced by item 3 above.
|
||||
|
||||
On the TTGO T-Display it is necessary to use physical pullup resistors on the
|
||||
pushbutton GPIO lines. According to the
|
||||
[ESP32 gurus](https://randomnerdtutorials.com/esp32-pinout-reference-gpios/)
|
||||
pins 36-39 do not have pullup support.
|
||||
pushbutton GPIO lines. This is because pins 36-39 do not have pullup support.
|
||||
[See ref](https://randomnerdtutorials.com/esp32-pinout-reference-gpios/).
|
||||
|
||||
On a Pyboard 1.1 with 320x240 ili9341 display it was necessary to use frozen
|
||||
bytecode: in this configuration running the `various.py` demo there was 29K of
|
||||
|
@ -441,6 +490,7 @@ minimal and aim to demonstrate a single technique.
|
|||
screen layout using widget metrics. Has a simple `uasyncio` task.
|
||||
* `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.
|
||||
|
||||
### 1.11.2 Test scripts
|
||||
|
||||
|
@ -1012,9 +1062,10 @@ Constructor mandatory positional args:
|
|||
|
||||
Optional keyword only arguments:
|
||||
* `shape=RECTANGLE` Must be `CIRCLE`, `RECTANGLE` or `CLIPPED_RECT`.
|
||||
* `height=20` Height of button or diameter in `CIRCLE` case.
|
||||
* `width=50` Width of button. If `text` is supplied and `width` is too low to
|
||||
accommodate the text, it will be increased to enable the text to fit.
|
||||
* `height=20` Height. In `CIRCLE` case any passed value is ignored.
|
||||
accommodate the text, it will be increased to enable the text to fit. In
|
||||
`CIRCLE` case any passed value is ignored.
|
||||
* `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
|
||||
|
@ -1189,7 +1240,8 @@ Constructor mandatory positional args:
|
|||
|
||||
Mandatory keyword only argument:
|
||||
* `elements` A list or tuple of strings to display. Must have at least one
|
||||
entry.
|
||||
entry. An alternative format is described below which enables each item in the
|
||||
list to have a separate callback.
|
||||
|
||||
Optional keyword only arguments:
|
||||
* `width=None` Control width in pixels. By default this is calculated to
|
||||
|
@ -1227,6 +1279,42 @@ 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
|
||||
means of the instance's `value` or `textvalue` methods.
|
||||
|
||||
#### Alternative approach
|
||||
|
||||
By default the `Listbox` runs a common callback regardless of the item chosen.
|
||||
This can be changed by specifying `elements` such that each element comprises a
|
||||
3-list or 3-tuple with the following contents:
|
||||
0. String to display.
|
||||
1. Callback.
|
||||
2. Tuple of args (may be ()).
|
||||
|
||||
In this case constructor args `callback` and `args` must not be supplied. Args
|
||||
received by the callback functions comprise the `Listbox` instance followed by
|
||||
any supplied args. The following is a complete example (minus initial `import`
|
||||
statements).
|
||||
|
||||
```python
|
||||
class BaseScreen(Screen):
|
||||
def __init__(self):
|
||||
def cb(lb, s):
|
||||
print('Callback', s)
|
||||
|
||||
def cb_radon(lb, s):
|
||||
print('Radioactive', s)
|
||||
|
||||
super().__init__()
|
||||
wri = CWriter(ssd, freesans20, GREEN, BLACK, verbose=False)
|
||||
els = (('Hydrogen', cb, ('H2',)),
|
||||
('Helium', cb, ('He',)),
|
||||
('Neon', cb, ('Ne',)),
|
||||
('Xenon', cb, ('Xe',)),
|
||||
('Radon', cb_radon, ('Ra',)))
|
||||
Listbox(wri, 2, 2, elements = els, bdcolor=RED)
|
||||
CloseButton(wri)
|
||||
|
||||
Screen.change(BaseScreen)
|
||||
```
|
||||
|
||||
###### [Contents](./README.md#0-contents)
|
||||
|
||||
# 13. Dropdown widget
|
||||
|
@ -1256,7 +1344,8 @@ Constructor mandatory positional args:
|
|||
|
||||
Mandatory keyword only argument:
|
||||
* `elements` A list or tuple of strings to display. Must have at least one
|
||||
entry. See below for an alternative way to use the `Dropdown`.
|
||||
entry. See below for an alternative way to use the `Dropdown` which enables
|
||||
each item on the dropdown list to have a separate callback.
|
||||
|
||||
Optional keyword only arguments:
|
||||
* `width=None` Control width in pixels. By default this is calculated to
|
||||
|
@ -1309,8 +1398,31 @@ comprises a 3-list or 3-tuple with the following contents:
|
|||
|
||||
In this case constructor args `callback` and `args` must not be supplied. Args
|
||||
received by the callback functions comprise the `Dropdown` instance followed by
|
||||
any supplied args.
|
||||
any supplied args. The following is a complete example (minus initial import
|
||||
statements):
|
||||
```python
|
||||
class BaseScreen(Screen):
|
||||
def __init__(self):
|
||||
def cb(dd, arg):
|
||||
print('Gas', arg)
|
||||
|
||||
def cb_radon(dd, arg):
|
||||
print('Radioactive', arg)
|
||||
|
||||
super().__init__()
|
||||
wri = CWriter(ssd, freesans20, GREEN, BLACK, verbose=False)
|
||||
els = (('hydrogen', cb, ('H2',)),
|
||||
('helium', cb, ('He',)),
|
||||
('neon', cb, ('Ne',)),
|
||||
('xenon', cb, ('Xe',)),
|
||||
('radon', cb_radon, ('Ra',)))
|
||||
Dropdown(wri, 2, 2, elements = els,
|
||||
bdcolor = RED, fgcolor=RED, fontcolor = YELLOW)
|
||||
CloseButton(wri)
|
||||
|
||||
|
||||
Screen.change(BaseScreen)
|
||||
```
|
||||
###### [Contents](./README.md#0-contents)
|
||||
|
||||
# 14. DialogBox class
|
||||
|
@ -2120,17 +2232,18 @@ value changes. This enables dynamic color change.
|
|||
|
||||
# 22 Menu class
|
||||
|
||||
The `Menu` class is under development. API may change.
|
||||
|
||||
```python
|
||||
from gui.widgets.menu import Menu
|
||||
```
|
||||
![Image](./images/menu.JPG)
|
||||
|
||||
This enables the creation of single or two level menus. The top level of the
|
||||
menu consists of a row of `Button` instances at the top of the screen. Each
|
||||
button can either call a callback or instantiate a dropdown list comprising the
|
||||
second menu level.
|
||||
The `Menu` class enables the creation of single or multiple level menus. The
|
||||
top level of the menu comprises a row of `Button` instances at the top of the
|
||||
physical screen. Each button can either call a callback or instantiate a
|
||||
dropdown menu comprising the second menu level.
|
||||
|
||||
Each item on a dropdown menu can invoke either a callback or a lower level
|
||||
menu.
|
||||
|
||||
Constructor mandatory positional arg:
|
||||
1. `writer` The `Writer` instance (defines font) to use.
|
||||
|
@ -2140,28 +2253,88 @@ Keyword only args:
|
|||
* `bgcolor=None` Background color of buttons and dropdown.
|
||||
* `fgcolor=None` Foreground color.
|
||||
* `textcolor=None` Text color.
|
||||
* `select_color=DARKBLUE` Background color of selected item on dropdown list.
|
||||
* `select_color=DARKBLUE` Background color of selected item on dropdown menu.
|
||||
* `args` This should be a tuple containing a tuple of args for each entry in
|
||||
the top level menu. Each tuple should be of one of two forms:
|
||||
1. `(text, cb, (args,))` A single-level entry: the top level `Button` with
|
||||
text `text` runs the callback `cb` with positional args defined by the
|
||||
supplied tuple (which may be `()`). The callback receives an initial arg
|
||||
being the `Button` instance.
|
||||
2. `(text, cb, (args,), (elements,))` In this instance the top level `Button`
|
||||
triggers a dropdown list comprising a tuple of strings in `elements`. The
|
||||
callback `cb` is triggered if the user selects an entry. The callback
|
||||
receives an initial arg which is a `Listbox` instance: the chosen entry may
|
||||
be retrieved by running `lb.value()` or `lb.textvalue()`.
|
||||
2. `(text, (element0, element1,...))` In this instance the top level `Button`
|
||||
triggers a dropdown menu defined by data in the `elements` tuple.
|
||||
|
||||
In this example the first two items trigger submenus, the third runs a
|
||||
callback. In this example the two submenus share a callback (`cb_sm`), which
|
||||
uses the passed arg to determine which menu it was called from.
|
||||
Each element in the `elements` tuple is a tuple defining a menu item. This can
|
||||
take two forms, each of which has the text for the menu item as the first
|
||||
value:
|
||||
1. `(text, cb, (args,))` The element triggers callback `cb` with positional
|
||||
args defined by the supplied tuple (which may be `()`). The callback receives
|
||||
an initial arg being the `Listbox` instance which corresponds to the parent
|
||||
dropdown menu.
|
||||
2. `(text, (elements,))` This element triggers a submenu with a recursive
|
||||
instance of `elements`.
|
||||
|
||||
The following (from `gui/demos/menui.py`) is complete apart from initial import
|
||||
statements. It illustrates a 3-level menu.
|
||||
```python
|
||||
mnu = (('Gas', cb_sm, (0,), ('Helium','Neon','Argon','Krypton','Xenon','Radon')),
|
||||
('Metal', cb_sm, (1,), ('Lithium', 'Sodium', 'Potassium','Rubidium','Caesium')),
|
||||
('View', cb, (2,)))
|
||||
Menu(wri, bgcolor=BLUE, textcolor=WHITE, args = mnu)
|
||||
class BaseScreen(Screen):
|
||||
|
||||
def __init__(self):
|
||||
def cb(button, n):
|
||||
print('Help callback', n)
|
||||
|
||||
def cb_sm(lb, n):
|
||||
print('Submenu callback', lb.value(), lb.textvalue(), n)
|
||||
|
||||
super().__init__()
|
||||
metals2 = (('Gold', cb_sm, (10,)),
|
||||
('Silver', cb_sm, (11,)),
|
||||
('Iron', cb_sm, (12,)),
|
||||
('Zinc', cb_sm, (13,)),
|
||||
('Copper', cb_sm, (14,))) # Level 3
|
||||
|
||||
gases = (('Helium', cb_sm, (0,)),
|
||||
('Neon', cb_sm, (1,)),
|
||||
('Argon', cb_sm, (2,)),
|
||||
('Krypton', cb_sm, (3,)),
|
||||
('Xenon', cb_sm, (4,)),
|
||||
('Radon', cb_sm, (5,))) # Level 2
|
||||
|
||||
metals = (('Lithium', cb_sm, (6,)),
|
||||
('Sodium', cb_sm, (7,)),
|
||||
('Potassium', cb_sm, (8,)),
|
||||
('Rubidium', cb_sm, (9,)),
|
||||
('More', metals2)) # Level 2
|
||||
|
||||
mnu = (('Gas', gases),
|
||||
('Metal', metals),
|
||||
('Help', cb, (2,))) # Top level 1
|
||||
|
||||
wri = CWriter(ssd, font, GREEN, BLACK, verbose=False)
|
||||
Menu(wri, bgcolor=BLUE, textcolor=WHITE, args = mnu)
|
||||
CloseButton(wri)
|
||||
|
||||
Screen.change(BaseScreen)
|
||||
```
|
||||
The code
|
||||
```python
|
||||
mnu = (('Gas', gases),
|
||||
('Metal',metals),
|
||||
('Help', cb, (2,)))
|
||||
```
|
||||
defines the top level, with the first two entries invoking submenus and the
|
||||
third running a callback `cb` with 2 as an arg.
|
||||
|
||||
This produces a second level menu with one entry ('More') invoking a third
|
||||
level (`metals2`):
|
||||
```python
|
||||
metals = (('Lithium', cb_sm, (6,)),
|
||||
('Sodium', cb_sm, (7,)),
|
||||
('Potassium', cb_sm, (8,)),
|
||||
('Rubidium', cb_sm, (9,)),
|
||||
('More', metals2))
|
||||
```
|
||||
The other entries all run `cb_sm` with a different arg. They could each run a
|
||||
different callback if the application required it.
|
||||
|
||||
###### [Contents](./README.md#0-contents)
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ display = None # Singleton instance
|
|||
ssd = None
|
||||
|
||||
gc.collect()
|
||||
__version__ = (0, 1, 2)
|
||||
__version__ = (0, 1, 3)
|
||||
|
||||
# Null function
|
||||
dolittle = lambda *_ : None
|
||||
|
|
|
@ -22,22 +22,29 @@ class BaseScreen(Screen):
|
|||
print('Submenu callback', lb.value(), lb.textvalue(), n)
|
||||
|
||||
super().__init__()
|
||||
mnu = (('Gas', (('Helium', cb_sm, (0,)),
|
||||
('Neon', cb_sm, (1,)),
|
||||
('Argon', cb_sm, (2,)),
|
||||
('Krypton', cb_sm, (3,)),
|
||||
('Xenon', cb_sm, (4,)),
|
||||
('Radon', cb_sm, (5,)))),
|
||||
('Metal',(('Lithium', cb_sm, (6,)),
|
||||
('Sodium', cb_sm, (7,)),
|
||||
('Potassium', cb_sm, (8,)),
|
||||
('Rubidium', cb_sm, (9,)),
|
||||
('More', (('Gold', cb_sm, (6,)),
|
||||
('Silver', cb_sm, (7,)),
|
||||
('Iron', cb_sm, (8,)),
|
||||
('Zinc', cb_sm, (9,)),
|
||||
('Copper', cb_sm, (10,)))))),
|
||||
metals2 = (('Gold', cb_sm, (6,)),
|
||||
('Silver', cb_sm, (7,)),
|
||||
('Iron', cb_sm, (8,)),
|
||||
('Zinc', cb_sm, (9,)),
|
||||
('Copper', cb_sm, (10,)))
|
||||
|
||||
gases = (('Helium', cb_sm, (0,)),
|
||||
('Neon', cb_sm, (1,)),
|
||||
('Argon', cb_sm, (2,)),
|
||||
('Krypton', cb_sm, (3,)),
|
||||
('Xenon', cb_sm, (4,)),
|
||||
('Radon', cb_sm, (5,)))
|
||||
|
||||
metals = (('Lithium', cb_sm, (6,)),
|
||||
('Sodium', cb_sm, (7,)),
|
||||
('Potassium', cb_sm, (8,)),
|
||||
('Rubidium', cb_sm, (9,)),
|
||||
('More', metals2))
|
||||
|
||||
mnu = (('Gas', gases),
|
||||
('Metal',metals),
|
||||
('Help', cb, (2,)))
|
||||
|
||||
wri = CWriter(ssd, font, GREEN, BLACK, verbose=False)
|
||||
Menu(wri, bgcolor=BLUE, textcolor=WHITE, args = mnu)
|
||||
CloseButton(wri)
|
||||
|
|
|
@ -99,5 +99,5 @@ class Dropdown(Widget):
|
|||
Screen.change(_ListDialog, args = args)
|
||||
|
||||
def _despatch(self, _): # Run the callback specified in elements
|
||||
x = self.els[self.value()]
|
||||
x = self.els[self()]
|
||||
x[1](self, *x[2])
|
||||
|
|
|
@ -27,26 +27,29 @@ class Listbox(Widget):
|
|||
fgcolor=None, bgcolor=None, bdcolor=False, fontcolor=None, select_color=DARKBLUE,
|
||||
callback=dolittle, args=[], also=0):
|
||||
|
||||
self.entry_height, height, textwidth = self.dimensions(writer, elements)
|
||||
e0 = elements[0]
|
||||
# Check whether elements specified as (str, str,...) or ([str, callback, args], [...)
|
||||
if isinstance(e0, tuple) or isinstance(e0, list):
|
||||
self.els = elements # Retain original for .despatch
|
||||
self.elements = [x[0] for x in elements] # Copy text component
|
||||
if callback is not dolittle:
|
||||
raise ValueError('Cannot specify callback.')
|
||||
self.cb = self.despatch
|
||||
else:
|
||||
self.cb = callback
|
||||
self.elements = elements
|
||||
if any(not isinstance(s, str) for s in self.elements):
|
||||
raise ValueError('Invalid elements arg.')
|
||||
self.entry_height, height, textwidth = self.dimensions(writer, self.elements)
|
||||
self.also = also
|
||||
if width is None:
|
||||
width = textwidth
|
||||
if not isinstance(value, int) or value >= len(elements):
|
||||
value = 0
|
||||
super().__init__(writer, row, col, height, width, fgcolor, bgcolor, bdcolor, value, True)
|
||||
self.cb = callback
|
||||
self.cb_args = args
|
||||
self.select_color = select_color
|
||||
self.fontcolor = fontcolor
|
||||
fail = False
|
||||
try:
|
||||
self.elements = [s for s in elements if type(s) is str]
|
||||
except:
|
||||
fail = True
|
||||
else:
|
||||
fail = len(self.elements) == 0
|
||||
if fail:
|
||||
raise ValueError('elements must be a list or tuple of one or more strings')
|
||||
self._value = value # No callback until user selects
|
||||
self.ev = value
|
||||
|
||||
|
@ -103,3 +106,7 @@ class Listbox(Widget):
|
|||
def leave(self):
|
||||
if (self.also & Listbox.ON_LEAVE) and self._value != self.ev:
|
||||
self.do_sel()
|
||||
|
||||
def despatch(self, _): # Run the callback specified in elements
|
||||
x = self.els[self()]
|
||||
x[1](self, *x[2])
|
||||
|
|
Ładowanie…
Reference in New Issue