Menu für ESP32, uMenu
umenu.py ist eine einfache MicroPython Bibliothek. Sie ermöglicht verschachtelte, multifunktio-nale Menüs mit kundenspezifischen Menüeinträgen, für die individuelle Funktionsaufrufe definiert werden können.
Das Original finden Sie hier: https://github.com/plugowski/umenu/blob/master/README.md
Die Rechte liegen bei Paweł Ługowski, siehe Copyright (C) 2021 am Ende dieses Artikels
Installation
Zur Installation muß man lediglich die Datei umenu.py auf das MicroPython-Board (z.B: Entwicklungskit mit dem ESP32) kopieren, z.B: in das Wurzelverzeichnis oder in einen /lib-Ordner.
Nur für Experten
Es gibt es eine elegantere Alternative: Sollte sich jemand MicroPython selbst kompilieren, dann ist es das Beste umenu.py gleich miteinzubinden - hierzu kopiert man die Quelldatei umenu.py in den Ordner ports/<board>/modules, kompiliert und flasht dann mit dieser Binärdatei wie gewohnt MicroPython, das jetzt umenu enthält, auf sein Experimentierboard.
Benutzung
Für ein einfaches Menü braucht man zuallererst ein Display, das man wie gewohnt erzeugt
import ssd1306
from machine import Pin, I2C
i2c = I2C(1, scl=Pin(22), sda=Pin(21))
display = ssd1306.SSD1306_I2C(128, 64, i2c)
Nach dem Import von umenu erzeugt man mit folgenden Angaben ein menu-Object :
- das gerade eben erzeugte Display
- wieviele Zeilen ein Bildschirm haben soll
- wie hoch eine Zeile sein soll, hier 10 Pixel
from umenu import *
menu = Menu(display, 5, 10)
auf ein SSD1306 mit 128x64 Pixeln passen gut 4 Zeilen a 12 Pixel Höhe oder 5 Zeilen a 10 Pixel Höhe
- Dann baut man den Menüscreen mit Titel am oberen Rand, aber noch ohne Einträge
- und zeigt ihn mit draw an
menu.set_screen(MenuScreen('Titel'))
menu.draw()
Und jetzt alle Basisschritte auf einmal:
import ssd1306
from machine import Pin, I2C
from umenu import *
i2c = I2C(1, scl=Pin(22), sda=Pin(21))
display = ssd1306.SSD1306_I2C(128, 64, i2c)
menu = Menu(display, 5, 10)
menu.set_screen(MenuScreen('Mein Menü'))
menu.draw()
Ergebnis:
Die Bibliothek erlaubt es sowohl Displays/Screens verschachtelt hinzuzufügen als auch eigene Displays/Screens mit eigener Logik auszustatten.
menu.current_screen.add(SubMenuItem('neuerMenüpunkt'))
menu.current_screen.add(SubMenuItem('test5'))
menu.draw()
Navigation im Menü
Um sich im Menü zu bewegen muß man Methoden der Klasse Menu auslösen
Methode | Effekt |
---|---|
Menu.move(Richtung:1|-1) |
|
Menu.click() |
|
Menu.reset() |
|
Menu.draw() |
|
Um sich im Menü zu bewegen und auszuwählen|aktivieren muß man jeweils eine der Methoden der Klasse Menu auslösen.
Menü Punkte
Für die Gestaltung von Menü's enthält umenu.py als Basisausstattung schon einige Menu-Objecte. Das sind Objekte mit denen Menüpunkte erstellt werden können, die bestimmte Effekte auslösen. Weitere Menu-Objekte können mithilfe von CustomItem's programmiert werden.
Item | Effekt | Methoden |
---|---|---|
MenuItem | übergeordnete Klasse von der alle weiteren MenuItems erben | __init__, visible, click, get_decorator, callback, _check_callable, _call_callable |
SubMenuItem |
Erzeugt ein weiteres Untermenü, das wiederum eine Liste mit Punkten|Untermenü's haben kann. |
__init__, click, add, reset erbt von MenuItem: visible, get_decorator, callback, _check_callable, _call_callable |
EnumItem | Selected List, here you can define list which will be displayed after click, and on select that element will be passed to callback |
__init__, choose, click, _get_index_by_key, _get_element, _set_decorator erbt von SubMenuItem: add, reset erbt von MenuItem: visible, get_decorator, callback, _check_callable, _call_callable |
InfoItem |
Ein Info-Objekt zeigt lediglich einen vorgegbenen Text, löst ansonsten aber keine Aktion aus bzw. hat keinen Effekt |
__init__, click erbt von MenuItem: visible, get_decorator, callback, _check_callable, _call_callable |
CallbackItem | Item on menu which is able to trigger any callback specified in argument. After callback, parent screen is returned, but can be disabled by setting return_parent to False |
__init__, click erbt von MenuItem: visible, get_decorator, callback, _check_callable, _call_callable |
ValueItem | Widget to adjust values, by incrementing or decrementing by specified amount. | |
ToggleItem |
Item to handle toggles, like on/off actions. You can specify state, and callback which will be called to change state. ToggleItem is an extension for CallbackItem |
|
ConfirmItem | Implementation of CallbackItem with prompt screen before calling custom function. Can be used when we need confirmation for specific action. If user select "no" option, callback won't be triggered. | |
CustomItem | Eine abstrakte Klasse um mit Kundenlogik überschrieben zu werden. Ganz unten gibt es hierzu ein Beispiel. Zusätzlich noch bei ValueItem nachschauen, denn dessen Realisierung erweitert ein CustomItem |
SubMenuItem
Creates new sub-menu with list of items
Arguments (common for all MenuItems):
- name - to define name visible on screen
- decorator - decorator is a text or symbol aligned to right side of screen, can be also callable which return proper string. Default: >
- visible - determine if current section should be visible (read more in Visibility section)
InfoItem
Dummy Item, shows only specified text, with no action.
Arguments:
See SubMenuItem, default decorator here is empty.
CallbackItem
Item on menu which is able to trigger any callback specified in argument. After callback, parent screen is returned, but can be disabled by setting return_parent to False.
Specific Arguments:
- callback - callable to trigger on click on item (more in section Callback)
- return_parent - to determine if parent should be returned or not
EnumItem
Selected List, here you can define list which will be displayed after click, and on select that element will be passed to callback
Specific Arguments:
- items - list of items, can be also list of dicts {'value': 'xxx', 'name': 'Fance name'}, where name will be displayed on screen and value passed to callback
- callback - callable called after selecting specific position
- selected - define which element should be selected (index or dict key)
ValueItem
Widget to adjust values, by incrementing or decrementing by specified amount.
Specific Arguments:
- value_reader - callable to read current value as start to adjust
- min_v - minimum value for range
- max_v - maximum value for range
- step - step to increment / decrement
- callback - callback called on every change of value, value will be passed as last argument
CustomItem
Abstract class to override by custom logic, see example below. Also you can check ValueItem implementation which extends CustomItem.
ToggleItem
Item to handle toggles, like on/off actions. You can specify
state, and callback which will be called to change state. ToggleItem is an extension for CallbackItem
Specific Arguments:
- state_callback - callback to check current state
- change_callback - callback to toggle current state (True/False)
ConfirmItem
Implementation of CallbackItem with prompt screen before calling custom function. Can be used when we need confirmation for specific action. If user select "no" option, callback won't be triggered.
Specific Arguments:
- question - can be None, then question "Are you sure?" will be visible
- answers - tuple for yes and no, it'll simply override default tuple ('yes', 'no')
Example menu
menu.set_screen(MenuScreen('Main Menu')
.add(SubMenuItem('WiFi')
.add(ToggleItem('Activate', wifi.get_status, wifi.activate)))
.add(SubMenuItem('Lights')
.add(ToggleItem('Headlight', (config.get_status, 1), (config.toggle, 1)))
.add(ToggleItem('Backlight', (config.get_status, 2), (config.toggle, 2)))
.add(SubMenuItem('Main Info')
.add(InfoItem('Status:', 'ok'))
.add(InfoItem('Temp:', '45.1')))
)
menu.draw()
Callbacks
In all MenuItems callbacks can be:
single callable if no parameters should be passed,
CallbackItem('Print it!', (print, 'hello there'))
# will print: hello there > like print('hello there')
or tuple where first element is callable, and second is a single arg or tuple with *args.
Zum Beispiel:
CallbackItem('Print it!', (print, (1, 2, 3)))
# will print: 1 2 3 > like print(*args) where *args are taken from tuple
Visibility
Every item can be hidden separately by setting named argument visible to False or by passing callable to check conditions if element should be vissible. Callable should return True or False.
CustomItem
To create your own menu logic, you can extend abstract class CustomItem class and implement at least draw() and select() function.
draw() is called once you click on specifiv CustomItem position, so basically it can do anything you want, what more that object has included display, so you can simply draw anything on OLED using driver's methods.
Example usage of CustomItem, to draw some status page:
class DrawCustomScreen(CustomItem):
def __init__(self, name):
super().__init__(name)
def select(self):
return self.parent # this is needed to go back to pre-
# vious view when SET button is pushed
def draw(self):
self.display.fill(0)
self.display.rect(0, 0, self.display.width, self.display.height, 1)
self.display.text('SHOW SOME TEXT', 0, 10, 1)
self.display.hline(0, 32, self.display.width, 1)
self.display.show()
menu.add_screen(MenuScreen('Main Menu')
.add(DrawCustomScreen('Text in frame'))
)
See examples/rotary_encoder_menu.py.
License
Copyright (C) 2021, Paweł Ługowski
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.