Skip to content

Commit 68c2551

Browse files
committed
libs/m5ui: Add M5ButtonMatrix support.
Signed-off-by: lbuque <[email protected]>
1 parent 618da72 commit 68c2551

File tree

10 files changed

+1509
-0
lines changed

10 files changed

+1509
-0
lines changed

docs/en/m5ui/buttonmatrix.rst

Lines changed: 472 additions & 0 deletions
Large diffs are not rendered by default.

docs/en/m5ui/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ Classes
3636
arc.rst
3737
bar.rst
3838
button.rst
39+
buttonmatrix.rst
3940
calendar.rst
4041
canvas.rst
4142
checkbox.rst

docs/en/refs/m5ui.buttonmatrix.ref

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
.. |cores3_buttonmatrix_basic_example.png| image:: https://static-cdn.m5stack.com/mpy_docs/m5ui/button_matrix/cores3_buttonmatrix_basic_example.png
2+
3+
.. |align_to.png| image:: https://static-cdn.m5stack.com/mpy_docs/m5ui/button_matrix/align_to.png
4+
.. |clear_button_ctrl.png| image:: https://static-cdn.m5stack.com/mpy_docs/m5ui/button_matrix/clear_button_ctrl.png
5+
.. |clear_button_ctrl_all.png| image:: https://static-cdn.m5stack.com/mpy_docs/m5ui/button_matrix/clear_button_ctrl_all.png
6+
.. |event.png| image:: https://static-cdn.m5stack.com/mpy_docs/m5ui/button_matrix/event.png
7+
.. |get_button_text.png| image:: https://static-cdn.m5stack.com/mpy_docs/m5ui/button_matrix/get_button_text.png
8+
.. |get_height.png| image:: https://static-cdn.m5stack.com/mpy_docs/m5ui/button_matrix/get_height.png
9+
.. |get_selected_button.png| image:: https://static-cdn.m5stack.com/mpy_docs/m5ui/button_matrix/get_selected_button.png
10+
.. |get_textarea.png| image:: https://static-cdn.m5stack.com/mpy_docs/m5ui/button_matrix/get_textarea.png
11+
.. |get_width.png| image:: https://static-cdn.m5stack.com/mpy_docs/m5ui/button_matrix/get_width.png
12+
.. |set_button_ctrl.png| image:: https://static-cdn.m5stack.com/mpy_docs/m5ui/button_matrix/set_button_ctrl.png
13+
.. |set_button_ctrl_all.png| image:: https://static-cdn.m5stack.com/mpy_docs/m5ui/button_matrix/set_button_ctrl_all.png
14+
.. |set_button_width.png| image:: https://static-cdn.m5stack.com/mpy_docs/m5ui/button_matrix/set_button_width.png
15+
.. |set_flag.png| image:: https://static-cdn.m5stack.com/mpy_docs/m5ui/button_matrix/set_flag.png
16+
.. |set_height.png| image:: https://static-cdn.m5stack.com/mpy_docs/m5ui/button_matrix/set_height.png
17+
.. |set_one_checked.png| image:: https://static-cdn.m5stack.com/mpy_docs/m5ui/button_matrix/set_one_checked.png
18+
.. |set_pos.png| image:: https://static-cdn.m5stack.com/mpy_docs/m5ui/button_matrix/set_pos.png
19+
.. |set_size.png| image:: https://static-cdn.m5stack.com/mpy_docs/m5ui/button_matrix/set_size.png
20+
.. |set_state.png| image:: https://static-cdn.m5stack.com/mpy_docs/m5ui/button_matrix/set_state.png
21+
.. |set_textarea.png| image:: https://static-cdn.m5stack.com/mpy_docs/m5ui/button_matrix/set_textarea.png
22+
.. |set_width.png| image:: https://static-cdn.m5stack.com/mpy_docs/m5ui/button_matrix/set_width.png
23+
.. |set_x.png| image:: https://static-cdn.m5stack.com/mpy_docs/m5ui/button_matrix/set_x.png
24+
.. |set_y.png| image:: https://static-cdn.m5stack.com/mpy_docs/m5ui/button_matrix/set_y.png
25+
.. |toggle_button_ctrl.png| image:: https://static-cdn.m5stack.com/mpy_docs/m5ui/button_matrix/toggle_button_ctrl.png
26+
.. |toggle_flag.png| image:: https://static-cdn.m5stack.com/mpy_docs/m5ui/button_matrix/toggle_flag.png
27+
.. |toggle_state.png| image:: https://static-cdn.m5stack.com/mpy_docs/m5ui/button_matrix/toggle_state.png
28+
29+
.. |cores3_buttonmatrix_basic_example.m5f2| raw:: html
30+
31+
<a
32+
href="https://uiflow2.m5stack.com/?example=https://raw.githubusercontent.com/m5stack/uiflow-micropython/develop/examples/m5ui/buttonmatrix/cores3_buttonmatrix_basic_example.m5f2"
33+
target="_blank"
34+
>
35+
cores3_buttonmatrix_basic_example.m5f2
36+
</a>

docs/locales/zh_CN/LC_MESSAGES/m5ui/buttonmatrix.po

Lines changed: 708 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"version":"V2.0","versionNumber":"V2.3.3","type":"core2","components":[{"name":"page0","type":"lvgl_page","layer":0,"screenId":"builtin","screenName":"","id":"w#OUTx5g2=WUes=i","createTime":1755596357827,"backgroundColor":"#ffffff","isLVGL":true,"isSelected":true},{"name":"textarea0","type":"lvgl_textarea","layer":1,"screenId":"builtin","screenName":"","id":"h*==WkzQoUKv*z$1","createTime":1755596420739,"x":24,"y":15,"width":150,"height":70,"color":"#212121","borderColor":"#e0e0e0","backgroundColor":"#ffffff","text":"","placeholder":"Placeholder...","font":"lv.font_montserrat_14","pageId":"w#OUTx5g2=WUes=i","isLVGL":true,"isSelected":false},{"name":"buttonmatrix0","type":"lvgl_buttonmatrix","layer":2,"screenId":"builtin","screenName":"","id":"n+@+k6w_5RL-ZEJs","createTime":1755596423752,"x":25,"y":100,"width":260,"height":130,"keyList":[["0","1","2","4"],["5","6","7","8","9"]],"targetTextarea":"h*==WkzQoUKv*z$1","pageId":"w#OUTx5g2=WUes=i","isLVGL":true,"isSelected":false},{"name":"label0","type":"lvgl_label","layer":3,"screenId":"builtin","screenName":"","id":"vWXox&Pi7i#P@4az","createTime":1755656081220,"x":189,"y":15,"color":"#c9c9c9","backgroundColor":"#ffffff","bg_opacity":0,"text":"last key:","font":"lv.font_montserrat_14","pageId":"w#OUTx5g2=WUes=i","isLVGL":true,"isSelected":false,"width":56,"height":15},{"name":"label1","type":"lvgl_label","layer":4,"screenId":"builtin","screenName":"","id":"w`*DcO^huctVu6Lc","createTime":1755656142705,"x":203,"y":42,"color":"#000000","backgroundColor":"#ffffff","bg_opacity":0,"text":"label1","font":"lv.font_montserrat_24","pageId":"w#OUTx5g2=WUes=i","isLVGL":true,"isSelected":false,"width":67,"height":27}],"resources":[{"hardware":["hardware_button","hardware_pin_button","imu","speaker","touch","mic","sdcard"]}],"units":[],"hats":[],"bases":[],"i2cs":[],"blockly":"<block type=\"basic_on_setup\" id=\"setup_block\" deletable=\"false\" x=\"50\" y=\"50\"><mutation isBegin=\"true\"></mutation><field name=\"UPDATEOP\">true</field><statement name=\"FUNC\"><block type=\"system_m5_begin\" id=\"n/EL5mgZYup*D.$s))da\"><next><block type=\"lvgl_page_screen_load\" id=\"yY9-+uFZoop$:Epo?PsR\"><field name=\"NAME\">page0</field></block></next></block></statement></block><block type=\"basic_on_loop\" id=\"loop_block\" deletable=\"false\" x=\"50\" y=\"210\"><mutation isUpdate=\"true\"></mutation><field name=\"UPDATEOP\">true</field><statement name=\"FUNC\"><block type=\"system_m5_update\" id=\"Ya^:riycUT^%I6hX;w.3\"></block></statement></block><block type=\"lvgl_buttonmatrix_event\" id=\"Px*p+V$6OyV)?+8cy+f`\" x=\"70\" y=\"330\"><field name=\"NAME\">buttonmatrix0</field><statement name=\"FUNC\"><block type=\"lvgl_label_set_text\" id=\"r5KxK(]/auvL1B#mp9V$\"><field name=\"NAME\">label1</field><value name=\"VALUE\"><shadow type=\"text\" id=\"D,*|?v[]w`.?VIAt;9Qf\"><field name=\"TEXT\">hello M5</field></shadow><block type=\"lvgl_buttonmatrix_get_button_text\" id=\"KxJpC?UCsx9bUq*M^G|j\"><field name=\"NAME\">buttonmatrix0</field><value name=\"VALUE\"><shadow type=\"math_number\" id=\"OP6E6@2~fn5us6g4TPj?\"><mutation max=\"Infinity\" min=\"0\" precision=\"0\"></mutation><field name=\"NUM\">0</field></shadow><block type=\"lvgl_buttonmatrix_get_selected_button\" id=\"ZWX(unyQ-A%2jR]q]p)D\"><field name=\"NAME\">buttonmatrix0</field></block></value></block></value></block></statement></block>","screen":[{"simulationName":"Built-in","type":"builtin","width":320,"height":240,"scale":0.77,"screenName":"","blockId":"","screenColorType":0,"rotation":1,"id":"builtin","createTime":1755596357824}],"logicWhenNum":0,"customList":[]}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
import os, sys, io
6+
import M5
7+
from M5 import *
8+
import m5ui
9+
import lvgl as lv
10+
11+
12+
page0 = None
13+
textarea0 = None
14+
buttonmatrix0 = None
15+
label0 = None
16+
label1 = None
17+
18+
19+
def buttonmatrix0_value_changed_event(event_struct):
20+
global page0, textarea0, buttonmatrix0, label0, label1
21+
label1.set_text(str(buttonmatrix0.get_button_text(buttonmatrix0.get_selected_button())))
22+
23+
24+
def buttonmatrix0_event_handler(event_struct):
25+
global page0, textarea0, buttonmatrix0, label0, label1
26+
event = event_struct.code
27+
if event == lv.EVENT.VALUE_CHANGED and True:
28+
buttonmatrix0_value_changed_event(event_struct)
29+
return
30+
31+
32+
def setup():
33+
global page0, textarea0, buttonmatrix0, label0, label1
34+
35+
M5.begin()
36+
Widgets.setRotation(1)
37+
m5ui.init()
38+
page0 = m5ui.M5Page(bg_c=0xFFFFFF)
39+
textarea0 = m5ui.M5TextArea(
40+
text="",
41+
placeholder="Placeholder...",
42+
x=24,
43+
y=15,
44+
w=150,
45+
h=70,
46+
font=lv.font_montserrat_14,
47+
bg_c=0xFFFFFF,
48+
border_c=0xE0E0E0,
49+
text_c=0x212121,
50+
parent=page0,
51+
)
52+
buttonmatrix0 = m5ui.M5ButtonMatrix(
53+
["0", "1", "2", "4", "\n", "5", "6", "7", "8", "9"],
54+
x=25,
55+
y=100,
56+
w=260,
57+
h=130,
58+
target_textarea=textarea0,
59+
parent=page0,
60+
)
61+
label0 = m5ui.M5Label(
62+
"last key:",
63+
x=189,
64+
y=15,
65+
text_c=0xC9C9C9,
66+
bg_c=0xFFFFFF,
67+
bg_opa=0,
68+
font=lv.font_montserrat_14,
69+
parent=page0,
70+
)
71+
label1 = m5ui.M5Label(
72+
"label1",
73+
x=203,
74+
y=42,
75+
text_c=0x000000,
76+
bg_c=0xFFFFFF,
77+
bg_opa=0,
78+
font=lv.font_montserrat_24,
79+
parent=page0,
80+
)
81+
82+
buttonmatrix0.add_event_cb(buttonmatrix0_event_handler, lv.EVENT.ALL, None)
83+
84+
page0.screen_load()
85+
86+
87+
def loop():
88+
global page0, textarea0, buttonmatrix0, label0, label1
89+
M5.update()
90+
91+
92+
if __name__ == "__main__":
93+
try:
94+
setup()
95+
while True:
96+
loop()
97+
except (Exception, KeyboardInterrupt) as e:
98+
try:
99+
m5ui.deinit()
100+
from utility import print_error_msg
101+
102+
print_error_msg(e)
103+
except ImportError:
104+
print("please update to latest firmware")

