Verified Commit 13d9cb5d authored by Rahix's avatar Rahix
Browse files

feat(pycardium): Add simple_menu

Signed-off-by: Rahix's avatarRahix <>
parent db7311c3
......@@ -87,7 +87,14 @@ html_context = {
# }}}
# -- Options for Auto-Doc ---------------------------------------------------- {{{
autodoc_mock_imports = ["sys_display", "sys_leds", "ucollections", "urandom", "utime"]
autodoc_mock_imports = [
autodoc_member_order = "bysource"
# }}}
......@@ -31,6 +31,7 @@ Last but not least, if you want to start hacking the lower-level firmware, the
``simple_menu`` - Draw a Menu
To allow quickly hacking some scripts, Pycardium has a small library for
displaying menus. You can use it like this:
.. code-block:: python
import color
import simple_menu
class MyMenu(simple_menu.Menu):
color_1 = color.CAMPGREEN
color_2 = color.CAMPGREEN_DARK
def on_select(self, name, index):
print("{!r} was selected!".format(name))
if __name__ == "__main__":
MyMenu(["foo", "bar", "baz"]).run()
.. autoclass:: simple_menu.Menu
.. autofunction:: simple_menu.button_events
......@@ -5,6 +5,7 @@ python_modules = files(
# MicroPython Standard-Library
import buttons
import color
import display
def button_events():
Iterate over button presses (event-loop).
This is just a helper function used internally by the menu. But you can of
course use it for your own scripts as well. It works like this:
.. code-block:: python
import simple_menu, buttons
for ev in simple_menu.button_events():
if ev == buttons.BOTTOM_LEFT:
# Left
elif ev == buttons.BOTTOM_RIGHT:
# Right
elif ev == buttons.TOP_RIGHT:
# Select
yield 0
v = | buttons.BOTTOM_RIGHT | buttons.TOP_RIGHT)
button_pressed = True if v != 0 else False
while True:
v = | buttons.BOTTOM_RIGHT | buttons.TOP_RIGHT)
if v == 0:
button_pressed = False
if not button_pressed and v & buttons.BOTTOM_LEFT != 0:
button_pressed = True
yield buttons.BOTTOM_LEFT
if not button_pressed and v & buttons.BOTTOM_RIGHT != 0:
button_pressed = True
yield buttons.BOTTOM_RIGHT
if not button_pressed and v & buttons.TOP_RIGHT != 0:
button_pressed = True
yield buttons.TOP_RIGHT
class Menu:
A simple menu for card10.
This menu class is supposed to be inherited from to create a menu as shown
in the example above.
To instanciate the menu, pass a list of entries to the constructor:
.. code-block:: python
m = Menu(os.listdir("."))
Then, call :py:meth:`` to start the event loop.
color_1 = color.CHAOSBLUE
"""Background color A."""
color_2 = color.CHAOSBLUE_DARK
"""Background color B."""
color_text = color.WHITE
"""Text color."""
color_sel = color.COMMYELLOW
"""Color of the selector."""
def on_scroll(self, item, index):
Hook when the selector scrolls to a new item.
This hook is run everytime a scroll-up or scroll-down is performed.
Overwrite this function in your own menus if you want to do some action
every time a new item is scrolled onto.
:param item: The item which the selector now points to.
:param int index: Index into the ``entries`` list of the ``item``.
def on_select(self, item, index):
Hook when an item as selected.
The given ``index`` was selected with a SELECT button press. Overwrite
this function in your menu to perform an action on select.
:param item: The item which was selected.
:param int index: Index into the ``entries`` list of the ``item``.
def __init__(self, entries):
if len(entries) == 0:
raise ValueError("at least one entry is required")
self.entries = entries
self.idx = 0
self.disp =
def entry2name(self, value):
Convert an entry object to a string representation.
Overwrite this functio if your menu items are not plain strings.
.. code-block:: python
class MyMenu(simple_menu.Menu):
def entry2name(self, value):
return value[0]
[("a", 123), ("b", 321)]
return str(value)
def draw_entry(self, value, index, offset):
Draw a single entry.
This is an internal function; you can override it for customized behavior.
:param value: The value for this entry. Use this to identify
different entries.
:param int index: A unique index per entry. Stable for a certain entry,
but **not** an index into ``entries``.
:param int offset: Y-offset for this entry.
" " + self.entry2name(value) + " " * 9,
bg=self.color_1 if index % 2 == 0 else self.color_2,
def draw_menu(self, offset=0):
Draw the menu.
You'll probably never need to call this yourself; it is called
automatially in the event loop (:py:meth:``).
# Wrap around the list and draw entries from idx - 3 to idx + 4
for y, i in enumerate(
range(len(self.entries) + self.idx - 3, len(self.entries) + self.idx + 4)
self.entries[i % len(self.entries)], i, offset + y * 20 - 40
self.disp.line(4, 22, 11, 29, col=self.color_sel, size=2)
self.disp.line(3, 37, 11, 29, col=self.color_sel, size=2)
def run(self):
"""Start the event-loop."""
for ev in button_events():
if ev == buttons.BOTTOM_RIGHT:
self.idx = (self.idx + 1) % len(self.entries)
self.on_scroll(self.entries[self.idx], self.idx)
elif ev == buttons.BOTTOM_LEFT:
self.idx = (self.idx + len(self.entries) - 1) % len(self.entries)
self.on_scroll(self.entries[self.idx], self.idx)
elif ev == buttons.TOP_RIGHT:
self.on_select(self.entries[self.idx], self.idx)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment