10000 Fixes #202 · OneKeyHQ/lv_binding_micropython@efd296f · GitHub
[go: up one dir, main page]

Skip to content

Commit efd296f

Browse files
committed
Fixes lvgl#202
Event Loop: Add optional exception sink to catch exception thrown from task_handler. These exceptions are thrown in LVGL event handlers and cannont be handled directly. Instead they need to be recorded and raised later. By default, just print the exception and carry on. run_test.py: Use the exception sink to detect exceptions thrown in event handler context, record them and re-throw them later on the main context. Also, execute the input script with custom globals Update LVGL
1 parent 8d890d8 commit efd296f

File tree

6 files changed

+119
-97
lines changed

6 files changed

+119
-97
lines changed

examples/custom_widget_example.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ def event_cb(self, lv_cls, e):
8484

8585
if code == lv.EVENT.DRAW_MAIN:
8686
# Draw the widget
87-
clip_area = lv.area_t.__cast__(e.get_param())
88-
self.draw(obj, clip_area)
87+
draw_ctx = e.get_draw_ctx()
88+
self.draw(obj, draw_ctx)
8989
elif code in [
9090
lv.EVENT.STYLE_CHANGED,
9191
lv.EVENT.VALUE_CHANGED,
@@ -115,13 +115,13 @@ def calc(self, obj):
115115

116116
obj.valid = True
117117

118-
def draw(self, obj, clip_area):
118+
def draw(self, obj, draw_ctx):
119119
# If object invalidated, recalculate its parameters
120120
if not obj.valid:
121121
self.calc(obj)
122122

123123
# Draw the custom widget
124-
lv.draw_polygon(obj.points, len(obj.points), clip_area, obj.draw_desc)
124+
draw_ctx.polygon(obj.draw_desc, obj.points, len(obj.points))
125125

126126
##############################################################################
127127
# A Python class to wrap the LVGL custom widget

lib/display_driver_utils.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,16 @@
99

1010
class driver:
1111

12-
def __init__(self,width=420,height=320,orientation=ORIENT_PORTRAIT):
12+
def __init__(self,width=420,height=320,orientation=ORIENT_PORTRAIT, asynchronous=False, exception_sink=None):
1313

1414
if not lv.is_initialized():
1515
lv.init()
1616

1717
self.width = width
1818
self.height = height
1919
self.orientation = orientation
20+
self.asynchronous = asynchronous
21+
self.exception_sink = exception_sink
2022
self.disp = None
2123
self.touch = None
2224
self.type = None
@@ -27,7 +29,7 @@ def init_gui_SDL(self):
2729

2830
import SDL
2931
SDL.init(w=self.width, h=self.height, auto_refresh=False)
30-
self.event_loop = lv_utils.event_loop(refresh_cb = SDL.refresh)
32+
self.event_loop = lv_utils.event_loop(refresh_cb = SDL.refresh, asynchronous=self.asynchronous, exception_sink=self.exception_sink)
3133

3234
# Register SDL display driver.
3335

@@ -61,6 +63,8 @@ def init_gui_ili9341(self):
6163
from xpt2046 import xpt2046
6264
import espidf as esp
6365

66+
self.event_loop = lv_utils.event_loop(asynchronous=self.asynchronous, exception_sink=self.exception_sink)
67+
6468
if self.orientation == ORIENT_PORTRAIT:
6569
print ("Running the ili9341 lvgl version in portait mode")
6670

lib/lv_utils.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ class event_loop():
6565

6666
_current_instance = None
6767

68-
def __init__(self, freq=25, timer_id=default_timer_id, max_scheduled=2, refresh_cb=None, asynchronous=False):
68+
def __init__(self, freq=25, timer_id=default_timer_id, max_scheduled=2, refresh_cb=None, asynchronous=False, exception_sink=None):
6969
if self.is_running():
7070
raise RuntimeError("Event loop is already running!")
7171

@@ -76,6 +76,7 @@ def __init__(self, freq=25, timer_id=default_timer_id, max_scheduled=2, refresh_
7676

7777
self.delay = 1000 // freq
7878
self.refresh_cb = refresh_cb
79+
self.exception_sink = exception_sink if exception_sink else self.default_exception_sink
7980

8081
self.asynchronous = asynchronous
8182
if self.asynchronous:
@@ -114,9 +115,13 @@ def current_instance():
114115
return event_loop._current_instance
115116

116117
def task_handler(self, _):
117-
lv.task_handler()
118-
if self.refresh_cb: self.refresh_cb()
119-
self.scheduled -= 1
118+
try:
119+
lv.task_handler()
120+
if self.refresh_cb: self.refresh_cb()
121+
self.scheduled -= 1
122+
except Exception as e:
123+
if self.exception_sink:
124+
self.exception_sink(e)
120125

121126
def timer_cb(self, t):
122127
# Can be called in Interrupt context
@@ -141,4 +146,11 @@ async def async_timer(self):
141146
await uasyncio.sleep_ms(self.delay)
142147
lv.tick_inc(self.delay)
143148
self.refresh_event.set()
144-
149+
150+
151+
def default_exception_sink(self, e):
152+
exc = usys.exc_info()
153+
print('ERROR! %s: %s\n%s' % (
154+
exc[0].__name__,
155+
exc[1],
156+
exc[2] if exc[2] else ''))

lv_conf.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
#define LV_MEM_CUSTOM 1
5454
#if LV_MEM_CUSTOM == 0
5555
/*Size of the memory available for `lv_mem_alloc()` in bytes (>= 2kB)*/
56-
#define LV_MEM_SIZE (32U * 1024U) /*[bytes]*/
56+
#define LV_MEM_SIZE (48U * 1024U) /*[bytes]*/
5757

5858
/*Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too.*/
5959
#define LV_MEM_ADR 0 /*0: unused*/
@@ -579,31 +579,31 @@
579579
#define LV_USE_FS_STDIO 0
580580
#if LV_USE_FS_STDIO
581581
#define LV_FS_STDIO_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
582-
#define LV_FS_STDIO_PATH "" /*Set the working directory. File/directory paths ill be appended to it.*/
582+
#define LV_FS_STDIO_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/
583583
#define LV_FS_STDIO_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/
584584
#endif
585585

586586
/*API for open, read, etc*/
587587
#define LV_USE_FS_POSIX 0
588588
#if LV_USE_FS_POSIX
589589
#define LV_FS_POSIX_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
590-
#define LV_FS_POSIX_PATH "" /*Set the working directory. File/directory paths ill be appended to it.*/
590+
#define LV_FS_POSIX_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/
591591
#define LV_FS_POSIX_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/
592592
#endif
593593

594594
/*API for CreateFile, ReadFile, etc*/
595595
#define LV_USE_FS_WIN32 0
596596
#if LV_USE_FS_WIN32
597597
#define LV_FS_WIN32_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
598-
#define LV_FS_WIN32_PATH "" /*Set the working directory. File/directory path ill be appended to it.*/
598+
#define LV_FS_WIN32_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/
599599
#define LV_FS_WIN32_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/
600600
#endif
601601

602602
/*API for FATFS (needs to be added separately). Uses f_open, f_read, etc*/
603603
#define LV_USE_FS_FATFS 0
604604
#if LV_USE_FS_FATFS
605605
#define LV_FS_FATFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
606-
#define LV_FS_FATSF_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/
606+
#define LV_FS_FATFS_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/
607607
#endif
608608

609609
/*PNG decoder library*/

tests/run_test.py

Lines changed: 86 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -19,88 +19,94 @@
1919
#
2020
##############################################################################
2121

22-
class __test():
23-
24-
DELAY_MS=25
25-
MAX_CHILDREN=100
26-
27-
import sys
28-
import gc
29-
import os
30-
import time
31-
import lvgl as lv
32-
33-
events = [lv.EVENT.SCROLL, lv.EVENT.CLICKED, lv.EVENT.VALUE_CHANGED, lv.EVENT.READY]
34-
35-
def __init__(self):
36-
self.sys.path.append('') # See: https://github.com/micropython/micropython/issues/6419
37-
import display_driver_utils
38-
self.lv.init()
39-
driver = display_driver_utils.driver()
40-
scr = self.lv.scr_act()
41-
self.objects = []
42-
43-
def collect_objects(self, obj, user_data):
44-
if hasattr(obj, 'lv_obj'):
45-
obj = obj.lv_obj
46-
self.objects.append(obj)
47-
return self.lv.obj.TREE_WALK.NEXT
48-
49-
def send_events(self):
50-
for obj in self.objects:
51-
if self.lv.obj.__cast__(obj): # skip deleted objects
52-
obj_info = ''
53-
if hasattr(obj, 'get_text'):
54-
obj_info += ' text:"%s"' % obj.get_text()
55-
if hasattr(obj, 'get_value'):
56-
obj_info += ' value:"%s"' % obj.get_value()
57-
print('%s %s' % (obj, obj_info))
58-
for event in self.events:
59-
if not self.lv.obj.__cast__(obj): # skip deleted objects
60-
continue
61-
# print('\t%s' % get_member_name(lv.EVENT, event))
62-
self.lv.event_send(obj, event, None)
63-
self.time.sleep_ms(self.DELAY_MS)
64-
self.gc.collect()
65-
66-
def run(self):
67-
try:
68-
global __file__
69-
import lv_utils
70-
71-
script = self.sys.argv[1]
72-
script_path = script[:script.rfind('/')] if script.find('/') >= 0 else '.'
73-
script_name = script[script.rfind('/')+1:] if script.find('/') >= 0 else script
74-
75-
print('Running %s ...' % script)
76-
77-
with open(script, 'r') as file:
78-
file_string = file.read()
79-
self.os.chdir(script_path)
80-
__file__ = script_name
81-
self.sys.argv[0] = __file__
82-
del self.sys.argv[1]
83-
exec(file_string)
84-
self.time.sleep_ms(self.DELAY_MS)
85-
self.gc.collect()
86-
self.lv.scr_act().tree_walk(self.collect_objects, None)
87-
self.send_events()
88-
self.time.sleep_ms(self.DELAY_MS)
89-
if lv_utils.event_loop.is_running():
90-
lv_utils.event_loop.current_instance().deinit()
91-
self.time.sleep_ms(self.DELAY_MS)
92-
93-
except:
94-
exc = self.sys.exc_info()
95-
print('ERROR! %s: %s\n%s' % (
96-
exc[0].__name__,
97-
exc[1],
98-
exc[2] if exc[2] else ''))
99-
100-
self.sys.exit(255) # 255 to exit xargs
22+
DELAY_MS=25
23+
MAX_CHILDREN=100
10124

25+
import sys
26+
sys.path.append('') # See: https://github.com/micropython/micropython/issues/6419
10227

28+
import gc
29+
import os
30+
import time
10331
import lvgl as lv
104-
__test().run()
32+
import lv_utils
33+
import display_driver_utils
10534

35+
events = [lv.EVENT.SCROLL, lv.EVENT.CLICKED, lv.EVENT.VALUE_CHANGED, lv.EVENT.READY]
10636

37+
class ExceptionHandler:
38+
def __init__(self):
39+
self.recorded_exception = None
40+
41+
def handle_exceptions(self, e):
42+
lv_utils.event_loop.current_instance().deinit()
43+
if not self.recorded_exception:
44+
self.recorded_exception = e
45+
46+
def reraise(self):
47+
if self.recorded_exception:
48+
raise self.recorded_exception
49+
50+
lv.init()
51+
exception_handler = ExceptionHandler()
52+
driver = display_driver_utils.driver(exception_sink = exception_handler.handle_exceptions)
53+
scr = lv.scr_act()
54+
objects = []
55+
56+
def collect_objects(obj, user_data):
57+
if hasattr(obj, 'lv_obj'):
58+
obj = obj.lv_obj
59+
objects.append(obj)
60+
return lv.obj.TREE_WALK.NEXT
61+
62+
def send_events():
63+
for obj in objects:
64+
if lv.obj.__cast__(obj): # skip deleted objects
65+
obj_info = ''
66+
if hasattr(obj, 'get_text'):
67+
obj_info += ' text:"%s"' % obj.get_text()
68+
if hasattr(obj, 'get_value'):
69+
obj_info += ' value:"%s"' % obj.get_value()
70+
print('%s %s' % (obj, obj_info))
71+
for event in events:
72+
if not lv.obj.__cast__(obj): # skip deleted objects
73+
continue
74+
# print('\t%s' % get_member_name(lv.EVENT, event))
75+
lv.event_send(obj, event, None)
76+
time.sleep_ms(DELAY_MS)
77+
gc.collect()
78+
79+
def run():
80+
try:
81+
script = sys.argv[1]
82+
script_path = script[:script.rfind('/')] if script.find('/') >= 0 else '.'
83+
script_name = script[script.rfind('/')+1:] if script.find('/') >= 0 else script
84+
85+
print('Running %s ...' % script)
86+
87+
with open(script, 'r') as file:
88+
file_string = file.read()
89+
os.chdir(script_path)
90+
sys.argv[0] = script_name
91+
del sys.argv[1]
92+
exec(file_string, {'__file__': script_name, 'lv': lv})
93+
time.sleep_ms(DELAY_MS)
94+
gc.collect()
95+
lv.scr_act().tree_walk(collect_objects, None)
96+
send_events()
97+
time.sleep_ms(DELAY_MS)
98+
exception_handler.reraise()
99+
if lv_utils.event_loop.is_running():
100+
lv_utils.event_loop.current_instance().deinit()
101+
time.sleep_ms(DELAY_MS)
102+
103+
except:
104+
exc = sys.exc_info()
105+
print('ERROR! %s: %s\n%s' % (
106+
exc[0].__name__,
107+
exc[1],
108+
exc[2] if exc[2] else ''))
109+
110+
sys.exit(255) # 255 to exit xargs
111+
112+
run()

0 commit comments

Comments
 (0)
0