m5stack/libs/m5ui/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"M5Arc": "arc",
99
"M5Bar": "bar",
1010
"M5Button": "button",
11+
"M5ButtonMatrix": "buttonmatrix",
1112
"M5Calendar": "calendar",
1213
"M5Canvas": "canvas",
1314
"M5Checkbox": "checkbox",

m5stack/libs/m5ui/buttonmatrix.py

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
from .base import M5Base
6+
import lvgl as lv
7+
8+
9+
class M5ButtonMatrix(lv.buttonmatrix):
10+
"""Create a button matrix object.
11+
12+
:param list map: A list of button labels. Use "\\\\n" to create a new row.
13+
:param int x: The x position of the button matrix.
14+
:param int y: The y position of the button matrix.
15+
:param int w: The width of the button matrix.
16+
:param int h: The height of the button matrix.
17+
:param m5ui.M5TextArea target_textarea: A M5TextArea to display the button text when a button is pressed.
18+
:param lv.obj parent: The parent object to attach the button matrix to.
19+
20+
UiFlow2 Code Block:
21+
22+
None
23+
24+
MicroPython Code Block:
25+
26+
.. code-block:: python
27+
28+
import m5ui
29+
import lvgl as lv
30+
31+
m5ui.init()
32+
page0 = m5ui.M5Page()
33+
page0.screen_load()
34+
textarea0 = m5ui.M5TextArea(x=10, y=10, w=200, h=60, parent=page0)
35+
buttonmatrix_0 = m5ui.M5ButtonMatrix(
36+
["0", "1", "2", "3", "4","\\n", "5", "6", "7", "8", "9",],
37+
x=10, y=80, w=260, h=130,
38+
target_textarea=textarea0,
39+
parent=page0
40+
)
41+
42+
"""
43+
44+
def __init__(
45+
self,
46+
map,
47+
x=0,
48+
y=0,
49+
w=260,
50+
h=130,
51+
target_textarea=None,
52+
parent=None,
53+
):
54+
super().__init__(parent)
55+
self.set_map(map)
56+
self.set_pos(x, y)
57+
self.set_size(w, h)
58+
59+
self.add_event_cb(self.value_changed_event, lv.EVENT.VALUE_CHANGED, None)
60+
self.textarea = target_textarea # To hold a reference to a M5TextArea if set
61+
62+
def value_changed_event(self, event_struct):
63+
btn_id = self.get_selected_button()
64+
if self.textarea:
65+
self.textarea.add_text(self.get_button_text(btn_id))
66+
67+
def toggle_button_ctrl(self, btn_id, ctrl):
68+
"""Toggle control flags for a specific button.
69+
70+
:param int btn_id: The button ID to toggle control flags for.
71+
:param int ctrl: The control flags to toggle.
72+
73+
UiFlow2 Code Block:
74+
75+
|toggle_button_ctrl.png|
76+
77+
MicroPython Code Block:
78+
79+
.. code-block:: python
80+
81+
buttonmatrix_0.toggle_button_ctrl(0, lv.buttonmatrix.CTRL.HIDDEN)
82+
"""
83+
if self.has_button_ctrl(btn_id, ctrl):
84+
self.clear_button_ctrl(btn_id, ctrl)
85+
else:
86+
self.set_button_ctrl(btn_id, ctrl)
87+
88+
def set_textarea(self, textarea):
89+
"""Set a M5TextArea to display button text.
90+
91+
:param m5ui.M5TextArea textarea: The M5TextArea to set.
92+
93+
UiFlow2 Code Block:
94+
95+
|set_textarea.png|
96+
97+
MicroPython Code Block:
98+
99+
.. code-block:: python
100+
101+
buttonmatrix_0.set_textarea(textarea0)
102+
"""
103+
self.textarea = textarea
104+
105+
def get_textarea(self):
106+
"""Get the currently set M5TextArea.
107+
108+
:return: The M5TextArea currently set for the button matrix.
109+
:rtype: m5ui.M5TextArea
110+
111+
UiFlow2 Code Block:
112+
113+
|get_textarea.png|
114+
115+
MicroPython Code Block:
116+
117+
.. code-block:: python
118+
119+
textarea = buttonmatrix_0.get_textarea()
120+
"""
121+
return self.textarea
122+
123+
def __getattr__(self, name):
124+
if hasattr(M5Base, name):
125+
method = getattr(M5Base, name)
126+
bound_method = lambda *args, **kwargs: method(self, *args, **kwargs)
127+
setattr(self, name, bound_method)
128+
return bound_method
129+
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")

