|
| 1 | +import usys as sys |
| 2 | +sys.path.append('') # See: https://github.com/micropython/micropython/issues/6419 |
| 3 | + |
| 4 | +# See: https://pymotw.com/2/sys/tracing.html |
| 5 | + |
| 6 | +def mp_trace(frame, event, arg): |
| 7 | + co = frame.f_code |
| 8 | + func_name = co.co_name |
| 9 | + func_line_no = frame.f_lineno |
| 10 | + func_filename = co.co_filename |
| 11 | + print('[%s] [%s] %s:%s' % (event, func_name, func_filename, func_line_no)) |
| 12 | + return mp_trace |
| 13 | + |
| 14 | +# sys.settrace(mp_trace) |
| 15 | + |
| 16 | +import lvgl as lv |
| 17 | + |
| 18 | +############################################################################## |
| 19 | +# Styles |
| 20 | +############################################################################## |
| 21 | + |
| 22 | +class ColorStyle(lv.style_t): |
| 23 | + def __init__(self, color): |
| 24 | + super().__init__() |
| 25 | + self.set_bg_opa(lv.OPA.COVER) |
| 26 | + self.set_bg_color(lv.color_hex3(color)) |
| 27 | + self.set_bg_grad_color(lv.color_hex3(0xFFF)) |
| 28 | + self.set_bg_grad_dir(lv.GRAD_DIR.VER) |
| 29 | + self.set_bg_main_stop(0) |
| 30 | + self.set_bg_grad_stop(128) |
| 31 | + |
| 32 | +class ShadowStyle(lv.style_t): |
| 33 | + def __init__(self): |
| 34 | + super().__init__() |
| 35 | + self.set_shadow_opa(lv.OPA.COVER) |
| 36 | + self.set_shadow_width(3) |
| 37 | + self.set_shadow_color(lv.color_hex3(0xAAA)) |
| 38 | + self.set_shadow_offset_x(5) |
| 39 | + self.set_shadow_offset_y(3) |
| 40 | + self.set_shadow_spread(0) |
| 41 | + |
| 42 | +# A square button with a shadow when not pressed |
| 43 | +class ButtonStyle(lv.style_t): |
| 44 | + def __init__(self): |
| 45 | + super().__init__() |
| 46 | + self.set_radius(lv.dpx(8)) |
| 47 | + self.set_shadow_opa(lv.OPA.COVER) |
| 48 | + self.set_shadow_width(lv.dpx(10)) |
| 49 | + self.set_shadow_color(lv.color_hex3(0xAAA)) |
| 50 | + self.set_shadow_offset_x(lv.dpx(10)) |
| 51 | + self.set_shadow_offset_y(lv.dpx(10)) |
| 52 | + self.set_shadow_spread(0) |
| 53 | + |
| 54 | +class ButtonPressedStyle(lv.style_t): |
| 55 | + def __init__(self): |
| 56 | + super().__init__() |
| 57 | + self.set_shadow_offset_x(lv.dpx(0)) |
| 58 | + self.set_shadow_offset_y(lv.dpx(0)) |
| 59 | + |
| 60 | + |
| 61 | +############################################################################## |
| 62 | +# Themes |
| 63 | +############################################################################## |
| 64 | + |
| 65 | +class AdvancedDemoTheme(lv.theme_t): |
| 66 | + |
| 67 | + def __init__(self): |
| 68 | + super().__init__() |
| 69 | + self.button_style = ButtonStyle() |
| 70 | + self.button_pressed_style = ButtonPressedStyle() |
| 71 | + |
| 72 | + # This theme is based on active theme (material) |
| 73 | + base_theme = lv.theme_get_from_obj(lv.screen_active()) |
| 74 | + |
| 75 | + # This theme will be applied only after base theme is applied |
| 76 | + self.set_parent(base_theme) |
| 77 | + |
| 78 | + # Set the "apply" callback of this theme to our custom callback |
| 79 | + self.set_apply_cb(self.apply) |
| 80 | + |
| 81 | + # Activate this theme on default display |
| 82 | + lv.display_get_default().set_theme(self) |
| 83 | + |
| 84 | + def apply(self, theme, obj): |
| 85 | + if obj.get_class() == lv.button_class: |
| 86 | + obj.add_style(self.button_style, lv.PART.MAIN) |
| 87 | + obj.add_style(self.button_pressed_style, lv.PART.MAIN | lv.STATE.PRESSED) |
| 88 | + |
| 89 | +############################################################################## |
| 90 | + |
| 91 | +member_name_cache = {} |
| 92 | + |
| 93 | +def get_member_name(obj, value): |
| 94 | + try: |
| 95 | + return member_name_cache[id(obj)][id(value)] |
| 96 | + except KeyError: |
| 97 | + pass |
| 98 | + |
| 99 | + for member in dir(obj): |
| 100 | + if getattr(obj, member) == value: |
| 101 | + try: |
| 102 | + member_name_cache[id(obj)][id(value)] = member |
| 103 | + except KeyError: |
| 104 | + member_name_cache[id(obj)] = {id(value): member} |
| 105 | + return member |
| 106 | + |
| 107 | + |
| 108 | +class SymbolButton(lv.button): |
| 109 | + def __init__(self, parent, symbol, text): |
| 110 | + super().__init__(parent) |
| 111 | + self.symbol = lv.label(self) |
| 112 | + self.symbol.set_text(symbol) |
| 113 | + self.label = lv.label(self) |
| 114 | + self.label.set_text(text) |
| 115 | + self.set_flex_flow(lv.FLEX_FLOW.COLUMN) |
| 116 | + self.set_flex_align(lv.FLEX_ALIGN.SPACE_EVENLY, lv.FLEX_ALIGN.CENTER, lv.FLEX_ALIGN.CENTER) |
| 117 | + |
| 118 | + |
| 119 | +class Page_Buttons: |
| 120 | + def __init__(self, app, page): |
| 121 | + self.app = app |
| 122 | + self.page = page |
| 123 | + self.button_event_count = {'Play': 0, 'Pause': 0} |
| 124 | + |
| 125 | + self.page.set_flex_flow(lv.FLEX_FLOW.ROW) |
| 126 | + self.page.set_flex_align(lv.FLEX_ALIGN.SPACE_EVENLY, lv.FLEX_ALIGN.CENTER, lv.FLEX_ALIGN.START) |
| 127 | + |
| 128 | + self.button1 = SymbolButton(page, lv.SYMBOL.PLAY, "Play") |
| 129 | + self.button1.set_size(80, 80) |
| 130 | + |
| 131 | + self.button2 = SymbolButton(page, lv.SYMBOL.PAUSE, "Pause") |
| 132 | + self.button2.set_size(80, 80) |
| 133 | + |
| 134 | + self.label = lv.label(page) |
| 135 | + self.label.add_flag(lv.obj.FLAG.IGNORE_LAYOUT) |
| 136 | + self.label.align(lv.ALIGN.BOTTOM_LEFT, 0, 0) |
| 137 | + |
| 138 | + def button_cb(event, name): |
| 139 | + self.button_event_count[name] += 1 |
| 140 | + event_name = get_member_name(lv.EVENT, event.code) |
| 141 | + if all((not event_name.startswith(s)) for s in ['DRAW', 'GET', 'STYLE', 'REFR']): |
| 142 | + self.label.set_text('[%d] %s %s' % (self.button_event_count[name], name, event_name)) |
| 143 | + |
| 144 | + for button, name in [(self.button1, 'Play'), (self.button2, 'Pause')]: |
| 145 | + button.add_event_cb(lambda event, button_name=name: button_cb(event, button_name), lv.EVENT.ALL, None) |
| 146 | + |
| 147 | + |
| 148 | +class Page_Simple: |
| 149 | + def __init__(self, app, page): |
| 150 | + self.app = app |
| 151 | + self.page = page |
| 152 | + self.test_events = [] |
| 153 | + |
| 154 | + self.page.set_flex_flow(lv.FLEX_FLOW.COLUMN) |
| 155 | + self.page.set_flex_align(lv.FLEX_ALIGN.SPACE_EVENLY, lv.FLEX_ALIGN.CENTER, lv.FLEX_ALIGN.CENTER) |
| 156 | + |
| 157 | + # slider |
| 158 | + self.slider = lv.slider(page) |
| 159 | + self.slider.set_width(lv.pct(80)) |
| 160 | + self.slider_label = lv.label(page) |
| 161 | + self.slider.add_event_cb(self.on_slider_changed, lv.EVENT.VALUE_CHANGED, None) |
| 162 | + self.on_slider_changed(None) |
| 163 | + |
| 164 | + # style selector |
| 165 | + self.styles = [('Gray', ColorStyle(0xCCC)), |
| 166 | + ('Red', ColorStyle(0xF00)), |
| 167 | + ('Green',ColorStyle(0x0F0)), |
| 168 | + ('Blue', ColorStyle(0x00F))] |
| 169 | + |
| 170 | + self.style_selector = lv.dropdown(page) |
| 171 | + self.style_selector.add_style(ShadowStyle(), lv.PART.MAIN) |
| 172 | + self.style_selector.align(lv.ALIGN.OUT_BOTTOM_LEFT, 0, 40) |
| 173 | + self.style_selector.set_options('\n'.join(x[0] for x in self.styles)) |
| 174 | + self.style_selector.add_event_cb(self.on_style_selector_changed, lv.EVENT.VALUE_CHANGED, None) |
| 175 | + |
| 176 | + # counter button |
| 177 | + self.counter_button = lv.button(page) |
| 178 | + self.counter_button.set_size(80,80) |
| 179 | + self.counter_label = lv.label(self.counter_button) |
| 180 | + self.counter_label.set_text("Count") |
| 181 | + self.counter_label.align(lv.ALIGN.CENTER, 0, 0) |
| 182 | + self.counter_button.add_event_cb(self.on_counter_button, lv.EVENT.CLICKED, None) |
| 183 | + self.counter = 0 |
| 184 | + |
| 185 | + def on_slider_changed(self, event): |
| 186 | + self.slider_label.set_text(str(self.slider.get_value())) |
| 187 | + |
| 188 | + def on_style_selector_changed(self, event): |
| 189 | + selected = self.style_selector.get_selected() |
| 190 | + tabview = self.app.screen_main.tabview |
| 191 | + if hasattr(self, 'selected_style'): tabview.remove_style(self.selected_style, lv.PART.MAIN) |
| 192 | + self.selected_style = self.styles[selected][1] |
| 193 | + tabview.add_style(self.selected_style, lv.PART.MAIN) |
| 194 | + |
| 195 | + def on_counter_button(self, event): |
| 196 | + self.counter += 1 |
| 197 | + self.counter_label.set_text(str(self.counter)) |
| 198 | + |
| 199 | +class Anim(lv.anim_t): |
| 200 | + def __init__(self, obj, val, size, exec_cb, path_cb, time=500, playback=False, completed_cb=None): |
| 201 | + super().__init__() |
| 202 | + self.init() |
| 203 | + self.set_time(time) |
| 204 | + self.set_values(val, val + size) |
| 205 | + if callable(exec_cb): |
| 206 | + self.set_custom_exec_cb(exec_cb) |
| 207 | + else: |
| 208 | + self.set_exec_cb(obj, exec_cb) |
| 209 | + self.set_path_cb(path_cb) |
| 210 | + if playback: |
| 211 | + self.set_playback(0) |
| 212 | + if completed_cb: |
| 213 | + self.set_completed_cb(completed_cb) |
| 214 | + self.start() |
| 215 | + |
| 216 | + |
| 217 | +class AnimatedChart(lv.chart): |
| 218 | + def __init__(self, parent, val, size): |
| 219 | + super().__init__(parent) |
| 220 | + self.val = val |
| 221 | + self.size = size |
| 222 | + self.max = 2000 |
| 223 | + self.min = 500 |
| 224 | + self.factor = 100 |
| 225 | + self.anim_phase1() |
| 226 | + |
| 227 | + def anim_phase1(self): |
| 228 | + self.phase1 = Anim( |
| 229 | + self, |
| 230 | + self.val, |
| 231 | + self.size, |
| 232 | + lambda a, val: self.set_range(self.AXIS.PRIMARY_Y, 0, val), |
| 233 | + lv.anim_t.path_ease_in, |
| 234 | + completed_cb=lambda a:self.anim_phase2(), |
| 235 | + time=(self.max * self.factor) // 100, |
| 236 | + ) |
| 237 | + |
| 238 | + def anim_phase2(self): |
| 239 | + self.phase2 = Anim( |
| 240 | + self, |
| 241 | + self.val + self.size, |
| 242 | + -self.size, |
| 243 | + lambda a, val: self.set_range(self.AXIS.PRIMARY_Y, 0, val), |
| 244 | + lv.anim_t.path_ease_out, |
| 245 | + completed_cb=lambda a:self.anim_phase1(), |
| 246 | + time=(self.min * self.factor) // 100, |
| 247 | + ) |
| 248 | +class Page_Text: |
| 249 | + def __init__(self, app, page): |
| 250 | + self.app = app |
| 251 | + self.page = page |
| 252 | + self.page.set_flex_flow(lv.FLEX_FLOW.ROW) |
| 253 | + self.page.set_flex_align(lv.FLEX_ALIGN.SPACE_EVENLY, lv.FLEX_ALIGN.CENTER, lv.FLEX_ALIGN.CENTER) |
| 254 | + self.ta = lv.textarea(self.page) |
| 255 | + self.ta.set_height(lv.pct(100)) |
| 256 | + self.ta.set_width(lv.pct(100)) |
| 257 | + |
| 258 | +class Page_Chart: |
| 259 | + def __init__(self, app, page): |
| 260 | + self.app = app |
| 261 | + self.page = page |
| 262 | + self.page.set_flex_flow(lv.FLEX_FLOW.ROW) |
| 263 | + self.page.set_flex_align(lv.FLEX_ALIGN.SPACE_EVENLY, lv.FLEX_ALIGN.CENTER, lv.FLEX_ALIGN.CENTER) |
| 264 | + self.page.set_style_pad_all(10, lv.PART.MAIN) |
| 265 | + self.page.set_style_pad_gap(10, lv.PART.MAIN) |
| 266 | + self.chart = AnimatedChart(page, 100, 1000) |
| 267 | + self.chart.set_flex_grow(1) |
| 268 | + self.chart.set_height(lv.pct(100)) |
| 269 | + self.series1 = self.chart.add_series(lv.color_hex(0xFF0000), self.chart.AXIS.PRIMARY_Y) |
| 270 | + self.chart.set_type(self.chart.TYPE.LINE) |
| 271 | + self.chart.set_style_line_width(3, lv.PART.ITEMS) |
| 272 | + self.chart.add_style(ColorStyle(0x055), lv.PART.ITEMS) |
| 273 | + self.chart.set_range(self.chart.AXIS.PRIMARY_Y, 0, 100) |
| 274 | + self.chart.set_point_count(10) |
| 275 | + self.chart.set_ext_y_array(self.series1, [10, 20, 30, 20, 10, 40, 50, 90, 95, 90]) |
| 276 | + # self.chart.set_x_tick_texts("a\nb\nc\nd\ne", 2, lv.chart.AXIS.DRAW_LAST_TICK) |
| 277 | + # self.chart.set_x_tick_length(10, 5) |
| 278 | + # self.chart.set_y_tick_texts("1\n2\n3\n4\n5", 2, lv.chart.AXIS.DRAW_LAST_TICK) |
| 279 | + # self.chart.set_y_tick_length(10, 5) |
| 280 | + self.chart.set_div_line_count(5, 5) |
| 281 | + |
| 282 | + # Create a slider that controls the chart animation speed |
| 283 | + |
| 284 | + def on_slider_changed(event): |
| 285 | + self.chart.factor = self.slider.get_value() |
| 286 | + |
| 287 | + self.slider = lv.slider(page) |
| 288 | + self.slider.set_size(10, lv.pct(100)) |
| 289 | + self.slider.set_range(10, 200) |
| 290 | + self.slider.set_value(self.chart.factor, 0) |
| 291 | + self.slider.add_event_cb(on_slider_changed, lv.EVENT.VALUE_CHANGED, None) |
| 292 | + |
| 293 | +class Screen_Main(lv.obj): |
| 294 | + def __init__(self, app, *args, **kwds): |
| 295 | + self.app = app |
| 296 | + super().__init__(*args, **kwds) |
| 297 | + self.theme = AdvancedDemoTheme() |
| 298 | + self.tabview = lv.tabview(self) |
| 299 | + self.page_simple = Page_Simple(self.app, self.tabview.add_tab("Simple")) |
| 300 | + self.page_buttons = Page_Buttons(self.app, self.tabview.add_tab("Buttons")) |
| 301 | + self.page_text = Page_Text(self.app, self.tabview.add_tab("Text")) |
| 302 | + self.page_chart = Page_Chart(self.app, self.tabview.add_tab("Chart")) |
| 303 | + |
| 304 | + |
| 305 | +class AdvancedDemoApplication: |
| 306 | + def init_gui(self): |
| 307 | + |
| 308 | + self.group = lv.group_create() |
| 309 | + self.group.set_default() |
| 310 | + |
| 311 | + # Identify platform and initialize it |
| 312 | + |
| 313 | + self.screen_main = Screen_Main(self) |
| 314 | + lv.screen_load(self.screen_main) |
| 315 | + |
| 316 | + |
| 317 | +app = AdvancedDemoApplication() |
| 318 | +app.init_gui() |
0 commit comments