11import lvgl as lv
22
3+
4+ def safe_widget_access (callback ):
5+ """
6+ Wrapper to safely access a widget, catching LvReferenceError.
7+
8+ If the widget has been deleted, the callback is silently skipped.
9+ This prevents crashes when animations try to access deleted widgets.
10+
11+ Args:
12+ callback: Function to call (should access a widget)
13+
14+ Returns:
15+ None (always, even if callback returns a value)
16+ """
17+ try :
18+ callback ()
19+ except Exception as e :
20+ # Check if it's an LvReferenceError (widget was deleted)
21+ if "LvReferenceError" in str (type (e ).__name__ ) or "Referenced object was deleted" in str (e ):
22+ # Widget was deleted - silently ignore
23+ pass
24+ else :
25+ # Some other error - re-raise it
26+ raise
27+
28+
329class WidgetAnimator :
430
531# def __init__(self):
@@ -27,10 +53,10 @@ def show_widget(widget, anim_type="fade", duration=500, delay=0):
2753 anim .set_values (0 , 255 )
2854 anim .set_duration (duration )
2955 anim .set_delay (delay )
30- anim .set_custom_exec_cb (lambda anim , value : widget .set_style_opa (value , 0 ))
56+ anim .set_custom_exec_cb (lambda anim , value : safe_widget_access ( lambda : widget .set_style_opa (value , 0 ) ))
3157 anim .set_path_cb (lv .anim_t .path_ease_in_out )
3258 # Ensure opacity is reset after animation
33- anim .set_completed_cb (lambda * args : widget .set_style_opa (255 , 0 ))
59+ anim .set_completed_cb (lambda * args : safe_widget_access ( lambda : widget .set_style_opa (255 , 0 ) ))
3460 elif anim_type == "slide_down" :
3561 print ("doing slide_down" )
3662 # Create slide-down animation (y from -height to original y)
@@ -42,10 +68,10 @@ def show_widget(widget, anim_type="fade", duration=500, delay=0):
4268 anim .set_values (original_y - height , original_y )
4369 anim .set_duration (duration )
4470 anim .set_delay (delay )
45- anim .set_custom_exec_cb (lambda anim , value : widget .set_y (value ))
71+ anim .set_custom_exec_cb (lambda anim , value : safe_widget_access ( lambda : widget .set_y (value ) ))
4672 anim .set_path_cb (lv .anim_t .path_ease_in_out )
4773 # Reset y position after animation
48- anim .set_completed_cb (lambda * args : widget .set_y (original_y ))
74+ anim .set_completed_cb (lambda * args : safe_widget_access ( lambda : widget .set_y (original_y ) ))
4975 elif anim_type == "slide_up" :
5076 # Create slide-up animation (y from +height to original y)
5177 # Seems to cause scroll bars to be added somehow if done to a keyboard at the bottom of the screen...
@@ -57,10 +83,10 @@ def show_widget(widget, anim_type="fade", duration=500, delay=0):
5783 anim .set_values (original_y + height , original_y )
5884 anim .set_duration (duration )
5985 anim .set_delay (delay )
60- anim .set_custom_exec_cb (lambda anim , value : widget .set_y (value ))
86+ anim .set_custom_exec_cb (lambda anim , value : safe_widget_access ( lambda : widget .set_y (value ) ))
6187 anim .set_path_cb (lv .anim_t .path_ease_in_out )
6288 # Reset y position after animation
63- anim .set_completed_cb (lambda * args : widget .set_y (original_y ))
89+ anim .set_completed_cb (lambda * args : safe_widget_access ( lambda : widget .set_y (original_y ) ))
6490
6591 # Store and start animation
6692 #self.animations[widget] = anim
@@ -77,10 +103,10 @@ def hide_widget(widget, anim_type="fade", duration=500, delay=0, hide=True):
77103 anim .set_values (255 , 0 )
78104 anim .set_duration (duration )
79105 anim .set_delay (delay )
80- anim .set_custom_exec_cb (lambda anim , value : widget .set_style_opa (value , 0 ))
106+ anim .set_custom_exec_cb (lambda anim , value : safe_widget_access ( lambda : widget .set_style_opa (value , 0 ) ))
81107 anim .set_path_cb (lv .anim_t .path_ease_in_out )
82108 # Set HIDDEN flag after animation
83- anim .set_completed_cb (lambda * args : WidgetAnimator .hide_complete_cb (widget , hide = hide ))
109+ anim .set_completed_cb (lambda * args : safe_widget_access ( lambda : WidgetAnimator .hide_complete_cb (widget , hide = hide ) ))
84110 elif anim_type == "slide_down" :
85111 # Create slide-down animation (y from original y to +height)
86112 # Seems to cause scroll bars to be added somehow if done to a keyboard at the bottom of the screen...
@@ -92,10 +118,10 @@ def hide_widget(widget, anim_type="fade", duration=500, delay=0, hide=True):
92118 anim .set_values (original_y , original_y + height )
93119 anim .set_duration (duration )
94120 anim .set_delay (delay )
95- anim .set_custom_exec_cb (lambda anim , value : widget .set_y (value ))
121+ anim .set_custom_exec_cb (lambda anim , value : safe_widget_access ( lambda : widget .set_y (value ) ))
96122 anim .set_path_cb (lv .anim_t .path_ease_in_out )
97123 # Set HIDDEN flag after animation
98- anim .set_completed_cb (lambda * args : WidgetAnimator .hide_complete_cb (widget , original_y , hide ))
124+ anim .set_completed_cb (lambda * args : safe_widget_access ( lambda : WidgetAnimator .hide_complete_cb (widget , original_y , hide ) ))
99125 elif anim_type == "slide_up" :
100126 print ("hide with slide_up" )
101127 # Create slide-up animation (y from original y to -height)
@@ -107,10 +133,10 @@ def hide_widget(widget, anim_type="fade", duration=500, delay=0, hide=True):
107133 anim .set_values (original_y , original_y - height )
108134 anim .set_duration (duration )
109135 anim .set_delay (delay )
110- anim .set_custom_exec_cb (lambda anim , value : widget .set_y (value ))
136+ anim .set_custom_exec_cb (lambda anim , value : safe_widget_access ( lambda : widget .set_y (value ) ))
111137 anim .set_path_cb (lv .anim_t .path_ease_in_out )
112138 # Set HIDDEN flag after animation
113- anim .set_completed_cb (lambda * args : WidgetAnimator .hide_complete_cb (widget , original_y , hide ))
139+ anim .set_completed_cb (lambda * args : safe_widget_access ( lambda : WidgetAnimator .hide_complete_cb (widget , original_y , hide ) ))
114140
115141 # Store and start animation
116142 #self.animations[widget] = anim
0 commit comments