m5stack/libs/m5ui/manifest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"bar.py",
1111
"base.py",
1212
"button.py",
13+
"buttonmatrix.py",
1314
"calendar.py",
1415
"canvas.py",
1516
"checkbox.py",

tests/m5ui/test_buttonmatrix.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
import lvgl as lv
6+
import sys
7+
8+
sys.path.append("../../m5stack/libs")
9+
import m5ui
10+
import unittest
11+
12+
13+
class Test(unittest.TestCase):
14+
def __init__(self) -> None:
15+
super().__init__()
16+
m5ui.init()
17+
page0 = m5ui.M5Page()
18+
self.textarea0 = m5ui.M5TextArea(x=10, y=10, w=200, h=60, parent=page0)
19+
self.buttonmatrix0 = m5ui.M5ButtonMatrix(
20+
["0", "1", "2", "4", "\n", "5", "6", "7", "8", "9"],
21+
x=25,
22+
y=100,
23+
w=260,
24+
h=130,
25+
target_textarea=self.textarea0,
26+
parent=page0,
27+
)
28+
page0.screen_load()
29+
30+
def test_get_textarea(self):
31+
self.buttonmatrix0.set_textarea(None)
32+
self.assertIsNone(self.buttonmatrix0.textarea)
33+
self.buttonmatrix0.set_textarea(self.textarea0)
34+
self.assertIsInstance(self.buttonmatrix0.textarea, m5ui.M5TextArea)
35+
36+
def test_toggle_button_ctrl(self):
37+
self.buttonmatrix0.toggle_button_ctrl(0, lv.buttonmatrix.CTRL.NO_REPEAT)
38+
self.assertTrue(self.buttonmatrix0.has_button_ctrl(0, lv.buttonmatrix.CTRL.NO_REPEAT))
39+
40+
def test_text_area_update(self):
41+
self.textarea0.set_text("")
42+
self.buttonmatrix0.set_selected_button(0)
43+
self.buttonmatrix0.send_event(lv.EVENT.VALUE_CHANGED, None)
44+
self.assertEqual(self.textarea0.get_text(), "0")
45+
46+
self.buttonmatrix0.set_selected_button(1)
47+
self.buttonmatrix0.send_event(lv.EVENT.VALUE_CHANGED, None)
48+
self.assertEqual(self.textarea0.get_text(), "01")
49+
50+
self.buttonmatrix0.set_selected_button(2)
51+
self.buttonmatrix0.send_event(lv.EVENT.VALUE_CHANGED, None)
52+
self.assertEqual(self.textarea0.get_text(), "012")
53+
54+
55+
if __name__ == "__main__":
56+
unittest.main()

0 commit comments

Comments
 (0)