0% found this document useful (0 votes)
2K views1,091 pages

LVGL Library

This document provides documentation for the LVGL graphics library. It describes key features, examples, getting started, porting to new platforms, and overviews of core concepts like objects, styles, events and input handling.

Uploaded by

a9220300793
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2K views1,091 pages

LVGL Library

This document provides documentation for the LVGL graphics library. It describes key features, examples, getting started, porting to new platforms, and overviews of core concepts like objects, styles, events and input handling.

Uploaded by

a9220300793
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

LVGL Documentation 9.

LVGL community

Feb 27, 2023


CONTENTS

1 Introduction 2
1.1 Key features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2 Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3 License . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.4 Repository layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.5 Release policy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.6 FAQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

2 Examples 8
2.1 Get started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.2 Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.3 Animations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
2.4 Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
2.5 Layouts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
2.6 Scrolling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
2.7 Widgets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76

3 Get started 237


3.1 Quick overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
3.2 Platforms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
3.3 (RT)OS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267
3.4 Bindings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270

4 Porting 281
4.1 Set up a project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
4.2 Display interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
4.3 Input device interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
4.4 Tick interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304
4.5 Timer Handler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305
4.6 Sleep management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305
4.7 Operating system and interrupts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306
4.8 Logging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307
4.9 Add custom GPU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308

5 Overview 313
5.1 Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313
5.2 Positions, sizes, and layouts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319
5.3 Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328
5.4 Style properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382
5.5 Scroll . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393

i
5.6 Layers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 409
5.7 Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 411
5.8 Input devices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427
5.9 Displays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 439
5.10 Colors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450
5.11 Fonts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461
5.12 Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 468
5.13 File system . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 478
5.14 Animations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 486
5.15 Timers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 507
5.16 Drawing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 512
5.17 Renderers and GPUs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 518
5.18 New widget . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 519

6 Widgets 520
6.1 Base object (lv_obj) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 520
6.2 Arc (lv_arc) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 535
6.3 Animation Image (lv_animimg) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 546
6.4 Bar (lv_bar) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 550
6.5 Button (lv_btn) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 562
6.6 Button matrix (lv_btnmatrix) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 569
6.7 Calendar (lv_calendar) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 583
6.8 Chart (lv_chart) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 589
6.9 Color wheel (lv_colorwheel) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 631
6.10 Canvas (lv_canvas) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 636
6.11 Checkbox (lv_checkbox) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 652
6.12 Drop-down list (lv_dropdown) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 659
6.13 Image (lv_img) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 670
6.14 Image button (lv_imgbtn) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 684
6.15 Keyboard (lv_keyboard) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 689
6.16 Label (lv_label) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 697
6.17 LED (lv_led) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 710
6.18 Line (lv_line) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 713
6.19 List (lv_list) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 716
6.20 Menu (lv_menu) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 725
6.21 Meter (lv_meter) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 747
6.22 Message box (lv_msgbox) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 764
6.23 Roller (lv_roller) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 768
6.24 Slider (lv_slider) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 779
6.25 Span (lv_span) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 788
6.26 Spinbox (lv_spinbox) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 797
6.27 Spinner (lv_spinner) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 802
6.28 Switch (lv_switch) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 804
6.29 Table (lv_table) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 807
6.30 Tabview (lv_tabview) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 819
6.31 Text area (lv_textarea) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 825
6.32 Tile view (lv_tileview) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 839
6.33 Window (lv_win) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 843

7 Layouts 847
7.1 Flex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 847
7.2 Grid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 860

8 3rd party libraries 876

ii
8.1 File System Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 876
8.2 BMP decoder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 876
8.3 JPG decoder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 878
8.4 PNG decoder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 880
8.5 GIF decoder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 882
8.6 FreeType support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 884
8.7 Tiny TTF font engine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 888
8.8 QR code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 891
8.9 Barcode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 894
8.10 Lottie player . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 897
8.11 FFmpeg support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 904

9 Others 909
9.1 Snapshot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 909
9.2 Monkey . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 913
9.3 Grid navigation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 917
9.4 File Explorer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 928
9.5 Fragment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 946
9.6 Messaging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 957
9.7 Image font (imgfont) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 973
9.8 Pinyin IME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 976

10 Contributing 985
10.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 985
10.2 Pull request . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 986
10.3 Developer Certification of Origin (DCO) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 988
10.4 Ways to contribute . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 989

11 Changelog 993
11.1 v8.3.5 7 February 2023 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 993
11.2 v8.3.4 15 December 2022 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 994
11.3 v8.3.3 06 October 2022 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 995
11.4 v8.3.2 27 September 2022 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 995
11.5 v8.3.1 25 July 2022 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 996
11.6 v8.3.0 6 July 2022 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 996
11.7 v8.2.0 31 January 2022 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1004
11.8 v8.1.0 10 November 2021 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1012
11.9 v8.0.2 (16.07.2021) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1028
11.10 v8.0.1 (14.06.2021) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1029
11.11 v8.0.0 (01.06.2021) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1031
11.12 v7.11.0 (16.03.2021) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1033
11.13 v7.10.1 (16.02.2021) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1034
11.14 v7.10.0 (02.02.2021) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1034
11.15 v7.9.1 (19.01.2021) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1034
11.16 v7.9.0 (05.01.2021) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1035
11.17 v7.8.1 (15.12.2020) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1035
11.18 v7.8.0 (01.12.2020) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1035
11.19 v7.7.2 (17.11.2020) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1036
11.20 v7.7.1 (03.11.2020) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1036
11.21 v7.7.0 (20.10.2020) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1037
11.22 v7.6.1 (06.10.2020) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1037
11.23 v7.6.0 (22.09.2020) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1038
11.24 v7.5.0 (15.09.2020) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1038
11.25 v7.4.0 (01.09.2020) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1038

iii
11.26 v7.3.1 (18.08.2020) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1039
11.27 v7.3.0 (04.08.2020) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1040
11.28 v7.2.0 (21.07.2020) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1040
11.29 v7.1.0 (07.07.2020) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1041
11.30 v7.0.2 (16.06.2020) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1042
11.31 v7.0.1 (01.06.2020) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1043
11.32 v7.0.0 (18.05.2020) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1043

12 Roadmap 1047
12.1 Planned for v9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1047
12.2 Planned in general . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1049
12.3 Ideas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1050

Index 1051

iv
LVGL Documentation 9.0

PDF version: LVGL.pdf

CONTENTS 1
CHAPTER

ONE

INTRODUCTION

LVGL (Light and Versatile Graphics Library) is a free and open-source graphics library providing everything you need
to create an embedded GUI with easy-to-use graphical elements, beautiful visual effects and a low memory footprint.

1.1 Key features

• Powerful building blocks such as buttons, charts, lists, sliders, images, etc.
• Advanced graphics with animations, anti-aliasing, opacity, smooth scrolling
• Various input devices such as touchpad, mouse, keyboard, encoder, etc.
• Multi-language support with UTF-8 encoding
• Multi-display support, i.e. use multiple TFT, monochrome displays simultaneously
• Fully customizable graphic elements with CSS-like styles
• Hardware independent: use with any microcontroller or display
• Scalable: able to operate with little memory (64 kB Flash, 16 kB RAM)
• OS, external memory and GPU are supported but not required
• Single frame buffer operation even with advanced graphic effects
• Written in C for maximal compatibility (C++ compatible)
• Simulator to start embedded GUI design on a PC without embedded hardware
• Binding to MicroPython
• Tutorials, examples, themes for rapid GUI design
• Documentation is available online and as PDF
• Free and open-source under MIT license

2
LVGL Documentation 9.0

1.2 Requirements

Basically, every modern controller which is able to drive a display is suitable to run LVGL. The minimal requirements
are:

1.3 License

The LVGL project (including all repositories) is licensed under MIT license. This means you can use it even in commercial
projects.
It's not mandatory, but we highly appreciate it if you write a few words about your project in the My projects category of
the forum or a private message to lvgl.io.
Although you can get LVGL for free there is a massive amount of work behind it. It's created by a group of volunteers
who made it available for you in their free time.
To make the LVGL project sustainable, please consider contributing to the project. You can choose from many different
ways of contributing such as simply writing a tweet about you using LVGL, fixing bugs, translating the documentation, or
even becoming a maintainer.

1.4 Repository layout

All repositories of the LVGL project are hosted on GitHub: https://github.com/lvgl


You will find these repositories there:
• lvgl The library itself with many examples and demos.
• lv_drivers Display and input device drivers
• blog Source of the blog's site (https://blog.lvgl.io)
• sim Source of the online simulator's site (https://sim.lvgl.io)
• lv_port_... LVGL ports to development boards or environments
• lv_binding_.. Bindings to other languages

1.5 Release policy

The core repositories follow the rules of Semantic versioning:


• Major versions for incompatible API changes. E.g. v5.0.0, v6.0.0
• Minor version for new but backward-compatible functionalities. E.g. v6.1.0, v6.2.0
• Patch version for backward-compatible bug fixes. E.g. v6.1.1, v6.1.2
Tags like vX.Y.Z are created for every release.

1.2. Requirements 3
LVGL Documentation 9.0

1.5.1 Release cycle

• Bug fixes: Released on demand even weekly


• Minor releases: Every 3-4 months
• Major releases: Approximately yearly

1.5.2 Branches

The core repositories have at least the following branches:


• master latest version, patches are merged directly here.
• release/vX.Y stable versions of the minor releases
• fix/some-description temporary branches for bug fixes
• feat/some-description temporary branches for features

1.5.3 Changelog

The changes are recorded in CHANGELOG.md.

1.5.4 Version support

Before v8 the last minor release of each major series was supported for 1 year. Starting from v8, every minor release is
supported for 1 year.

1.6 FAQ

1.6.1 Where can I ask questions?

You can ask questions in the forum: https://forum.lvgl.io/.


We use GitHub issues for development related discussion. You should use them only if your question or issue is tightly
related to the development of the library.
Before posting a question, please ready this FAQ section as you might find answer to your issue here too.

1.6.2 Is my MCU/hardware supported?

Every MCU which is capable of driving a display via parallel port, SPI, RGB interface or anything else and fulfills the
Requirements is supported by LVGL.
This includes:
• "Common" MCUs like STM32F, STM32H, NXP Kinetis, LPC, iMX, dsPIC33, PIC32, SWM341 etc.
• Bluetooth, GSM, Wi-Fi modules like Nordic NRF, Espressif ESP32 and Raspberry Pi Pico W
• Linux with frame buffer device such as /dev/fb0. This includes Single-board computers like the Raspberry Pi
• Anything else with a strong enough MCU and a peripheral to drive a display

1.6. FAQ 4
LVGL Documentation 9.0

1.6.3 Is my display supported?

LVGL needs just one simple driver function to copy an array of pixels into a given area of the display. If you can do this
with your display then you can use it with LVGL.
Some examples of the supported display types:
• TFTs with 16 or 24 bit color depth
• Monitors with an HDMI port
• Small monochrome displays
• Gray-scale displays
• even LED matrices
• or any other display where you can control the color/state of the pixels
See the Porting section to learn more.

1.6.4 LVGL doesn't start, randomly crashes or nothing is drawn on the display.
What can be the problem?

• Try increasing LV_MEM_SIZE.


• Be sure lv_disp_t, lv_indev_t and lv_fs_drv_t are global or static.
• Be sure your display works without LVGL. E.g. paint it to red on start up.
• Enable Logging
• Enable asserts in lv_conf.h (LV_USE_ASSERT_...)
• If you use an RTOS
– increase the stack size of the task which calls lv_timer_handler()
– Be sure you used a mutex as described here

1.6.5 My display driver is not called. What have I missed?

Be sure you are calling lv_tick_inc(x) in an interrupt and lv_timer_handler() in your main while(1).
Learn more in the Tick and Timer handler sections.

1.6.6 Why is the display driver called only once? Only the upper part of the display
is refreshed.

Be sure you are calling lv_disp_flush_ready(drv) at the end of your "display flush callback".

1.6. FAQ 5
LVGL Documentation 9.0

1.6.7 Why do I see only garbage on the screen?

Probably there a bug in your display driver. Try the following code without using LVGL. You should see a square with
red-blue gradient.

#define BUF_W 20
#define BUF_H 10

lv_color_t buf[BUF_W * BUF_H];


lv_color_t * buf_p = buf;
uint16_t x, y;
for(y = 0; y < BUF_H; y++) {
lv_color_t c = lv_color_mix(LV_COLOR_BLUE, LV_COLOR_RED, (y * 255) / BUF_H);
for(x = 0; x < BUF_W; x++){
(*buf_p) = c;
buf_p++;
}
}

lv_area_t a;
a.x1 = 10;
a.y1 = 40;
a.x2 = a.x1 + BUF_W - 1;
a.y2 = a.y1 + BUF_H - 1;
my_flush_cb(NULL, &a, buf);

1.6.8 Why do I see nonsense colors on the screen?

Probably LVGL's color format is not compatible with your display's color format. Check LV_COLOR_DEPTH in
lv_conf.h.

1.6.9 How to speed up my UI?

• Turn on compiler optimization and enable cache if your MCU has it


• Increase the size of the display buffer
• Use two display buffers and flush the buffer with DMA (or similar peripheral) in the background
• Increase the clock speed of the SPI or parallel port if you use them to drive the display
• If your display has an SPI port consider changing to a model with a parallel interface because it has much higher
throughput
• Keep the display buffer in internal RAM (not in external SRAM) because LVGL uses it a lot and it should have a
fast access time

1.6. FAQ 6
LVGL Documentation 9.0

1.6.10 How to reduce flash/ROM usage?

You can disable all the unused features (such as animations, file system, GPU etc.) and object types in lv_conf.h.
If you are using GCC/CLANG you can add -fdata-sections -ffunction-sections compiler flags and
--gc-sections linker flag to remove unused functions and variables from the final binary. If possible, add the
-flto compiler flag to enable link-time-optimisation together with -Os for GCC or -Oz for CLANG.

1.6.11 How to reduce the RAM usage

• Lower the size of the Display buffer


• Reduce LV_MEM_SIZE in lv_conf.h. This memory is used when you create objects like buttons, labels, etc.
• To work with lower LV_MEM_SIZE you can create objects only when required and delete them when they are not
needed anymore

1.6.12 How to work with an operating system?

To work with an operating system where tasks can interrupt each other (preemptively) you should protect LVGL related
function calls with a mutex. See the Operating system and interrupts section to learn more.

1.6. FAQ 7
CHAPTER

TWO

EXAMPLES

2.1 Get started

2.1.1 A very simple "hello world" label

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_LABEL

/**
* Basic example to create a "Hello world" label
*/
void lv_example_get_started_1(void)
{
/*Change the active screen's background color*/
lv_obj_set_style_bg_color(lv_scr_act(), lv_color_hex(0x003a57), LV_PART_MAIN);

/*Create a white label, set its text and align it to the center*/
lv_obj_t * label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "Hello world");
lv_obj_set_style_text_color(lv_scr_act(), lv_color_hex(0xffffff), LV_PART_MAIN);
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
}

#endif

# Change the active screen's background color


scr = lv.scr_act()
scr.set_style_bg_color(lv.color_hex(0x003a57), lv.PART.MAIN)

# Create a white label, set its text and align it to the center
label = lv.label(lv.scr_act())
label.set_text("Hello world")
label.set_style_text_color(lv.color_hex(0xffffff), lv.PART.MAIN)
label.align(lv.ALIGN.CENTER, 0, 0)

8
LVGL Documentation 9.0

2.1.2 A button with a label and react on click event

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_BTN

static void btn_event_cb(lv_event_t * e)


{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * btn = lv_event_get_target(e);
if(code == LV_EVENT_CLICKED) {
static uint8_t cnt = 0;
cnt++;

/*Get the first child of the button which is the label and change its text*/
lv_obj_t * label = lv_obj_get_child(btn, 0);
lv_label_set_text_fmt(label, "Button: %d", cnt);
}
}

/**
* Create a button with a label and react on click event.
*/
void lv_example_get_started_2(void)
{
lv_obj_t * btn = lv_btn_create(lv_scr_act()); /*Add a button the current␣
,→screen*/

lv_obj_set_pos(btn, 10, 10); /*Set its position*/


lv_obj_set_size(btn, 120, 50); /*Set its size*/
lv_obj_add_event(btn, btn_event_cb, LV_EVENT_ALL, NULL); /*Assign a␣
,→callback to the button*/

lv_obj_t * label = lv_label_create(btn); /*Add a label to the button*/


lv_label_set_text(label, "Button"); /*Set the labels text*/
lv_obj_center(label);
}

#endif

class CounterBtn():
def __init__(self):
self.cnt = 0
#
# Create a button with a label and react on click event.
#

btn = lv.btn(lv.scr_act()) # Add a button the␣


,→current screen
btn.set_pos(10, 10) # Set its position
btn.set_size(120, 50) # Set its size
btn.align(lv.ALIGN.CENTER,0,0)
btn.add_event(self.btn_event_cb, lv.EVENT.ALL, None) # Assign a callback to␣
,→the button

label = lv.label(btn) # Add a label to the␣


,→button

label.set_text("Button") # Set the labels text


label.center()
(continues on next page)

2.1. Get started 9


LVGL Documentation 9.0

(continued from previous page)

def btn_event_cb(self,e):
code = e.get_code()
btn = e.get_target_obj()
if code == lv.EVENT.CLICKED:
self.cnt += 1

# Get the first child of the button which is the label and change its text
label = btn.get_child(0)
label.set_text("Button: " + str(self.cnt))

counterBtn = CounterBtn()

2.1.3 Create styles from scratch for buttons

#include "../lv_examples.h"
#if LV_USE_BTN && LV_BUILD_EXAMPLES

static lv_style_t style_btn;


static lv_style_t style_btn_pressed;
static lv_style_t style_btn_red;

static lv_color_t darken(const lv_color_filter_dsc_t * dsc, lv_color_t color, lv_opa_


,→t opa)

{
LV_UNUSED(dsc);
return lv_color_darken(color, opa);
}

static void style_init(void)


{
/*Create a simple button style*/
lv_style_init(&style_btn);
lv_style_set_radius(&style_btn, 10);
lv_style_set_bg_opa(&style_btn, LV_OPA_COVER);
lv_style_set_bg_color(&style_btn, lv_palette_lighten(LV_PALETTE_GREY, 3));
lv_style_set_bg_grad_color(&style_btn, lv_palette_main(LV_PALETTE_GREY));
lv_style_set_bg_grad_dir(&style_btn, LV_GRAD_DIR_VER);

lv_style_set_border_color(&style_btn, lv_color_black());
lv_style_set_border_opa(&style_btn, LV_OPA_20);
lv_style_set_border_width(&style_btn, 2);

lv_style_set_text_color(&style_btn, lv_color_black());

/*Create a style for the pressed state.


*Use a color filter to simply modify all colors in this state*/
static lv_color_filter_dsc_t color_filter;
lv_color_filter_dsc_init(&color_filter, darken);
lv_style_init(&style_btn_pressed);
lv_style_set_color_filter_dsc(&style_btn_pressed, &color_filter);
lv_style_set_color_filter_opa(&style_btn_pressed, LV_OPA_20);
(continues on next page)

2.1. Get started 10


LVGL Documentation 9.0

(continued from previous page)

/*Create a red style. Change only some colors.*/


lv_style_init(&style_btn_red);
lv_style_set_bg_color(&style_btn_red, lv_palette_main(LV_PALETTE_RED));
lv_style_set_bg_grad_color(&style_btn_red, lv_palette_lighten(LV_PALETTE_RED, 3));
}

/**
* Create styles from scratch for buttons.
*/
void lv_example_get_started_3(void)
{
/*Initialize the style*/
style_init();

/*Create a button and use the new styles*/


lv_obj_t * btn = lv_btn_create(lv_scr_act());
/* Remove the styles coming from the theme
* Note that size and position are also stored as style properties
* so lv_obj_remove_style_all will remove the set size and position too */
lv_obj_remove_style_all(btn);
lv_obj_set_pos(btn, 10, 10);
lv_obj_set_size(btn, 120, 50);
lv_obj_add_style(btn, &style_btn, 0);
lv_obj_add_style(btn, &style_btn_pressed, LV_STATE_PRESSED);

/*Add a label to the button*/


lv_obj_t * label = lv_label_create(btn);
lv_label_set_text(label, "Button");
lv_obj_center(label);

/*Create another button and use the red style too*/


lv_obj_t * btn2 = lv_btn_create(lv_scr_act());
lv_obj_remove_style_all(btn2); /*Remove the styles coming␣
,→from the theme*/

lv_obj_set_pos(btn2, 10, 80);


lv_obj_set_size(btn2, 120, 50);
lv_obj_add_style(btn2, &style_btn, 0);
lv_obj_add_style(btn2, &style_btn_red, 0);
lv_obj_add_style(btn2, &style_btn_pressed, LV_STATE_PRESSED);
lv_obj_set_style_radius(btn2, LV_RADIUS_CIRCLE, 0); /*Add a local style too*/

label = lv_label_create(btn2);
lv_label_set_text(label, "Button 2");
lv_obj_center(label);
}

#endif

#
# Create styles from scratch for buttons.
#
style_btn = lv.style_t()
style_btn_red = lv.style_t()
style_btn_pressed = lv.style_t()

(continues on next page)

2.1. Get started 11


LVGL Documentation 9.0

(continued from previous page)


# Create a simple button style
style_btn.init()
style_btn.set_radius(10)
style_btn.set_bg_opa(lv.OPA.COVER)
style_btn.set_bg_color(lv.palette_lighten(lv.PALETTE.GREY, 3))
style_btn.set_bg_grad_color(lv.palette_main(lv.PALETTE.GREY))
style_btn.set_bg_grad_dir(lv.GRAD_DIR.VER)

# Add a border
style_btn.set_border_color(lv.color_white())
style_btn.set_border_opa(lv.OPA._70)
style_btn.set_border_width(2)

# Set the text style


style_btn.set_text_color(lv.color_white())

# Create a red style. Change only some colors.


style_btn_red.init()
style_btn_red.set_bg_color(lv.palette_main(lv.PALETTE.RED))
style_btn_red.set_bg_grad_color(lv.palette_lighten(lv.PALETTE.RED, 2))

# Create a style for the pressed state.


style_btn_pressed.init()
style_btn_pressed.set_bg_color(lv.palette_main(lv.PALETTE.BLUE))
style_btn_pressed.set_bg_grad_color(lv.palette_darken(lv.PALETTE.RED, 3))

# Create a button and use the new styles


btn = lv.btn(lv.scr_act()) # Add a button the current screen
# Remove the styles coming from the theme
# Note that size and position are also stored as style properties
# so lv_obj_remove_style_all will remove the set size and position too
btn.remove_style_all() # Remove the styles coming from the theme
btn.set_pos(10, 10) # Set its position
btn.set_size(120, 50) # Set its size
btn.add_style(style_btn, 0)
btn.add_style(style_btn_pressed, lv.STATE.PRESSED)

label = lv.label(btn) # Add a label to the button


label.set_text("Button") # Set the labels text
label.center()

# Create a slider in the center of the display


slider = lv.slider(lv.scr_act())
slider.set_width(200) # Set the width
slider.center() # Align to the␣
,→center of the parent (screen)

# Create another button and use the red style too


btn2 = lv.btn(lv.scr_act())
btn2.remove_style_all() # Remove the styles coming from the theme
btn2.set_pos(10, 80) # Set its position
btn2.set_size(120, 50) # Set its size
btn2.add_style(style_btn, 0)
btn2.add_style(style_btn_red, 0)
btn2.add_style(style_btn_pressed, lv.STATE.PRESSED)
btn2.set_style_radius(lv.RADIUS_CIRCLE, 0) # Add a local style

(continues on next page)

2.1. Get started 12


LVGL Documentation 9.0

(continued from previous page)


label = lv.label(btn2) # Add a label to the button
label.set_text("Button 2") # Set the labels text
label.center()

2.1.4 Create a slider and write its value on a label

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_SLIDER

static lv_obj_t * label;

static void slider_event_cb(lv_event_t * e)


{
lv_obj_t * slider = lv_event_get_target(e);

/*Refresh the text*/


lv_label_set_text_fmt(label, "%"LV_PRId32, lv_slider_get_value(slider));
lv_obj_align_to(label, slider, LV_ALIGN_OUT_TOP_MID, 0, -15); /*Align top of␣
,→the slider*/

/**
* Create a slider and write its value on a label.
*/
void lv_example_get_started_4(void)
{
/*Create a slider in the center of the display*/
lv_obj_t * slider = lv_slider_create(lv_scr_act());
lv_obj_set_width(slider, 200); /*Set the width*/
lv_obj_center(slider); /*Align to the center of␣
,→the parent (screen)*/

lv_obj_add_event(slider, slider_event_cb, LV_EVENT_VALUE_CHANGED, NULL); /


,→*Assign an event function*/

/*Create a label above the slider*/


label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "0");
lv_obj_align_to(label, slider, LV_ALIGN_OUT_TOP_MID, 0, -15); /*Align top of␣
,→the slider*/

#endif

def slider_event_cb(e):
slider = e.get_target_obj()

# Refresh the text


label.set_text(str(slider.get_value()))

#
# Create a slider and write its value on a label.
#
(continues on next page)

2.1. Get started 13


LVGL Documentation 9.0

(continued from previous page)

# Create a slider in the center of the display


slider = lv.slider(lv.scr_act())
slider.set_width(200) # Set the width
slider.center() # Align to the␣
,→center of the parent (screen)

slider.add_event(slider_event_cb, lv.EVENT.VALUE_CHANGED, None) # Assign an event␣


,→function

# Create a label above the slider


label = lv.label(lv.scr_act())
label.set_text("0")
label.align_to(slider, lv.ALIGN.OUT_TOP_MID, 0, -15) # Align below the␣
,→slider

2.2 Styles

2.2.1 Size styles

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_IMG

/**
* Using the Size, Position and Padding style properties
*/
void lv_example_style_1(void)
{
static lv_style_t style;
lv_style_init(&style);
lv_style_set_radius(&style, 5);

/*Make a gradient*/
lv_style_set_width(&style, 150);
lv_style_set_height(&style, LV_SIZE_CONTENT);

lv_style_set_pad_ver(&style, 20);
lv_style_set_pad_left(&style, 5);

lv_style_set_x(&style, lv_pct(50));
lv_style_set_y(&style, 80);

/*Create an object with the new style*/


lv_obj_t * obj = lv_obj_create(lv_scr_act());
lv_obj_add_style(obj, &style, 0);

lv_obj_t * label = lv_label_create(obj);


lv_label_set_text(label, "Hello");
}

#endif

2.2. Styles 14
LVGL Documentation 9.0

#
# Using the Size, Position and Padding style properties
#
style = lv.style_t()
style.init()
style.set_radius(5)

# Make a gradient
style.set_width(150)
style.set_height(lv.SIZE_CONTENT)

style.set_pad_ver(20)
style.set_pad_left(5)

style.set_x(lv.pct(50))
style.set_y(80)

# Create an object with the new style


obj = lv.obj(lv.scr_act())
obj.add_style(style, 0)

label = lv.label(obj)
label.set_text("Hello")

2.2.2 Background styles

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES

/**
* Using the background style properties
*/
void lv_example_style_2(void)
{
static lv_style_t style;
lv_style_init(&style);
lv_style_set_radius(&style, 5);

/*Make a gradient*/
lv_style_set_bg_opa(&style, LV_OPA_COVER);
static lv_grad_dsc_t grad;
grad.dir = LV_GRAD_DIR_VER;
grad.stops_count = 2;
grad.stops[0].color = lv_palette_lighten(LV_PALETTE_GREY, 1);
grad.stops[1].color = lv_palette_main(LV_PALETTE_BLUE);

/*Shift the gradient to the bottom*/


grad.stops[0].frac = 128;
grad.stops[1].frac = 192;

lv_style_set_bg_grad(&style, &grad);

/*Create an object with the new style*/


lv_obj_t * obj = lv_obj_create(lv_scr_act());
(continues on next page)

2.2. Styles 15
LVGL Documentation 9.0

(continued from previous page)


lv_obj_add_style(obj, &style, 0);
lv_obj_center(obj);
}

#endif

#
# Using the background style properties
#
style = lv.style_t()
style.init()
style.set_radius(5)

# Make a gradient
style.set_bg_opa(lv.OPA.COVER)
style.set_bg_color(lv.palette_lighten(lv.PALETTE.GREY, 1))
style.set_bg_grad_color(lv.palette_main(lv.PALETTE.BLUE))
style.set_bg_grad_dir(lv.GRAD_DIR.VER)

# Shift the gradient to the bottom


style.set_bg_main_stop(128)
style.set_bg_grad_stop(192)

# Create an object with the new style


obj = lv.obj(lv.scr_act())
obj.add_style(style, 0)
obj.center()

2.2.3 Border styles

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES

/**
* Using the border style properties
*/
void lv_example_style_3(void)
{
static lv_style_t style;
lv_style_init(&style);

/*Set a background color and a radius*/


lv_style_set_radius(&style, 10);
lv_style_set_bg_opa(&style, LV_OPA_COVER);
lv_style_set_bg_color(&style, lv_palette_lighten(LV_PALETTE_GREY, 1));

/*Add border to the bottom+right*/


lv_style_set_border_color(&style, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_border_width(&style, 5);
lv_style_set_border_opa(&style, LV_OPA_50);
lv_style_set_border_side(&style, LV_BORDER_SIDE_BOTTOM | LV_BORDER_SIDE_RIGHT);

/*Create an object with the new style*/


lv_obj_t * obj = lv_obj_create(lv_scr_act());
(continues on next page)

2.2. Styles 16
LVGL Documentation 9.0

(continued from previous page)


lv_obj_add_style(obj, &style, 0);
lv_obj_center(obj);
}

#endif

#
# Using the border style properties
#
style = lv.style_t()
style.init()

# Set a background color and a radius


style.set_radius(10)
style.set_bg_opa(lv.OPA.COVER)
style.set_bg_color(lv.palette_lighten(lv.PALETTE.GREY, 1))

# Add border to the bottom+right


style.set_border_color(lv.palette_main(lv.PALETTE.BLUE))
style.set_border_width(5)
style.set_border_opa(lv.OPA._50)
style.set_border_side(lv.BORDER_SIDE.BOTTOM | lv.BORDER_SIDE.RIGHT)

# Create an object with the new style


obj = lv.obj(lv.scr_act())
obj.add_style(style, 0)
obj.center()

2.2.4 Outline styles

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES

/**
* Using the outline style properties
*/
void lv_example_style_4(void)
{
static lv_style_t style;
lv_style_init(&style);

/*Set a background color and a radius*/


lv_style_set_radius(&style, 5);
lv_style_set_bg_opa(&style, LV_OPA_COVER);
lv_style_set_bg_color(&style, lv_palette_lighten(LV_PALETTE_GREY, 1));

/*Add outline*/
lv_style_set_outline_width(&style, 2);
lv_style_set_outline_color(&style, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_outline_pad(&style, 8);

/*Create an object with the new style*/


lv_obj_t * obj = lv_obj_create(lv_scr_act());
lv_obj_add_style(obj, &style, 0);
(continues on next page)

2.2. Styles 17
LVGL Documentation 9.0

(continued from previous page)


lv_obj_center(obj);
}

#endif

#
# Using the outline style properties
#

style = lv.style_t()
style.init()

# Set a background color and a radius


style.set_radius(5)
style.set_bg_opa(lv.OPA.COVER)
style.set_bg_color(lv.palette_lighten(lv.PALETTE.GREY, 1))

# Add outline
style.set_outline_width(2)
style.set_outline_color(lv.palette_main(lv.PALETTE.BLUE))
style.set_outline_pad(8)

# Create an object with the new style


obj = lv.obj(lv.scr_act())
obj.add_style(style, 0)
obj.center()

2.2.5 Shadow styles

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES

/**
* Using the Shadow style properties
*/
void lv_example_style_5(void)
{
static lv_style_t style;
lv_style_init(&style);

/*Set a background color and a radius*/


lv_style_set_radius(&style, 5);
lv_style_set_bg_opa(&style, LV_OPA_COVER);
lv_style_set_bg_color(&style, lv_palette_lighten(LV_PALETTE_GREY, 1));

/*Add a shadow*/
lv_style_set_shadow_width(&style, 55);
lv_style_set_shadow_color(&style, lv_palette_main(LV_PALETTE_BLUE));
// lv_style_set_shadow_ofs_x(&style, 10);
// lv_style_set_shadow_ofs_y(&style, 20);

/*Create an object with the new style*/


lv_obj_t * obj = lv_obj_create(lv_scr_act());
(continues on next page)

2.2. Styles 18
LVGL Documentation 9.0

(continued from previous page)


lv_obj_add_style(obj, &style, 0);
lv_obj_center(obj);
}

#endif

#
# Using the Shadow style properties
#

style = lv.style_t()
style.init()

# Set a background color and a radius


style.set_radius(5)
style.set_bg_opa(lv.OPA.COVER)
style.set_bg_color(lv.palette_lighten(lv.PALETTE.GREY, 1))

# Add a shadow
style.set_shadow_width(8)
style.set_shadow_color(lv.palette_main(lv.PALETTE.BLUE))
style.set_shadow_ofs_x(10)
style.set_shadow_ofs_y(20)

# Create an object with the new style


obj = lv.obj(lv.scr_act())
obj.add_style(style, 0)
obj.center()

2.2.6 Image styles

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_IMG

/**
* Using the Image style properties
*/
void lv_example_style_6(void)
{
static lv_style_t style;
lv_style_init(&style);

/*Set a background color and a radius*/


lv_style_set_radius(&style, 5);
lv_style_set_bg_opa(&style, LV_OPA_COVER);
lv_style_set_bg_color(&style, lv_palette_lighten(LV_PALETTE_GREY, 3));
lv_style_set_border_width(&style, 2);
lv_style_set_border_color(&style, lv_palette_main(LV_PALETTE_BLUE));

lv_style_set_img_recolor(&style, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_img_recolor_opa(&style, LV_OPA_50);
lv_style_set_transform_angle(&style, 300);

/*Create an object with the new style*/


(continues on next page)

2.2. Styles 19
LVGL Documentation 9.0

(continued from previous page)


lv_obj_t * obj = lv_img_create(lv_scr_act());
lv_obj_add_style(obj, &style, 0);

LV_IMG_DECLARE(img_cogwheel_argb);
lv_img_set_src(obj, &img_cogwheel_argb);

lv_obj_center(obj);
}

#endif

# Create an image from the png file


try:
with open('../assets/img_cogwheel_argb.png', 'rb') as f:
png_data = f.read()
except:
print("Could not find img_cogwheel_argb.png")
sys.exit()

img_cogwheel_argb = lv.img_dsc_t({
'data_size': len(png_data),
'data': png_data
})

#
# Using the Image style properties
#
style = lv.style_t()
style.init()

# Set a background color and a radius


style.set_radius(5)
style.set_bg_opa(lv.OPA.COVER)
style.set_bg_color(lv.palette_lighten(lv.PALETTE.GREY, 3))
style.set_border_width(2)
style.set_border_color(lv.palette_main(lv.PALETTE.BLUE))

style.set_img_recolor(lv.palette_main(lv.PALETTE.BLUE))
style.set_img_recolor_opa(lv.OPA._50)
# style.set_transform_angle(300)

# Create an object with the new style


obj = lv.img(lv.scr_act())
obj.add_style(style, 0)

obj.set_src(img_cogwheel_argb)

obj.center()

2.2. Styles 20
LVGL Documentation 9.0

2.2.7 Text styles

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_LABEL

/**
* Using the text style properties
*/
void lv_example_style_8(void)
{
static lv_style_t style;
lv_style_init(&style);

lv_style_set_radius(&style, 5);
lv_style_set_bg_opa(&style, LV_OPA_COVER);
lv_style_set_bg_color(&style, lv_palette_lighten(LV_PALETTE_GREY, 2));
lv_style_set_border_width(&style, 2);
lv_style_set_border_color(&style, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_pad_all(&style, 10);

lv_style_set_text_color(&style, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_text_letter_space(&style, 5);
lv_style_set_text_line_space(&style, 20);
lv_style_set_text_decor(&style, LV_TEXT_DECOR_UNDERLINE);

/*Create an object with the new style*/


lv_obj_t * obj = lv_label_create(lv_scr_act());
lv_obj_add_style(obj, &style, 0);
lv_label_set_text(obj, "Text of\n"
"a label");

lv_obj_center(obj);
}

#endif

#
# Using the text style properties
#

style = lv.style_t()
style.init()

style.set_radius(5)
style.set_bg_opa(lv.OPA.COVER)
style.set_bg_color(lv.palette_lighten(lv.PALETTE.GREY, 3))
style.set_border_width(2)
style.set_border_color(lv.palette_main(lv.PALETTE.BLUE))
style.set_pad_all(10)

style.set_text_color(lv.palette_main(lv.PALETTE.BLUE))
style.set_text_letter_space(5)
style.set_text_line_space(20)
style.set_text_decor(lv.TEXT_DECOR.UNDERLINE)

# Create an object with the new style


obj = lv.label(lv.scr_act())
(continues on next page)

2.2. Styles 21
LVGL Documentation 9.0

(continued from previous page)


obj.add_style(style, 0)
obj.set_text("Text of\n"
"a label")

obj.center()

2.2.8 Line styles

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_LINE

/**
* Using the line style properties
*/
void lv_example_style_9(void)
{
static lv_style_t style;
lv_style_init(&style);

lv_style_set_line_color(&style, lv_palette_main(LV_PALETTE_GREY));
lv_style_set_line_width(&style, 6);
lv_style_set_line_rounded(&style, true);

/*Create an object with the new style*/


lv_obj_t * obj = lv_line_create(lv_scr_act());
lv_obj_add_style(obj, &style, 0);

static lv_point_t p[] = {{10, 30}, {30, 50}, {100, 0}};


lv_line_set_points(obj, p, 3);

lv_obj_center(obj);
}

#endif

#
# Using the line style properties
#

style = lv.style_t()
style.init()

style.set_line_color(lv.palette_main(lv.PALETTE.GREY))
style.set_line_width(6)
style.set_line_rounded(True)

# Create an object with the new style


obj = lv.line(lv.scr_act())
obj.add_style(style, 0)
p = [ {"x":10, "y":30},
{"x":30, "y":50},
{"x":100, "y":0}]

(continues on next page)

2.2. Styles 22
LVGL Documentation 9.0

(continued from previous page)


obj.set_points(p, 3)

obj.center()

2.2.9 Transition

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_IMG

/**
* Creating a transition
*/
void lv_example_style_10(void)
{
static const lv_style_prop_t props[] = {LV_STYLE_BG_COLOR, LV_STYLE_BORDER_COLOR,␣
,→LV_STYLE_BORDER_WIDTH, 0};

/* A default transition
* Make it fast (100ms) and start with some delay (200 ms)*/
static lv_style_transition_dsc_t trans_def;
lv_style_transition_dsc_init(&trans_def, props, lv_anim_path_linear, 100, 200,␣
,→NULL);

/* A special transition when going to pressed state


* Make it slow (500 ms) but start without delay*/
static lv_style_transition_dsc_t trans_pr;
lv_style_transition_dsc_init(&trans_pr, props, lv_anim_path_linear, 500, 0, NULL);

static lv_style_t style_def;


lv_style_init(&style_def);
lv_style_set_transition(&style_def, &trans_def);

static lv_style_t style_pr;


lv_style_init(&style_pr);
lv_style_set_bg_color(&style_pr, lv_palette_main(LV_PALETTE_RED));
lv_style_set_border_width(&style_pr, 6);
lv_style_set_border_color(&style_pr, lv_palette_darken(LV_PALETTE_RED, 3));
lv_style_set_transition(&style_pr, &trans_pr);

/*Create an object with the new style_pr*/


lv_obj_t * obj = lv_obj_create(lv_scr_act());
lv_obj_add_style(obj, &style_def, 0);
lv_obj_add_style(obj, &style_pr, LV_STATE_PRESSED);

lv_obj_center(obj);
}

#endif

#
# Creating a transition
#

props = [lv.STYLE.BG_COLOR, lv.STYLE.BORDER_COLOR, lv.STYLE.BORDER_WIDTH, 0]


(continues on next page)

2.2. Styles 23
LVGL Documentation 9.0

(continued from previous page)

# A default transition
# Make it fast (100ms) and start with some delay (200 ms)

trans_def = lv.style_transition_dsc_t()
trans_def.init(props, lv.anim_t.path_linear, 100, 200, None)

# A special transition when going to pressed state


# Make it slow (500 ms) but start without delay

trans_pr = lv.style_transition_dsc_t()
trans_pr.init(props, lv.anim_t.path_linear, 500, 0, None)

style_def = lv.style_t()
style_def.init()
style_def.set_transition(trans_def)

style_pr = lv.style_t()
style_pr.init()
style_pr.set_bg_color(lv.palette_main(lv.PALETTE.RED))
style_pr.set_border_width(6)
style_pr.set_border_color(lv.palette_darken(lv.PALETTE.RED, 3))
style_pr.set_transition(trans_pr)

# Create an object with the new style_pr


obj = lv.obj(lv.scr_act())
obj.add_style(style_def, 0)
obj.add_style(style_pr, lv.STATE.PRESSED)

obj.center()

2.2.10 Using multiple styles

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_IMG

/**
* Using multiple styles
*/
void lv_example_style_11(void)
{
/*A base style*/
static lv_style_t style_base;
lv_style_init(&style_base);
lv_style_set_bg_color(&style_base, lv_palette_main(LV_PALETTE_LIGHT_BLUE));
lv_style_set_border_color(&style_base, lv_palette_darken(LV_PALETTE_LIGHT_BLUE,␣
,→3));

lv_style_set_border_width(&style_base, 2);
lv_style_set_radius(&style_base, 10);
lv_style_set_shadow_width(&style_base, 10);
lv_style_set_shadow_ofs_y(&style_base, 5);
lv_style_set_shadow_opa(&style_base, LV_OPA_50);
lv_style_set_text_color(&style_base, lv_color_white());
lv_style_set_width(&style_base, 100);
(continues on next page)

2.2. Styles 24
LVGL Documentation 9.0

(continued from previous page)


lv_style_set_height(&style_base, LV_SIZE_CONTENT);

/*Set only the properties that should be different*/


static lv_style_t style_warning;
lv_style_init(&style_warning);
lv_style_set_bg_color(&style_warning, lv_palette_main(LV_PALETTE_YELLOW));
lv_style_set_border_color(&style_warning, lv_palette_darken(LV_PALETTE_YELLOW,␣
,→3));

lv_style_set_text_color(&style_warning, lv_palette_darken(LV_PALETTE_YELLOW, 4));

/*Create an object with the base style only*/


lv_obj_t * obj_base = lv_obj_create(lv_scr_act());
lv_obj_add_style(obj_base, &style_base, 0);
lv_obj_align(obj_base, LV_ALIGN_LEFT_MID, 20, 0);

lv_obj_t * label = lv_label_create(obj_base);


lv_label_set_text(label, "Base");
lv_obj_center(label);

/*Create another object with the base style and earnings style too*/
lv_obj_t * obj_warning = lv_obj_create(lv_scr_act());
lv_obj_add_style(obj_warning, &style_base, 0);
lv_obj_add_style(obj_warning, &style_warning, 0);
lv_obj_align(obj_warning, LV_ALIGN_RIGHT_MID, -20, 0);

label = lv_label_create(obj_warning);
lv_label_set_text(label, "Warning");
lv_obj_center(label);
}

#endif

#
# Using multiple styles
#
# A base style

style_base = lv.style_t()
style_base.init()
style_base.set_bg_color(lv.palette_main(lv.PALETTE.LIGHT_BLUE))
style_base.set_border_color(lv.palette_darken(lv.PALETTE.LIGHT_BLUE, 3))
style_base.set_border_width(2)
style_base.set_radius(10)
style_base.set_shadow_width(10)
style_base.set_shadow_ofs_y(5)
style_base.set_shadow_opa(lv.OPA._50)
style_base.set_text_color(lv.color_white())
style_base.set_width(100)
style_base.set_height(lv.SIZE_CONTENT)

# Set only the properties that should be different


style_warning = lv.style_t()
style_warning.init()
style_warning.set_bg_color(lv.palette_main(lv.PALETTE.YELLOW))
style_warning.set_border_color(lv.palette_darken(lv.PALETTE.YELLOW, 3))
style_warning.set_text_color(lv.palette_darken(lv.PALETTE.YELLOW, 4))
(continues on next page)

2.2. Styles 25
LVGL Documentation 9.0

(continued from previous page)

# Create an object with the base style only


obj_base = lv.obj(lv.scr_act())
obj_base.add_style(style_base, 0)
obj_base.align(lv.ALIGN.LEFT_MID, 20, 0)

label = lv.label(obj_base)
label.set_text("Base")
label.center()

# Create another object with the base style and earnings style too
obj_warning = lv.obj(lv.scr_act())
obj_warning.add_style(style_base, 0)
obj_warning.add_style(style_warning, 0)
obj_warning.align(lv.ALIGN.RIGHT_MID, -20, 0)

label = lv.label(obj_warning)
label.set_text("Warning")
label.center()

2.2.11 Local styles

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_IMG

/**
* Local styles
*/
void lv_example_style_12(void)
{
static lv_style_t style;
lv_style_init(&style);
lv_style_set_bg_color(&style, lv_palette_main(LV_PALETTE_GREEN));
lv_style_set_border_color(&style, lv_palette_lighten(LV_PALETTE_GREEN, 3));
lv_style_set_border_width(&style, 3);

lv_obj_t * obj = lv_obj_create(lv_scr_act());


lv_obj_add_style(obj, &style, 0);

/*Overwrite the background color locally*/


lv_obj_set_style_bg_color(obj, lv_palette_main(LV_PALETTE_ORANGE), LV_PART_MAIN);

lv_obj_center(obj);
}

#endif

#
# Local styles
#

style = lv.style_t()
style.init()
style.set_bg_color(lv.palette_main(lv.PALETTE.GREEN))
(continues on next page)

2.2. Styles 26
LVGL Documentation 9.0

(continued from previous page)


style.set_border_color(lv.palette_lighten(lv.PALETTE.GREEN, 3))
style.set_border_width(3)

obj = lv.obj(lv.scr_act())
obj.add_style(style, 0)

# Overwrite the background color locally


obj.set_style_bg_color(lv.palette_main(lv.PALETTE.ORANGE), lv.PART.MAIN)

obj.center()

2.2.12 Add styles to parts and states

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_IMG

/**
* Add styles to parts and states
*/
void lv_example_style_13(void)
{
static lv_style_t style_indic;
lv_style_init(&style_indic);
lv_style_set_bg_color(&style_indic, lv_palette_lighten(LV_PALETTE_RED, 3));
lv_style_set_bg_grad_color(&style_indic, lv_palette_main(LV_PALETTE_RED));
lv_style_set_bg_grad_dir(&style_indic, LV_GRAD_DIR_HOR);

static lv_style_t style_indic_pr;


lv_style_init(&style_indic_pr);
lv_style_set_shadow_color(&style_indic_pr, lv_palette_main(LV_PALETTE_RED));
lv_style_set_shadow_width(&style_indic_pr, 10);
lv_style_set_shadow_spread(&style_indic_pr, 3);

/*Create an object with the new style_pr*/


lv_obj_t * obj = lv_slider_create(lv_scr_act());
lv_obj_add_style(obj, &style_indic, LV_PART_INDICATOR);
lv_obj_add_style(obj, &style_indic_pr, LV_PART_INDICATOR | LV_STATE_PRESSED);
lv_slider_set_value(obj, 70, LV_ANIM_OFF);
lv_obj_center(obj);
}

#endif

#
# Add styles to parts and states
#

style_indic = lv.style_t()
style_indic.init()
style_indic.set_bg_color(lv.palette_lighten(lv.PALETTE.RED, 3))
style_indic.set_bg_grad_color(lv.palette_main(lv.PALETTE.RED))
style_indic.set_bg_grad_dir(lv.GRAD_DIR.HOR)

style_indic_pr = lv.style_t()
(continues on next page)

2.2. Styles 27
LVGL Documentation 9.0

(continued from previous page)


style_indic_pr.init()
style_indic_pr.set_shadow_color(lv.palette_main(lv.PALETTE.RED))
style_indic_pr.set_shadow_width(10)
style_indic_pr.set_shadow_spread(3)

# Create an object with the new style_pr


obj = lv.slider(lv.scr_act())
obj.add_style(style_indic, lv.PART.INDICATOR)
obj.add_style(style_indic_pr, lv.PART.INDICATOR | lv.STATE.PRESSED)
obj.set_value(70, lv.ANIM.OFF)
obj.center()

2.2.13 Extending the current theme

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_IMG

static lv_style_t style_btn;

/*Will be called when the styles of the base theme are already added
to add new styles*/
static void new_theme_apply_cb(lv_theme_t * th, lv_obj_t * obj)
{
LV_UNUSED(th);

if(lv_obj_check_type(obj, &lv_btn_class)) {
lv_obj_add_style(obj, &style_btn, 0);
}
}

static void new_theme_init_and_set(void)


{
/*Initialize the styles*/
lv_style_init(&style_btn);
lv_style_set_bg_color(&style_btn, lv_palette_main(LV_PALETTE_GREEN));
lv_style_set_border_color(&style_btn, lv_palette_darken(LV_PALETTE_GREEN, 3));
lv_style_set_border_width(&style_btn, 3);

/*Initialize the new theme from the current theme*/


lv_theme_t * th_act = lv_disp_get_theme(NULL);
static lv_theme_t th_new;
th_new = *th_act;

/*Set the parent theme and the style apply callback for the new theme*/
lv_theme_set_parent(&th_new, th_act);
lv_theme_set_apply_cb(&th_new, new_theme_apply_cb);

/*Assign the new theme to the current display*/


lv_disp_set_theme(NULL, &th_new);
}

(continues on next page)

2.2. Styles 28
LVGL Documentation 9.0

(continued from previous page)


/**
* Extending the current theme
*/
void lv_example_style_14(void)
{
lv_obj_t * btn;
lv_obj_t * label;

btn = lv_btn_create(lv_scr_act());
lv_obj_align(btn, LV_ALIGN_TOP_MID, 0, 20);

label = lv_label_create(btn);
lv_label_set_text(label, "Original theme");

new_theme_init_and_set();

btn = lv_btn_create(lv_scr_act());
lv_obj_align(btn, LV_ALIGN_BOTTOM_MID, 0, -20);

label = lv_label_create(btn);
lv_label_set_text(label, "New theme");
}

#endif

# Will be called when the styles of the base theme are already added
# to add new styles

class NewTheme(lv.theme_t):
def __init__(self):
super().__init__()
# Initialize the styles
self.style_btn = lv.style_t()
self.style_btn.init()
self.style_btn.set_bg_color(lv.palette_main(lv.PALETTE.GREEN))
self.style_btn.set_border_color(lv.palette_darken(lv.PALETTE.GREEN, 3))
self.style_btn.set_border_width(3)

# This theme is based on active theme


th_act = lv.theme_get_from_obj(lv.scr_act())
# This theme will be applied only after base theme is applied
self.set_parent(th_act)

class ExampleStyle_14:

def __init__(self):
#
# Extending the current theme
#

btn = lv.btn(lv.scr_act())
btn.align(lv.ALIGN.TOP_MID, 0, 20)

label = lv.label(btn)
(continues on next page)

2.2. Styles 29
LVGL Documentation 9.0

(continued from previous page)


label.set_text("Original theme")

self.new_theme_init_and_set()

btn = lv.btn(lv.scr_act())
btn.align(lv.ALIGN.BOTTOM_MID, 0, -20)

label = lv.label(btn)
label.set_text("New theme")

def new_theme_apply_cb(self, th, obj):


print(th,obj)
if obj.get_class() == lv.btn_class:
obj.add_style(self.th_new.style_btn, 0)

def new_theme_init_and_set(self):
print("new_theme_init_and_set")
# Initialize the new theme from the current theme
self.th_new = NewTheme()
self.th_new.set_apply_cb(self.new_theme_apply_cb)
lv.disp_get_default().set_theme(self.th_new)

exampleStyle_14 = ExampleStyle_14()

2.2.14 Opacity and Transformations

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_BTN && LV_USE_LABEL

/**
* Opacity and Transformations
*/
void lv_example_style_15(void)
{
lv_obj_t * btn;
lv_obj_t * label;

/*Normal button*/
btn = lv_btn_create(lv_scr_act());
lv_obj_set_size(btn, 100, 40);
lv_obj_align(btn, LV_ALIGN_CENTER, 0, -70);

label = lv_label_create(btn);
lv_label_set_text(label, "Normal");
lv_obj_center(label);

/*Set opacity
*The button and the label is rendered to a layer first and that layer is␣
,→blended*/

btn = lv_btn_create(lv_scr_act());
lv_obj_set_size(btn, 100, 40);
(continues on next page)

2.2. Styles 30
LVGL Documentation 9.0

(continued from previous page)


lv_obj_set_style_opa(btn, LV_OPA_50, 0);
lv_obj_align(btn, LV_ALIGN_CENTER, 0, 0);

label = lv_label_create(btn);
lv_label_set_text(label, "Opa:50%");
lv_obj_center(label);

/*Set transformations
*The button and the label is rendered to a layer first and that layer is␣
,→transformed*/

btn = lv_btn_create(lv_scr_act());
lv_obj_set_size(btn, 100, 40);
lv_obj_set_style_transform_angle(btn, 150, 0); /*15 deg*/
lv_obj_set_style_transform_zoom(btn, 256 + 64, 0); /*1.25x*/
lv_obj_set_style_transform_pivot_x(btn, 50, 0);
lv_obj_set_style_transform_pivot_y(btn, 20, 0);
lv_obj_set_style_opa(btn, LV_OPA_50, 0);
lv_obj_align(btn, LV_ALIGN_CENTER, 0, 70);

label = lv_label_create(btn);
lv_label_set_text(label, "Transf.");
lv_obj_center(label);
}

#endif

Error encountered while trying to open /home/runner/work/lvgl/lvgl/examples/styles/lv_


,→example_style_15.py

2.3 Animations

2.3.1 Start animation on an event

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_SWITCH

static void anim_x_cb(void * var, int32_t v)


{
lv_obj_set_x(var, v);
}

static void sw_event_cb(lv_event_t * e)


{
lv_obj_t * sw = lv_event_get_target(e);
lv_obj_t * label = lv_event_get_user_data(e);

if(lv_obj_has_state(sw, LV_STATE_CHECKED)) {
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, label);
lv_anim_set_values(&a, lv_obj_get_x(label), 100);
lv_anim_set_time(&a, 500);
lv_anim_set_exec_cb(&a, anim_x_cb);
(continues on next page)

2.3. Animations 31
LVGL Documentation 9.0

(continued from previous page)


lv_anim_set_path_cb(&a, lv_anim_path_overshoot);
lv_anim_start(&a);
}
else {
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, label);
lv_anim_set_values(&a, lv_obj_get_x(label), -lv_obj_get_width(label));
lv_anim_set_time(&a, 500);
lv_anim_set_exec_cb(&a, anim_x_cb);
lv_anim_set_path_cb(&a, lv_anim_path_ease_in);
lv_anim_start(&a);
}

/**
* Start animation on an event
*/
void lv_example_anim_1(void)
{
lv_obj_t * label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "Hello animations!");
lv_obj_set_pos(label, 100, 10);

lv_obj_t * sw = lv_switch_create(lv_scr_act());
lv_obj_center(sw);
lv_obj_add_state(sw, LV_STATE_CHECKED);
lv_obj_add_event(sw, sw_event_cb, LV_EVENT_VALUE_CHANGED, label);
}

#endif

def anim_x_cb(label, v):


label.set_x(v)

def sw_event_cb(e,label):
sw = e.get_target_obj()

if sw.has_state(lv.STATE.CHECKED):
a = lv.anim_t()
a.init()
a.set_var(label)
a.set_values(label.get_x(), 100)
a.set_time(500)
a.set_path_cb(lv.anim_t.path_overshoot)
a.set_custom_exec_cb(lambda a,val: anim_x_cb(label,val))
lv.anim_t.start(a)
else:
a = lv.anim_t()
a.init()
a.set_var(label)
a.set_values(label.get_x(), -label.get_width())
a.set_time(500)
a.set_path_cb(lv.anim_t.path_ease_in)
(continues on next page)

2.3. Animations 32
LVGL Documentation 9.0

(continued from previous page)


a.set_custom_exec_cb(lambda a,val: anim_x_cb(label,val))
lv.anim_t.start(a)

#
# Start animation on an event
#

label = lv.label(lv.scr_act())
label.set_text("Hello animations!")
label.set_pos(100, 10)

sw = lv.switch(lv.scr_act())
sw.center()
sw.add_state(lv.STATE.CHECKED)
sw.add_event(lambda e: sw_event_cb(e,label), lv.EVENT.VALUE_CHANGED, None)

2.3.2 Playback animation

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_SWITCH

static void anim_x_cb(void * var, int32_t v)


{
lv_obj_set_x(var, v);
}

static void anim_size_cb(void * var, int32_t v)


{
lv_obj_set_size(var, v, v);
}

/**
* Create a playback animation
*/
void lv_example_anim_2(void)
{

lv_obj_t * obj = lv_obj_create(lv_scr_act());


lv_obj_set_style_bg_color(obj, lv_palette_main(LV_PALETTE_RED), 0);
lv_obj_set_style_radius(obj, LV_RADIUS_CIRCLE, 0);

lv_obj_align(obj, LV_ALIGN_LEFT_MID, 10, 0);

lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, obj);
lv_anim_set_values(&a, 10, 50);
lv_anim_set_time(&a, 1000);
lv_anim_set_playback_delay(&a, 100);
(continues on next page)

2.3. Animations 33
LVGL Documentation 9.0

(continued from previous page)


lv_anim_set_playback_time(&a, 300);
lv_anim_set_repeat_delay(&a, 500);
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
lv_anim_set_path_cb(&a, lv_anim_path_ease_in_out);

lv_anim_set_exec_cb(&a, anim_size_cb);
lv_anim_start(&a);
lv_anim_set_exec_cb(&a, anim_x_cb);
lv_anim_set_values(&a, 10, 240);
lv_anim_start(&a);
}

#endif

def anim_x_cb(obj, v):


obj.set_x(v)

def anim_size_cb(obj, v):


obj.set_size(v, v)

#
# Create a playback animation
#
obj = lv.obj(lv.scr_act())
obj.set_style_bg_color(lv.palette_main(lv.PALETTE.RED), 0)
obj.set_style_radius(lv.RADIUS_CIRCLE, 0)

obj.align(lv.ALIGN.LEFT_MID, 10, 0)

a1 = lv.anim_t()
a1.init()
a1.set_var(obj)
a1.set_values(10, 50)
a1.set_time(1000)
a1.set_playback_delay(100)
a1.set_playback_time(300)
a1.set_repeat_delay(500)
a1.set_repeat_count(lv.ANIM_REPEAT_INFINITE)
a1.set_path_cb(lv.anim_t.path_ease_in_out)
a1.set_custom_exec_cb(lambda a1,val: anim_size_cb(obj,val))
lv.anim_t.start(a1)

a2 = lv.anim_t()
a2.init()
a2.set_var(obj)
a2.set_values(10, 240)
a2.set_time(1000)
a2.set_playback_delay(100)
a2.set_playback_time(300)
a2.set_repeat_delay(500)
a2.set_repeat_count(lv.ANIM_REPEAT_INFINITE)
a2.set_path_cb(lv.anim_t.path_ease_in_out)
a2.set_custom_exec_cb(lambda a1,val: anim_x_cb(obj,val))
lv.anim_t.start(a2)

2.3. Animations 34
LVGL Documentation 9.0

2.3.3 Animation timeline

#include "../lv_examples.h"
#if LV_USE_FLEX && LV_BUILD_EXAMPLES

static lv_anim_timeline_t * anim_timeline = NULL;

static lv_obj_t * obj1 = NULL;


static lv_obj_t * obj2 = NULL;
static lv_obj_t * obj3 = NULL;

static const lv_coord_t obj_width = 90;


static const lv_coord_t obj_height = 70;

static void set_width(void * var, int32_t v)


{
lv_obj_set_width((lv_obj_t *)var, v);
}

static void set_height(void * var, int32_t v)


{
lv_obj_set_height((lv_obj_t *)var, v);
}

static void anim_timeline_create(void)


{
/* obj1 */
lv_anim_t a1;
lv_anim_init(&a1);
lv_anim_set_var(&a1, obj1);
lv_anim_set_values(&a1, 0, obj_width);
lv_anim_set_early_apply(&a1, false);
lv_anim_set_exec_cb(&a1, (lv_anim_exec_xcb_t)set_width);
lv_anim_set_path_cb(&a1, lv_anim_path_overshoot);
lv_anim_set_time(&a1, 300);

lv_anim_t a2;
lv_anim_init(&a2);
lv_anim_set_var(&a2, obj1);
lv_anim_set_values(&a2, 0, obj_height);
lv_anim_set_early_apply(&a2, false);
lv_anim_set_exec_cb(&a2, (lv_anim_exec_xcb_t)set_height);
lv_anim_set_path_cb(&a2, lv_anim_path_ease_out);
lv_anim_set_time(&a2, 300);

/* obj2 */
lv_anim_t a3;
lv_anim_init(&a3);
lv_anim_set_var(&a3, obj2);
lv_anim_set_values(&a3, 0, obj_width);
lv_anim_set_early_apply(&a3, false);
lv_anim_set_exec_cb(&a3, (lv_anim_exec_xcb_t)set_width);
lv_anim_set_path_cb(&a3, lv_anim_path_overshoot);
lv_anim_set_time(&a3, 300);

lv_anim_t a4;
lv_anim_init(&a4);
(continues on next page)

2.3. Animations 35
LVGL Documentation 9.0

(continued from previous page)


lv_anim_set_var(&a4, obj2);
lv_anim_set_values(&a4, 0, obj_height);
lv_anim_set_early_apply(&a4, false);
lv_anim_set_exec_cb(&a4, (lv_anim_exec_xcb_t)set_height);
lv_anim_set_path_cb(&a4, lv_anim_path_ease_out);
lv_anim_set_time(&a4, 300);

/* obj3 */
lv_anim_t a5;
lv_anim_init(&a5);
lv_anim_set_var(&a5, obj3);
lv_anim_set_values(&a5, 0, obj_width);
lv_anim_set_early_apply(&a5, false);
lv_anim_set_exec_cb(&a5, (lv_anim_exec_xcb_t)set_width);
lv_anim_set_path_cb(&a5, lv_anim_path_overshoot);
lv_anim_set_time(&a5, 300);

lv_anim_t a6;
lv_anim_init(&a6);
lv_anim_set_var(&a6, obj3);
lv_anim_set_values(&a6, 0, obj_height);
lv_anim_set_early_apply(&a6, false);
lv_anim_set_exec_cb(&a6, (lv_anim_exec_xcb_t)set_height);
lv_anim_set_path_cb(&a6, lv_anim_path_ease_out);
lv_anim_set_time(&a6, 300);

/* Create anim timeline */


anim_timeline = lv_anim_timeline_create();
lv_anim_timeline_add(anim_timeline, 0, &a1);
lv_anim_timeline_add(anim_timeline, 0, &a2);
lv_anim_timeline_add(anim_timeline, 200, &a3);
lv_anim_timeline_add(anim_timeline, 200, &a4);
lv_anim_timeline_add(anim_timeline, 400, &a5);
lv_anim_timeline_add(anim_timeline, 400, &a6);
}

static void btn_start_event_handler(lv_event_t * e)


{
lv_obj_t * btn = lv_event_get_target(e);

if(!anim_timeline) {
anim_timeline_create();
}

bool reverse = lv_obj_has_state(btn, LV_STATE_CHECKED);


lv_anim_timeline_set_reverse(anim_timeline, reverse);
lv_anim_timeline_start(anim_timeline);
}

static void btn_del_event_handler(lv_event_t * e)


{
LV_UNUSED(e);
if(anim_timeline) {
lv_anim_timeline_del(anim_timeline);
anim_timeline = NULL;
}
}
(continues on next page)

2.3. Animations 36
LVGL Documentation 9.0

(continued from previous page)

static void btn_stop_event_handler(lv_event_t * e)


{
LV_UNUSED(e);
if(anim_timeline) {
lv_anim_timeline_stop(anim_timeline);
}
}

static void slider_prg_event_handler(lv_event_t * e)


{
lv_obj_t * slider = lv_event_get_target(e);

if(!anim_timeline) {
anim_timeline_create();
}

int32_t progress = lv_slider_get_value(slider);


lv_anim_timeline_set_progress(anim_timeline, progress);
}

/**
* Create an animation timeline
*/
void lv_example_anim_timeline_1(void)
{
lv_obj_t * par = lv_scr_act();
lv_obj_set_flex_flow(par, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(par, LV_FLEX_ALIGN_SPACE_AROUND, LV_FLEX_ALIGN_CENTER, LV_
,→FLEX_ALIGN_CENTER);

/* create btn_start */
lv_obj_t * btn_start = lv_btn_create(par);
lv_obj_add_event(btn_start, btn_start_event_handler, LV_EVENT_VALUE_CHANGED,␣
,→NULL);

lv_obj_add_flag(btn_start, LV_OBJ_FLAG_IGNORE_LAYOUT);
lv_obj_add_flag(btn_start, LV_OBJ_FLAG_CHECKABLE);
lv_obj_align(btn_start, LV_ALIGN_TOP_MID, -100, 20);

lv_obj_t * label_start = lv_label_create(btn_start);


lv_label_set_text(label_start, "Start");
lv_obj_center(label_start);

/* create btn_del */
lv_obj_t * btn_del = lv_btn_create(par);
lv_obj_add_event(btn_del, btn_del_event_handler, LV_EVENT_CLICKED, NULL);
lv_obj_add_flag(btn_del, LV_OBJ_FLAG_IGNORE_LAYOUT);
lv_obj_align(btn_del, LV_ALIGN_TOP_MID, 0, 20);

lv_obj_t * label_del = lv_label_create(btn_del);


lv_label_set_text(label_del, "Delete");
lv_obj_center(label_del);

/* create btn_stop */
lv_obj_t * btn_stop = lv_btn_create(par);
lv_obj_add_event(btn_stop, btn_stop_event_handler, LV_EVENT_CLICKED, NULL);
lv_obj_add_flag(btn_stop, LV_OBJ_FLAG_IGNORE_LAYOUT);
(continues on next page)

2.3. Animations 37
LVGL Documentation 9.0

(continued from previous page)


lv_obj_align(btn_stop, LV_ALIGN_TOP_MID, 100, 20);

lv_obj_t * label_stop = lv_label_create(btn_stop);


lv_label_set_text(label_stop, "Stop");
lv_obj_center(label_stop);

/* create slider_prg */
lv_obj_t * slider_prg = lv_slider_create(par);
lv_obj_add_event(slider_prg, slider_prg_event_handler, LV_EVENT_VALUE_CHANGED,␣
,→NULL);

lv_obj_add_flag(slider_prg, LV_OBJ_FLAG_IGNORE_LAYOUT);
lv_obj_align(slider_prg, LV_ALIGN_BOTTOM_MID, 0, -20);
lv_slider_set_range(slider_prg, 0, 65535);

/* create 3 objects */
obj1 = lv_obj_create(par);
lv_obj_set_size(obj1, obj_width, obj_height);

obj2 = lv_obj_create(par);
lv_obj_set_size(obj2, obj_width, obj_height);

obj3 = lv_obj_create(par);
lv_obj_set_size(obj3, obj_width, obj_height);
}

#endif

class LV_ExampleAnimTimeline_1(object):

def __init__(self):
self.obj_width = 120
self.obj_height = 150
#
# Create an animation timeline
#

self.par = lv.scr_act()
self.par.set_flex_flow(lv.FLEX_FLOW.ROW)
self.par.set_flex_align(lv.FLEX_ALIGN.SPACE_AROUND, lv.FLEX_ALIGN.CENTER, lv.
,→FLEX_ALIGN.CENTER)

self.btn_run = lv.btn(self.par)
self.btn_run.add_event(self.btn_run_event_handler, lv.EVENT.VALUE_CHANGED,␣
,→ None)
self.btn_run.add_flag(lv.obj.FLAG.IGNORE_LAYOUT)
self.btn_run.add_flag(lv.obj.FLAG.CHECKABLE)
self.btn_run.align(lv.ALIGN.TOP_MID, -50, 20)

self.label_run = lv.label(self.btn_run)
self.label_run.set_text("Run")
self.label_run.center()

self.btn_del = lv.btn(self.par)
self.btn_del.add_event(self.btn_del_event_handler, lv.EVENT.CLICKED, None)
self.btn_del.add_flag(lv.obj.FLAG.IGNORE_LAYOUT)
self.btn_del.align(lv.ALIGN.TOP_MID, 50, 20)
(continues on next page)

2.3. Animations 38
LVGL Documentation 9.0

(continued from previous page)

self.label_del = lv.label(self.btn_del)
self.label_del.set_text("Stop")
self.label_del.center()

self.slider = lv.slider(self.par)
self.slider.add_event(self.slider_prg_event_handler, lv.EVENT.VALUE_CHANGED,␣
,→ None)
self.slider.add_flag(lv.obj.FLAG.IGNORE_LAYOUT)
self.slider.align(lv.ALIGN.BOTTOM_RIGHT, -20, -20)
self.slider.set_range(0, 65535)

self.obj1 = lv.obj(self.par)
self.obj1.set_size(self.obj_width, self.obj_height)

self.obj2 = lv.obj(self.par)
self.obj2.set_size(self.obj_width, self.obj_height)

self.obj3 = lv.obj(self.par)
self.obj3.set_size(self.obj_width, self.obj_height)

self.anim_timeline = None

def set_width(self,obj, v):


obj.set_width(v)

def set_height(self,obj, v):


obj.set_height(v)

def anim_timeline_create(self):
# obj1
self.a1 = lv.anim_t()
self.a1.init()
self.a1.set_values(0, self.obj_width)
self.a1.set_early_apply(False)
self.a1.set_custom_exec_cb(lambda a,v: self.set_width(self.obj1,v))
self.a1.set_path_cb(lv.anim_t.path_overshoot)
self.a1.set_time(300)

self.a2 = lv.anim_t()
self.a2.init()
self.a2.set_values(0, self.obj_height)
self.a2.set_early_apply(False)
self.a2.set_custom_exec_cb(lambda a,v: self.set_height(self.obj1,v))
self.a2.set_path_cb(lv.anim_t.path_ease_out)
self.a2.set_time(300)

# obj2
self.a3=lv.anim_t()
self.a3.init()
self.a3.set_values(0, self.obj_width)
self.a3.set_early_apply(False)
self.a3.set_custom_exec_cb(lambda a,v: self.set_width(self.obj2,v))
self.a3.set_path_cb(lv.anim_t.path_overshoot)
self.a3.set_time(300)

self.a4 = lv.anim_t()
(continues on next page)

2.3. Animations 39
LVGL Documentation 9.0

(continued from previous page)


self.a4.init()
self.a4.set_values(0, self.obj_height)
self.a4.set_early_apply(False)
self.a4.set_custom_exec_cb(lambda a,v: self.set_height(self.obj2,v))
self.a4.set_path_cb(lv.anim_t.path_ease_out)
self.a4.set_time(300)

# obj3
self.a5 = lv.anim_t()
self.a5.init()
self.a5.set_values(0, self.obj_width)
self.a5.set_early_apply(False)
self.a5.set_custom_exec_cb(lambda a,v: self.set_width(self.obj3,v))
self.a5.set_path_cb(lv.anim_t.path_overshoot)
self.a5.set_time(300)

self.a6 = lv.anim_t()
self.a6.init()
self.a6.set_values(0, self.obj_height)
self.a6.set_early_apply(False)
self.a6.set_custom_exec_cb(lambda a,v: self.set_height(self.obj3,v))
self.a6.set_path_cb(lv.anim_t.path_ease_out)
self.a6.set_time(300)

# Create anim timeline


print("Create new anim_timeline")
self.anim_timeline = lv.anim_timeline_create()
self.anim_timeline.add(0, self.a1)
self.anim_timeline.add(0, self.a2)
self.anim_timeline.add(200, self.a3)
self.anim_timeline.add(200, self.a4)
self.anim_timeline.add(400, self.a5)
self.anim_timeline.add(400, self.a6)

def slider_prg_event_handler(self,e):
slider = e.get_target_obj()

if not self.anim_timeline:
self.anim_timeline_create()

progress = slider.get_value()
self.anim_timeline.set_progress(progress)

def btn_run_event_handler(self,e):
btn = e.get_target_obj()
if not self.anim_timeline:
self.anim_timeline_create()

reverse = btn.has_state(lv.STATE.CHECKED)
self.anim_timeline.set_reverse(reverse)
self.anim_timeline.start()

def btn_del_event_handler(self,e):
if self.anim_timeline:
self.anim_timeline._del()
self.anim_timeline = None
(continues on next page)

2.3. Animations 40
LVGL Documentation 9.0

(continued from previous page)

lv_example_anim_timeline_1 = LV_ExampleAnimTimeline_1()

2.4 Events

2.4.1 Button click event

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_SWITCH

static void event_cb(lv_event_t * e)


{
LV_LOG_USER("Clicked");

static uint32_t cnt = 1;


lv_obj_t * btn = lv_event_get_target(e);
lv_obj_t * label = lv_obj_get_child(btn, 0);
lv_label_set_text_fmt(label, "%"LV_PRIu32, cnt);
cnt++;
}

/**
* Add click event to a button
*/
void lv_example_event_1(void)
{
lv_obj_t * btn = lv_btn_create(lv_scr_act());
lv_obj_set_size(btn, 100, 50);
lv_obj_center(btn);
lv_obj_add_event(btn, event_cb, LV_EVENT_CLICKED, NULL);

lv_obj_t * label = lv_label_create(btn);


lv_label_set_text(label, "Click me!");
lv_obj_center(label);
}

#endif

class Event_1():
def __init__(self):
self.cnt = 1
#
# Add click event to a button
#

btn = lv.btn(lv.scr_act())
btn.set_size(100, 50)
btn.center()
btn.add_event(self.event_cb, lv.EVENT.CLICKED, None)

label = lv.label(btn)
label.set_text("Click me!")
(continues on next page)

2.4. Events 41
LVGL Documentation 9.0

(continued from previous page)


label.center()

def event_cb(self,e):
print("Clicked")

btn = e.get_target_obj()
label = btn.get_child(0)
label.set_text(str(self.cnt))
self.cnt += 1

evt1 = Event_1()

2.4.2 Handle multiple events

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_SWITCH

static void event_cb(lv_event_t * e)


{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * label = lv_event_get_user_data(e);

switch(code) {
case LV_EVENT_PRESSED:
lv_label_set_text(label, "The last button event:\nLV_EVENT_PRESSED");
break;
case LV_EVENT_CLICKED:
lv_label_set_text(label, "The last button event:\nLV_EVENT_CLICKED");
break;
case LV_EVENT_LONG_PRESSED:
lv_label_set_text(label, "The last button event:\nLV_EVENT_LONG_PRESSED");
break;
case LV_EVENT_LONG_PRESSED_REPEAT:
lv_label_set_text(label, "The last button event:\nLV_EVENT_LONG_PRESSED_
,→REPEAT");

break;
default:
break;
}
}

/**
* Handle multiple events
*/
void lv_example_event_2(void)
{
lv_obj_t * btn = lv_btn_create(lv_scr_act());
lv_obj_set_size(btn, 100, 50);
lv_obj_center(btn);

lv_obj_t * btn_label = lv_label_create(btn);


lv_label_set_text(btn_label, "Click me!");
lv_obj_center(btn_label);

(continues on next page)

2.4. Events 42
LVGL Documentation 9.0

(continued from previous page)


lv_obj_t * info_label = lv_label_create(lv_scr_act());
lv_label_set_text(info_label, "The last button event:\nNone");

lv_obj_add_event(btn, event_cb, LV_EVENT_ALL, info_label);


}

#endif

def event_cb(e,label):
code = e.get_code()
if code == lv.EVENT.PRESSED:
label.set_text("The last button event:\nLV_EVENT_PRESSED")
elif code == lv.EVENT.CLICKED:
label.set_text("The last button event:\nLV_EVENT_CLICKED")
elif code == lv.EVENT.LONG_PRESSED:
label.set_text("The last button event:\nLV_EVENT_LONG_PRESSED")
elif code == lv.EVENT.LONG_PRESSED_REPEAT:
label.set_text("The last button event:\nLV_EVENT_LONG_PRESSED_REPEAT")
btn = lv.btn(lv.scr_act())
btn.set_size(100, 50)
btn.center()

btn_label = lv.label(btn)
btn_label.set_text("Click me!")
btn_label.center()

info_label = lv.label(lv.scr_act())
info_label.set_text("The last button event:\nNone")

btn.add_event(lambda e: event_cb(e,info_label), lv.EVENT.ALL, None)

2.4.3 Event bubbling

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_FLEX

static void event_cb(lv_event_t * e)


{
/*The original target of the event. Can be the buttons or the container*/
lv_obj_t * target = lv_event_get_target(e);

/*The current target is always the container as the event is added to it*/
lv_obj_t * cont = lv_event_get_current_target(e);

/*If container was clicked do nothing*/


if(target == cont) return;

/*Make the clicked buttons red*/


lv_obj_set_style_bg_color(target, lv_palette_main(LV_PALETTE_RED), 0);
}

/**
* Demonstrate event bubbling
*/
(continues on next page)

2.4. Events 43
LVGL Documentation 9.0

(continued from previous page)


void lv_example_event_3(void)
{

lv_obj_t * cont = lv_obj_create(lv_scr_act());


lv_obj_set_size(cont, 290, 200);
lv_obj_center(cont);
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_ROW_WRAP);

uint32_t i;
for(i = 0; i < 30; i++) {
lv_obj_t * btn = lv_btn_create(cont);
lv_obj_set_size(btn, 80, 50);
lv_obj_add_flag(btn, LV_OBJ_FLAG_EVENT_BUBBLE);

lv_obj_t * label = lv_label_create(btn);


lv_label_set_text_fmt(label, "%"LV_PRIu32, i);
lv_obj_center(label);
}

lv_obj_add_event(cont, event_cb, LV_EVENT_CLICKED, NULL);


}

#endif

def event_cb(e):

# The original target of the event. Can be the buttons or the container
target = e.get_target_obj()
# print(type(target))

# If container was clicked do nothing


if type(target) != type(lv.btn()):
return

# Make the clicked buttons red


target.set_style_bg_color(lv.palette_main(lv.PALETTE.RED), 0)

#
# Demonstrate event bubbling
#

cont = lv.obj(lv.scr_act())
cont.set_size(320, 200)
cont.center()
cont.set_flex_flow(lv.FLEX_FLOW.ROW_WRAP)

for i in range(30):
btn = lv.btn(cont)
btn.set_size(80, 50)
btn.add_flag(lv.obj.FLAG.EVENT_BUBBLE)

label = lv.label(btn)
label.set_text(str(i))
label.center()

cont.add_event(event_cb, lv.EVENT.CLICKED, None)

2.4. Events 44
LVGL Documentation 9.0

2.4.4 Draw event

#include "../lv_examples.h"

#if LV_BUILD_EXAMPLES

static uint32_t size = 0;


static bool size_dec = false;

static void timer_cb(lv_timer_t * timer)


{
lv_obj_invalidate(timer->user_data);
if(size_dec) size--;
else size++;

if(size == 50) size_dec = true;


else if(size == 0) size_dec = false;
}

static void event_cb(lv_event_t * e)


{
lv_obj_t * obj = lv_event_get_target(e);
lv_obj_draw_part_dsc_t * dsc = lv_event_get_draw_part_dsc(e);
if(dsc->class_p == &lv_obj_class && dsc->part == LV_PART_MAIN) {
lv_draw_rect_dsc_t draw_dsc;
lv_draw_rect_dsc_init(&draw_dsc);
draw_dsc.bg_color = lv_color_hex(0xffaaaa);
draw_dsc.radius = LV_RADIUS_CIRCLE;
draw_dsc.border_color = lv_color_hex(0xff5555);
draw_dsc.border_width = 2;
draw_dsc.outline_color = lv_color_hex(0xff0000);
draw_dsc.outline_pad = 3;
draw_dsc.outline_width = 2;

lv_area_t a;
a.x1 = 0;
a.y1 = 0;
a.x2 = size;
a.y2 = size;
lv_area_align(&obj->coords, &a, LV_ALIGN_CENTER, 0, 0);

lv_draw_rect(dsc->draw_ctx, &draw_dsc, &a);


}
}

/**
* Demonstrate the usage of draw event
*/
void lv_example_event_4(void)
{
lv_obj_t * cont = lv_obj_create(lv_scr_act());
lv_obj_set_size(cont, 200, 200);
lv_obj_center(cont);
lv_obj_add_event(cont, event_cb, LV_EVENT_DRAW_PART_END, NULL);
lv_timer_create(timer_cb, 30, cont);
}

#endif

2.4. Events 45
LVGL Documentation 9.0

class LV_Example_Event_4:

def __init__(self):
#
# Demonstrate the usage of draw event
#
self.size = 0
self.size_dec = False
self.cont = lv.obj(lv.scr_act())
self.cont.set_size(200, 200)
self.cont.center()
self.cont.add_event(self.event_cb, lv.EVENT.DRAW_PART_END, None)
lv.timer_create(self.timer_cb, 30, None)

def timer_cb(self,timer) :
self.cont.invalidate()
if self.size_dec :
self.size -= 1
else :
self.size += 1

if self.size == 50 :
self.size_dec = True
elif self.size == 0:
self.size_dec = False

def event_cb(self,e) :
obj = e.get_target_obj()
dsc = e.get_draw_part_dsc()
if dsc.class_p == lv.obj_class and dsc.part == lv.PART.MAIN :
draw_dsc = lv.draw_rect_dsc_t()
draw_dsc.init()
draw_dsc.bg_color = lv.color_hex(0xffaaaa)
draw_dsc.radius = lv.RADIUS_CIRCLE
draw_dsc.border_color = lv.color_hex(0xff5555)
draw_dsc.border_width = 2
draw_dsc.outline_color = lv.color_hex(0xff0000)
draw_dsc.outline_pad = 3
draw_dsc.outline_width = 2

a = lv.area_t()
a.x1 = 0
a.y1 = 0
a.x2 = self.size
a.y2 = self.size
coords = lv.area_t()
obj.get_coords(coords)
coords.align(a, lv.ALIGN.CENTER, 0, 0)

dsc.draw_ctx.rect(draw_dsc, a)

lv_example_event_4 = LV_Example_Event_4()

2.4. Events 46
LVGL Documentation 9.0

2.5 Layouts

2.5.1 Flex

A simple row and a column layout with flexbox

#include "../../lv_examples.h"
#if LV_USE_FLEX && LV_BUILD_EXAMPLES

/**
* A simple row and a column layout with flexbox
*/
void lv_example_flex_1(void)
{
/*Create a container with ROW flex direction*/
lv_obj_t * cont_row = lv_obj_create(lv_scr_act());
lv_obj_set_size(cont_row, 300, 75);
lv_obj_align(cont_row, LV_ALIGN_TOP_MID, 0, 5);
lv_obj_set_flex_flow(cont_row, LV_FLEX_FLOW_ROW);

/*Create a container with COLUMN flex direction*/


lv_obj_t * cont_col = lv_obj_create(lv_scr_act());
lv_obj_set_size(cont_col, 200, 150);
lv_obj_align_to(cont_col, cont_row, LV_ALIGN_OUT_BOTTOM_MID, 0, 5);
lv_obj_set_flex_flow(cont_col, LV_FLEX_FLOW_COLUMN);

uint32_t i;
for(i = 0; i < 10; i++) {
lv_obj_t * obj;
lv_obj_t * label;

/*Add items to the row*/


obj = lv_btn_create(cont_row);
lv_obj_set_size(obj, 100, LV_PCT(100));

label = lv_label_create(obj);
lv_label_set_text_fmt(label, "Item: %"LV_PRIu32"", i);
lv_obj_center(label);

/*Add items to the column*/


obj = lv_btn_create(cont_col);
lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);

label = lv_label_create(obj);
lv_label_set_text_fmt(label, "Item: %"LV_PRIu32, i);
lv_obj_center(label);
}
}

#endif

#
# A simple row and a column layout with flexbox
#

(continues on next page)

2.5. Layouts 47
LVGL Documentation 9.0

(continued from previous page)


# Create a container with ROW flex direction
cont_row = lv.obj(lv.scr_act())
cont_row.set_size(300, 75)
cont_row.align(lv.ALIGN.TOP_MID, 0, 5)
cont_row.set_flex_flow(lv.FLEX_FLOW.ROW)

# Create a container with COLUMN flex direction


cont_col = lv.obj(lv.scr_act())
cont_col.set_size(200, 150)
cont_col.align_to(cont_row, lv.ALIGN.OUT_BOTTOM_MID, 0, 5)
cont_col.set_flex_flow(lv.FLEX_FLOW.COLUMN)

for i in range(10):
# Add items to the row
obj = lv.btn(cont_row)
obj.set_size(100, lv.pct(100))

label = lv.label(obj)
label.set_text("Item: {:d}".format(i))
label.center()

# Add items to the column


obj = lv.btn(cont_col)
obj.set_size(lv.pct(100), lv.SIZE_CONTENT)

label = lv.label(obj)
label.set_text("Item: {:d}".format(i))
label.center()

Arrange items in rows with wrap and even spacing

#include "../../lv_examples.h"
#if LV_USE_FLEX && LV_BUILD_EXAMPLES

/**
* Arrange items in rows with wrap and place the items to get even space around them.
*/
void lv_example_flex_2(void)
{
static lv_style_t style;
lv_style_init(&style);
lv_style_set_flex_flow(&style, LV_FLEX_FLOW_ROW_WRAP);
lv_style_set_flex_main_place(&style, LV_FLEX_ALIGN_SPACE_EVENLY);
lv_style_set_layout(&style, LV_LAYOUT_FLEX);

lv_obj_t * cont = lv_obj_create(lv_scr_act());


lv_obj_set_size(cont, 300, 220);
lv_obj_center(cont);
lv_obj_add_style(cont, &style, 0);

uint32_t i;
for(i = 0; i < 8; i++) {
lv_obj_t * obj = lv_obj_create(cont);
lv_obj_set_size(obj, 70, LV_SIZE_CONTENT);
(continues on next page)

2.5. Layouts 48
LVGL Documentation 9.0

(continued from previous page)


lv_obj_add_flag(obj, LV_OBJ_FLAG_CHECKABLE);

lv_obj_t * label = lv_label_create(obj);


lv_label_set_text_fmt(label, "%"LV_PRIu32, i);
lv_obj_center(label);
}
}

#endif

#
# Arrange items in rows with wrap and place the items to get even space around them.
#
style = lv.style_t()
style.init()
style.set_flex_flow(lv.FLEX_FLOW.ROW_WRAP)
style.set_flex_main_place(lv.FLEX_ALIGN.SPACE_EVENLY)
style.set_layout(lv.LAYOUT_FLEX.value)

cont = lv.obj(lv.scr_act())
cont.set_size(300, 220)
cont.center()
cont.add_style(style, 0)

for i in range(8):
obj = lv.obj(cont)
obj.set_size(70, lv.SIZE_CONTENT)

label = lv.label(obj)
label.set_text("{:d}".format(i))
label.center()

Demonstrate flex grow

#include "../../lv_examples.h"
#if LV_USE_FLEX && LV_BUILD_EXAMPLES

/**
* Demonstrate flex grow.
*/
void lv_example_flex_3(void)
{
lv_obj_t * cont = lv_obj_create(lv_scr_act());
lv_obj_set_size(cont, 300, 220);
lv_obj_center(cont);
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_ROW);

lv_obj_t * obj;
obj = lv_obj_create(cont);
lv_obj_set_size(obj, 40, 40); /*Fix size*/

obj = lv_obj_create(cont);
lv_obj_set_height(obj, 40);
(continues on next page)

2.5. Layouts 49
LVGL Documentation 9.0

(continued from previous page)


lv_obj_set_flex_grow(obj, 1); /*1 portion from the free space*/

obj = lv_obj_create(cont);
lv_obj_set_height(obj, 40);
lv_obj_set_flex_grow(obj, 2); /*2 portion from the free space*/

obj = lv_obj_create(cont);
lv_obj_set_size(obj, 40, 40); /*Fix size. It is flushed to the right by␣
,→the "grow" items*/

#endif

#
# Demonstrate flex grow.
#

cont = lv.obj(lv.scr_act())
cont.set_size(300, 220)
cont.center()
cont.set_flex_flow(lv.FLEX_FLOW.ROW)

obj = lv.obj(cont)
obj.set_size(40, 40) # Fix size

obj = lv.obj(cont)
obj.set_height(40)
obj.set_flex_grow(1) # 1 portion from the free space

obj = lv.obj(cont)
obj.set_height(40)
obj.set_flex_grow(2) # 2 portion from the free space

obj = lv.obj(cont)
obj.set_size(40, 40) # Fix size. It is flushed to the right by the "grow"␣
,→items

Demonstrate flex grow.

#include "../../lv_examples.h"
#if LV_USE_FLEX && LV_BUILD_EXAMPLES

/**
* Reverse the order of flex items
*/
void lv_example_flex_4(void)
{

lv_obj_t * cont = lv_obj_create(lv_scr_act());


lv_obj_set_size(cont, 300, 220);
lv_obj_center(cont);
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN_REVERSE);

(continues on next page)

2.5. Layouts 50
LVGL Documentation 9.0

(continued from previous page)


uint32_t i;
for(i = 0; i < 6; i++) {
lv_obj_t * obj = lv_obj_create(cont);
lv_obj_set_size(obj, 100, 50);

lv_obj_t * label = lv_label_create(obj);


lv_label_set_text_fmt(label, "Item: %"LV_PRIu32, i);
lv_obj_center(label);
}
}

#endif

#
# Reverse the order of flex items
#
cont = lv.obj(lv.scr_act())
cont.set_size(300, 220)
cont.center()
cont.set_flex_flow(lv.FLEX_FLOW.COLUMN_REVERSE)

for i in range(6):
obj = lv.obj(cont)
obj.set_size(100, 50)

label = lv.label(obj)
label.set_text("Item: " + str(i))
label.center()

Demonstrate column and row gap style properties

#include "../../lv_examples.h"
#if LV_USE_FLEX && LV_BUILD_EXAMPLES

static void row_gap_anim(void * obj, int32_t v)


{
lv_obj_set_style_pad_row(obj, v, 0);
}

static void column_gap_anim(void * obj, int32_t v)


{
lv_obj_set_style_pad_column(obj, v, 0);
}

/**
* Demonstrate the effect of column and row gap style properties
*/
void lv_example_flex_5(void)
{
lv_obj_t * cont = lv_obj_create(lv_scr_act());
lv_obj_set_size(cont, 300, 220);
lv_obj_center(cont);
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_ROW_WRAP);
(continues on next page)

2.5. Layouts 51
LVGL Documentation 9.0

(continued from previous page)

uint32_t i;
for(i = 0; i < 9; i++) {
lv_obj_t * obj = lv_obj_create(cont);
lv_obj_set_size(obj, 70, LV_SIZE_CONTENT);

lv_obj_t * label = lv_label_create(obj);


lv_label_set_text_fmt(label, "%"LV_PRIu32, i);
lv_obj_center(label);
}

lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, cont);
lv_anim_set_values(&a, 0, 10);
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);

lv_anim_set_exec_cb(&a, row_gap_anim);
lv_anim_set_time(&a, 500);
lv_anim_set_playback_time(&a, 500);
lv_anim_start(&a);

lv_anim_set_exec_cb(&a, column_gap_anim);
lv_anim_set_time(&a, 3000);
lv_anim_set_playback_time(&a, 3000);
lv_anim_start(&a);
}

#endif

def row_gap_anim(obj, v):


obj.set_style_pad_row(v, 0)

def column_gap_anim(obj, v):


obj.set_style_pad_column(v, 0)

#
# Demonstrate the effect of column and row gap style properties
#

cont = lv.obj(lv.scr_act())
cont.set_size(300, 220)
cont.center()
cont.set_flex_flow(lv.FLEX_FLOW.ROW_WRAP)

for i in range(9):
obj = lv.obj(cont)
obj.set_size(70, lv.SIZE_CONTENT)

label = lv.label(obj)
label.set_text(str(i))
label.center()

a_row = lv.anim_t()
a_row.init()
(continues on next page)

2.5. Layouts 52
LVGL Documentation 9.0

(continued from previous page)


a_row.set_var(cont)
a_row.set_values(0, 10)
a_row.set_repeat_count(lv.ANIM_REPEAT_INFINITE)

a_row.set_time(500)
a_row.set_playback_time(500)
a_row.set_custom_exec_cb(lambda a,val: row_gap_anim(cont,val))
lv.anim_t.start(a_row)

a_col = lv.anim_t()
a_col.init()
a_col.set_var(cont)
a_col.set_values(0, 10)
a_col.set_repeat_count(lv.ANIM_REPEAT_INFINITE)

a_col.set_time(3000)
a_col.set_playback_time(3000)
a_col.set_custom_exec_cb(lambda a,val: column_gap_anim(cont,val))

lv.anim_t.start(a_col)

RTL base direction changes order of the items

#include "../../lv_examples.h"
#if LV_USE_FLEX && LV_BUILD_EXAMPLES

/**
* RTL base direction changes order of the items.
* Also demonstrate how horizontal scrolling works with RTL.
*/
void lv_example_flex_6(void)
{
lv_obj_t * cont = lv_obj_create(lv_scr_act());
lv_obj_set_style_base_dir(cont, LV_BASE_DIR_RTL, 0);
lv_obj_set_size(cont, 300, 220);
lv_obj_center(cont);
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_ROW_WRAP);

uint32_t i;
for(i = 0; i < 20; i++) {
lv_obj_t * obj = lv_obj_create(cont);
lv_obj_set_size(obj, 70, LV_SIZE_CONTENT);

lv_obj_t * label = lv_label_create(obj);


lv_label_set_text_fmt(label, "%"LV_PRIu32, i);
lv_obj_center(label);
}
}
#endif

#
# RTL base direction changes order of the items.
# Also demonstrate how horizontal scrolling works with RTL.
(continues on next page)

2.5. Layouts 53
LVGL Documentation 9.0

(continued from previous page)


#

cont = lv.obj(lv.scr_act())
cont.set_style_base_dir(lv.BASE_DIR.RTL,0)
cont.set_size(300, 220)
cont.center()
cont.set_flex_flow(lv.FLEX_FLOW.ROW_WRAP)

for i in range(20):
obj = lv.obj(cont)
obj.set_size(70, lv.SIZE_CONTENT)

label = lv.label(obj)
label.set_text(str(i))
label.center()

2.5.2 Grid

A simple grid

#include "../../lv_examples.h"
#if LV_USE_GRID && LV_BUILD_EXAMPLES

/**
* A simple grid
*/
void lv_example_grid_1(void)
{
static lv_coord_t col_dsc[] = {70, 70, 70, LV_GRID_TEMPLATE_LAST};
static lv_coord_t row_dsc[] = {50, 50, 50, LV_GRID_TEMPLATE_LAST};

/*Create a container with grid*/


lv_obj_t * cont = lv_obj_create(lv_scr_act());
lv_obj_set_style_grid_column_dsc_array(cont, col_dsc, 0);
lv_obj_set_style_grid_row_dsc_array(cont, row_dsc, 0);
lv_obj_set_size(cont, 300, 220);
lv_obj_center(cont);
lv_obj_set_layout(cont, LV_LAYOUT_GRID);

lv_obj_t * label;
lv_obj_t * obj;

uint32_t i;
for(i = 0; i < 9; i++) {
uint8_t col = i % 3;
uint8_t row = i / 3;

obj = lv_btn_create(cont);
/*Stretch the cell horizontally and vertically too
*Set span to 1 to make the cell 1 column/row sized*/
lv_obj_set_grid_cell(obj, LV_GRID_ALIGN_STRETCH, col, 1,
LV_GRID_ALIGN_STRETCH, row, 1);

(continues on next page)

2.5. Layouts 54
LVGL Documentation 9.0

(continued from previous page)


label = lv_label_create(obj);
lv_label_set_text_fmt(label, "c%d, r%d", col, row);
lv_obj_center(label);
}
}

#endif

#
# A simple grid
#

col_dsc = [70, 70, 70, lv.GRID_TEMPLATE_LAST]


row_dsc = [50, 50, 50, lv.GRID_TEMPLATE_LAST]

# Create a container with grid


cont = lv.obj(lv.scr_act())
cont.set_style_grid_column_dsc_array(col_dsc, 0)
cont.set_style_grid_row_dsc_array(row_dsc, 0)
cont.set_size(300, 220)
cont.center()
cont.set_layout(lv.LAYOUT_GRID.value)

for i in range(9):
col = i % 3
row = i // 3

obj = lv.btn(cont)
# Stretch the cell horizontally and vertically too
# Set span to 1 to make the cell 1 column/row sized
obj.set_grid_cell(lv.GRID_ALIGN.STRETCH, col, 1,
lv.GRID_ALIGN.STRETCH, row, 1)

label = lv.label(obj)
label.set_text("c" +str(col) + "r" +str(row))
label.center()

Demonstrate cell placement and span

#include "../../lv_examples.h"
#if LV_USE_GRID && LV_BUILD_EXAMPLES

/**
* Demonstrate cell placement and span
*/
void lv_example_grid_2(void)
{
static lv_coord_t col_dsc[] = {70, 70, 70, LV_GRID_TEMPLATE_LAST};
static lv_coord_t row_dsc[] = {50, 50, 50, LV_GRID_TEMPLATE_LAST};

/*Create a container with grid*/


lv_obj_t * cont = lv_obj_create(lv_scr_act());
(continues on next page)

2.5. Layouts 55
LVGL Documentation 9.0

(continued from previous page)


lv_obj_set_grid_dsc_array(cont, col_dsc, row_dsc);
lv_obj_set_size(cont, 300, 220);
lv_obj_center(cont);

lv_obj_t * label;
lv_obj_t * obj;

/*Cell to 0;0 and align to to the start (left/top) horizontally and vertically␣
,→ too*/
obj = lv_obj_create(cont);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_grid_cell(obj, LV_GRID_ALIGN_START, 0, 1,
LV_GRID_ALIGN_START, 0, 1);
label = lv_label_create(obj);
lv_label_set_text(label, "c0, r0");

/*Cell to 1;0 and align to to the start (left) horizontally and center vertically␣
,→ too*/
obj = lv_obj_create(cont);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_grid_cell(obj, LV_GRID_ALIGN_START, 1, 1,
LV_GRID_ALIGN_CENTER, 0, 1);
label = lv_label_create(obj);
lv_label_set_text(label, "c1, r0");

/*Cell to 2;0 and align to to the start (left) horizontally and end (bottom)␣
,→ vertically too*/
obj = lv_obj_create(cont);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_grid_cell(obj, LV_GRID_ALIGN_START, 2, 1,
LV_GRID_ALIGN_END, 0, 1);
label = lv_label_create(obj);
lv_label_set_text(label, "c2, r0");

/*Cell to 1;1 but 2 column wide (span = 2).Set width and height to stretched.*/
obj = lv_obj_create(cont);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_grid_cell(obj, LV_GRID_ALIGN_STRETCH, 1, 2,
LV_GRID_ALIGN_STRETCH, 1, 1);
label = lv_label_create(obj);
lv_label_set_text(label, "c1-2, r1");

/*Cell to 0;1 but 2 rows tall (span = 2).Set width and height to stretched.*/
obj = lv_obj_create(cont);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_grid_cell(obj, LV_GRID_ALIGN_STRETCH, 0, 1,
LV_GRID_ALIGN_STRETCH, 1, 2);
label = lv_label_create(obj);
lv_label_set_text(label, "c0\nr1-2");
}

#endif

#
# Demonstrate cell placement and span
#
(continues on next page)

2.5. Layouts 56
LVGL Documentation 9.0

(continued from previous page)

col_dsc = [70, 70, 70, lv.GRID_TEMPLATE_LAST]


row_dsc = [50, 50, 50, lv.GRID_TEMPLATE_LAST]

# Create a container with grid


cont = lv.obj(lv.scr_act())
cont.set_grid_dsc_array(col_dsc, row_dsc)
cont.set_size(300, 220)
cont.center()

# Cell to 0;0 and align to the start (left/top) horizontally and vertically too
obj = lv.obj(cont)
obj.set_size(lv.SIZE_CONTENT, lv.SIZE_CONTENT)
obj.set_grid_cell(lv.GRID_ALIGN.START, 0, 1,
lv.GRID_ALIGN.START, 0, 1)
label = lv.label(obj)
label.set_text("c0, r0")

# Cell to 1;0 and align to the start (left) horizontally and center vertically too
obj = lv.obj(cont)
obj.set_size(lv.SIZE_CONTENT, lv.SIZE_CONTENT)
obj.set_grid_cell(lv.GRID_ALIGN.START, 1, 1,
lv.GRID_ALIGN.CENTER, 0, 1)
label = lv.label(obj)
label.set_text("c1, r0")

# Cell to 2;0 and align to the start (left) horizontally and end (bottom) vertically␣
,→too

obj = lv.obj(cont)
obj.set_size(lv.SIZE_CONTENT, lv.SIZE_CONTENT)
obj.set_grid_cell(lv.GRID_ALIGN.START, 2, 1,
lv.GRID_ALIGN.END, 0, 1)
label = lv.label(obj)
label.set_text("c2, r0")

# Cell to 1;1 but 2 column wide (span = 2).Set width and height to stretched.
obj = lv.obj(cont)
obj.set_size(lv.SIZE_CONTENT, lv.SIZE_CONTENT)
obj.set_grid_cell(lv.GRID_ALIGN.STRETCH, 1, 2,
lv.GRID_ALIGN.STRETCH, 1, 1)
label = lv.label(obj)
label.set_text("c1-2, r1")

# Cell to 0;1 but 2 rows tall (span = 2).Set width and height to stretched.
obj = lv.obj(cont)
obj.set_size(lv.SIZE_CONTENT, lv.SIZE_CONTENT)
obj.set_grid_cell(lv.GRID_ALIGN.STRETCH, 0, 1,
lv.GRID_ALIGN.STRETCH, 1, 2)
label = lv.label(obj)
label.set_text("c0\nr1-2")

2.5. Layouts 57
LVGL Documentation 9.0

Demonstrate grid's "free unit"

#include "../../lv_examples.h"
#if LV_USE_GRID && LV_BUILD_EXAMPLES

/**
* Demonstrate grid's "free unit"
*/
void lv_example_grid_3(void)
{
/*Column 1: fix width 60 px
*Column 2: 1 unit from the remaining free space
*Column 3: 2 unit from the remaining free space*/
static lv_coord_t col_dsc[] = {60, LV_GRID_FR(1), LV_GRID_FR(2), LV_GRID_TEMPLATE_
,→LAST};

/*Row 1: fix width 50 px


*Row 2: 1 unit from the remaining free space
*Row 3: fix width 50 px*/
static lv_coord_t row_dsc[] = {50, LV_GRID_FR(1), 50, LV_GRID_TEMPLATE_LAST};

/*Create a container with grid*/


lv_obj_t * cont = lv_obj_create(lv_scr_act());
lv_obj_set_size(cont, 300, 220);
lv_obj_center(cont);
lv_obj_set_grid_dsc_array(cont, col_dsc, row_dsc);

lv_obj_t * label;
lv_obj_t * obj;
uint32_t i;
for(i = 0; i < 9; i++) {
uint8_t col = i % 3;
uint8_t row = i / 3;

obj = lv_obj_create(cont);
/*Stretch the cell horizontally and vertically too
*Set span to 1 to make the cell 1 column/row sized*/
lv_obj_set_grid_cell(obj, LV_GRID_ALIGN_STRETCH, col, 1,
LV_GRID_ALIGN_STRETCH, row, 1);

label = lv_label_create(obj);
lv_label_set_text_fmt(label, "%d,%d", col, row);
lv_obj_center(label);
}
}

#endif

#
# Demonstrate grid's "free unit"
#

# Column 1: fix width 60 px


# Column 2: 1 unit from the remaining free space
# Column 3: 2 unit from the remaining free space

col_dsc = [60, lv.grid_fr(1), lv.grid_fr(2), lv.GRID_TEMPLATE_LAST]


(continues on next page)

2.5. Layouts 58
LVGL Documentation 9.0

(continued from previous page)

# Row 1: fix width 60 px


# Row 2: 1 unit from the remaining free space
# Row 3: fix width 60 px

row_dsc = [40, lv.grid_fr(1), 40, lv.GRID_TEMPLATE_LAST]

# Create a container with grid


cont = lv.obj(lv.scr_act())
cont.set_size(300, 220)
cont.center()
cont.set_grid_dsc_array(col_dsc, row_dsc)

for i in range(9):
col = i % 3
row = i // 3

obj = lv.obj(cont)
# Stretch the cell horizontally and vertically too
# Set span to 1 to make the cell 1 column/row sized
obj.set_grid_cell(lv.GRID_ALIGN.STRETCH, col, 1,
lv.GRID_ALIGN.STRETCH, row, 1)

label = lv.label(obj)
label.set_text("%d,%d"%(col, row))
label.center()

Demonstrate track placement

#include "../../lv_examples.h"
#if LV_USE_GRID && LV_BUILD_EXAMPLES

/**
* Demonstrate track placement
*/
void lv_example_grid_4(void)
{
static lv_coord_t col_dsc[] = {60, 60, 60, LV_GRID_TEMPLATE_LAST};
static lv_coord_t row_dsc[] = {45, 45, 45, LV_GRID_TEMPLATE_LAST};

/*Add space between the columns and move the rows to the bottom (end)*/

/*Create a container with grid*/


lv_obj_t * cont = lv_obj_create(lv_scr_act());
lv_obj_set_grid_align(cont, LV_GRID_ALIGN_SPACE_BETWEEN, LV_GRID_ALIGN_END);
lv_obj_set_grid_dsc_array(cont, col_dsc, row_dsc);
lv_obj_set_size(cont, 300, 220);
lv_obj_center(cont);

lv_obj_t * label;
lv_obj_t * obj;
uint32_t i;
for(i = 0; i < 9; i++) {
(continues on next page)

2.5. Layouts 59
LVGL Documentation 9.0

(continued from previous page)


uint8_t col = i % 3;
uint8_t row = i / 3;

obj = lv_obj_create(cont);
/*Stretch the cell horizontally and vertically too
*Set span to 1 to make the cell 1 column/row sized*/
lv_obj_set_grid_cell(obj, LV_GRID_ALIGN_STRETCH, col, 1,
LV_GRID_ALIGN_STRETCH, row, 1);

label = lv_label_create(obj);
lv_label_set_text_fmt(label, "%d,%d", col, row);
lv_obj_center(label);
}
}

#endif

#
# Demonstrate track placement
#

col_dsc = [60, 60, 60, lv.GRID_TEMPLATE_LAST]


row_dsc = [40, 40, 40, lv.GRID_TEMPLATE_LAST]

# Add space between the columns and move the rows to the bottom (end)

# Create a container with grid


cont = lv.obj(lv.scr_act())
cont.set_grid_align(lv.GRID_ALIGN.SPACE_BETWEEN, lv.GRID_ALIGN.END)
cont.set_grid_dsc_array(col_dsc, row_dsc)
cont.set_size(300, 220)
cont.center()

for i in range(9):
col = i % 3
row = i // 3

obj = lv.obj(cont)
# Stretch the cell horizontally and vertically too
# Set span to 1 to make the cell 1 column/row sized
obj.set_grid_cell(lv.GRID_ALIGN.STRETCH, col, 1,
lv.GRID_ALIGN.STRETCH, row, 1)

label = lv.label(obj)
label.set_text("{:d}{:d}".format(col, row))
label.center()

2.5. Layouts 60
LVGL Documentation 9.0

Demonstrate column and row gap

#include "../../lv_examples.h"
#if LV_USE_GRID && LV_BUILD_EXAMPLES

static void row_gap_anim(void * obj, int32_t v)


{
lv_obj_set_style_pad_row(obj, v, 0);
}

static void column_gap_anim(void * obj, int32_t v)


{
lv_obj_set_style_pad_column(obj, v, 0);
}

/**
* Demonstrate column and row gap
*/
void lv_example_grid_5(void)
{

/*60x60 cells*/
static lv_coord_t col_dsc[] = {60, 60, 60, LV_GRID_TEMPLATE_LAST};
static lv_coord_t row_dsc[] = {45, 45, 45, LV_GRID_TEMPLATE_LAST};

/*Create a container with grid*/


lv_obj_t * cont = lv_obj_create(lv_scr_act());
lv_obj_set_size(cont, 300, 220);
lv_obj_center(cont);
lv_obj_set_grid_dsc_array(cont, col_dsc, row_dsc);

lv_obj_t * label;
lv_obj_t * obj;
uint32_t i;
for(i = 0; i < 9; i++) {
uint8_t col = i % 3;
uint8_t row = i / 3;

obj = lv_obj_create(cont);
lv_obj_set_grid_cell(obj, LV_GRID_ALIGN_STRETCH, col, 1,
LV_GRID_ALIGN_STRETCH, row, 1);
label = lv_label_create(obj);
lv_label_set_text_fmt(label, "%d,%d", col, row);
lv_obj_center(label);
}

lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, cont);
lv_anim_set_values(&a, 0, 10);
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);

lv_anim_set_exec_cb(&a, row_gap_anim);
lv_anim_set_time(&a, 500);
lv_anim_set_playback_time(&a, 500);
lv_anim_start(&a);

(continues on next page)

2.5. Layouts 61
LVGL Documentation 9.0

(continued from previous page)


lv_anim_set_exec_cb(&a, column_gap_anim);
lv_anim_set_time(&a, 3000);
lv_anim_set_playback_time(&a, 3000);
lv_anim_start(&a);
}

#endif

def row_gap_anim(obj, v):


obj.set_style_pad_row(v, 0)

def column_gap_anim(obj, v):


obj.set_style_pad_column(v, 0)

#
# Demonstrate column and row gap
#

# 60x60 cells
col_dsc = [60, 60, 60, lv.GRID_TEMPLATE_LAST]
row_dsc = [40, 40, 40, lv.GRID_TEMPLATE_LAST]

# Create a container with grid


cont = lv.obj(lv.scr_act())
cont.set_size(300, 220)
cont.center()
cont.set_grid_dsc_array(col_dsc, row_dsc)

for i in range(9):
col = i % 3
row = i // 3

obj = lv.obj(cont)
obj.set_grid_cell(lv.GRID_ALIGN.STRETCH, col, 1,
lv.GRID_ALIGN.STRETCH, row, 1)
label = lv.label(obj)
label.set_text("{:d},{:d}".format(col, row))
label.center()

a_row = lv.anim_t()
a_row.init()
a_row.set_var(cont)
a_row.set_values(0, 10)
a_row.set_repeat_count(lv.ANIM_REPEAT_INFINITE)
a_row.set_time(500)
a_row.set_playback_time(500)
a_row. set_custom_exec_cb(lambda a,val: row_gap_anim(cont,val))
lv.anim_t.start(a_row)

a_col = lv.anim_t()
a_col.init()
a_col.set_var(cont)
a_col.set_values(0, 10)
a_col.set_repeat_count(lv.ANIM_REPEAT_INFINITE)
a_col.set_time(500)
(continues on next page)

2.5. Layouts 62
LVGL Documentation 9.0

(continued from previous page)


a_col.set_playback_time(500)
a_col. set_custom_exec_cb(lambda a,val: column_gap_anim(cont,val))
lv.anim_t.start(a_col)

Demonstrate RTL direction on grid

#include "../../lv_examples.h"
#if LV_USE_GRID && LV_BUILD_EXAMPLES

/**
* Demonstrate RTL direction on grid
*/
void lv_example_grid_6(void)
{

static lv_coord_t col_dsc[] = {60, 60, 60, LV_GRID_TEMPLATE_LAST};


static lv_coord_t row_dsc[] = {45, 45, 45, LV_GRID_TEMPLATE_LAST};

/*Create a container with grid*/


lv_obj_t * cont = lv_obj_create(lv_scr_act());
lv_obj_set_size(cont, 300, 220);
lv_obj_center(cont);
lv_obj_set_style_base_dir(cont, LV_BASE_DIR_RTL, 0);
lv_obj_set_grid_dsc_array(cont, col_dsc, row_dsc);

lv_obj_t * label;
lv_obj_t * obj;
uint32_t i;
for(i = 0; i < 9; i++) {
uint8_t col = i % 3;
uint8_t row = i / 3;

obj = lv_obj_create(cont);
/*Stretch the cell horizontally and vertically too
*Set span to 1 to make the cell 1 column/row sized*/
lv_obj_set_grid_cell(obj, LV_GRID_ALIGN_STRETCH, col, 1,
LV_GRID_ALIGN_STRETCH, row, 1);

label = lv_label_create(obj);
lv_label_set_text_fmt(label, "%d,%d", col, row);
lv_obj_center(label);
}
}

#endif

#
# Demonstrate RTL direction on grid
#
col_dsc = [60, 60, 60, lv.GRID_TEMPLATE_LAST]
row_dsc = [40, 40, 40, lv.GRID_TEMPLATE_LAST]

(continues on next page)

2.5. Layouts 63
LVGL Documentation 9.0

(continued from previous page)


# Create a container with grid
cont = lv.obj(lv.scr_act())
cont.set_size(300, 220)
cont.center()
cont.set_style_base_dir(lv.BASE_DIR.RTL,0)
cont.set_grid_dsc_array(col_dsc, row_dsc)

for i in range(9):
col = i % 3
row = i // 3

obj = lv.obj(cont)
# Stretch the cell horizontally and vertically too
# Set span to 1 to make the cell 1 column/row sized
obj.set_grid_cell(lv.GRID_ALIGN.STRETCH, col, 1,
lv.GRID_ALIGN.STRETCH, row, 1)

label = lv.label(obj)
label.set_text("{:d},{:d}".format(col, row))
label.center()

2.6 Scrolling

2.6.1 Nested scrolling

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES

/**
* Demonstrate how scrolling appears automatically
*/
void lv_example_scroll_1(void)
{
/*Create an object with the new style*/
lv_obj_t * panel = lv_obj_create(lv_scr_act());
lv_obj_set_size(panel, 200, 200);
lv_obj_center(panel);

lv_obj_t * child;
lv_obj_t * label;

child = lv_obj_create(panel);
lv_obj_set_pos(child, 0, 0);
lv_obj_set_size(child, 70, 70);
label = lv_label_create(child);
lv_label_set_text(label, "Zero");
lv_obj_center(label);

child = lv_obj_create(panel);
lv_obj_set_pos(child, 160, 80);
lv_obj_set_size(child, 80, 80);

(continues on next page)

2.6. Scrolling 64
LVGL Documentation 9.0

(continued from previous page)


lv_obj_t * child2 = lv_btn_create(child);
lv_obj_set_size(child2, 100, 50);

label = lv_label_create(child2);
lv_label_set_text(label, "Right");
lv_obj_center(label);

child = lv_obj_create(panel);
lv_obj_set_pos(child, 40, 160);
lv_obj_set_size(child, 100, 70);
label = lv_label_create(child);
lv_label_set_text(label, "Bottom");
lv_obj_center(label);
}

#endif

#
# Demonstrate how scrolling appears automatically
#
# Create an object with the new style
panel = lv.obj(lv.scr_act())
panel.set_size(200, 200)
panel.center()

child = lv.obj(panel)
child.set_pos(0, 0)
label = lv.label(child)
label.set_text("Zero")
label.center()

child = lv.obj(panel)
child.set_pos(-40, 100)
label = lv.label(child)
label.set_text("Left")
label.center()

child = lv.obj(panel)
child.set_pos(90, -30)
label = lv.label(child)
label.set_text("Top")
label.center()

child = lv.obj(panel)
child.set_pos(150, 80)
label = lv.label(child)
label.set_text("Right")
label.center()

child = lv.obj(panel)
child.set_pos(60, 170)
label = lv.label(child)
label.set_text("Bottom")
label.center()

2.6. Scrolling 65
LVGL Documentation 9.0

2.6.2 Snapping

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_FLEX

static void sw_event_cb(lv_event_t * e)


{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * sw = lv_event_get_target(e);

if(code == LV_EVENT_VALUE_CHANGED) {
lv_obj_t * list = lv_event_get_user_data(e);

if(lv_obj_has_state(sw, LV_STATE_CHECKED)) lv_obj_add_flag(list, LV_OBJ_FLAG_


,→ SCROLL_ONE);
else lv_obj_clear_flag(list, LV_OBJ_FLAG_SCROLL_ONE);
}
}

/**
* Show an example to scroll snap
*/
void lv_example_scroll_2(void)
{
lv_obj_t * panel = lv_obj_create(lv_scr_act());
lv_obj_set_size(panel, 280, 120);
lv_obj_set_scroll_snap_x(panel, LV_SCROLL_SNAP_CENTER);
lv_obj_set_flex_flow(panel, LV_FLEX_FLOW_ROW);
lv_obj_align(panel, LV_ALIGN_CENTER, 0, 20);

uint32_t i;
for(i = 0; i < 10; i++) {
lv_obj_t * btn = lv_btn_create(panel);
lv_obj_set_size(btn, 150, lv_pct(100));

lv_obj_t * label = lv_label_create(btn);


if(i == 3) {
lv_label_set_text_fmt(label, "Panel %"LV_PRIu32"\nno snap", i);
lv_obj_clear_flag(btn, LV_OBJ_FLAG_SNAPPABLE);
}
else {
lv_label_set_text_fmt(label, "Panel %"LV_PRIu32, i);
}

lv_obj_center(label);
}
lv_obj_update_snap(panel, LV_ANIM_ON);

#if LV_USE_SWITCH
/*Switch between "One scroll" and "Normal scroll" mode*/
lv_obj_t * sw = lv_switch_create(lv_scr_act());
lv_obj_align(sw, LV_ALIGN_TOP_RIGHT, -20, 10);
lv_obj_add_event(sw, sw_event_cb, LV_EVENT_ALL, panel);
lv_obj_t * label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "One scroll");
lv_obj_align_to(label, sw, LV_ALIGN_OUT_BOTTOM_MID, 0, 5);
#endif
(continues on next page)

2.6. Scrolling 66
LVGL Documentation 9.0

(continued from previous page)


}

#endif

def sw_event_cb(e,panel):

code = e.get_code()
sw = e.get_target_obj()

if code == lv.EVENT.VALUE_CHANGED:

if sw.has_state(lv.STATE.CHECKED):
panel.add_flag(lv.obj.FLAG.SCROLL_ONE)
else:
panel.clear_flag(lv.obj.FLAG.SCROLL_ONE)

#
# Show an example to scroll snap
#

panel = lv.obj(lv.scr_act())
panel.set_size(280, 150)
panel.set_scroll_snap_x(lv.SCROLL_SNAP.CENTER)
panel.set_flex_flow(lv.FLEX_FLOW.ROW)
panel.center()

for i in range(10):
btn = lv.btn(panel)
btn.set_size(150, 100)

label = lv.label(btn)
if i == 3:
label.set_text("Panel {:d}\nno snap".format(i))
btn.clear_flag(lv.obj.FLAG.SNAPPABLE)
else:
label.set_text("Panel {:d}".format(i))
label.center()

panel.update_snap(lv.ANIM.ON)

# Switch between "One scroll" and "Normal scroll" mode


sw = lv.switch(lv.scr_act())
sw.align(lv.ALIGN.TOP_RIGHT, -20, 10)
sw.add_event(lambda evt: sw_event_cb(evt,panel), lv.EVENT.ALL, None)
label = lv.label(lv.scr_act())
label.set_text("One scroll")
label.align_to(sw, lv.ALIGN.OUT_BOTTOM_MID, 0, 5)

2.6. Scrolling 67
LVGL Documentation 9.0

2.6.3 Floating button

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_LIST

static uint32_t btn_cnt = 1;

static void float_btn_event_cb(lv_event_t * e)


{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * float_btn = lv_event_get_target(e);

if(code == LV_EVENT_CLICKED) {
lv_obj_t * list = lv_event_get_user_data(e);
char buf[32];
lv_snprintf(buf, sizeof(buf), "Track %d", (int)btn_cnt);
lv_obj_t * list_btn = lv_list_add_btn(list, LV_SYMBOL_AUDIO, buf);
btn_cnt++;

lv_obj_move_foreground(float_btn);

lv_obj_scroll_to_view(list_btn, LV_ANIM_ON);
}
}

/**
* Create a list with a floating button
*/
void lv_example_scroll_3(void)
{
lv_obj_t * list = lv_list_create(lv_scr_act());
lv_obj_set_size(list, 280, 220);
lv_obj_center(list);

for(btn_cnt = 1; btn_cnt <= 2; btn_cnt++) {


char buf[32];
lv_snprintf(buf, sizeof(buf), "Track %d", (int)btn_cnt);
lv_list_add_btn(list, LV_SYMBOL_AUDIO, buf);
}

lv_obj_t * float_btn = lv_btn_create(list);


lv_obj_set_size(float_btn, 50, 50);
lv_obj_add_flag(float_btn, LV_OBJ_FLAG_FLOATING);
lv_obj_align(float_btn, LV_ALIGN_BOTTOM_RIGHT, 0, -lv_obj_get_style_pad_
,→right(list, LV_PART_MAIN));

lv_obj_add_event(float_btn, float_btn_event_cb, LV_EVENT_ALL, list);


lv_obj_set_style_radius(float_btn, LV_RADIUS_CIRCLE, 0);
lv_obj_set_style_bg_img_src(float_btn, LV_SYMBOL_PLUS, 0);
lv_obj_set_style_text_font(float_btn, lv_theme_get_font_large(float_btn), 0);
}

#endif

class ScrollExample_3():
def __init__(self):
self.btn_cnt = 1
#
(continues on next page)

2.6. Scrolling 68
LVGL Documentation 9.0

(continued from previous page)


# Create a list with a floating button
#

list = lv.list(lv.scr_act())
list.set_size(280, 220)
list.center()

for btn_cnt in range(2):


list.add_btn(lv.SYMBOL.AUDIO,"Track {:d}".format(btn_cnt))

float_btn = lv.btn(list)
float_btn.set_size(50, 50)
float_btn.add_flag(lv.obj.FLAG.FLOATING)
float_btn.align(lv.ALIGN.BOTTOM_RIGHT, 0, -list.get_style_pad_right(lv.PART.
,→ MAIN))
float_btn.add_event(lambda evt: self.float_btn_event_cb(evt,list), lv.EVENT.
,→ ALL, None)
float_btn.set_style_radius(lv.RADIUS_CIRCLE, 0)
float_btn.set_style_bg_img_src(lv.SYMBOL.PLUS, 0)
float_btn.set_style_text_font(lv.theme_get_font_large(float_btn), 0)

def float_btn_event_cb(self,e,list):
code = e.get_code()
float_btn = e.get_target_obj()

if code == lv.EVENT.CLICKED:
list_btn = list.add_btn(lv.SYMBOL.AUDIO, "Track {:d}".format(self.btn_
,→ cnt))
self.btn_cnt += 1

float_btn.move_foreground()

list_btn.scroll_to_view(lv.ANIM.ON)

scroll_example_3 = ScrollExample_3()

2.6.4 Styling the scrollbars

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_LIST

/**
* Styling the scrollbars
*/
void lv_example_scroll_4(void)
{
lv_obj_t * obj = lv_obj_create(lv_scr_act());
lv_obj_set_size(obj, 200, 100);
lv_obj_center(obj);

lv_obj_t * label = lv_label_create(obj);


(continues on next page)

2.6. Scrolling 69
LVGL Documentation 9.0

(continued from previous page)


lv_label_set_text(label,
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n"
"Etiam dictum, tortor vestibulum lacinia laoreet, mi neque␣
,→consectetur neque, vel mattis odio dolor egestas ligula. \n"

"Sed vestibulum sapien nulla, id convallis ex porttitor nec. \n"


"Duis et massa eu libero accumsan faucibus a in arcu. \n"
"Ut pulvinar odio lorem, vel tempus turpis condimentum quis.␣
,→Nam consectetur condimentum sem in auctor. \n"

"Sed nisl augue, venenatis in blandit et, gravida ac tortor. \n"


"Etiam dapibus elementum suscipit. \n"
"Proin mollis sollicitudin convallis. \n"
"Integer dapibus tempus arcu nec viverra. \n"
"Donec molestie nulla enim, eu interdum velit placerat quis. \n"
"Donec id efficitur risus, at molestie turpis. \n"
"Suspendisse vestibulum consectetur nunc ut commodo. \n"
"Fusce molestie rhoncus nisi sit amet tincidunt. \n"
"Suspendisse a nunc ut magna ornare volutpat.");

/*Remove the style of scrollbar to have clean start*/


lv_obj_remove_style(obj, NULL, LV_PART_SCROLLBAR | LV_STATE_ANY);

/*Create a transition the animate the some properties on state change*/


static const lv_style_prop_t props[] = {LV_STYLE_BG_OPA, LV_STYLE_WIDTH, 0};
static lv_style_transition_dsc_t trans;
lv_style_transition_dsc_init(&trans, props, lv_anim_path_linear, 200, 0, NULL);

/*Create a style for the scrollbars*/


static lv_style_t style;
lv_style_init(&style);
lv_style_set_width(&style, 4); /*Width of the scrollbar*/
lv_style_set_pad_right(&style, 5); /*Space from the parallel side*/
lv_style_set_pad_top(&style, 5); /*Space from the perpendicular side*/

lv_style_set_radius(&style, 2);
lv_style_set_bg_opa(&style, LV_OPA_70);
lv_style_set_bg_color(&style, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_border_color(&style, lv_palette_darken(LV_PALETTE_BLUE, 3));
lv_style_set_border_width(&style, 2);
lv_style_set_shadow_width(&style, 8);
lv_style_set_shadow_spread(&style, 2);
lv_style_set_shadow_color(&style, lv_palette_darken(LV_PALETTE_BLUE, 1));

lv_style_set_transition(&style, &trans);

/*Make the scrollbars wider and use 100% opacity when scrolled*/
static lv_style_t style_scrolled;
lv_style_init(&style_scrolled);
lv_style_set_width(&style_scrolled, 8);
lv_style_set_bg_opa(&style_scrolled, LV_OPA_COVER);

lv_obj_add_style(obj, &style, LV_PART_SCROLLBAR);


lv_obj_add_style(obj, &style_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED);
}

#endif

2.6. Scrolling 70
LVGL Documentation 9.0

#
# Styling the scrollbars
#
obj = lv.obj(lv.scr_act())
obj.set_size(200, 100)
obj.center()

label = lv.label(obj)
label.set_text(
"""
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Etiam dictum, tortor vestibulum lacinia laoreet, mi neque consectetur neque, vel␣
,→mattis odio dolor egestas ligula.

Sed vestibulum sapien nulla, id convallis ex porttitor nec.


Duis et massa eu libero accumsan faucibus a in arcu.
Ut pulvinar odio lorem, vel tempus turpis condimentum quis. Nam consectetur␣
,→condimentum sem in auctor.

Sed nisl augue, venenatis in blandit et, gravida ac tortor.


Etiam dapibus elementum suscipit.
Proin mollis sollicitudin convallis.
Integer dapibus tempus arcu nec viverra.
Donec molestie nulla enim, eu interdum velit placerat quis.
Donec id efficitur risus, at molestie turpis.
Suspendisse vestibulum consectetur nunc ut commodo.
Fusce molestie rhoncus nisi sit amet tincidunt.
Suspendisse a nunc ut magna ornare volutpat.
""")

# Remove the style of scrollbar to have clean start


obj.remove_style(None, lv.PART.SCROLLBAR | lv.STATE.ANY)

# Create a transition the animate the some properties on state change


props = [lv.STYLE.BG_OPA, lv.STYLE.WIDTH, 0]
trans = lv.style_transition_dsc_t()
trans.init(props, lv.anim_t.path_linear, 200, 0, None)

# Create a style for the scrollbars


style = lv.style_t()
style.init()
style.set_width(4) # Width of the scrollbar
style.set_pad_right(5) # Space from the parallel side
style.set_pad_top(5) # Space from the perpendicular side

style.set_radius(2)
style.set_bg_opa(lv.OPA._70)
style.set_bg_color(lv.palette_main(lv.PALETTE.BLUE))
style.set_border_color(lv.palette_darken(lv.PALETTE.BLUE, 3))
style.set_border_width(2)
style.set_shadow_width(8)
style.set_shadow_spread(2)
style.set_shadow_color(lv.palette_darken(lv.PALETTE.BLUE, 1))

style.set_transition(trans)

# Make the scrollbars wider and use 100% opacity when scrolled
style_scrolled = lv.style_t()
(continues on next page)

2.6. Scrolling 71
LVGL Documentation 9.0

(continued from previous page)


style_scrolled.init()
style_scrolled.set_width(8)
style_scrolled.set_bg_opa(lv.OPA.COVER)

obj.add_style(style, lv.PART.SCROLLBAR)
obj.add_style(style_scrolled, lv.PART.SCROLLBAR | lv.STATE.SCROLLED)

2.6.5 Right to left scrolling

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_FONT_DEJAVU_16_PERSIAN_HEBREW

/**
* Scrolling with Right To Left base direction
*/
void lv_example_scroll_5(void)
{
lv_obj_t * obj = lv_obj_create(lv_scr_act());
lv_obj_set_style_base_dir(obj, LV_BASE_DIR_RTL, 0);
lv_obj_set_size(obj, 200, 100);
lv_obj_center(obj);

lv_obj_t * label = lv_label_create(obj);


lv_label_set_text(label,
"‫ُکنترولر‬
‫ )به میکرو‬:‫ انگلیسی‬Microcontroller) ‫␣ریزپردازنده گونهای‬
,→‫ٔه دارای که است‬
‫( تصادفی دسترسی حافظ‬RAM) ‫ٔه و‬
‫( فقطخواندنی حافظ‬ROM)، ،‫␣پورتهای تایمر‬
,→‫( خروجی و ورودی‬I/O) ‫( ترتیبی درگاه و‬Serial Port ‫ پورت‬،(‫ تراشه خود درون سریال‬،‫␣و است‬

,→‫ کنترل را دیگر ابزارهای تنهایی به میتواند‬.‫ یک دیگر عبارت به کند‬،‫␣مدار میکروکنترلر‬

,→‫ یک از که است کوچکی مجتمع‬CPU ‫ مانند دیگری اجزای و کوچک‬،‫␣خروجی و ورودی درگاههای تایمر‬

,→‫ تشکیل حافظه و دیجیتال و آنالوگ‬.‫;)"شدهاست‬

lv_obj_set_width(label, 400);
lv_obj_set_style_text_font(label, &lv_font_dejavu_16_persian_hebrew, 0);

#endif

#
# Scrolling with Right To Left base direction
#
obj = lv.obj(lv.scr_act())
obj.set_style_base_dir(lv.BASE_DIR.RTL, 0)
obj.set_size(200, 100)
obj.center()

label = lv.label(obj)
label.set_text("‫ُکنترولر‬‫ )به میکرو‬:‫ انگلیسی‬Microcontroller) ‫␣که است ریزپردازنده گونهای‬
,→‫ٔه دارای‬
‫( تصادفی دسترسی حافظ‬RAM) ‫ٔه و‬
‫( فقطخواندنی حافظ‬ROM)، ،‫␣و ورودی پورتهای تایمر‬
,→‫( خروجی‬I/O) ‫( ترتیبی درگاه و‬Serial Port ‫ پورت‬،(‫ تراشه خود درون سریال‬،‫␣میتواند و است‬

,→‫ کنترل را دیگر ابزارهای تنهایی به‬.‫ یک دیگر عبارت به کند‬،‫␣مجتمع مدار میکروکنترلر‬

,→‫ یک از که است کوچکی‬CPU ‫ مانند دیگری اجزای و کوچک‬،‫␣خروجی و ورودی درگاههای تایمر‬

,→‫ تشکیل حافظه و دیجیتال و آنالوگ‬.‫)"شدهاست‬

(continues on next page)

2.6. Scrolling 72
LVGL Documentation 9.0

(continued from previous page)


label.set_width(400)
label.set_style_text_font(lv.font_dejavu_16_persian_hebrew, 0)

2.6.6 Translate on scroll

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_FLEX

static void scroll_event_cb(lv_event_t * e)


{
lv_obj_t * cont = lv_event_get_target(e);

lv_area_t cont_a;
lv_obj_get_coords(cont, &cont_a);
lv_coord_t cont_y_center = cont_a.y1 + lv_area_get_height(&cont_a) / 2;

lv_coord_t r = lv_obj_get_height(cont) * 7 / 10;


uint32_t i;
uint32_t child_cnt = lv_obj_get_child_cnt(cont);
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = lv_obj_get_child(cont, i);
lv_area_t child_a;
lv_obj_get_coords(child, &child_a);

lv_coord_t child_y_center = child_a.y1 + lv_area_get_height(&child_a) / 2;

lv_coord_t diff_y = child_y_center - cont_y_center;


diff_y = LV_ABS(diff_y);

/*Get the x of diff_y on a circle.*/


lv_coord_t x;
/*If diff_y is out of the circle use the last point of the circle (the␣
,→radius)*/

if(diff_y >= r) {
x = r;
}
else {
/*Use Pythagoras theorem to get x from radius and y*/
uint32_t x_sqr = r * r - diff_y * diff_y;
lv_sqrt_res_t res;
lv_sqrt(x_sqr, &res, 0x8000); /*Use lvgl's built in sqrt root function*/
x = r - res.i;
}

/*Translate the item by the calculated X coordinate*/


lv_obj_set_style_translate_x(child, x, 0);

/*Use some opacity with larger translations*/


lv_opa_t opa = lv_map(x, 0, r, LV_OPA_TRANSP, LV_OPA_COVER);
lv_obj_set_style_opa(child, LV_OPA_COVER - opa, 0);
}
}

(continues on next page)

2.6. Scrolling 73
LVGL Documentation 9.0

(continued from previous page)


/**
* Translate the object as they scroll
*/
void lv_example_scroll_6(void)
{
lv_obj_t * cont = lv_obj_create(lv_scr_act());
lv_obj_set_size(cont, 200, 200);
lv_obj_center(cont);
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN);
lv_obj_add_event(cont, scroll_event_cb, LV_EVENT_SCROLL, NULL);
lv_obj_set_style_radius(cont, LV_RADIUS_CIRCLE, 0);
lv_obj_set_style_clip_corner(cont, true, 0);
lv_obj_set_scroll_dir(cont, LV_DIR_VER);
lv_obj_set_scroll_snap_y(cont, LV_SCROLL_SNAP_CENTER);
lv_obj_set_scrollbar_mode(cont, LV_SCROLLBAR_MODE_OFF);

uint32_t i;
for(i = 0; i < 20; i++) {
lv_obj_t * btn = lv_btn_create(cont);
lv_obj_set_width(btn, lv_pct(100));

lv_obj_t * label = lv_label_create(btn);


lv_label_set_text_fmt(label, "Button %"LV_PRIu32, i);
}

/*Update the buttons position manually for first*/


lv_obj_send_event(cont, LV_EVENT_SCROLL, NULL);

/*Be sure the fist button is in the middle*/


lv_obj_scroll_to_view(lv_obj_get_child(cont, 0), LV_ANIM_OFF);
}

#endif

def scroll_event_cb(e):

cont = e.get_target_obj()

cont_a = lv.area_t()
cont.get_coords(cont_a)
cont_y_center = cont_a.y1 + cont_a.get_height() // 2

r = cont.get_height() * 7 // 10

child_cnt = cont.get_child_cnt()
for i in range(child_cnt):
child = cont.get_child(i)
child_a = lv.area_t()
child.get_coords(child_a)

child_y_center = child_a.y1 + child_a.get_height() // 2

diff_y = child_y_center - cont_y_center


diff_y = abs(diff_y)

# Get the x of diff_y on a circle.


(continues on next page)

2.6. Scrolling 74
LVGL Documentation 9.0

(continued from previous page)

# If diff_y is out of the circle use the last point of the circle (the radius)
if diff_y >= r:
x = r
else:
# Use Pythagoras theorem to get x from radius and y
x_sqr = r * r - diff_y * diff_y
res = lv.sqrt_res_t()
lv.sqrt(x_sqr, res, 0x8000) # Use lvgl's built in sqrt root function
x = r - res.i

# Translate the item by the calculated X coordinate


child.set_style_translate_x(x, 0)

# Use some opacity with larger translations


opa = lv.map(x, 0, r, lv.OPA.TRANSP, lv.OPA.COVER)
child.set_style_opa(lv.OPA.COVER - opa, 0)

#
# Translate the object as they scroll
#

cont = lv.obj(lv.scr_act())
cont.set_size(200, 200)
cont.center()
cont.set_flex_flow(lv.FLEX_FLOW.COLUMN)
cont.add_event(scroll_event_cb, lv.EVENT.SCROLL, None)
cont.set_style_radius(lv.RADIUS_CIRCLE, 0)
cont.set_style_clip_corner(True, 0)
cont.set_scroll_dir(lv.DIR.VER)
cont.set_scroll_snap_y(lv.SCROLL_SNAP.CENTER)
cont.set_scrollbar_mode(lv.SCROLLBAR_MODE.OFF)

for i in range(20):
btn = lv.btn(cont)
btn.set_width(lv.pct(100))

label = lv.label(btn)
label.set_text("Button " + str(i))

# Update the buttons position manually for first*


cont.send_event(lv.EVENT.SCROLL, None)

# Be sure the fist button is in the middle


#lv.obj.scroll_to_view(cont.get_child(0), lv.ANIM.OFF)
cont.get_child(0).scroll_to_view(lv.ANIM.OFF)

2.6. Scrolling 75
LVGL Documentation 9.0

2.7 Widgets

2.7.1 Base object

Base objects with custom styles

#include "../../lv_examples.h"
#if LV_BUILD_EXAMPLES

void lv_example_obj_1(void)
{
lv_obj_t * obj1;
obj1 = lv_obj_create(lv_scr_act());
lv_obj_set_size(obj1, 100, 50);
lv_obj_align(obj1, LV_ALIGN_CENTER, -60, -30);

static lv_style_t style_shadow;


lv_style_init(&style_shadow);
lv_style_set_shadow_width(&style_shadow, 10);
lv_style_set_shadow_spread(&style_shadow, 5);
lv_style_set_shadow_color(&style_shadow, lv_palette_main(LV_PALETTE_BLUE));

lv_obj_t * obj2;
obj2 = lv_obj_create(lv_scr_act());
lv_obj_add_style(obj2, &style_shadow, 0);
lv_obj_align(obj2, LV_ALIGN_CENTER, 60, 30);
}
#endif

obj1 = lv.obj(lv.scr_act())
obj1.set_size(100, 50)
obj1.align(lv.ALIGN.CENTER, -60, -30)

style_shadow = lv.style_t()
style_shadow.init()
style_shadow.set_shadow_width(10)
style_shadow.set_shadow_spread(5)
style_shadow.set_shadow_color(lv.palette_main(lv.PALETTE.BLUE))

obj2 = lv.obj(lv.scr_act())
obj2.add_style(style_shadow, 0)
obj2.align(lv.ALIGN.CENTER, 60, 30)

Make an object draggable

#include "../../lv_examples.h"
#if LV_BUILD_EXAMPLES

static void drag_event_handler(lv_event_t * e)


{
lv_obj_t * obj = lv_event_get_target(e);

(continues on next page)

2.7. Widgets 76
LVGL Documentation 9.0

(continued from previous page)


lv_indev_t * indev = lv_indev_get_act();
if(indev == NULL) return;

lv_point_t vect;
lv_indev_get_vect(indev, &vect);

lv_coord_t x = lv_obj_get_x(obj) + vect.x;


lv_coord_t y = lv_obj_get_y(obj) + vect.y;
lv_obj_set_pos(obj, x, y);
}

/**
* Make an object dragable.
*/
void lv_example_obj_2(void)
{
lv_obj_t * obj;
obj = lv_obj_create(lv_scr_act());
lv_obj_set_size(obj, 150, 100);
lv_obj_add_event(obj, drag_event_handler, LV_EVENT_PRESSING, NULL);

lv_obj_t * label = lv_label_create(obj);


lv_label_set_text(label, "Drag me");
lv_obj_center(label);

}
#endif

def drag_event_handler(e):

obj = e.get_target_obj()

indev = lv.indev_get_act()

vect = lv.point_t()
indev.get_vect(vect)
x = obj.get_x() + vect.x
y = obj.get_y() + vect.y
obj.set_pos(x, y)

#
# Make an object dragable.
#

obj = lv.obj(lv.scr_act())
obj.set_size(150, 100)
obj.add_event(drag_event_handler, lv.EVENT.PRESSING, None)

label = lv.label(obj)
label.set_text("Drag me")
label.center()

2.7. Widgets 77
LVGL Documentation 9.0

2.7.2 Arc

Simple Arc

#include "../../lv_examples.h"

#if LV_USE_ARC && LV_BUILD_EXAMPLES

static void value_changed_event_cb(lv_event_t * e);

void lv_example_arc_1(void)
{
lv_obj_t * label = lv_label_create(lv_scr_act());

/*Create an Arc*/
lv_obj_t * arc = lv_arc_create(lv_scr_act());
lv_obj_set_size(arc, 150, 150);
lv_arc_set_rotation(arc, 135);
lv_arc_set_bg_angles(arc, 0, 270);
lv_arc_set_value(arc, 10);
lv_obj_center(arc);
lv_obj_add_event(arc, value_changed_event_cb, LV_EVENT_VALUE_CHANGED, label);

/*Manually update the label for the first time*/


lv_obj_send_event(arc, LV_EVENT_VALUE_CHANGED, NULL);
}

static void value_changed_event_cb(lv_event_t * e)


{
lv_obj_t * arc = lv_event_get_target(e);
lv_obj_t * label = lv_event_get_user_data(e);

lv_label_set_text_fmt(label, "%d%%", lv_arc_get_value(arc));

/*Rotate the label to the current position of the arc*/


lv_arc_rotate_obj_to_angle(arc, label, 25);
}

#endif

# Create an Arc
arc = lv.arc(lv.scr_act())
arc.set_end_angle(200)
arc.set_size(150, 150)
arc.center()

2.7. Widgets 78
LVGL Documentation 9.0

Loader with Arc

#include "../../lv_examples.h"

#if LV_USE_ARC && LV_BUILD_EXAMPLES

static void set_angle(void * obj, int32_t v)


{
lv_arc_set_value(obj, v);
}

/**
* Create an arc which acts as a loader.
*/
void lv_example_arc_2(void)
{
/*Create an Arc*/
lv_obj_t * arc = lv_arc_create(lv_scr_act());
lv_arc_set_rotation(arc, 270);
lv_arc_set_bg_angles(arc, 0, 360);
lv_obj_remove_style(arc, NULL, LV_PART_KNOB); /*Be sure the knob is not␣
,→displayed*/

lv_obj_clear_flag(arc, LV_OBJ_FLAG_CLICKABLE); /*To not allow adjusting by␣


,→click*/

lv_obj_center(arc);

lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, arc);
lv_anim_set_exec_cb(&a, set_angle);
lv_anim_set_time(&a, 1000);
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE); /*Just for the demo*/
lv_anim_set_repeat_delay(&a, 500);
lv_anim_set_values(&a, 0, 100);
lv_anim_start(&a);

#endif

#
# An `lv_timer` to call periodically to set the angles of the arc
#
class ArcLoader():
def __init__(self):
self.a = 270

def arc_loader_cb(self,tim,arc):
# print(tim,arc)
self.a += 5

arc.set_end_angle(self.a)

if self.a >= 270 + 360:


tim._del()
(continues on next page)

2.7. Widgets 79
LVGL Documentation 9.0

(continued from previous page)

#
# Create an arc which acts as a loader.
#

# Create an Arc
arc = lv.arc(lv.scr_act())
arc.set_bg_angles(0, 360)
arc.set_angles(270, 270)
arc.center()

# create the loader


arc_loader = ArcLoader()

# Create an `lv_timer` to update the arc.

timer = lv.timer_create_basic()
timer.set_period(20)
timer.set_cb(lambda src: arc_loader.arc_loader_cb(timer,arc))

2.7.3 Bar

Simple Bar

#include "../../lv_examples.h"
#if LV_USE_BAR && LV_BUILD_EXAMPLES

void lv_example_bar_1(void)
{
lv_obj_t * bar1 = lv_bar_create(lv_scr_act());
lv_obj_set_size(bar1, 200, 20);
lv_obj_center(bar1);
lv_bar_set_value(bar1, 70, LV_ANIM_OFF);
}

#endif

bar1 = lv.bar(lv.scr_act())
bar1.set_size(200, 20)
bar1.center()
bar1.set_value(70, lv.ANIM.OFF)

2.7. Widgets 80
LVGL Documentation 9.0

Styling a bar

#include "../../lv_examples.h"
#if LV_USE_BAR && LV_BUILD_EXAMPLES

/**
* Example of styling the bar
*/
void lv_example_bar_2(void)
{
static lv_style_t style_bg;
static lv_style_t style_indic;

lv_style_init(&style_bg);
lv_style_set_border_color(&style_bg, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_border_width(&style_bg, 2);
lv_style_set_pad_all(&style_bg, 6); /*To make the indicator smaller*/
lv_style_set_radius(&style_bg, 6);
lv_style_set_anim_time(&style_bg, 1000);

lv_style_init(&style_indic);
lv_style_set_bg_opa(&style_indic, LV_OPA_COVER);
lv_style_set_bg_color(&style_indic, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_radius(&style_indic, 3);

lv_obj_t * bar = lv_bar_create(lv_scr_act());


lv_obj_remove_style_all(bar); /*To have a clean start*/
lv_obj_add_style(bar, &style_bg, 0);
lv_obj_add_style(bar, &style_indic, LV_PART_INDICATOR);

lv_obj_set_size(bar, 200, 20);


lv_obj_center(bar);
lv_bar_set_value(bar, 100, LV_ANIM_ON);
}

#endif

#
# Example of styling the bar
#
style_bg = lv.style_t()
style_indic = lv.style_t()

style_bg.init()
style_bg.set_border_color(lv.palette_main(lv.PALETTE.BLUE))
style_bg.set_border_width(2)
style_bg.set_pad_all(6) # To make the indicator smaller
style_bg.set_radius(6)
style_bg.set_anim_time(1000)

style_indic.init()
style_indic.set_bg_opa(lv.OPA.COVER)
style_indic.set_bg_color(lv.palette_main(lv.PALETTE.BLUE))
style_indic.set_radius(3)

bar = lv.bar(lv.scr_act())
bar.remove_style_all() # To have a clean start
(continues on next page)

2.7. Widgets 81
LVGL Documentation 9.0

(continued from previous page)


bar.add_style(style_bg, 0)
bar.add_style(style_indic, lv.PART.INDICATOR)

bar.set_size(200, 20)
bar.center()
bar.set_value(100, lv.ANIM.ON)

Temperature meter

#include "../../lv_examples.h"
#if LV_USE_BAR && LV_BUILD_EXAMPLES

static void set_temp(void * bar, int32_t temp)


{
lv_bar_set_value(bar, temp, LV_ANIM_ON);
}

/**
* A temperature meter example
*/
void lv_example_bar_3(void)
{
static lv_style_t style_indic;

lv_style_init(&style_indic);
lv_style_set_bg_opa(&style_indic, LV_OPA_COVER);
lv_style_set_bg_color(&style_indic, lv_palette_main(LV_PALETTE_RED));
lv_style_set_bg_grad_color(&style_indic, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_bg_grad_dir(&style_indic, LV_GRAD_DIR_VER);

lv_obj_t * bar = lv_bar_create(lv_scr_act());


lv_obj_add_style(bar, &style_indic, LV_PART_INDICATOR);
lv_obj_set_size(bar, 20, 200);
lv_obj_center(bar);
lv_bar_set_range(bar, -20, 40);

lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_exec_cb(&a, set_temp);
lv_anim_set_time(&a, 3000);
lv_anim_set_playback_time(&a, 3000);
lv_anim_set_var(&a, bar);
lv_anim_set_values(&a, -20, 40);
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
lv_anim_start(&a);
}

#endif

def set_temp(bar, temp):


bar.set_value(temp, lv.ANIM.ON)

(continues on next page)

2.7. Widgets 82
LVGL Documentation 9.0

(continued from previous page)


#
# A temperature meter example
#

style_indic = lv.style_t()

style_indic.init()
style_indic.set_bg_opa(lv.OPA.COVER)
style_indic.set_bg_color(lv.palette_main(lv.PALETTE.RED))
style_indic.set_bg_grad_color(lv.palette_main(lv.PALETTE.BLUE))
style_indic.set_bg_grad_dir(lv.GRAD_DIR.VER)

bar = lv.bar(lv.scr_act())
bar.add_style(style_indic, lv.PART.INDICATOR)
bar.set_size(20, 200)
bar.center()
bar.set_range(-20, 40)

a = lv.anim_t()
a.init()
a.set_time(3000)
a.set_playback_time(3000)
a.set_var(bar)
a.set_values(-20, 40)
a.set_repeat_count(lv.ANIM_REPEAT_INFINITE)
a.set_custom_exec_cb(lambda a, val: set_temp(bar,val))
lv.anim_t.start(a)

Stripe pattern and range value

#include "../../lv_examples.h"
#if LV_USE_BAR && LV_BUILD_EXAMPLES

/**
* Bar with stripe pattern and ranged value
*/
void lv_example_bar_4(void)
{
LV_IMG_DECLARE(img_skew_strip);
static lv_style_t style_indic;

lv_style_init(&style_indic);
lv_style_set_bg_img_src(&style_indic, &img_skew_strip);
lv_style_set_bg_img_tiled(&style_indic, true);
lv_style_set_bg_img_opa(&style_indic, LV_OPA_30);

lv_obj_t * bar = lv_bar_create(lv_scr_act());


lv_obj_add_style(bar, &style_indic, LV_PART_INDICATOR);

lv_obj_set_size(bar, 260, 20);


lv_obj_center(bar);
lv_bar_set_mode(bar, LV_BAR_MODE_RANGE);
lv_bar_set_value(bar, 90, LV_ANIM_OFF);
(continues on next page)

2.7. Widgets 83
LVGL Documentation 9.0

(continued from previous page)


lv_bar_set_start_value(bar, 20, LV_ANIM_OFF);
}

#endif

#
# get an icon
#
def get_icon(filename,xres,yres):
try:
sdl_filename = "../../assets/" + filename + "_" + str(xres) + "x" + str(yres)␣
,→+ "_argb8888.fnt"

print("file name: ", sdl_filename)


with open(sdl_filename,'rb') as f:
icon_data = f.read()
except:
print("Could not find image file: " + filename)
return None

icon_dsc = lv.img_dsc_t(
{
"header": {"always_zero": 0, "w": xres, "h": yres, "cf": lv.COLOR_FORMAT.
,→NATIVE_ALPHA},

"data": icon_data,
"data_size": len(icon_data),
}
)
return icon_dsc

#
# Bar with stripe pattern and ranged value
#

img_skew_strip_dsc = get_icon("img_skew_strip",80,20)
style_indic = lv.style_t()

style_indic.init()
style_indic.set_bg_img_src(img_skew_strip_dsc)
style_indic.set_bg_img_tiled(True)
style_indic.set_bg_img_opa(lv.OPA._30)

bar = lv.bar(lv.scr_act())
bar.add_style(style_indic, lv.PART.INDICATOR)

bar.set_size(260, 20)
bar.center()
bar.set_mode(lv.bar.MODE.RANGE)
bar.set_value(90, lv.ANIM.OFF)
bar.set_start_value(20, lv.ANIM.OFF)

2.7. Widgets 84
LVGL Documentation 9.0

Bar with LTR and RTL base direction

#include "../../lv_examples.h"
#if LV_USE_BAR && LV_BUILD_EXAMPLES

/**
* Bar with LTR and RTL base direction
*/
void lv_example_bar_5(void)
{
lv_obj_t * label;

lv_obj_t * bar_ltr = lv_bar_create(lv_scr_act());


lv_obj_set_size(bar_ltr, 200, 20);
lv_bar_set_value(bar_ltr, 70, LV_ANIM_OFF);
lv_obj_align(bar_ltr, LV_ALIGN_CENTER, 0, -30);

label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "Left to Right base direction");
lv_obj_align_to(label, bar_ltr, LV_ALIGN_OUT_TOP_MID, 0, -5);

lv_obj_t * bar_rtl = lv_bar_create(lv_scr_act());


lv_obj_set_style_base_dir(bar_rtl, LV_BASE_DIR_RTL, 0);
lv_obj_set_size(bar_rtl, 200, 20);
lv_bar_set_value(bar_rtl, 70, LV_ANIM_OFF);
lv_obj_align(bar_rtl, LV_ALIGN_CENTER, 0, 30);

label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "Right to Left base direction");
lv_obj_align_to(label, bar_rtl, LV_ALIGN_OUT_TOP_MID, 0, -5);
}

#endif

#
# Bar with LTR and RTL base direction
#

bar_ltr = lv.bar(lv.scr_act())
bar_ltr.set_size(200, 20)
bar_ltr.set_value(70, lv.ANIM.OFF)
bar_ltr.align(lv.ALIGN.CENTER, 0, -30)

label = lv.label(lv.scr_act())
label.set_text("Left to Right base direction")
label.align_to(bar_ltr, lv.ALIGN.OUT_TOP_MID, 0, -5)

bar_rtl = lv.bar(lv.scr_act())
bar_rtl.set_style_base_dir(lv.BASE_DIR.RTL,0)
bar_rtl.set_size(200, 20)
bar_rtl.set_value(70, lv.ANIM.OFF)
bar_rtl.align(lv.ALIGN.CENTER, 0, 30)

label = lv.label(lv.scr_act())
label.set_text("Right to Left base direction")
label.align_to(bar_rtl, lv.ALIGN.OUT_TOP_MID, 0, -5)

2.7. Widgets 85
LVGL Documentation 9.0

Custom drawer to show the current value

#include "../../lv_examples.h"
#if LV_USE_BAR && LV_BUILD_EXAMPLES

static void set_value(void * bar, int32_t v)


{
lv_bar_set_value(bar, v, LV_ANIM_OFF);
}

static void event_cb(lv_event_t * e)


{
lv_obj_draw_part_dsc_t * dsc = lv_event_get_draw_part_dsc(e);
if(dsc->part != LV_PART_INDICATOR) return;

lv_obj_t * obj = lv_event_get_target(e);

lv_draw_label_dsc_t label_dsc;
lv_draw_label_dsc_init(&label_dsc);
label_dsc.font = LV_FONT_DEFAULT;

char buf[8];
lv_snprintf(buf, sizeof(buf), "%d", (int)lv_bar_get_value(obj));

lv_point_t txt_size;
lv_txt_get_size(&txt_size, buf, label_dsc.font, label_dsc.letter_space, label_dsc.
,→line_space, LV_COORD_MAX,

label_dsc.flag);

lv_area_t txt_area;
/*If the indicator is long enough put the text inside on the right*/
if(lv_area_get_width(dsc->draw_area) > txt_size.x + 20) {
txt_area.x2 = dsc->draw_area->x2 - 5;
txt_area.x1 = txt_area.x2 - txt_size.x + 1;
label_dsc.color = lv_color_white();
}
/*If the indicator is still short put the text out of it on the right*/
else {
txt_area.x1 = dsc->draw_area->x2 + 5;
txt_area.x2 = txt_area.x1 + txt_size.x - 1;
label_dsc.color = lv_color_black();
}

txt_area.y1 = dsc->draw_area->y1 + (lv_area_get_height(dsc->draw_area) - txt_size.


,→ y) / 2;
txt_area.y2 = txt_area.y1 + txt_size.y - 1;

lv_draw_label(dsc->draw_ctx, &label_dsc, &txt_area, buf, NULL);


}

/**
* Custom drawer on the bar to display the current value
*/
void lv_example_bar_6(void)
{
lv_obj_t * bar = lv_bar_create(lv_scr_act());
lv_obj_add_event(bar, event_cb, LV_EVENT_DRAW_PART_END, NULL);
(continues on next page)

2.7. Widgets 86
LVGL Documentation 9.0

(continued from previous page)


lv_obj_set_size(bar, 200, 20);
lv_obj_center(bar);

lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, bar);
lv_anim_set_values(&a, 0, 100);
lv_anim_set_exec_cb(&a, set_value);
lv_anim_set_time(&a, 2000);
lv_anim_set_playback_time(&a, 2000);
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
lv_anim_start(&a);

#endif

def set_value(bar, v):


bar.set_value(v, lv.ANIM.OFF)

def event_cb(e):
dsc = lv.obj_draw_part_dsc_t.__cast__(e.get_param())
if dsc.part != lv.PART.INDICATOR:
return

obj= e.get_target_obj()

label_dsc = lv.draw_label_dsc_t()
label_dsc.init()
# label_dsc.font = LV_FONT_DEFAULT;

value_txt = str(obj.get_value())
txt_size = lv.point_t()
lv.txt_get_size(txt_size, value_txt, label_dsc.font, label_dsc.letter_space,␣
,→label_dsc.line_space, lv.COORD.MAX, label_dsc.flag)

txt_area = lv.area_t()
# If the indicator is long enough put the text inside on the right
if dsc.draw_area.get_width() > txt_size.x + 20:
txt_area.x2 = dsc.draw_area.x2 - 5
txt_area.x1 = txt_area.x2 - txt_size.x + 1
label_dsc.color = lv.color_white()
# If the indicator is still short put the text out of it on the right*/
else:
txt_area.x1 = dsc.draw_area.x2 + 5
txt_area.x2 = txt_area.x1 + txt_size.x - 1
label_dsc.color = lv.color_black()

txt_area.y1 = dsc.draw_area.y1 + (dsc.draw_area.get_height() - txt_size.y) // 2


txt_area.y2 = txt_area.y1 + txt_size.y - 1

dsc.draw_ctx.label(label_dsc, txt_area, value_txt, None)

#
# Custom drawer on the bar to display the current value
#
(continues on next page)

2.7. Widgets 87
LVGL Documentation 9.0

(continued from previous page)

bar = lv.bar(lv.scr_act())
bar.add_event(event_cb, lv.EVENT.DRAW_PART_END, None)
bar.set_size(200, 20)
bar.center()

a = lv.anim_t()
a.init()
a.set_var(bar)
a.set_values(0, 100)
a.set_custom_exec_cb(lambda a,val: set_value(bar,val))
a.set_time(2000)
a.set_playback_time(2000)
a.set_repeat_count(lv.ANIM_REPEAT_INFINITE)
lv.anim_t.start(a)

2.7.4 Button

Simple Buttons

#include "../../lv_examples.h"
#if LV_USE_BTN && LV_BUILD_EXAMPLES

static void event_handler(lv_event_t * e)


{
lv_event_code_t code = lv_event_get_code(e);

if(code == LV_EVENT_CLICKED) {
LV_LOG_USER("Clicked");
}
else if(code == LV_EVENT_VALUE_CHANGED) {
LV_LOG_USER("Toggled");
}
}

void lv_example_btn_1(void)
{
lv_obj_t * label;

lv_obj_t * btn1 = lv_btn_create(lv_scr_act());


lv_obj_add_event(btn1, event_handler, LV_EVENT_ALL, NULL);
lv_obj_align(btn1, LV_ALIGN_CENTER, 0, -40);

label = lv_label_create(btn1);
lv_label_set_text(label, "Button");
lv_obj_center(label);

lv_obj_t * btn2 = lv_btn_create(lv_scr_act());


lv_obj_add_event(btn2, event_handler, LV_EVENT_ALL, NULL);
lv_obj_align(btn2, LV_ALIGN_CENTER, 0, 40);
lv_obj_add_flag(btn2, LV_OBJ_FLAG_CHECKABLE);
lv_obj_set_height(btn2, LV_SIZE_CONTENT);

(continues on next page)

2.7. Widgets 88
LVGL Documentation 9.0

(continued from previous page)


label = lv_label_create(btn2);
lv_label_set_text(label, "Toggle");
lv_obj_center(label);
}
#endif

def event_handler(evt):
code = evt.get_code()

if code == lv.EVENT.CLICKED:
print("Clicked event seen")
elif code == lv.EVENT.VALUE_CHANGED:
print("Value changed seen")

# create a simple button


btn1 = lv.btn(lv.scr_act())

# attach the callback


btn1.add_event(event_handler,lv.EVENT.ALL, None)

btn1.align(lv.ALIGN.CENTER,0,-40)
label=lv.label(btn1)
label.set_text("Button")

# create a toggle button


btn2 = lv.btn(lv.scr_act())

# attach the callback


#btn2.add_event(event_handler,lv.EVENT.VALUE_CHANGED,None)
btn2.add_event(event_handler,lv.EVENT.ALL, None)

btn2.align(lv.ALIGN.CENTER,0,40)
btn2.add_flag(lv.obj.FLAG.CHECKABLE)
btn2.set_height(lv.SIZE_CONTENT)

label=lv.label(btn2)
label.set_text("Toggle")
label.center()

Styling buttons

#include "../../lv_examples.h"
#if LV_USE_BTN && LV_BUILD_EXAMPLES

/**
* Style a button from scratch
*/
void lv_example_btn_2(void)
{
/*Init the style for the default state*/
static lv_style_t style;
lv_style_init(&style);

lv_style_set_radius(&style, 3);
(continues on next page)

2.7. Widgets 89
LVGL Documentation 9.0

(continued from previous page)

lv_style_set_bg_opa(&style, LV_OPA_100);
lv_style_set_bg_color(&style, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_bg_grad_color(&style, lv_palette_darken(LV_PALETTE_BLUE, 2));
lv_style_set_bg_grad_dir(&style, LV_GRAD_DIR_VER);

lv_style_set_border_opa(&style, LV_OPA_40);
lv_style_set_border_width(&style, 2);
lv_style_set_border_color(&style, lv_palette_main(LV_PALETTE_GREY));

lv_style_set_shadow_width(&style, 8);
lv_style_set_shadow_color(&style, lv_palette_main(LV_PALETTE_GREY));
lv_style_set_shadow_ofs_y(&style, 8);

lv_style_set_outline_opa(&style, LV_OPA_COVER);
lv_style_set_outline_color(&style, lv_palette_main(LV_PALETTE_BLUE));

lv_style_set_text_color(&style, lv_color_white());
lv_style_set_pad_all(&style, 10);

/*Init the pressed style*/


static lv_style_t style_pr;
lv_style_init(&style_pr);

/*Add a large outline when pressed*/


lv_style_set_outline_width(&style_pr, 30);
lv_style_set_outline_opa(&style_pr, LV_OPA_TRANSP);

lv_style_set_translate_y(&style_pr, 5);
lv_style_set_shadow_ofs_y(&style_pr, 3);
lv_style_set_bg_color(&style_pr, lv_palette_darken(LV_PALETTE_BLUE, 2));
lv_style_set_bg_grad_color(&style_pr, lv_palette_darken(LV_PALETTE_BLUE, 4));

/*Add a transition to the outline*/


static lv_style_transition_dsc_t trans;
static lv_style_prop_t props[] = {LV_STYLE_OUTLINE_WIDTH, LV_STYLE_OUTLINE_OPA, 0}
,→ ;
lv_style_transition_dsc_init(&trans, props, lv_anim_path_linear, 300, 0, NULL);

lv_style_set_transition(&style_pr, &trans);

lv_obj_t * btn1 = lv_btn_create(lv_scr_act());


lv_obj_remove_style_all(btn1); /*Remove the style coming␣
,→from the theme*/

lv_obj_add_style(btn1, &style, 0);


lv_obj_add_style(btn1, &style_pr, LV_STATE_PRESSED);
lv_obj_set_size(btn1, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_center(btn1);

lv_obj_t * label = lv_label_create(btn1);


lv_label_set_text(label, "Button");
lv_obj_center(label);
}
#endif

2.7. Widgets 90
LVGL Documentation 9.0

#
# Style a button from scratch
#

# Init the style for the default state


style = lv.style_t()
style.init()

style.set_radius(3)

style.set_bg_opa(lv.OPA.COVER)
style.set_bg_color(lv.palette_main(lv.PALETTE.BLUE))
style.set_bg_grad_color(lv.palette_darken(lv.PALETTE.BLUE, 2))
style.set_bg_grad_dir(lv.GRAD_DIR.VER)

style.set_border_opa(lv.OPA._40)
style.set_border_width(2)
style.set_border_color(lv.palette_main(lv.PALETTE.GREY))

style.set_shadow_width(8)
style.set_shadow_color(lv.palette_main(lv.PALETTE.GREY))
style.set_shadow_ofs_y(8)

style.set_outline_opa(lv.OPA.COVER)
style.set_outline_color(lv.palette_main(lv.PALETTE.BLUE))

style.set_text_color(lv.color_white())
style.set_pad_all(10)

# Init the pressed style


style_pr = lv.style_t()
style_pr.init()

# Add a large outline when pressed


style_pr.set_outline_width(30)
style_pr.set_outline_opa(lv.OPA.TRANSP)

style_pr.set_translate_y(5)
style_pr.set_shadow_ofs_y(3)
style_pr.set_bg_color(lv.palette_darken(lv.PALETTE.BLUE, 2))
style_pr.set_bg_grad_color(lv.palette_darken(lv.PALETTE.BLUE, 4))

# Add a transition to the outline


trans = lv.style_transition_dsc_t()
props = [lv.STYLE.OUTLINE_WIDTH, lv.STYLE.OUTLINE_OPA, 0]
trans.init(props, lv.anim_t.path_linear, 300, 0, None)

style_pr.set_transition(trans)

btn1 = lv.btn(lv.scr_act())
btn1.remove_style_all() # Remove the style coming from the␣
,→theme

btn1.add_style(style, 0)
btn1.add_style(style_pr, lv.STATE.PRESSED)
btn1.set_size(lv.SIZE_CONTENT, lv.SIZE_CONTENT)
btn1.center()

(continues on next page)

2.7. Widgets 91
LVGL Documentation 9.0

(continued from previous page)


label = lv.label(btn1)
label.set_text("Button")
label.center()

Gummy button

#include "../../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_BTN

/**
* Create a style transition on a button to act like a gum when clicked
*/
void lv_example_btn_3(void)
{
/*Properties to transition*/
static lv_style_prop_t props[] = {
LV_STYLE_TRANSFORM_WIDTH, LV_STYLE_TRANSFORM_HEIGHT, LV_STYLE_TEXT_LETTER_
,→SPACE, 0

};

/*Transition descriptor when going back to the default state.


*Add some delay to be sure the press transition is visible even if the press was␣
,→very short*/

static lv_style_transition_dsc_t transition_dsc_def;


lv_style_transition_dsc_init(&transition_dsc_def, props, lv_anim_path_overshoot,␣
,→250, 100, NULL);

/*Transition descriptor when going to pressed state.


*No delay, go to presses state immediately*/
static lv_style_transition_dsc_t transition_dsc_pr;
lv_style_transition_dsc_init(&transition_dsc_pr, props, lv_anim_path_ease_in_out,␣
,→250, 0, NULL);

/*Add only the new transition to he default state*/


static lv_style_t style_def;
lv_style_init(&style_def);
lv_style_set_transition(&style_def, &transition_dsc_def);

/*Add the transition and some transformation to the presses state.*/


static lv_style_t style_pr;
lv_style_init(&style_pr);
lv_style_set_transform_width(&style_pr, 10);
lv_style_set_transform_height(&style_pr, -10);
lv_style_set_text_letter_space(&style_pr, 10);
lv_style_set_transition(&style_pr, &transition_dsc_pr);

lv_obj_t * btn1 = lv_btn_create(lv_scr_act());


lv_obj_align(btn1, LV_ALIGN_CENTER, 0, -80);
lv_obj_add_style(btn1, &style_pr, LV_STATE_PRESSED);
lv_obj_add_style(btn1, &style_def, 0);

lv_obj_t * label = lv_label_create(btn1);


lv_label_set_text(label, "Gum");
}
(continues on next page)

2.7. Widgets 92
LVGL Documentation 9.0

(continued from previous page)


#endif

#
# Create a style transition on a button to act like a gum when clicked
#

# Properties to transition
props = [lv.STYLE.TRANSFORM_WIDTH, lv.STYLE.TRANSFORM_HEIGHT, lv.STYLE.TEXT_LETTER_
,→SPACE, 0]

# Transition descriptor when going back to the default state.


# Add some delay to be sure the press transition is visible even if the press was␣
,→very short*/

transition_dsc_def = lv.style_transition_dsc_t()
transition_dsc_def.init(props, lv.anim_t.path_overshoot, 250, 100, None)

# Transition descriptor when going to pressed state.


# No delay, go to pressed state immediately
transition_dsc_pr = lv.style_transition_dsc_t()
transition_dsc_pr.init(props, lv.anim_t.path_ease_in_out, 250, 0, None)

# Add only the new transition to the default state


style_def = lv.style_t()
style_def.init()
style_def.set_transition(transition_dsc_def)

# Add the transition and some transformation to the presses state.


style_pr = lv.style_t()
style_pr.init()
style_pr.set_transform_width(10)
style_pr.set_transform_height(-10)
style_pr.set_text_letter_space(10)
style_pr.set_transition(transition_dsc_pr)

btn1 = lv.btn(lv.scr_act())
btn1.align(lv.ALIGN.CENTER, 0, -80)
btn1.add_style(style_pr, lv.STATE.PRESSED)
btn1.add_style(style_def, 0)

label = lv.label(btn1)
label.set_text("Gum")

2.7.5 Button matrix

Simple Button matrix

#include "../../lv_examples.h"
#if LV_USE_BTNMATRIX && LV_BUILD_EXAMPLES

static void event_handler(lv_event_t * e)


{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
(continues on next page)

2.7. Widgets 93
LVGL Documentation 9.0

(continued from previous page)


if(code == LV_EVENT_VALUE_CHANGED) {
uint32_t id = lv_btnmatrix_get_selected_btn(obj);
const char * txt = lv_btnmatrix_get_btn_text(obj, id);
LV_UNUSED(txt);
LV_LOG_USER("%s was pressed\n", txt);
}
}

static const char * btnm_map[] = {"1", "2", "3", "4", "5", "\n",
"6", "7", "8", "9", "0", "\n",
"Action1", "Action2", ""
};

void lv_example_btnmatrix_1(void)
{
lv_obj_t * btnm1 = lv_btnmatrix_create(lv_scr_act());
lv_btnmatrix_set_map(btnm1, btnm_map);
lv_btnmatrix_set_btn_width(btnm1, 10, 2); /*Make "Action1" twice as wide␣
,→as "Action2"*/

lv_btnmatrix_set_btn_ctrl(btnm1, 10, LV_BTNMATRIX_CTRL_CHECKABLE);


lv_btnmatrix_set_btn_ctrl(btnm1, 11, LV_BTNMATRIX_CTRL_CHECKED);
lv_obj_align(btnm1, LV_ALIGN_CENTER, 0, 0);
lv_obj_add_event(btnm1, event_handler, LV_EVENT_ALL, NULL);
}

#endif

def event_handler(e):
code = e.get_code()
obj = e.get_target_obj()

if code == lv.EVENT.VALUE_CHANGED :
id = obj.get_selected_btn()
txt = obj.get_btn_text(id)

print("%s was pressed"%txt)

btnm_map = ["1", "2", "3", "4", "5", "\n",


"6", "7", "8", "9", "0", "\n",
"Action1", "Action2", ""]

btnm1 = lv.btnmatrix(lv.scr_act())
btnm1.set_map(btnm_map)
btnm1.set_btn_width(10, 2) # Make "Action1" twice as wide as "Action2"
btnm1.set_btn_ctrl(10, lv.btnmatrix.CTRL.CHECKABLE)
btnm1.set_btn_ctrl(11, lv.btnmatrix.CTRL.CHECKED)
btnm1.align(lv.ALIGN.CENTER, 0, 0)
btnm1.add_event(event_handler, lv.EVENT.ALL, None)

#endif

2.7. Widgets 94
LVGL Documentation 9.0

Custom buttons

#include "../../lv_examples.h"
#if LV_USE_BTNMATRIX && LV_BUILD_EXAMPLES

static void event_cb(lv_event_t * e)


{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_DRAW_PART_BEGIN) {
lv_obj_draw_part_dsc_t * dsc = lv_event_get_draw_part_dsc(e);

/*When the button matrix draws the buttons...*/


if(dsc->class_p == &lv_btnmatrix_class && dsc->type == LV_BTNMATRIX_DRAW_PART_
,→ BTN) {
/*Change the draw descriptor of the 2nd button*/
if(dsc->id == 1) {
dsc->rect_dsc->radius = 0;
if(lv_btnmatrix_get_selected_btn(obj) == dsc->id) dsc->rect_dsc->bg_
,→color = lv_palette_darken(LV_PALETTE_BLUE, 3);

else dsc->rect_dsc->bg_color = lv_palette_main(LV_PALETTE_BLUE);

dsc->rect_dsc->shadow_width = 6;
dsc->rect_dsc->shadow_ofs_x = 3;
dsc->rect_dsc->shadow_ofs_y = 3;
dsc->label_dsc->color = lv_color_white();
}
/*Change the draw descriptor of the 3rd button*/
else if(dsc->id == 2) {
dsc->rect_dsc->radius = LV_RADIUS_CIRCLE;
if(lv_btnmatrix_get_selected_btn(obj) == dsc->id) dsc->rect_dsc->bg_
,→color = lv_palette_darken(LV_PALETTE_RED, 3);

else dsc->rect_dsc->bg_color = lv_palette_main(LV_PALETTE_RED);

dsc->label_dsc->color = lv_color_white();
}
else if(dsc->id == 3) {
dsc->label_dsc->opa = LV_OPA_TRANSP; /*Hide the text if any*/

}
}
}
if(code == LV_EVENT_DRAW_PART_END) {
lv_obj_draw_part_dsc_t * dsc = lv_event_get_draw_part_dsc(e);

/*When the button matrix draws the buttons...*/


if(dsc->class_p == &lv_btnmatrix_class && dsc->type == LV_BTNMATRIX_DRAW_PART_
,→ BTN) {
/*Add custom content to the 4th button when the button itself was drawn*/
if(dsc->id == 3) {
LV_IMG_DECLARE(img_star);
lv_img_header_t header;
lv_res_t res = lv_img_decoder_get_info(&img_star, &header);
if(res != LV_RES_OK) return;

lv_area_t a;
(continues on next page)

2.7. Widgets 95
LVGL Documentation 9.0

(continued from previous page)


a.x1 = dsc->draw_area->x1 + (lv_area_get_width(dsc->draw_area) -␣
,→ header.w) / 2;
a.x2 = a.x1 + header.w - 1;
a.y1 = dsc->draw_area->y1 + (lv_area_get_height(dsc->draw_area) -␣
,→ header.h) / 2;
a.y2 = a.y1 + header.h - 1;

lv_draw_img_dsc_t img_draw_dsc;
lv_draw_img_dsc_init(&img_draw_dsc);
img_draw_dsc.recolor = lv_color_black();
if(lv_btnmatrix_get_selected_btn(obj) == dsc->id) img_draw_dsc.
,→recolor_opa = LV_OPA_30;

lv_draw_img(dsc->draw_ctx, &img_draw_dsc, &a, &img_star);


}
}
}
}

/**
* Add custom drawer to the button matrix to customize buttons one by one
*/
void lv_example_btnmatrix_2(void)
{
lv_obj_t * btnm = lv_btnmatrix_create(lv_scr_act());
lv_obj_add_event(btnm, event_cb, LV_EVENT_ALL, NULL);
lv_obj_center(btnm);
}

#endif

# Create an image from the png file


try:
with open('../../assets/img_star.png','rb') as f:
png_data = f.read()
except:
print("Could not find star.png")
sys.exit()

img_star_argb = lv.img_dsc_t({
'data_size': len(png_data),
'data': png_data
})

def event_cb(e):
code = e.get_code()
obj = e.get_target_obj()
dsc = lv.obj_draw_part_dsc_t.__cast__(e.get_param())
if code == lv.EVENT.DRAW_PART_BEGIN:
# Change the draw descriptor the 2nd button
if dsc.id == 1:
dsc.rect_dsc.radius = 0
if obj.get_selected_btn() == dsc.id:
dsc.rect_dsc.bg_color = lv.palette_darken(lv.PALETTE.GREY, 3)
else:
dsc.rect_dsc.bg_color = lv.palette_main(lv.PALETTE.BLUE)
(continues on next page)

2.7. Widgets 96
LVGL Documentation 9.0

(continued from previous page)

dsc.rect_dsc.shadow_width = 6
dsc.rect_dsc.shadow_ofs_x = 3
dsc.rect_dsc.shadow_ofs_y = 3
dsc.label_dsc.color = lv.color_white()

# Change the draw descriptor the 3rd button

elif dsc.id == 2:
dsc.rect_dsc.radius = lv.RADIUS_CIRCLE
if obj.get_selected_btn() == dsc.id:
dsc.rect_dsc.bg_color = lv.palette_darken(lv.PALETTE.RED, 3)
else:
dsc.rect_dsc.bg_color = lv.palette_main(lv.PALETTE.RED)

dsc.label_dsc.color = lv.color_white()
elif dsc.id == 3:
dsc.label_dsc.opa = lv.OPA.TRANSP # Hide the text if any

if code == lv.EVENT.DRAW_PART_END:
# Add custom content to the 4th button when the button itself was drawn
if dsc.id == 3:
# LV_IMG_DECLARE(img_star)
header = lv.img_header_t()
res = lv.img.decoder_get_info(img_star_argb, header)
if res != lv.RES.OK:
print("error when getting image header")
return
else:
a = lv.area_t()
a.x1 = dsc.draw_area.x1 + (dsc.draw_area.get_width() - header.w) // 2
a.x2 = a.x1 + header.w - 1
a.y1 = dsc.draw_area.y1 + (dsc.draw_area.get_height() - header.h) // 2
a.y2 = a.y1 + header.h - 1
img_draw_dsc = lv.draw_img_dsc_t()
img_draw_dsc.init()
img_draw_dsc.recolor = lv.color_black()
if obj.get_selected_btn() == dsc.id:
img_draw_dsc.recolor_opa = lv.OPA._30

dsc.draw_ctx.img(img_draw_dsc, a, img_star_argb)

#
# Add custom drawer to the button matrix to c
#
btnm = lv.btnmatrix(lv.scr_act())
btnm.add_event(event_cb, lv.EVENT.ALL, None)
btnm.center()

2.7. Widgets 97
LVGL Documentation 9.0

Pagination

#include "../../lv_examples.h"
#if LV_USE_BTNMATRIX && LV_BUILD_EXAMPLES

static void event_cb(lv_event_t * e)


{
lv_obj_t * obj = lv_event_get_target(e);
uint32_t id = lv_btnmatrix_get_selected_btn(obj);
bool prev = id == 0 ? true : false;
bool next = id == 6 ? true : false;
if(prev || next) {
/*Find the checked button*/
uint32_t i;
for(i = 1; i < 7; i++) {
if(lv_btnmatrix_has_btn_ctrl(obj, i, LV_BTNMATRIX_CTRL_CHECKED)) break;
}

if(prev && i > 1) i--;


else if(next && i < 5) i++;

lv_btnmatrix_set_btn_ctrl(obj, i, LV_BTNMATRIX_CTRL_CHECKED);
}
}

/**
* Make a button group (pagination)
*/
void lv_example_btnmatrix_3(void)
{
static lv_style_t style_bg;
lv_style_init(&style_bg);
lv_style_set_pad_all(&style_bg, 0);
lv_style_set_pad_gap(&style_bg, 0);
lv_style_set_clip_corner(&style_bg, true);
lv_style_set_radius(&style_bg, LV_RADIUS_CIRCLE);
lv_style_set_border_width(&style_bg, 0);

static lv_style_t style_btn;


lv_style_init(&style_btn);
lv_style_set_radius(&style_btn, 0);
lv_style_set_border_width(&style_btn, 1);
lv_style_set_border_opa(&style_btn, LV_OPA_50);
lv_style_set_border_color(&style_btn, lv_palette_main(LV_PALETTE_GREY));
lv_style_set_border_side(&style_btn, LV_BORDER_SIDE_INTERNAL);
lv_style_set_radius(&style_btn, 0);

static const char * map[] = {LV_SYMBOL_LEFT, "1", "2", "3", "4", "5", LV_SYMBOL_
,→ RIGHT, ""};

lv_obj_t * btnm = lv_btnmatrix_create(lv_scr_act());


lv_btnmatrix_set_map(btnm, map);
lv_obj_add_style(btnm, &style_bg, 0);
lv_obj_add_style(btnm, &style_btn, LV_PART_ITEMS);
lv_obj_add_event(btnm, event_cb, LV_EVENT_VALUE_CHANGED, NULL);
lv_obj_set_size(btnm, 225, 35);
(continues on next page)

2.7. Widgets 98
LVGL Documentation 9.0

(continued from previous page)

/*Allow selecting on one number at time*/


lv_btnmatrix_set_btn_ctrl_all(btnm, LV_BTNMATRIX_CTRL_CHECKABLE);
lv_btnmatrix_clear_btn_ctrl(btnm, 0, LV_BTNMATRIX_CTRL_CHECKABLE);
lv_btnmatrix_clear_btn_ctrl(btnm, 6, LV_BTNMATRIX_CTRL_CHECKABLE);

lv_btnmatrix_set_one_checked(btnm, true);
lv_btnmatrix_set_btn_ctrl(btnm, 1, LV_BTNMATRIX_CTRL_CHECKED);

lv_obj_center(btnm);

#endif

def event_cb(e):
obj = e.get_target_obj()
id = obj.get_selected_btn()
if id == 0:
prev = True
else:
prev = False
if id == 6:
next = True
else:
next = False
if prev or next:
# Find the checked butto
for i in range(7):
if obj.has_btn_ctrl(i, lv.btnmatrix.CTRL.CHECKED):
break
if prev and i > 1:
i-=1
elif next and i < 5:
i+=1

obj.set_btn_ctrl(i, lv.btnmatrix.CTRL.CHECKED)

#
# Make a button group
#

style_bg = lv.style_t()
style_bg.init()
style_bg.set_pad_all(0)
style_bg.set_pad_gap(0)
style_bg.set_clip_corner(True)
style_bg.set_radius(lv.RADIUS_CIRCLE)
style_bg.set_border_width(0)

style_btn = lv.style_t()
style_btn.init()
style_btn.set_radius(0)
style_btn.set_border_width(1)
style_btn.set_border_opa(lv.OPA._50)
(continues on next page)

2.7. Widgets 99
LVGL Documentation 9.0

(continued from previous page)


style_btn.set_border_color(lv.palette_main(lv.PALETTE.GREY))
style_btn.set_border_side(lv.BORDER_SIDE.INTERNAL)
style_btn.set_radius(0)

map = [lv.SYMBOL.LEFT,"1","2", "3", "4", "5",lv.SYMBOL.RIGHT, ""]

btnm = lv.btnmatrix(lv.scr_act())
btnm.set_map(map)
btnm.add_style(style_bg, 0)
btnm.add_style(style_btn, lv.PART.ITEMS)
btnm.add_event(event_cb, lv.EVENT.VALUE_CHANGED, None)
btnm.set_size(225, 35)

# Allow selecting on one number at time


btnm.set_btn_ctrl_all(lv.btnmatrix.CTRL.CHECKABLE)
btnm.clear_btn_ctrl(0, lv.btnmatrix.CTRL.CHECKABLE)
btnm.clear_btn_ctrl(6, lv.btnmatrix.CTRL.CHECKABLE)

btnm.set_one_checked(True)
btnm.set_btn_ctrl(1, lv.btnmatrix.CTRL.CHECKED)

btnm.center()

2.7.6 Calendar

Calendar with header

#include "../../lv_examples.h"
#if LV_USE_CALENDAR && LV_BUILD_EXAMPLES

static void event_handler(lv_event_t * e)


{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_current_target(e);

if(code == LV_EVENT_VALUE_CHANGED) {
lv_calendar_date_t date;
if(lv_calendar_get_pressed_date(obj, &date)) {
LV_LOG_USER("Clicked date: %02d.%02d.%d", date.day, date.month, date.
,→year);

}
}
}

void lv_example_calendar_1(void)
{
lv_obj_t * calendar = lv_calendar_create(lv_scr_act());
lv_obj_set_size(calendar, 185, 185);
lv_obj_align(calendar, LV_ALIGN_CENTER, 0, 27);
lv_obj_add_event(calendar, event_handler, LV_EVENT_ALL, NULL);

lv_calendar_set_today_date(calendar, 2021, 02, 23);


lv_calendar_set_showed_date(calendar, 2021, 02);
(continues on next page)

2.7. Widgets 100


LVGL Documentation 9.0

(continued from previous page)

/*Highlight a few days*/


static lv_calendar_date_t highlighted_days[3]; /*Only its pointer will be␣
,→saved so should be static*/

highlighted_days[0].year = 2021;
highlighted_days[0].month = 02;
highlighted_days[0].day = 6;

highlighted_days[1].year = 2021;
highlighted_days[1].month = 02;
highlighted_days[1].day = 11;

highlighted_days[2].year = 2022;
highlighted_days[2].month = 02;
highlighted_days[2].day = 22;

lv_calendar_set_highlighted_dates(calendar, highlighted_days, 3);

#if LV_USE_CALENDAR_HEADER_DROPDOWN
lv_calendar_header_dropdown_create(calendar);
#elif LV_USE_CALENDAR_HEADER_ARROW
lv_calendar_header_arrow_create(calendar);
#endif
lv_calendar_set_showed_date(calendar, 2021, 10);
}

#endif

def event_handler(e):
code = e.get_code()

if code == lv.EVENT.VALUE_CHANGED:
source = e.get_current_target_obj()
date = lv.calendar_date_t()
if source.get_pressed_date(date) == lv.RES.OK:
calendar.set_today_date(date.year, date.month, date.day)
print("Clicked date: %02d.%02d.%02d"%(date.day, date.month, date.year))

calendar = lv.calendar(lv.scr_act())
calendar.set_size(200, 200)
calendar.align(lv.ALIGN.CENTER, 0, 20)
calendar.add_event(event_handler, lv.EVENT.ALL, None)

calendar.set_today_date(2021, 02, 23)


calendar.set_showed_date(2021, 02)

# Highlight a few days


highlighted_days=[
lv.calendar_date_t({'year':2021, 'month':2, 'day':6}),
lv.calendar_date_t({'year':2021, 'month':2, 'day':11}),
lv.calendar_date_t({'year':2021, 'month':2, 'day':22})
]

calendar.set_highlighted_dates(highlighted_days, len(highlighted_days))
(continues on next page)

2.7. Widgets 101


LVGL Documentation 9.0

(continued from previous page)

lv.calendar_header_dropdown(calendar)

2.7.7 Canvas

Drawing on the Canvas and rotate

#include "../../lv_examples.h"
#if LV_USE_CANVAS && LV_BUILD_EXAMPLES

#define CANVAS_WIDTH 200


#define CANVAS_HEIGHT 150

void lv_example_canvas_1(void)
{
lv_draw_rect_dsc_t rect_dsc;
lv_draw_rect_dsc_init(&rect_dsc);
rect_dsc.radius = 10;
rect_dsc.bg_opa = LV_OPA_COVER;
rect_dsc.bg_grad.dir = LV_GRAD_DIR_HOR;
rect_dsc.bg_grad.stops[0].color = lv_palette_main(LV_PALETTE_RED);
rect_dsc.bg_grad.stops[1].color = lv_palette_main(LV_PALETTE_BLUE);
rect_dsc.border_width = 2;
rect_dsc.border_opa = LV_OPA_90;
rect_dsc.border_color = lv_color_white();
rect_dsc.shadow_width = 5;
rect_dsc.shadow_ofs_x = 5;
rect_dsc.shadow_ofs_y = 5;

lv_draw_label_dsc_t label_dsc;
lv_draw_label_dsc_init(&label_dsc);
label_dsc.color = lv_palette_main(LV_PALETTE_ORANGE);

static uint8_t cbuf[LV_CANVAS_BUF_SIZE_TRUE_COLOR(CANVAS_WIDTH, CANVAS_HEIGHT)];

lv_obj_t * canvas = lv_canvas_create(lv_scr_act());


lv_canvas_set_buffer(canvas, cbuf, CANVAS_WIDTH, CANVAS_HEIGHT, LV_COLOR_FORMAT_
,→NATIVE);

lv_obj_center(canvas);
lv_canvas_fill_bg(canvas, lv_palette_lighten(LV_PALETTE_GREY, 3), LV_OPA_COVER);

lv_canvas_draw_rect(canvas, 70, 60, 100, 70, &rect_dsc);

lv_canvas_draw_text(canvas, 40, 20, 100, &label_dsc, "Some text on text canvas");

/*Test the rotation. It requires another buffer where the original image is␣
,→stored.
*So copy the current image to buffer and rotate it to the canvas*/
static uint8_t cbuf_tmp[LV_CANVAS_BUF_SIZE_TRUE_COLOR(CANVAS_WIDTH, CANVAS_
,→HEIGHT)];

memcpy(cbuf_tmp, cbuf, sizeof(cbuf_tmp));


lv_img_dsc_t img;
img.data = (void *)cbuf_tmp;
(continues on next page)

2.7. Widgets 102


LVGL Documentation 9.0

(continued from previous page)


img.header.cf = LV_COLOR_FORMAT_NATIVE;
img.header.w = CANVAS_WIDTH;
img.header.h = CANVAS_HEIGHT;

lv_canvas_fill_bg(canvas, lv_palette_lighten(LV_PALETTE_GREY, 3), LV_OPA_COVER);


lv_canvas_transform(canvas, &img, 120, LV_ZOOM_NONE, 0, 0, CANVAS_WIDTH / 2,␣
,→CANVAS_HEIGHT / 2, true);

#endif

_CANVAS_WIDTH = 200
_CANVAS_HEIGHT = 150
LV_ZOOM_NONE = 256

rect_dsc = lv.draw_rect_dsc_t()
rect_dsc.init()
rect_dsc.radius = 10
rect_dsc.bg_opa = lv.OPA.COVER
rect_dsc.bg_grad.dir = lv.GRAD_DIR.HOR
rect_dsc.bg_grad.stops[0].color = lv.palette_main(lv.PALETTE.RED)
rect_dsc.bg_grad.stops[1].color = lv.palette_main(lv.PALETTE.BLUE)
rect_dsc.border_width = 2
rect_dsc.border_opa = lv.OPA._90
rect_dsc.border_color = lv.color_white()
rect_dsc.shadow_width = 5
rect_dsc.shadow_ofs_x = 5
rect_dsc.shadow_ofs_y = 5

label_dsc = lv.draw_label_dsc_t()
label_dsc.init()
label_dsc.color = lv.palette_main(lv.PALETTE.YELLOW)

cbuf = bytearray(_CANVAS_WIDTH * _CANVAS_HEIGHT * 4)

canvas = lv.canvas(lv.scr_act())
canvas.set_buffer(cbuf, _CANVAS_WIDTH, _CANVAS_HEIGHT, lv.COLOR_FORMAT.NATIVE)
canvas.center()
canvas.fill_bg(lv.palette_lighten(lv.PALETTE.GREY, 3), lv.OPA.COVER)

canvas.draw_rect(70, 60, 100, 70, rect_dsc)


canvas.draw_text(40, 20, 100, label_dsc, "Some text on text canvas")

# Test the rotation. It requires another buffer where the original image is stored.
# So copy the current image to buffer and rotate it to the canvas

img = lv.img_dsc_t()
img.data = cbuf[:]
img.header.cf = lv.COLOR_FORMAT.NATIVE
img.header.w = _CANVAS_WIDTH
img.header.h = _CANVAS_HEIGHT

canvas.fill_bg(lv.palette_lighten(lv.PALETTE.GREY, 3), lv.OPA.COVER)


canvas.transform(img, 30, LV_ZOOM_NONE, 0, 0, _CANVAS_WIDTH // 2, _CANVAS_HEIGHT // 2,
,→ True)

2.7. Widgets 103


LVGL Documentation 9.0

Transparent Canvas with chroma keying

#include "../../lv_examples.h"
#if LV_USE_CANVAS && LV_BUILD_EXAMPLES

#define CANVAS_WIDTH 50
#define CANVAS_HEIGHT 50

/**
* Create a transparent canvas with Chroma keying and indexed color format (palette).
*/
void lv_example_canvas_2(void)
{
/*Create a button to better see the transparency*/
lv_btn_create(lv_scr_act());

/*Create a buffer for the canvas*/


static uint8_t cbuf[LV_CANVAS_BUF_SIZE_INDEXED_1BIT(CANVAS_WIDTH, CANVAS_HEIGHT)];

/*Create a canvas and initialize its palette*/


lv_obj_t * canvas = lv_canvas_create(lv_scr_act());
lv_canvas_set_buffer(canvas, cbuf, CANVAS_WIDTH, CANVAS_HEIGHT, LV_COLOR_FORMAT_
,→I1);

lv_canvas_set_palette(canvas, 0, lv_color_to32(LV_COLOR_CHROMA_KEY));
lv_canvas_set_palette(canvas, 1, lv_color_to32(lv_palette_main(LV_PALETTE_RED)));

/*Create colors with the indices of the palette*/


lv_color_t c0;
lv_color_t c1;

lv_color_set_int(&c0, 0);
lv_color_set_int(&c1, 1);

/*Red background (There is no dedicated alpha channel in indexed images so LV_OPA_


,→ COVER is ignored)*/
lv_canvas_fill_bg(canvas, c1, LV_OPA_COVER);

/*Create hole on the canvas*/


uint32_t x;
uint32_t y;
for(y = 10; y < 30; y++) {
for(x = 5; x < 20; x++) {
lv_canvas_set_px(canvas, x, y, c0, LV_OPA_COVER);
}
}
}
#endif

import math

CANVAS_WIDTH = 50
CANVAS_HEIGHT = 50
LV_COLOR_CHROMA_KEY = lv.color_hex(0x00ff00)

def LV_IMG_BUF_SIZE_ALPHA_1BIT(w, h):


return int(math.floor((w + 7) / 8 ) * h)

(continues on next page)

2.7. Widgets 104


LVGL Documentation 9.0

(continued from previous page)


def LV_IMG_BUF_SIZE_INDEXED_1BIT(w, h):
return LV_IMG_BUF_SIZE_ALPHA_1BIT(w, h) + 4 * 2

def LV_CANVAS_BUF_SIZE_INDEXED_1BIT(w, h):


return LV_IMG_BUF_SIZE_INDEXED_1BIT(w, h)

#
# Create a transparent canvas with Chroma keying and indexed color format (palette).
#

# Create a button to better see the transparency


btn=lv.btn(lv.scr_act())

# Create a buffer for the canvas


cbuf= bytearray(LV_CANVAS_BUF_SIZE_INDEXED_1BIT(CANVAS_WIDTH, CANVAS_HEIGHT))

# Create a canvas and initialize its palette


canvas = lv.canvas(lv.scr_act())
canvas.set_buffer(cbuf, CANVAS_WIDTH, CANVAS_HEIGHT, lv.COLOR_FORMAT.I1)
canvas.set_palette(0, LV_COLOR_CHROMA_KEY)
canvas.set_palette(1, lv.palette_main(lv.PALETTE.RED))

# Create colors with the indices of the palette


c0 = lv.color_t()
c1 = lv.color_t()

c0.set_int(0)
c1.set_int(1)

# Red background (There is no dedicated alpha channel in indexed images so LV_OPA_


,→COVER is ignored)

canvas.fill_bg(c1, lv.OPA.COVER)

# Create hole on the canvas


for y in range(10,30):
for x in range(5,20):
canvas.set_px(x, y, c0, lv.OPA.COVER)

Draw a rectangle to the canvas

#include "../../lv_examples.h"
#if LV_USE_CANVAS && LV_BUILD_EXAMPLES

#define CANVAS_WIDTH 50
#define CANVAS_HEIGHT 50

/**
* Draw a rectangle to the canvas
*/
void lv_example_canvas_3(void)
{
/*Create a buffer for the canvas*/
static uint8_t cbuf[LV_CANVAS_BUF_SIZE_TRUE_COLOR(CANVAS_WIDTH, CANVAS_HEIGHT)];

/*Create a canvas and initialize its palette*/


(continues on next page)

2.7. Widgets 105


LVGL Documentation 9.0

(continued from previous page)


lv_obj_t * canvas = lv_canvas_create(lv_scr_act());
lv_canvas_set_buffer(canvas, cbuf, CANVAS_WIDTH, CANVAS_HEIGHT, LV_COLOR_FORMAT_
,→NATIVE);

lv_canvas_fill_bg(canvas, lv_color_hex3(0xccc), LV_OPA_COVER);


lv_obj_center(canvas);

lv_draw_rect_dsc_t dsc;
lv_draw_rect_dsc_init(&dsc);
dsc.bg_color = lv_palette_main(LV_PALETTE_RED);
dsc.border_color = lv_palette_main(LV_PALETTE_BLUE);
dsc.border_width = 3;
dsc.outline_color = lv_palette_main(LV_PALETTE_GREEN);
dsc.outline_width = 2;
dsc.outline_pad = 2;
dsc.outline_opa = LV_OPA_50;
dsc.radius = 5;
dsc.border_width = 3;
lv_canvas_draw_rect(canvas, 10, 10, 30, 20, &dsc);

}
#endif

CANVAS_WIDTH = 50
CANVAS_HEIGHT = 50

LV_COLOR_SIZE = 32
#
# Draw a rectangle to the canvas
#
# Create a buffer for the canvas
cbuf = bytearray((LV_COLOR_SIZE // 8) * CANVAS_WIDTH * CANVAS_HEIGHT)

# Create a canvas and initialize its palette*/


canvas = lv.canvas(lv.scr_act())
canvas.set_buffer(cbuf, CANVAS_WIDTH, CANVAS_HEIGHT, lv.COLOR_FORMAT.NATIVE)

canvas.fill_bg(lv.color_hex3(0xccc), lv.OPA.COVER)
canvas.center()

dsc = lv.draw_rect_dsc_t()
dsc.init()

dsc.bg_color = lv.palette_main(lv.PALETTE.RED)
dsc.border_color = lv.palette_main(lv.PALETTE.BLUE)
dsc.border_width = 3
dsc.outline_color = lv.palette_main(lv.PALETTE.GREEN)
dsc.outline_width = 2
dsc.outline_pad = 2
dsc.outline_opa = lv.OPA._50
dsc.radius = 5
dsc.border_width = 3
canvas.draw_rect(10, 10, 30, 20, dsc)

2.7. Widgets 106


LVGL Documentation 9.0

Draw a label to the canvas

#include "../../lv_examples.h"
#if LV_USE_CANVAS && LV_FONT_MONTSERRAT_18 && LV_BUILD_EXAMPLES

#define CANVAS_WIDTH 50
#define CANVAS_HEIGHT 50

/**
* Draw a text to the canvas
*/
void lv_example_canvas_4(void)
{
/*Create a buffer for the canvas*/
static uint8_t cbuf[LV_CANVAS_BUF_SIZE_TRUE_COLOR(CANVAS_WIDTH, CANVAS_HEIGHT)];

/*Create a canvas and initialize its palette*/


lv_obj_t * canvas = lv_canvas_create(lv_scr_act());
lv_canvas_set_buffer(canvas, cbuf, CANVAS_WIDTH, CANVAS_HEIGHT, LV_COLOR_FORMAT_
,→NATIVE);

lv_canvas_fill_bg(canvas, lv_color_hex3(0xccc), LV_OPA_COVER);


lv_obj_center(canvas);

lv_draw_label_dsc_t dsc;
lv_draw_label_dsc_init(&dsc);
dsc.color = lv_palette_main(LV_PALETTE_RED);
dsc.font = &lv_font_montserrat_18;
dsc.decor = LV_TEXT_DECOR_UNDERLINE;

lv_canvas_draw_text(canvas, 10, 10, 30, &dsc, "Hello");


}
#endif

import fs_driver

CANVAS_WIDTH = 50
CANVAS_HEIGHT = 50

LV_COLOR_SIZE = 32

#
# Draw a text to the canvas
#

# Create a buffer for the canvas


cbuf = bytearray((LV_COLOR_SIZE // 8) * CANVAS_WIDTH * CANVAS_HEIGHT)

# Create a canvas and initialize its palette


canvas = lv.canvas(lv.scr_act())
canvas.set_buffer(cbuf, CANVAS_WIDTH, CANVAS_HEIGHT, lv.COLOR_FORMAT.NATIVE)
canvas.fill_bg(lv.color_hex3(0xccc), lv.OPA.COVER)
canvas.center()

dsc = lv.draw_label_dsc_t()
dsc.init()

dsc.color = lv.palette_main(lv.PALETTE.RED)
(continues on next page)

2.7. Widgets 107


LVGL Documentation 9.0

(continued from previous page)

try:
dsc.font = lv_font_montserrat_18
except:
# needed for dynamic font loading
fs_drv = lv.fs_drv_t()
fs_driver.fs_register(fs_drv, 'S')

print("Loading font montserrat_18")


font_montserrat_18 = lv.font_load("S:../../assets/font/montserrat-18.fnt")
if not font_montserrat_18:
print("Font loading failed")
else:
dsc.font = font_montserrat_18

dsc.decor = lv.TEXT_DECOR.UNDERLINE
print('Printing "Hello"')
canvas.draw_text(10, 10, 30, dsc, "Hello")

Draw an arc to the canvas

#include "../../lv_examples.h"
#if LV_USE_CANVAS && LV_BUILD_EXAMPLES

#define CANVAS_WIDTH 50
#define CANVAS_HEIGHT 50

/**
* Draw an arc to the canvas
*/
void lv_example_canvas_5(void)
{
/*Create a buffer for the canvas*/
static uint8_t cbuf[LV_CANVAS_BUF_SIZE_TRUE_COLOR(CANVAS_WIDTH, CANVAS_HEIGHT)];

/*Create a canvas and initialize its palette*/


lv_obj_t * canvas = lv_canvas_create(lv_scr_act());
lv_canvas_set_buffer(canvas, cbuf, CANVAS_WIDTH, CANVAS_HEIGHT, LV_COLOR_FORMAT_
,→NATIVE);

lv_canvas_fill_bg(canvas, lv_color_hex3(0xccc), LV_OPA_COVER);


lv_obj_center(canvas);

lv_draw_arc_dsc_t dsc;
lv_draw_arc_dsc_init(&dsc);
dsc.color = lv_palette_main(LV_PALETTE_RED);
dsc.width = 5;

lv_canvas_draw_arc(canvas, 25, 25, 15, 0, 220, &dsc);


}
#endif

2.7. Widgets 108


LVGL Documentation 9.0

CANVAS_WIDTH = 50
CANVAS_HEIGHT = 50

LV_COLOR_SIZE = 32

#
# Draw an arc to the canvas
#

# Create a buffer for the canvas


cbuf = bytearray((LV_COLOR_SIZE // 8) * CANVAS_WIDTH * CANVAS_HEIGHT)

# Create a canvas and initialize its palette


canvas = lv.canvas(lv.scr_act())
canvas.set_buffer(cbuf, CANVAS_WIDTH, CANVAS_HEIGHT, lv.COLOR_FORMAT.NATIVE)
canvas.fill_bg(lv.color_hex3(0xccc), lv.OPA.COVER)
canvas.center()

dsc = lv.draw_arc_dsc_t()
dsc.init()
dsc.color = lv.palette_main(lv.PALETTE.RED)
dsc.width = 5

canvas.draw_arc(25, 25, 15, 0, 220, dsc)

Draw an image to the canvas

#include "../../lv_examples.h"
#if LV_USE_CANVAS && LV_BUILD_EXAMPLES

#define CANVAS_WIDTH 50
#define CANVAS_HEIGHT 50

/**
* Draw an image to the canvas
*/
void lv_example_canvas_6(void)
{
/*Create a buffer for the canvas*/
static uint8_t cbuf[LV_CANVAS_BUF_SIZE_TRUE_COLOR(CANVAS_WIDTH, CANVAS_HEIGHT)];

/*Create a canvas and initialize its palette*/


lv_obj_t * canvas = lv_canvas_create(lv_scr_act());
lv_canvas_set_buffer(canvas, cbuf, CANVAS_WIDTH, CANVAS_HEIGHT, LV_COLOR_FORMAT_
,→NATIVE);

lv_canvas_fill_bg(canvas, lv_color_hex3(0xccc), LV_OPA_COVER);


lv_obj_center(canvas);

lv_draw_img_dsc_t dsc;
lv_draw_img_dsc_init(&dsc);

LV_IMG_DECLARE(img_star);
lv_canvas_draw_img(canvas, 5, 5, &img_star, &dsc);
}
#endif

2.7. Widgets 109


LVGL Documentation 9.0

CANVAS_WIDTH = 50
CANVAS_HEIGHT = 50

LV_COLOR_SIZE = 32

# Create an image from the png file


try:
with open('../../assets/img_star.png','rb') as f:
png_data = f.read()
except:
print("Could not find star.png")
sys.exit()

img_star_argb = lv.img_dsc_t({
'data_size': len(png_data),
'data': png_data
})

#
# Draw an image to the canvas
#

# Create a buffer for the canvas


cbuf = bytearray((LV_COLOR_SIZE // 8) * CANVAS_WIDTH * CANVAS_HEIGHT)

# Create a canvas and initialize its palette


canvas = lv.canvas(lv.scr_act())
canvas.set_buffer(cbuf, CANVAS_WIDTH, CANVAS_HEIGHT, lv.COLOR_FORMAT.NATIVE)
canvas.fill_bg(lv.color_hex3(0xccc), lv.OPA.COVER)
canvas.center()

dsc = lv.draw_img_dsc_t()
dsc.init()

canvas.draw_img(5, 5, img_star_argb, dsc)

Draw a line to the canvas

#include "../../lv_examples.h"
#if LV_USE_CANVAS&& LV_BUILD_EXAMPLES

#define CANVAS_WIDTH 50
#define CANVAS_HEIGHT 50

/**
* Draw a line to the canvas
*/
void lv_example_canvas_7(void)
{
/*Create a buffer for the canvas*/
static uint8_t cbuf[LV_CANVAS_BUF_SIZE_TRUE_COLOR(CANVAS_WIDTH, CANVAS_HEIGHT)];

/*Create a canvas and initialize its palette*/


lv_obj_t * canvas = lv_canvas_create(lv_scr_act());
(continues on next page)

2.7. Widgets 110


LVGL Documentation 9.0

(continued from previous page)


lv_canvas_set_buffer(canvas, cbuf, CANVAS_WIDTH, CANVAS_HEIGHT, LV_COLOR_FORMAT_
,→NATIVE);

lv_canvas_fill_bg(canvas, lv_color_hex3(0xccc), LV_OPA_COVER);


lv_obj_center(canvas);

lv_draw_line_dsc_t dsc;
lv_draw_line_dsc_init(&dsc);
dsc.color = lv_palette_main(LV_PALETTE_RED);
dsc.width = 4;
dsc.round_end = 1;
dsc.round_start = 1;

lv_point_t p[] = {{15, 15}, {35, 10}, {10, 40}};


lv_canvas_draw_line(canvas, p, 3, &dsc);
}
#endif

CANVAS_WIDTH = 50
CANVAS_HEIGHT = 50

LV_COLOR_SIZE = 32

#
# Draw a line to the canvas
#

# Create a buffer for the canvas


cbuf = bytearray((LV_COLOR_SIZE // 8) * CANVAS_WIDTH * CANVAS_HEIGHT)

# Create a canvas and initialize its palette


canvas = lv.canvas(lv.scr_act())
canvas.set_buffer(cbuf, CANVAS_WIDTH, CANVAS_HEIGHT, lv.COLOR_FORMAT.NATIVE)
canvas.fill_bg(lv.color_hex3(0xccc), lv.OPA.COVER)
canvas.center()

dsc = lv.draw_line_dsc_t()
dsc.init()

dsc.color = lv.palette_main(lv.PALETTE.RED)
dsc.width = 4
dsc.round_end = 1
dsc.round_start = 1

p = [ {"x":15,"y":15},
{"x":35,"y":10},
{"x":10,"y":40} ]

canvas.draw_line(p, 3, dsc)

2.7. Widgets 111


LVGL Documentation 9.0

2.7.8 Chart

Line Chart

#include "../../lv_examples.h"
#if LV_USE_CHART && LV_BUILD_EXAMPLES

void lv_example_chart_1(void)
{
/*Create a chart*/
lv_obj_t * chart;
chart = lv_chart_create(lv_scr_act());
lv_obj_set_size(chart, 200, 150);
lv_obj_center(chart);
lv_chart_set_type(chart, LV_CHART_TYPE_LINE); /*Show lines and points too*/

/*Add two data series*/


lv_chart_series_t * ser1 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_
,→RED), LV_CHART_AXIS_PRIMARY_Y);

lv_chart_series_t * ser2 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_


,→GREEN), LV_CHART_AXIS_SECONDARY_Y);

/*Set the next points on 'ser1'*/


lv_chart_set_next_value(chart, ser1, 10);
lv_chart_set_next_value(chart, ser1, 10);
lv_chart_set_next_value(chart, ser1, 10);
lv_chart_set_next_value(chart, ser1, 10);
lv_chart_set_next_value(chart, ser1, 10);
lv_chart_set_next_value(chart, ser1, 10);
lv_chart_set_next_value(chart, ser1, 10);
lv_chart_set_next_value(chart, ser1, 30);
lv_chart_set_next_value(chart, ser1, 70);
lv_chart_set_next_value(chart, ser1, 90);

/*Directly set points on 'ser2'*/


ser2->y_points[0] = 90;
ser2->y_points[1] = 70;
ser2->y_points[2] = 65;
ser2->y_points[3] = 65;
ser2->y_points[4] = 65;
ser2->y_points[5] = 65;
ser2->y_points[6] = 65;
ser2->y_points[7] = 65;
ser2->y_points[8] = 65;
ser2->y_points[9] = 65;

lv_chart_refresh(chart); /*Required after direct set*/


}

#endif

# Create a chart
chart = lv.chart(lv.scr_act())
chart.set_size(200, 150)
chart.center()
chart.set_type(lv.chart.TYPE.LINE) # Show lines and points too

(continues on next page)

2.7. Widgets 112


LVGL Documentation 9.0

(continued from previous page)


# Add two data series
ser1 = chart.add_series(lv.palette_main(lv.PALETTE.RED), lv.chart.AXIS.PRIMARY_Y)
ser2 = chart.add_series(lv.palette_main(lv.PALETTE.GREEN), lv.chart.AXIS.SECONDARY_Y)
print(ser2)
# Set next points on ser1
chart.set_next_value(ser1,10)
chart.set_next_value(ser1,10)
chart.set_next_value(ser1,10)
chart.set_next_value(ser1,10)
chart.set_next_value(ser1,10)
chart.set_next_value(ser1,10)
chart.set_next_value(ser1,10)
chart.set_next_value(ser1,30)
chart.set_next_value(ser1,70)
chart.set_next_value(ser1,90)

# Directly set points on 'ser2'


ser2.y_points = [90, 70, 65, 65, 65, 65, 65, 65, 65, 65]
chart.refresh() # Required after direct set

Faded area line chart with custom division lines

#include "../../lv_examples.h"
#if LV_USE_CHART && LV_USE_DRAW_MASKS && LV_BUILD_EXAMPLES

static lv_obj_t * chart1;


static lv_chart_series_t * ser1;
static lv_chart_series_t * ser2;

static void draw_event_cb(lv_event_t * e)


{
lv_obj_t * obj = lv_event_get_target(e);

/*Add the faded area before the lines are drawn*/


lv_obj_draw_part_dsc_t * dsc = lv_event_get_draw_part_dsc(e);
if(dsc->part == LV_PART_ITEMS) {
if(!dsc->p1 || !dsc->p2) return;

/*Add a line mask that keeps the area below the line*/
lv_draw_mask_line_param_t line_mask_param;
lv_draw_mask_line_points_init(&line_mask_param, dsc->p1->x, dsc->p1->y, dsc->
,→p2->x, dsc->p2->y,

LV_DRAW_MASK_LINE_SIDE_BOTTOM);
int16_t line_mask_id = lv_draw_mask_add(&line_mask_param, NULL);

/*Add a fade effect: transparent bottom covering top*/


lv_coord_t h = lv_obj_get_height(obj);
lv_draw_mask_fade_param_t fade_mask_param;
lv_draw_mask_fade_init(&fade_mask_param, &obj->coords, LV_OPA_COVER, obj->
,→coords.y1 + h / 8, LV_OPA_TRANSP,

obj->coords.y2);
int16_t fade_mask_id = lv_draw_mask_add(&fade_mask_param, NULL);

/*Draw a rectangle that will be affected by the mask*/


(continues on next page)

2.7. Widgets 113


LVGL Documentation 9.0

(continued from previous page)


lv_draw_rect_dsc_t draw_rect_dsc;
lv_draw_rect_dsc_init(&draw_rect_dsc);
draw_rect_dsc.bg_opa = LV_OPA_20;
draw_rect_dsc.bg_color = dsc->line_dsc->color;

lv_area_t a;
a.x1 = dsc->p1->x;
a.x2 = dsc->p2->x - 1;
a.y1 = LV_MIN(dsc->p1->y, dsc->p2->y);
a.y2 = obj->coords.y2;
lv_draw_rect(dsc->draw_ctx, &draw_rect_dsc, &a);

/*Remove the masks*/


lv_draw_mask_free_param(&line_mask_param);
lv_draw_mask_free_param(&fade_mask_param);
lv_draw_mask_remove_id(line_mask_id);
lv_draw_mask_remove_id(fade_mask_id);
}
/*Hook the division lines too*/
else if(dsc->part == LV_PART_MAIN) {
if(dsc->line_dsc == NULL || dsc->p1 == NULL || dsc->p2 == NULL) return;

/*Vertical line*/
if(dsc->p1->x == dsc->p2->x) {
dsc->line_dsc->color = lv_palette_lighten(LV_PALETTE_GREY, 1);
if(dsc->id == 3) {
dsc->line_dsc->width = 2;
dsc->line_dsc->dash_gap = 0;
dsc->line_dsc->dash_width = 0;
}
else {
dsc->line_dsc->width = 1;
dsc->line_dsc->dash_gap = 6;
dsc->line_dsc->dash_width = 6;
}
}
/*Horizontal line*/
else {
if(dsc->id == 2) {
dsc->line_dsc->width = 2;
dsc->line_dsc->dash_gap = 0;
dsc->line_dsc->dash_width = 0;
}
else {
dsc->line_dsc->width = 2;
dsc->line_dsc->dash_gap = 6;
dsc->line_dsc->dash_width = 6;
}

if(dsc->id == 1 || dsc->id == 3) {
dsc->line_dsc->color = lv_palette_main(LV_PALETTE_GREEN);
}
else {
dsc->line_dsc->color = lv_palette_lighten(LV_PALETTE_GREY, 1);
}
}
}
(continues on next page)

2.7. Widgets 114


LVGL Documentation 9.0

(continued from previous page)


}

static void add_data(lv_timer_t * timer)


{
LV_UNUSED(timer);
static uint32_t cnt = 0;
lv_chart_set_next_value(chart1, ser1, lv_rand(20, 90));

if(cnt % 4 == 0) lv_chart_set_next_value(chart1, ser2, lv_rand(40, 60));

cnt++;
}

/**
* Add a faded area effect to the line chart and make some division lines ticker
*/
void lv_example_chart_2(void)
{
/*Create a chart1*/
chart1 = lv_chart_create(lv_scr_act());
lv_obj_set_size(chart1, 200, 150);
lv_obj_center(chart1);
lv_chart_set_type(chart1, LV_CHART_TYPE_LINE); /*Show lines and points too*/

lv_chart_set_div_line_count(chart1, 5, 7);

lv_obj_add_event(chart1, draw_event_cb, LV_EVENT_DRAW_PART_BEGIN, NULL);


lv_chart_set_update_mode(chart1, LV_CHART_UPDATE_MODE_CIRCULAR);

/*Add two data series*/


ser1 = lv_chart_add_series(chart1, lv_palette_main(LV_PALETTE_RED), LV_CHART_AXIS_
,→PRIMARY_Y);

ser2 = lv_chart_add_series(chart1, lv_palette_main(LV_PALETTE_BLUE), LV_CHART_


,→AXIS_SECONDARY_Y);

uint32_t i;
for(i = 0; i < 10; i++) {
lv_chart_set_next_value(chart1, ser1, lv_rand(20, 90));
lv_chart_set_next_value(chart1, ser2, lv_rand(30, 70));
}

lv_timer_create(add_data, 200, NULL);


}

#endif

def draw_event_cb(e):

obj = e.get_target_obj()

# Add the faded area before the lines are drawn


dsc = lv.obj_draw_part_dsc_t.__cast__(e.get_param())
if dsc.part != lv.PART.ITEMS:
return
if not dsc.p1 or not dsc.p2:
return
(continues on next page)

2.7. Widgets 115


LVGL Documentation 9.0

(continued from previous page)

# Add a line mask that keeps the area below the line
line_mask_param = lv.draw_mask_line_param_t()
line_mask_param.points_init(dsc.p1.x, dsc.p1.y, dsc.p2.x, dsc.p2.y, lv.DRAW_MASK_
,→LINE_SIDE.BOTTOM)

# line_mask_id = line_mask_param.draw_mask_add(None)
line_mask_id = lv.draw_mask_add(line_mask_param, None)
# Add a fade effect: transparent bottom covering top
h = obj.get_height()
fade_mask_param = lv.draw_mask_fade_param_t()
coords = lv.area_t()
obj.get_coords(coords)
fade_mask_param.init(coords, lv.OPA.COVER, coords.y1 + h // 8, lv.OPA.TRANSP,
,→coords.y2)

fade_mask_id = lv.draw_mask_add(fade_mask_param,None)

# Draw a rectangle that will be affected by the mask


draw_rect_dsc = lv.draw_rect_dsc_t()
draw_rect_dsc.init()
draw_rect_dsc.bg_opa = lv.OPA._20
draw_rect_dsc.bg_color = dsc.line_dsc.color

a = lv.area_t()
a.x1 = dsc.p1.x
a.x2 = dsc.p2.x - 1
a.y1 = min(dsc.p1.y, dsc.p2.y)
coords = lv.area_t()
obj.get_coords(coords)
a.y2 = coords.y2
dsc.draw_ctx.rect(draw_rect_dsc, a)

# Remove the masks


lv.draw_mask_remove_id(line_mask_id)
lv.draw_mask_remove_id(fade_mask_id)

def add_data(timer):
# LV_UNUSED(timer);
cnt = 0
chart1.set_next_value(ser1, lv.rand(20, 90))

if cnt % 4 == 0:
chart1.set_next_value(ser2, lv.rand(40, 60))

cnt +=1

#
# Add a faded area effect to the line chart
#

# Create a chart1
chart1 = lv.chart(lv.scr_act())
chart1.set_size(200, 150)
chart1.center()
chart1.set_type(lv.chart.TYPE.LINE) # Show lines and points too

chart1.add_event(draw_event_cb, lv.EVENT.DRAW_PART_BEGIN, None)


(continues on next page)

2.7. Widgets 116


LVGL Documentation 9.0

(continued from previous page)


chart1.set_update_mode(lv.chart.UPDATE_MODE.CIRCULAR)

# Add two data series


ser1 = chart1.add_series(lv.palette_main(lv.PALETTE.RED), lv.chart.AXIS.PRIMARY_Y)
ser2 = chart1.add_series(lv.palette_main(lv.PALETTE.BLUE), lv.chart.AXIS.SECONDARY_Y)

for i in range(10):
chart1.set_next_value(ser1, lv.rand(20, 90))
chart1.set_next_value(ser2, lv.rand(30, 70))

timer = lv.timer_create(add_data, 200, None)

Axis ticks and labels with scrolling

#include "../../lv_examples.h"
#if LV_USE_CHART && LV_BUILD_EXAMPLES

static void draw_event_cb(lv_event_t * e)


{
lv_obj_draw_part_dsc_t * dsc = lv_event_get_draw_part_dsc(e);
if(!lv_obj_draw_part_check_type(dsc, &lv_chart_class, LV_CHART_DRAW_PART_TICK_
,→LABEL)) return;

if(dsc->id == LV_CHART_AXIS_PRIMARY_X && dsc->text) {


const char * month[] = {"Jan", "Febr", "March", "Apr", "May", "Jun", "July",
,→"Aug", "Sept", "Oct", "Nov", "Dec"};

lv_snprintf(dsc->text, dsc->text_length, "%s", month[dsc->value]);


}
}

/**
* Add ticks and labels to the axis and demonstrate scrolling
*/
void lv_example_chart_3(void)
{
/*Create a chart*/
lv_obj_t * chart;
chart = lv_chart_create(lv_scr_act());
lv_obj_set_size(chart, 200, 150);
lv_obj_center(chart);
lv_chart_set_type(chart, LV_CHART_TYPE_BAR);
lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_Y, 0, 100);
lv_chart_set_range(chart, LV_CHART_AXIS_SECONDARY_Y, 0, 400);
lv_chart_set_point_count(chart, 12);
lv_obj_add_event(chart, draw_event_cb, LV_EVENT_DRAW_PART_BEGIN, NULL);

/*Add ticks and label to every axis*/


lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_X, 10, 5, 12, 3, true, 40);
lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_Y, 10, 5, 6, 2, true, 50);
lv_chart_set_axis_tick(chart, LV_CHART_AXIS_SECONDARY_Y, 10, 5, 3, 4, true, 50);

/*Zoom in a little in X*/


lv_chart_set_zoom_x(chart, 800);

/*Add two data series*/


(continues on next page)

2.7. Widgets 117


LVGL Documentation 9.0

(continued from previous page)


lv_chart_series_t * ser1 = lv_chart_add_series(chart, lv_palette_lighten(LV_
,→PALETTE_GREEN, 2), LV_CHART_AXIS_PRIMARY_Y);

lv_chart_series_t * ser2 = lv_chart_add_series(chart, lv_palette_darken(LV_


,→PALETTE_GREEN, 2),

LV_CHART_AXIS_SECONDARY_Y);

/*Set the next points on 'ser1'*/


lv_chart_set_next_value(chart, ser1, 31);
lv_chart_set_next_value(chart, ser1, 66);
lv_chart_set_next_value(chart, ser1, 10);
lv_chart_set_next_value(chart, ser1, 89);
lv_chart_set_next_value(chart, ser1, 63);
lv_chart_set_next_value(chart, ser1, 56);
lv_chart_set_next_value(chart, ser1, 32);
lv_chart_set_next_value(chart, ser1, 35);
lv_chart_set_next_value(chart, ser1, 57);
lv_chart_set_next_value(chart, ser1, 85);
lv_chart_set_next_value(chart, ser1, 22);
lv_chart_set_next_value(chart, ser1, 58);

lv_coord_t * ser2_array = lv_chart_get_y_array(chart, ser2);


/*Directly set points on 'ser2'*/
ser2_array[0] = 92;
ser2_array[1] = 71;
ser2_array[2] = 61;
ser2_array[3] = 15;
ser2_array[4] = 21;
ser2_array[5] = 35;
ser2_array[6] = 35;
ser2_array[7] = 58;
ser2_array[8] = 31;
ser2_array[9] = 53;
ser2_array[10] = 33;
ser2_array[11] = 73;

lv_chart_refresh(chart); /*Required after direct set*/


}

#endif

def draw_event_cb(e):

dsc = lv.obj_draw_part_dsc_t.__cast__(e.get_param())
if dsc.part == lv.PART.TICKS and dsc.id == lv.chart.AXIS.PRIMARY_X:
month = ["Jan", "Febr", "March", "Apr", "May", "Jun", "July", "Aug", "Sept",
,→"Oct", "Nov", "Dec"]

# dsc.text is defined char text[16], I must therefore convert the Python␣


,→string to a byte_array

dsc.text = bytes(month[dsc.value],"ascii")
#
# Add ticks and labels to the axis and demonstrate scrolling
#

# Create a chart
chart = lv.chart(lv.scr_act())
chart.set_size(200, 150)
(continues on next page)

2.7. Widgets 118


LVGL Documentation 9.0

(continued from previous page)


chart.center()
chart.set_type(lv.chart.TYPE.BAR)
chart.set_range(lv.chart.AXIS.PRIMARY_Y, 0, 100)
chart.set_range(lv.chart.AXIS.SECONDARY_Y, 0, 400)
chart.set_point_count(12)
chart.add_event(draw_event_cb, lv.EVENT.DRAW_PART_BEGIN, None)

# Add ticks and label to every axis


chart.set_axis_tick(lv.chart.AXIS.PRIMARY_X, 10, 5, 12, 3, True, 40)
chart.set_axis_tick(lv.chart.AXIS.PRIMARY_Y, 10, 5, 6, 2, True, 50)
chart.set_axis_tick(lv.chart.AXIS.SECONDARY_Y, 10, 5, 3, 4,True, 50)

# Zoom in a little in X
chart.set_zoom_x(800)

# Add two data series


ser1 = lv.chart.add_series(chart, lv.palette_lighten(lv.PALETTE.GREEN, 2), lv.chart.
,→AXIS.PRIMARY_Y)

ser2 = lv.chart.add_series(chart, lv.palette_darken(lv.PALETTE.GREEN, 2), lv.chart.


,→AXIS.SECONDARY_Y)

# Set the next points on 'ser1'


chart.set_next_value(ser1, 31)
chart.set_next_value(ser1, 66)
chart.set_next_value(ser1, 10)
chart.set_next_value(ser1, 89)
chart.set_next_value(ser1, 63)
chart.set_next_value(ser1, 56)
chart.set_next_value(ser1, 32)
chart.set_next_value(ser1, 35)
chart.set_next_value(ser1, 57)
chart.set_next_value(ser1, 85)
chart.set_next_value(ser1, 22)
chart.set_next_value(ser1, 58)

# Directly set points on 'ser2'


ser2.y_points = [92,71,61,15,21,35,35,58,31,53,33,73]

chart.refresh() # Required after direct set

Show the value of the pressed points

#include "../../lv_examples.h"
#if LV_USE_CHART && LV_BUILD_EXAMPLES

static void event_cb(lv_event_t * e)


{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * chart = lv_event_get_target(e);

if(code == LV_EVENT_VALUE_CHANGED) {
lv_obj_invalidate(chart);
}
(continues on next page)

2.7. Widgets 119


LVGL Documentation 9.0

(continued from previous page)


if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
lv_coord_t * s = lv_event_get_param(e);
*s = LV_MAX(*s, 20);
}
else if(code == LV_EVENT_DRAW_POST_END) {
int32_t id = lv_chart_get_pressed_point(chart);
if(id == LV_CHART_POINT_NONE) return;

LV_LOG_USER("Selected point %d", (int)id);

lv_chart_series_t * ser = lv_chart_get_series_next(chart, NULL);


while(ser) {
lv_point_t p;
lv_chart_get_point_pos_by_id(chart, ser, id, &p);

lv_coord_t * y_array = lv_chart_get_y_array(chart, ser);


lv_coord_t value = y_array[id];

char buf[16];
lv_snprintf(buf, sizeof(buf), LV_SYMBOL_DUMMY"$%d", value);

lv_draw_rect_dsc_t draw_rect_dsc;
lv_draw_rect_dsc_init(&draw_rect_dsc);
draw_rect_dsc.bg_color = lv_color_black();
draw_rect_dsc.bg_opa = LV_OPA_50;
draw_rect_dsc.radius = 3;
draw_rect_dsc.bg_img_src = buf;
draw_rect_dsc.bg_img_recolor = lv_color_white();

lv_area_t a;
a.x1 = chart->coords.x1 + p.x - 20;
a.x2 = chart->coords.x1 + p.x + 20;
a.y1 = chart->coords.y1 + p.y - 30;
a.y2 = chart->coords.y1 + p.y - 10;

lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);


lv_draw_rect(draw_ctx, &draw_rect_dsc, &a);

ser = lv_chart_get_series_next(chart, ser);


}
}
else if(code == LV_EVENT_RELEASED) {
lv_obj_invalidate(chart);
}
}

/**
* Show the value of the pressed points
*/
void lv_example_chart_4(void)
{
/*Create a chart*/
lv_obj_t * chart;
chart = lv_chart_create(lv_scr_act());
lv_obj_set_size(chart, 200, 150);
lv_obj_center(chart);

(continues on next page)

2.7. Widgets 120


LVGL Documentation 9.0

(continued from previous page)


lv_obj_add_event(chart, event_cb, LV_EVENT_ALL, NULL);
lv_obj_refresh_ext_draw_size(chart);

/*Zoom in a little in X*/


lv_chart_set_zoom_x(chart, 800);

/*Add two data series*/


lv_chart_series_t * ser1 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_
,→RED), LV_CHART_AXIS_PRIMARY_Y);

lv_chart_series_t * ser2 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_


,→GREEN), LV_CHART_AXIS_PRIMARY_Y);

uint32_t i;
for(i = 0; i < 10; i++) {
lv_chart_set_next_value(chart, ser1, lv_rand(60, 90));
lv_chart_set_next_value(chart, ser2, lv_rand(10, 40));
}
}

#endif

#!/opt/bin/lv_micropython -i
import lvgl as lv

def event_cb(e):

code = e.get_code()
chart = e.get_target_obj()

if code == lv.EVENT.VALUE_CHANGED:
chart.invalidate()

if code == lv.EVENT.REFR_EXT_DRAW_SIZE:
# s = lv.coord_t.__cast__(e.get_param())
# print("s: {:d}".format(s))
e.set_ext_draw_size(20)

elif code == lv.EVENT.DRAW_POST_END:


id = chart.get_pressed_point()
if id == lv.CHART_POINT_NONE :
return

# print("Selected point {:d}".format(id))

ser = chart.get_series_next(None)

while(ser) :
p = lv.point_t()
chart.get_point_pos_by_id(ser, id, p)
# print("point coords: x: {:d}, y: {:d}".format(p.x,p.y))
y_array = chart.get_y_array(ser)
value = y_array[id]

buf = lv.SYMBOL.DUMMY + "{:2d}".format(value)

draw_rect_dsc = lv.draw_rect_dsc_t()
draw_rect_dsc.init()
(continues on next page)

2.7. Widgets 121


LVGL Documentation 9.0

(continued from previous page)


draw_rect_dsc.bg_color = lv.color_black()
draw_rect_dsc.bg_opa = lv.OPA._50
draw_rect_dsc.radius = 3
draw_rect_dsc.bg_img_src = buf
draw_rect_dsc.bg_img_recolor = lv.color_white()

coords = lv.area_t()
chart.get_coords(coords)
# print("coords: x1: {:d}, y1: {:d}".format(coords.x1, coords.y1))
a = lv.area_t()
a.x1 = coords.x1 + p.x - 20
a.x2 = coords.x1 + p.x + 20
a.y1 = coords.y1 + p.y - 30
a.y2 = coords.y1 + p.y - 10
# print("a: x1: {:d}, x2: {:d}, y1: {:d}, y2: {:d}".format(a.x1,a.x2,a.y1,
,→ a.y2))
draw_ctx = e.get_draw_ctx()
draw_ctx.rect(draw_rect_dsc, a)

ser = chart.get_series_next(ser)

elif code == lv.EVENT.RELEASED:


chart.invalidate()

#
# Show the value of the pressed points
#

# Create a chart
chart = lv.chart(lv.scr_act())
chart.set_size(200, 150)
chart.center()

chart.add_event(event_cb, lv.EVENT.ALL, None)

chart.refresh_ext_draw_size()

# Zoom in a little in X
chart.set_zoom_x(800)

# Add two data series


ser1 = chart.add_series(lv.palette_main(lv.PALETTE.RED), lv.chart.AXIS.PRIMARY_Y)
ser2 = chart.add_series(lv.palette_main(lv.PALETTE.GREEN), lv.chart.AXIS.PRIMARY_Y)
for i in range(10):
chart.set_next_value(ser1, lv.rand(60, 90))
chart.set_next_value(ser2, lv.rand(10, 40))

2.7. Widgets 122


LVGL Documentation 9.0

Display 1000 data points with zooming and scrolling

#include "../../lv_examples.h"
#if LV_USE_CHART && LV_USE_SLIDER && LV_BUILD_EXAMPLES

static lv_obj_t * chart;


/* Source: https://github.com/ankur219/ECG-Arrhythmia-classification/blob/
,→642230149583adfae1e4bd26c6f0e1fd8af2be0e/sample.csv*/

static const lv_coord_t ecg_sample[] = {


-2, 2, 0, -15, -39, -63, -71, -68, -67, -69, -84, -95, -104, -107, -108, -107, -
,→107, -107, -107, -114, -118, -117,

-112, -100, -89, -83, -71, -64, -58, -58, -62, -62, -58, -51, -46, -39, -27, -
,→10, 4, 7, 1, -3, 0, 14, 24, 30, 25, 19,

13, 7, 12, 15, 18, 21, 13, 6, 9, 8, 17, 19, 13, 11, 11, 11, 23, 30, 37, 34,␣
,→25, 14, 15, 19, 28, 31, 26, 23, 25, 31,

39, 37, 37, 34, 30, 32, 22, 29, 31, 33, 37, 23, 13, 7, 2, 4, -2, 2, 11, 22,␣
,→33, 19, -1, -27, -55, -67, -72, -71, -63,

-49, -18, 35, 113, 230, 369, 525, 651, 722, 730, 667, 563, 454, 357, 305, 288,
,→ 274, 255, 212, 173, 143, 117, 82, 39,

-13, -53, -78, -91, -101, -113, -124, -131, -131, -131, -129, -128, -129, -
,→125, -123, -123, -129, -139, -148, -153,

-159, -166, -183, -205, -227, -243, -248, -246, -254, -280, -327, -381, -429,␣
,→-473, -517, -556, -592, -612, -620,

-620, -614, -604, -591, -574, -540, -497, -441, -389, -358, -336, -313, -284,␣
,→-222, -167, -114, -70, -47, -28, -4, 12,

38, 52, 58, 56, 56, 57, 68, 77, 86, 86, 80, 69, 67, 70, 82, 85, 89, 90, 89,␣
,→89, 88, 91, 96, 97, 91, 83, 78, 82, 88, 95,

96, 105, 106, 110, 102, 100, 96, 98, 97, 101, 98, 99, 100, 107, 113, 119, 115,
,→ 110, 96, 85, 73, 64, 69, 76, 79,

78, 75, 85, 100, 114, 113, 105, 96, 84, 74, 66, 60, 75, 85, 89, 83, 67, 61,␣
,→67, 73, 79, 74, 63, 57, 56, 58, 61, 55,

48, 45, 46, 55, 62, 55, 49, 43, 50, 59, 63, 57, 40, 31, 23, 25, 27, 31, 35,␣
,→34, 30, 36, 34, 42, 38, 36, 40, 46, 50,

47, 32, 30, 32, 52, 67, 73, 71, 63, 54, 53, 45, 41, 28, 13, 3, 1, 4, 4, -8, -
,→23, -32, -31, -19, -5, 3, 9, 13, 19,

24, 27, 29, 25, 22, 26, 32, 42, 51, 56, 60, 57, 55, 53, 53, 54, 59, 54, 49,␣
,→26, -3, -11, -20, -47, -100, -194, -236,

-212, -123, 8, 103, 142, 147, 120, 105, 98, 93, 81, 61, 40, 26, 28, 30, 30,␣
,→27, 19, 17, 21, 20, 19, 19, 22, 36, 40,

35, 20, 7, 1, 10, 18, 27, 22, 6, -4, -2, 3, 6, -2, -13, -14, -10, -2, 3, 2, -
,→1, -5, -10, -19, -32, -42, -55, -60,

-68, -77, -86, -101, -110, -117, -115, -104, -92, -84, -85, -84, -73, -65, -
,→52, -50, -45, -35, -20, -3, 12, 20, 25,

26, 28, 28, 30, 28, 25, 28, 33, 42, 42, 36, 23, 9, 0, 1, -4, 1, -4, -4, 1, 5,␣
,→9, 9, -3, -1, -18, -50, -108, -190,

-272, -340, -408, -446, -537, -643, -777, -894, -920, -853, -697, -461, -251,␣
,→-60, 58, 103, 129, 139, 155, 170, 173,

178, 185, 190, 193, 200, 208, 215, 225, 224, 232, 234, 240, 240, 236, 229,␣
,→226, 224, 232, 233, 232, 224, 219, 219,

223, 231, 226, 223, 219, 218, 223, 223, 223, 233, 245, 268, 286, 296, 295,␣
,→283, 271, 263, 252, 243, 226, 210, 197,

186, 171, 152, 133, 117, 114, 110, 107, 96, 80, 63, 48, 40, 38, 34, 28, 15, 2,
,→ -7, -11, -14, -18, -29, -37, -44, -50,

-58, -63, -61, -52, -50, -48, -61, -59, -58, -54, -47, -52, -62, -61, -64, -
,→54, -52, -59, -69, -76, -76, -69, -67,

-74, -78, -81, -80, -73, -65, -57, -53, -51, -47, -35, -27, -22, -22, -24, -
,→21, -17, -13, -10, -11, -13, -20, -20,

(continues on next page)

2.7. Widgets 123


LVGL Documentation 9.0

(continued from previous page)


-12, -2, 7, -1, -12, -16, -13, -2, 2, -4, -5, -2, 9, 19, 19, 14, 11, 13, 19,␣
,→21, 20, 18, 19, 19, 19, 16, 15, 13, 14,

9, 3, -5, -9, -5, -3, -2, -3, -3, 2, 8, 9, 9, 5, 6, 8, 8, 7, 4, 3, 4, 5, 3, 5,


,→ 5, 13, 13, 12, 10, 10, 15, 22, 17,

14, 7, 10, 15, 16, 11, 12, 10, 13, 9, -2, -4, -2, 7, 16, 16, 17, 16, 7, -1, -
,→16, -18, -16, -9, -4, -5, -10, -9, -8,

-3, -4, -10, -19, -20, -16, -9, -9, -23, -40, -48, -43, -33, -19, -21, -26, -
,→31, -33, -19, 0, 17, 24, 9, -17, -47,

-63, -67, -59, -52, -51, -50, -49, -42, -26, -21, -15, -20, -23, -22, -19, -
,→12, -8, 5, 18, 27, 32, 26, 25, 26, 22,

23, 17, 14, 17, 21, 25, 2, -45, -121, -196, -226, -200, -118, -9, 73, 126,␣
,→131, 114, 87, 60, 42, 29, 26, 34, 35, 34,

25, 12, 9, 7, 3, 2, -8, -11, 2, 23, 38, 41, 23, 9, 10, 13, 16, 8, -8, -17, -
,→23, -26, -25, -21, -15, -10, -13, -13,

-19, -22, -29, -40, -48, -48, -54, -55, -66, -82, -85, -90, -92, -98, -114, -
,→119, -124, -129, -132, -146, -146, -138,

-124, -99, -85, -72, -65, -65, -65, -66, -63, -64, -64, -58, -46, -26, -9, 2,␣
,→2, 4, 0, 1, 4, 3, 10, 11, 10, 2, -4,

0, 10, 18, 20, 6, 2, -9, -7, -3, -3, -2, -7, -12, -5, 5, 24, 36, 31, 25, 6, 3,
,→ 7, 12, 17, 11, 0, -6, -9, -8, -7, -5,

-6, -2, -2, -6, -2, 2, 14, 24, 22, 15, 8, 4, 6, 7, 12, 16, 25, 20, 7, -16, -
,→41, -60, -67, -65, -54, -35, -11, 30,

84, 175, 302, 455, 603, 707, 743, 714, 625, 519, 414, 337, 300, 281, 263, 239,
,→ 197, 163, 136, 109, 77, 34, -18, -50,

-66, -74, -79, -92, -107, -117, -127, -129, -135, -139, -141, -155, -159, -
,→167, -171, -169, -174, -175, -178, -191,

-202, -223, -235, -243, -237, -240, -256, -298, -345, -393, -432, -475, -518,␣
,→-565, -596, -619, -623, -623, -614,

-599, -583, -559, -524, -477, -425, -383, -357, -331, -301, -252, -198, -143,␣
,→-96, -57, -29, -8, 10, 31, 45, 60, 65,

70, 74, 76, 79, 82, 79, 75, 62,


};

static void slider_x_event_cb(lv_event_t * e)


{
lv_obj_t * obj = lv_event_get_target(e);
int32_t v = lv_slider_get_value(obj);
lv_chart_set_zoom_x(chart, v);
}

static void slider_y_event_cb(lv_event_t * e)


{
lv_obj_t * obj = lv_event_get_target(e);
int32_t v = lv_slider_get_value(obj);
lv_chart_set_zoom_y(chart, v);
}

/**
* Display 1000 data points with zooming and scrolling.
* See how the chart changes drawing mode (draw only vertical lines) when
* the points get too crowded.
*/
void lv_example_chart_5(void)
{
/*Create a chart*/
chart = lv_chart_create(lv_scr_act());
lv_obj_set_size(chart, 200, 150);
(continues on next page)

2.7. Widgets 124


LVGL Documentation 9.0

(continued from previous page)


lv_obj_align(chart, LV_ALIGN_CENTER, -30, -30);
lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_Y, -1000, 1000);

/*Do not display points on the data*/


lv_obj_set_style_size(chart, 0, 0, LV_PART_INDICATOR);

lv_chart_series_t * ser = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_


,→ RED), LV_CHART_AXIS_PRIMARY_Y);

uint32_t pcnt = sizeof(ecg_sample) / sizeof(ecg_sample[0]);


lv_chart_set_point_count(chart, pcnt);
lv_chart_set_ext_y_array(chart, ser, (lv_coord_t *)ecg_sample);

lv_obj_t * slider;
slider = lv_slider_create(lv_scr_act());
lv_slider_set_range(slider, LV_ZOOM_NONE, LV_ZOOM_NONE * 10);
lv_obj_add_event(slider, slider_x_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
lv_obj_set_size(slider, 200, 10);
lv_obj_align_to(slider, chart, LV_ALIGN_OUT_BOTTOM_MID, 0, 20);

slider = lv_slider_create(lv_scr_act());
lv_slider_set_range(slider, LV_ZOOM_NONE, LV_ZOOM_NONE * 10);
lv_obj_add_event(slider, slider_y_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
lv_obj_set_size(slider, 10, 150);
lv_obj_align_to(slider, chart, LV_ALIGN_OUT_RIGHT_MID, 20, 0);
}

#endif

# Source: https://github.com/ankur219/ECG-Arrhythmia-classification/blob/
,→642230149583adfae1e4bd26c6f0e1fd8af2be0e/sample.csv

ecg_sample = [
-2, 2, 0, -15, -39, -63, -71, -68, -67, -69, -84, -95, -104, -107, -108, -107, -
,→107, -107, -107, -114, -118, -117,

-112, -100, -89, -83, -71, -64, -58, -58, -62, -62, -58, -51, -46, -39, -27, -10,␣
,→4, 7, 1, -3, 0, 14, 24, 30, 25, 19,

13, 7, 12, 15, 18, 21, 13, 6, 9, 8, 17, 19, 13, 11, 11, 11, 23, 30, 37, 34, 25,␣
,→14, 15, 19, 28, 31, 26, 23, 25, 31,

39, 37, 37, 34, 30, 32, 22, 29, 31, 33, 37, 23, 13, 7, 2, 4, -2, 2, 11, 22, 33,␣
,→19, -1, -27, -55, -67, -72, -71, -63,

-49, -18, 35, 113, 230, 369, 525, 651, 722, 730, 667, 563, 454, 357, 305, 288,␣
,→274, 255, 212, 173, 143, 117, 82, 39,

-13, -53, -78, -91, -101, -113, -124, -131, -131, -131, -129, -128, -129, -125, -
,→123, -123, -129, -139, -148, -153,

-159, -166, -183, -205, -227, -243, -248, -246, -254, -280, -327, -381, -429, -
,→473, -517, -556, -592, -612, -620,

-620, -614, -604, -591, -574, -540, -497, -441, -389, -358, -336, -313, -284, -
,→222, -167, -114, -70, -47, -28, -4, 12,

38, 52, 58, 56, 56, 57, 68, 77, 86, 86, 80, 69, 67, 70, 82, 85, 89, 90, 89, 89,␣
,→88, 91, 96, 97, 91, 83, 78, 82, 88, 95,

96, 105, 106, 110, 102, 100, 96, 98, 97, 101, 98, 99, 100, 107, 113, 119, 115,␣
,→110, 96, 85, 73, 64, 69, 76, 79,

78, 75, 85, 100, 114, 113, 105, 96, 84, 74, 66, 60, 75, 85, 89, 83, 67, 61, 67,␣
,→73, 79, 74, 63, 57, 56, 58, 61, 55,

48, 45, 46, 55, 62, 55, 49, 43, 50, 59, 63, 57, 40, 31, 23, 25, 27, 31, 35, 34,␣
,→30, 36, 34, 42, 38, 36, 40, 46, 50,

(continues on next page)

2.7. Widgets 125


LVGL Documentation 9.0

(continued from previous page)


47, 32, 30, 32, 52, 67, 73, 71, 63, 54, 53, 45, 41, 28, 13, 3, 1, 4, 4, -8, -23, -
,→32, -31, -19, -5, 3, 9, 13, 19,

24, 27, 29, 25, 22, 26, 32, 42, 51, 56, 60, 57, 55, 53, 53, 54, 59, 54, 49, 26, -
,→3, -11, -20, -47, -100, -194, -236,

-212, -123, 8, 103, 142, 147, 120, 105, 98, 93, 81, 61, 40, 26, 28, 30, 30, 27,␣
,→19, 17, 21, 20, 19, 19, 22, 36, 40,

35, 20, 7, 1, 10, 18, 27, 22, 6, -4, -2, 3, 6, -2, -13, -14, -10, -2, 3, 2, -1, -
,→5, -10, -19, -32, -42, -55, -60,

-68, -77, -86, -101, -110, -117, -115, -104, -92, -84, -85, -84, -73, -65, -52, -
,→50, -45, -35, -20, -3, 12, 20, 25,

26, 28, 28, 30, 28, 25, 28, 33, 42, 42, 36, 23, 9, 0, 1, -4, 1, -4, -4, 1, 5, 9,␣
,→9, -3, -1, -18, -50, -108, -190,

-272, -340, -408, -446, -537, -643, -777, -894, -920, -853, -697, -461, -251, -60,
,→ 58, 103, 129, 139, 155, 170, 173,

178, 185, 190, 193, 200, 208, 215, 225, 224, 232, 234, 240, 240, 236, 229, 226,␣
,→224, 232, 233, 232, 224, 219, 219,

223, 231, 226, 223, 219, 218, 223, 223, 223, 233, 245, 268, 286, 296, 295, 283,␣
,→271, 263, 252, 243, 226, 210, 197,

186, 171, 152, 133, 117, 114, 110, 107, 96, 80, 63, 48, 40, 38, 34, 28, 15, 2, -7,
,→ -11, -14, -18, -29, -37, -44, -50,

-58, -63, -61, -52, -50, -48, -61, -59, -58, -54, -47, -52, -62, -61, -64, -54, -
,→52, -59, -69, -76, -76, -69, -67,

-74, -78, -81, -80, -73, -65, -57, -53, -51, -47, -35, -27, -22, -22, -24, -21, -
,→17, -13, -10, -11, -13, -20, -20,

-12, -2, 7, -1, -12, -16, -13, -2, 2, -4, -5, -2, 9, 19, 19, 14, 11, 13, 19, 21,␣
,→20, 18, 19, 19, 19, 16, 15, 13, 14,

9, 3, -5, -9, -5, -3, -2, -3, -3, 2, 8, 9, 9, 5, 6, 8, 8, 7, 4, 3, 4, 5, 3, 5, 5,␣


,→13, 13, 12, 10, 10, 15, 22, 17,

14, 7, 10, 15, 16, 11, 12, 10, 13, 9, -2, -4, -2, 7, 16, 16, 17, 16, 7, -1, -16, -
,→18, -16, -9, -4, -5, -10, -9, -8,

-3, -4, -10, -19, -20, -16, -9, -9, -23, -40, -48, -43, -33, -19, -21, -26, -31, -
,→33, -19, 0, 17, 24, 9, -17, -47,

-63, -67, -59, -52, -51, -50, -49, -42, -26, -21, -15, -20, -23, -22, -19, -12, -
,→8, 5, 18, 27, 32, 26, 25, 26, 22,

23, 17, 14, 17, 21, 25, 2, -45, -121, -196, -226, -200, -118, -9, 73, 126, 131,␣
,→114, 87, 60, 42, 29, 26, 34, 35, 34,

25, 12, 9, 7, 3, 2, -8, -11, 2, 23, 38, 41, 23, 9, 10, 13, 16, 8, -8, -17, -23, -
,→26, -25, -21, -15, -10, -13, -13,

-19, -22, -29, -40, -48, -48, -54, -55, -66, -82, -85, -90, -92, -98, -114, -119,␣
,→-124, -129, -132, -146, -146, -138,

-124, -99, -85, -72, -65, -65, -65, -66, -63, -64, -64, -58, -46, -26, -9, 2, 2,␣
,→4, 0, 1, 4, 3, 10, 11, 10, 2, -4,

0, 10, 18, 20, 6, 2, -9, -7, -3, -3, -2, -7, -12, -5, 5, 24, 36, 31, 25, 6, 3, 7,␣
,→12, 17, 11, 0, -6, -9, -8, -7, -5,

-6, -2, -2, -6, -2, 2, 14, 24, 22, 15, 8, 4, 6, 7, 12, 16, 25, 20, 7, -16, -41, -
,→60, -67, -65, -54, -35, -11, 30,

84, 175, 302, 455, 603, 707, 743, 714, 625, 519, 414, 337, 300, 281, 263, 239,␣
,→197, 163, 136, 109, 77, 34, -18, -50,

-66, -74, -79, -92, -107, -117, -127, -129, -135, -139, -141, -155, -159, -167, -
,→171, -169, -174, -175, -178, -191,

-202, -223, -235, -243, -237, -240, -256, -298, -345, -393, -432, -475, -518, -
,→565, -596, -619, -623, -623, -614,

-599, -583, -559, -524, -477, -425, -383, -357, -331, -301, -252, -198, -143, -96,
,→ -57, -29, -8, 10, 31, 45, 60, 65,

70, 74, 76, 79, 82, 79, 75, 62,


]

(continues on next page)

2.7. Widgets 126


LVGL Documentation 9.0

(continued from previous page)


def slider_x_event_cb(e):

slider = e.get_target_obj()
v = slider.get_value()
chart.set_zoom_x(v)

def slider_y_event_cb(e):

slider = e.get_target_obj()
v = slider.get_value()
chart.set_zoom_y(v)

#
# Display 1000 data points with zooming and scrolling.
# See how the chart changes drawing mode (draw only vertical lines) when
# the points get too crowded.

# Create a chart
chart = lv.chart(lv.scr_act())
chart.set_size(200, 150)
chart.align(lv.ALIGN.CENTER, -30, -30)
chart.set_range(lv.chart.AXIS.PRIMARY_Y, -1000, 1000)

# Do not display points on the data


chart.set_style_size(0, 0, lv.PART.INDICATOR)

ser = chart.add_series(lv.palette_main(lv.PALETTE.RED), lv.chart.AXIS.PRIMARY_Y)

pcnt = len(ecg_sample)
chart.set_point_count(pcnt)
chart.set_ext_y_array(ser, ecg_sample)

slider = lv.slider(lv.scr_act())
slider.set_range(lv.ZOOM_NONE, lv.ZOOM_NONE * 10)
slider.add_event(slider_x_event_cb, lv.EVENT.VALUE_CHANGED, None)
slider.set_size(200,10)
slider.align_to(chart, lv.ALIGN.OUT_BOTTOM_MID, 0, 20)

slider = lv.slider(lv.scr_act())
slider.set_range(lv.ZOOM_NONE, lv.ZOOM_NONE * 10)
slider.add_event(slider_y_event_cb, lv.EVENT.VALUE_CHANGED, None)
slider.set_size(10, 150)
slider.align_to(chart, lv.ALIGN.OUT_RIGHT_MID, 20, 0)

2.7. Widgets 127


LVGL Documentation 9.0

Show cursor on the clicked point

#include "../../lv_examples.h"
#if LV_USE_CHART && LV_BUILD_EXAMPLES

static lv_obj_t * chart;


static lv_chart_series_t * ser;
static lv_chart_cursor_t * cursor;

static void event_cb(lv_event_t * e)


{
static int32_t last_id = -1;
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);

if(code == LV_EVENT_VALUE_CHANGED) {
last_id = lv_chart_get_pressed_point(obj);
if(last_id != LV_CHART_POINT_NONE) {
lv_chart_set_cursor_point(obj, cursor, NULL, last_id);
}
}
else if(code == LV_EVENT_DRAW_PART_END) {
lv_obj_draw_part_dsc_t * dsc = lv_event_get_draw_part_dsc(e);
if(!lv_obj_draw_part_check_type(dsc, &lv_chart_class, LV_CHART_DRAW_PART_
,→CURSOR)) return;

if(dsc->p1 == NULL || dsc->p2 == NULL || dsc->p1->y != dsc->p2->y || last_id


,→< 0) return;

lv_coord_t * data_array = lv_chart_get_y_array(chart, ser);


lv_coord_t v = data_array[last_id];
char buf[16];
lv_snprintf(buf, sizeof(buf), "%d", v);

lv_point_t size;
lv_txt_get_size(&size, buf, LV_FONT_DEFAULT, 0, 0, LV_COORD_MAX, LV_TEXT_FLAG_
,→ NONE);

lv_area_t a;
a.y2 = dsc->p1->y - 5;
a.y1 = a.y2 - size.y - 10;
a.x1 = dsc->p1->x + 10;
a.x2 = a.x1 + size.x + 10;

lv_draw_rect_dsc_t draw_rect_dsc;
lv_draw_rect_dsc_init(&draw_rect_dsc);
draw_rect_dsc.bg_color = lv_palette_main(LV_PALETTE_BLUE);
draw_rect_dsc.radius = 3;

lv_draw_rect(dsc->draw_ctx, &draw_rect_dsc, &a);

lv_draw_label_dsc_t draw_label_dsc;
lv_draw_label_dsc_init(&draw_label_dsc);
draw_label_dsc.color = lv_color_white();
a.x1 += 5;
a.x2 -= 5;
a.y1 += 5;
a.y2 -= 5;
(continues on next page)

2.7. Widgets 128


LVGL Documentation 9.0

(continued from previous page)


lv_draw_label(dsc->draw_ctx, &draw_label_dsc, &a, buf, NULL);
}
}

/**
* Show cursor on the clicked point
*/
void lv_example_chart_6(void)
{
chart = lv_chart_create(lv_scr_act());
lv_obj_set_size(chart, 200, 150);
lv_obj_align(chart, LV_ALIGN_CENTER, 0, -10);

lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_Y, 10, 5, 6, 5, true, 40);


lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_X, 10, 5, 10, 1, true, 30);

lv_obj_add_event(chart, event_cb, LV_EVENT_ALL, NULL);


lv_obj_refresh_ext_draw_size(chart);

cursor = lv_chart_add_cursor(chart, lv_palette_main(LV_PALETTE_BLUE), LV_DIR_LEFT␣


,→ | LV_DIR_BOTTOM);

ser = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_RED), LV_CHART_AXIS_


,→ PRIMARY_Y);
uint32_t i;
for(i = 0; i < 10; i++) {
lv_chart_set_next_value(chart, ser, lv_rand(10, 90));
}

lv_chart_set_zoom_x(chart, 500);

lv_obj_t * label = lv_label_create(lv_scr_act());


lv_label_set_text(label, "Click on a point");
lv_obj_align_to(label, chart, LV_ALIGN_OUT_TOP_MID, 0, -5);
}

#endif

class ExampleChart_6():

def __init__(self):
self.last_id = -1
#
# Show cursor on the clicked point
#

chart = lv.chart(lv.scr_act())
chart.set_size(200, 150)
chart.align(lv.ALIGN.CENTER, 0, -10)

chart.set_axis_tick(lv.chart.AXIS.PRIMARY_Y, 10, 5, 6, 5, True, 40)


chart.set_axis_tick(lv.chart.AXIS.PRIMARY_X, 10, 5, 10, 1, True, 30)

chart.add_event(self.event_cb, lv.EVENT.ALL, None)


chart.refresh_ext_draw_size()

(continues on next page)

2.7. Widgets 129


LVGL Documentation 9.0

(continued from previous page)


self.cursor = chart.add_cursor(lv.palette_main(lv.PALETTE.BLUE), lv.DIR.LEFT␣
,→| lv.DIR.BOTTOM)

self.ser = chart.add_series(lv.palette_main(lv.PALETTE.RED), lv.chart.AXIS.


,→ PRIMARY_Y)

self.ser_p = []
for i in range(10):
self.ser_p.append(lv.rand(10,90))
self.ser.y_points = self.ser_p

newser = chart.get_series_next(None)
# print("length of data points: ",len(newser.points))
chart.set_zoom_x(500)

label = lv.label(lv.scr_act())
label.set_text("Click on a point")
label.align_to(chart, lv.ALIGN.OUT_TOP_MID, 0, -5)

def event_cb(self,e):

code = e.get_code()
chart = e.get_target_obj()

if code == lv.EVENT.VALUE_CHANGED:
# print("last_id: ",self.last_id)
self.last_id = chart.get_pressed_point()
if self.last_id != lv.CHART_POINT_NONE:
p = lv.point_t()
chart.get_point_pos_by_id(self.ser, self.last_id, p)
chart.set_cursor_point(self.cursor, None, self.last_id)

elif code == lv.EVENT.DRAW_PART_END:


# print("EVENT.DRAW_PART_END")
dsc = lv.obj_draw_part_dsc_t.__cast__(e.get_param())
# if dsc.p1 and dsc.p2:
# print("p1, p2", dsc.p1,dsc.p2)
# print("p1.y, p2.y", dsc.p1.y, dsc.p2.y)
# print("last_id: ",self.last_id)
if dsc.part == lv.PART.CURSOR and dsc.p1 and dsc.p2 and dsc.p1.y == dsc.
,→p2.y and self.last_id >= 0:

v = self.ser_p[self.last_id]

# print("value: ",v)
value_txt = str(v)
size = lv.point_t()
lv.txt_get_size(size, value_txt, lv.font_default(), 0, 0, lv.COORD.
,→MAX, lv.TEXT_FLAG.NONE)

a = lv.area_t()
a.y2 = dsc.p1.y - 5
a.y1 = a.y2 - size.y - 10
a.x1 = dsc.p1.x + 10
a.x2 = a.x1 + size.x + 10

(continues on next page)

2.7. Widgets 130


LVGL Documentation 9.0

(continued from previous page)


draw_rect_dsc = lv.draw_rect_dsc_t()
draw_rect_dsc.init()
draw_rect_dsc.bg_color = lv.palette_main(lv.PALETTE.BLUE)
draw_rect_dsc.radius = 3

lv.draw_rect(a, dsc.clip_area, draw_rect_dsc)

draw_label_dsc = lv.draw_label_dsc_t()
draw_label_dsc.init()
draw_label_dsc.color = lv.color_white()
a.x1 += 5
a.x2 -= 5
a.y1 += 5
a.y2 -= 5
lv.draw_label(a, dsc.clip_area, draw_label_dsc, value_txt, None)

example_chart_6 = ExampleChart_6()

Scatter chart

#include "../../lv_examples.h"
#if LV_USE_CHART && LV_BUILD_EXAMPLES

static void draw_event_cb(lv_event_t * e)


{
lv_obj_draw_part_dsc_t * dsc = lv_event_get_draw_part_dsc(e);
if(dsc->part == LV_PART_ITEMS) {
lv_obj_t * obj = lv_event_get_target(e);
lv_chart_series_t * ser = lv_chart_get_series_next(obj, NULL);
uint32_t cnt = lv_chart_get_point_count(obj);
/*Make older value more transparent*/
dsc->rect_dsc->bg_opa = (LV_OPA_COVER * dsc->id) / (cnt - 1);

/*Make smaller values blue, higher values red*/


lv_coord_t * x_array = lv_chart_get_x_array(obj, ser);
lv_coord_t * y_array = lv_chart_get_y_array(obj, ser);
/*dsc->id is the tells drawing order, but we need the ID of the point being␣
,→drawn.*/

uint32_t start_point = lv_chart_get_x_start_point(obj, ser);


uint32_t p_act = (start_point + dsc->id) % cnt; /*Consider start point to get␣
,→the index of the array*/

lv_opa_t x_opa = (x_array[p_act] * LV_OPA_50) / 200;


lv_opa_t y_opa = (y_array[p_act] * LV_OPA_50) / 1000;

dsc->rect_dsc->bg_color = lv_color_mix(lv_palette_main(LV_PALETTE_RED),
lv_palette_main(LV_PALETTE_BLUE),
x_opa + y_opa);
}
}

static void add_data(lv_timer_t * timer)


{
LV_UNUSED(timer);
lv_obj_t * chart = timer->user_data;
lv_chart_set_next_value2(chart, lv_chart_get_series_next(chart, NULL), lv_rand(0,␣
,→200), lv_rand(0, 1000)); (continues on next page)

2.7. Widgets 131


LVGL Documentation 9.0

(continued from previous page)


}

/**
* A scatter chart
*/
void lv_example_chart_7(void)
{
lv_obj_t * chart = lv_chart_create(lv_scr_act());
lv_obj_set_size(chart, 200, 150);
lv_obj_align(chart, LV_ALIGN_CENTER, 0, 0);
lv_obj_add_event(chart, draw_event_cb, LV_EVENT_DRAW_PART_BEGIN, NULL);
lv_obj_set_style_line_width(chart, 0, LV_PART_ITEMS); /*Remove the lines*/

lv_chart_set_type(chart, LV_CHART_TYPE_SCATTER);

lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_X, 5, 5, 5, 1, true, 30);


lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_Y, 10, 5, 6, 5, true, 50);

lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_X, 0, 200);


lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_Y, 0, 1000);

lv_chart_set_point_count(chart, 50);

lv_chart_series_t * ser = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_


,→ RED), LV_CHART_AXIS_PRIMARY_Y);
uint32_t i;
for(i = 0; i < 50; i++) {
lv_chart_set_next_value2(chart, ser, lv_rand(0, 200), lv_rand(0, 1000));
}

lv_timer_create(add_data, 100, chart);


}

#endif

#!/opt/bin/lv_micropython -i
import utime as time
import lvgl as lv

def draw_event_cb(e):
dsc = e.get_draw_part_dsc()
if dsc.part == lv.PART.ITEMS:
obj = e.get_target_obj()
ser = obj.get_series_next(None)
cnt = obj.get_point_count()
# print("cnt: ",cnt)
# Make older value more transparent
dsc.rect_dsc.bg_opa = (lv.OPA.COVER * dsc.id) // (cnt - 1)

# Make smaller values blue, higher values red


# x_array = chart.get_x_array(ser)
# y_array = chart.get_y_array(ser)
# dsc->id is the tells drawing order, but we need the ID of the point being␣
,→ drawn.
start_point = chart.get_x_start_point(ser)
# print("start point: ",start_point)
(continues on next page)

2.7. Widgets 132


LVGL Documentation 9.0

(continued from previous page)


p_act = (start_point + dsc.id) % cnt # Consider start point to get the index␣
,→of the array

# print("p_act", p_act)
x_opa = (x_array[p_act] * lv.OPA._50) // 200
y_opa = (y_array[p_act] * lv.OPA._50) // 1000

dsc.rect_dsc.bg_color = lv.palette_main(lv.PALETTE.RED).color_mix(
lv.palette_main(lv.PALETTE.BLUE),
x_opa + y_opa)

def add_data(timer,chart):
# print("add_data")
x = lv.rand(0,200)
y = lv.rand(0,1000)
chart.set_next_value2(ser, x, y)
# chart.set_next_value2(chart.gx, y)
x_array.pop(0)
x_array.append(x)
y_array.pop(0)
y_array.append(y)

#
# A scatter chart
#

chart = lv.chart(lv.scr_act())
chart.set_size(200, 150)
chart.align(lv.ALIGN.CENTER, 0, 0)
chart.add_event(draw_event_cb, lv.EVENT.DRAW_PART_BEGIN, None)
chart.set_style_line_width(0, lv.PART.ITEMS) # Remove the lines

chart.set_type(lv.chart.TYPE.SCATTER)

chart.set_axis_tick(lv.chart.AXIS.PRIMARY_X, 5, 5, 5, 1, True, 30)


chart.set_axis_tick(lv.chart.AXIS.PRIMARY_Y, 10, 5, 6, 5, True, 50)

chart.set_range(lv.chart.AXIS.PRIMARY_X, 0, 200)
chart.set_range(lv.chart.AXIS.PRIMARY_Y, 0, 1000)

chart.set_point_count(50)

ser = chart.add_series(lv.palette_main(lv.PALETTE.RED), lv.chart.AXIS.PRIMARY_Y)

x_array = []
y_array = []
for i in range(50):
x_array.append(lv.rand(0, 200))
y_array.append(lv.rand(0, 1000))

ser.x_points = x_array
ser.y_points = y_array

# Create an `lv_timer` to update the chart.

timer = lv.timer_create_basic()
timer.set_period(100)
timer.set_cb(lambda src: add_data(timer,chart))

2.7. Widgets 133


LVGL Documentation 9.0

Stacked area chart

#include "../../lv_examples.h"
#if LV_USE_CHART && LV_USE_DRAW_MASKS && LV_BUILD_EXAMPLES

/* A struct is used to keep track of the series list because later we need to draw␣
,→to the series in the reverse order to which they were initialised. */
typedef struct {
lv_obj_t * obj;
lv_chart_series_t * series_list[3];
} stacked_area_chart_t;

static stacked_area_chart_t stacked_area_chart;

/**
* Callback which draws the blocks of colour under the lines
**/
static void draw_event_cb(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target(e);

/*Add the faded area before the lines are drawn*/


lv_obj_draw_part_dsc_t * dsc = lv_event_get_draw_part_dsc(e);
if(dsc->part == LV_PART_ITEMS) {
if(!dsc->p1 || !dsc->p2)
return;

/*Add a line mask that keeps the area below the line*/
lv_draw_mask_line_param_t line_mask_param;
lv_draw_mask_line_points_init(&line_mask_param, dsc->p1->x, dsc->p1->y, dsc->
,→p2->x, dsc->p2->y,

LV_DRAW_MASK_LINE_SIDE_BOTTOM);
int16_t line_mask_id = lv_draw_mask_add(&line_mask_param, NULL);

/*Draw a rectangle that will be affected by the mask*/


lv_draw_rect_dsc_t draw_rect_dsc;
lv_draw_rect_dsc_init(&draw_rect_dsc);
draw_rect_dsc.bg_opa = LV_OPA_COVER;
draw_rect_dsc.bg_color = dsc->line_dsc->color;

lv_area_t a;
a.x1 = dsc->p1->x;
a.x2 = dsc->p2->x;
a.y1 = LV_MIN(dsc->p1->y, dsc->p2->y);
a.y2 = obj->coords.y2 -
13; /* -13 cuts off where the rectangle draws over the chart margin.␣
,→Without this an area of 0 doesn't look like 0 */

lv_draw_rect(dsc->draw_ctx, &draw_rect_dsc, &a);

/*Remove the mask*/


lv_draw_mask_free_param(&line_mask_param);
lv_draw_mask_remove_id(line_mask_id);
}
}

/**
* Helper function to round a fixed point number
(continues on next page)

2.7. Widgets 134


LVGL Documentation 9.0

(continued from previous page)


**/
static int32_t round_fixed_point(int32_t n, int8_t shift)
{
/* Create a bitmask to isolates the decimal part of the fixed point number */
int32_t mask = 1;
for(int32_t bit_pos = 0; bit_pos < shift; bit_pos++) {
mask = (mask << 1) + 1;
}

int32_t decimal_part = n & mask;

/* Get 0.5 as fixed point */


int32_t rounding_boundary = 1 << (shift - 1);

/* Return either the integer part of n or the integer part + 1 */


return (decimal_part < rounding_boundary) ? (n & ~mask) : ((n >> shift) + 1) <<␣
,→shift;

/**
* Stacked area chart
*/
void lv_example_chart_8(void)
{
/*Create a stacked_area_chart.obj*/
stacked_area_chart.obj = lv_chart_create(lv_scr_act());
lv_obj_set_size(stacked_area_chart.obj, 200, 150);
lv_obj_center(stacked_area_chart.obj);
lv_chart_set_type(stacked_area_chart.obj, LV_CHART_TYPE_LINE);
lv_chart_set_div_line_count(stacked_area_chart.obj, 5, 7);
lv_obj_add_event(stacked_area_chart.obj, draw_event_cb, LV_EVENT_DRAW_PART_BEGIN,␣
,→NULL);

/* Set range to 0 to 100 for percentages. Draw ticks */


lv_chart_set_range(stacked_area_chart.obj, LV_CHART_AXIS_PRIMARY_Y, 0, 100);
lv_chart_set_axis_tick(stacked_area_chart.obj, LV_CHART_AXIS_PRIMARY_Y, 3, 0, 5,␣
,→1, true, 30);

/*Set point size to 0 so the lines are smooth */


lv_obj_set_style_size(stacked_area_chart.obj, 0, 0, LV_PART_INDICATOR);

/*Add some data series*/


stacked_area_chart.series_list[0] = lv_chart_add_series(stacked_area_chart.obj,␣
,→lv_palette_main(LV_PALETTE_RED),

LV_CHART_AXIS_PRIMARY_Y);
stacked_area_chart.series_list[1] = lv_chart_add_series(stacked_area_chart.obj,␣
,→lv_palette_main(LV_PALETTE_BLUE),

LV_CHART_AXIS_PRIMARY_Y);
stacked_area_chart.series_list[2] = lv_chart_add_series(stacked_area_chart.obj,␣
,→lv_palette_main(LV_PALETTE_GREEN),

LV_CHART_AXIS_PRIMARY_Y);

for(int point = 0; point < 10; point++) {


/* Make some random data */
uint32_t vals[3] = {lv_rand(10, 20), lv_rand(20, 30), lv_rand(20, 30)};

int8_t fixed_point_shift = 5;
(continues on next page)

2.7. Widgets 135


LVGL Documentation 9.0

(continued from previous page)


uint32_t total = vals[0] + vals[1] + vals[2];
uint32_t draw_heights[3];
uint32_t int_sum = 0;
uint32_t decimal_sum = 0;

/* Fixed point cascade rounding ensures percentages add to 100 */


for(int32_t series_index = 0; series_index < 3; series_index++) {
decimal_sum += (((vals[series_index] * 100) << fixed_point_shift) /␣
,→total);

int_sum += (vals[series_index] * 100) / total;

int32_t modifier = (round_fixed_point(decimal_sum, fixed_point_shift) >>␣


,→ fixed_point_shift) - int_sum;

/* The draw heights are equal to the percentage of the total each value␣
,→ is + the cumulative sum of the previous percentages.
The accumulation is how the values get "stacked" */
draw_heights[series_index] = int_sum + modifier;

/* Draw to the series in the reverse order to which they were␣


,→ initialised.
Without this the higher values will draw on top of the lower ones.
This is because the Z-height of a series matches the order it was␣
,→initialised */
lv_chart_set_next_value(stacked_area_chart.obj, stacked_area_chart.series_
,→list[3 - series_index - 1],

draw_heights[series_index]);
}
}

lv_chart_refresh(stacked_area_chart.obj);
}

#endif

import lvgl as lv

# A class is used to keep track of the series list because later we


# need to draw to the series in the reverse order to which they were initialised.
class StackedAreaChart:
def __init__(self):
self.obj = None
self.series_list = [None, None, None]

stacked_area_chart = StackedAreaChart()

#
# Callback which draws the blocks of colour under the lines
#
def draw_event_cb(e):

obj = e.get_target_obj()
cont_a = lv.area_t()
obj.get_coords(cont_a)

#Add the faded area before the lines are drawn


(continues on next page)

2.7. Widgets 136


LVGL Documentation 9.0

(continued from previous page)


dsc = e.get_draw_part_dsc()
if dsc.part == lv.PART.ITEMS:
if not dsc.p1 or not dsc.p2:
return

# Add a line mask that keeps the area below the line
line_mask_param = lv.draw_mask_line_param_t()
line_mask_param.points_init(dsc.p1.x, dsc.p1.y, dsc.p2.x, dsc.p2.y, lv.DRAW_
,→MASK_LINE_SIDE.BOTTOM)

line_mask_id = lv.draw_mask_add(line_mask_param, None)

#Draw a rectangle that will be affected by the mask


draw_rect_dsc = lv.draw_rect_dsc_t()
draw_rect_dsc.init()
draw_rect_dsc.bg_opa = lv.OPA.COVER
draw_rect_dsc.bg_color = dsc.line_dsc.color

a = lv.area_t()
a.x1 = dsc.p1.x
a.x2 = dsc.p2.x
a.y1 = min(dsc.p1.y, dsc.p2.y)
a.y2 = cont_a.y2 - 13 # -13 cuts off where the rectangle draws over the chart␣
,→margin. Without this an area of 0 doesn't look like 0

dsc.draw_ctx.rect(draw_rect_dsc, a)

# Remove the mask


lv.draw_mask_free_param(line_mask_param)
lv.draw_mask_remove_id(line_mask_id)

#
# Helper function to round a fixed point number
#
def round_fixed_point(n, shift):
# Create a bitmask to isolates the decimal part of the fixed point number
mask = 1
for bit_pos in range(shift):
mask = (mask << 1) + 1

decimal_part = n & mask

# Get 0.5 as fixed point


rounding_boundary = 1 << (shift - 1)

# Return either the integer part of n or the integer part + 1


if decimal_part < rounding_boundary:
return (n & ~mask)
return ((n >> shift) + 1) << shift

#
# Stacked area chart
#
def lv_example_chart_8():

#Create a stacked_area_chart.obj
stacked_area_chart.obj = lv.chart(lv.scr_act())
(continues on next page)

2.7. Widgets 137


LVGL Documentation 9.0

(continued from previous page)


stacked_area_chart.obj.set_size(200, 150)
stacked_area_chart.obj.center()
stacked_area_chart.obj.set_type( lv.chart.TYPE.LINE)
stacked_area_chart.obj.set_div_line_count(5, 7)
stacked_area_chart.obj.add_event( draw_event_cb, lv.EVENT.DRAW_PART_BEGIN, None)

# Set range to 0 to 100 for percentages. Draw ticks


stacked_area_chart.obj.set_range(lv.chart.AXIS.PRIMARY_Y,0,100)
stacked_area_chart.obj.set_axis_tick(lv.chart.AXIS.PRIMARY_Y, 3, 0, 5, 1, True,␣
,→30)

#Set point size to 0 so the lines are smooth


stacked_area_chart.obj.set_style_size(0, 0, lv.PART.INDICATOR)

# Add some data series


stacked_area_chart.series_list[0] = stacked_area_chart.obj.add_series(lv.palette_
,→main(lv.PALETTE.RED), lv.chart.AXIS.PRIMARY_Y)

stacked_area_chart.series_list[1] = stacked_area_chart.obj.add_series(lv.palette_
,→main(lv.PALETTE.BLUE), lv.chart.AXIS.PRIMARY_Y)

stacked_area_chart.series_list[2] = stacked_area_chart.obj.add_series(lv.palette_
,→main(lv.PALETTE.GREEN), lv.chart.AXIS.PRIMARY_Y)

for point in range(10):


# Make some random data
vals = [lv.rand(10, 20), lv.rand(20, 30), lv.rand(20, 30)]

fixed_point_shift = 5
total = vals[0] + vals[1] + vals[2]
draw_heights = [0, 0, 0]
int_sum = 0
decimal_sum = 0

# Fixed point cascade rounding ensures percentages add to 100


for series_index in range(3):
decimal_sum += int(((vals[series_index] * 100) << fixed_point_shift) //␣
,→ total)
int_sum += int((vals[series_index] * 100) / total)

modifier = (round_fixed_point(decimal_sum, fixed_point_shift) >> fixed_


,→ point_shift) - int_sum

# The draw heights are equal to the percentage of the total each value␣
,→ is + the cumulative sum of the previous percentages.
# The accumulation is how the values get "stacked"
draw_heights[series_index] = int(int_sum + modifier)

# Draw to the series in the reverse order to which they were initialised.
# Without this the higher values will draw on top of the lower ones.
# This is because the Z-height of a series matches the order it was␣
,→initialised

stacked_area_chart.obj.set_next_value( stacked_area_chart.series_list[3 -␣
,→series_index - 1], draw_heights[series_index])

stacked_area_chart.obj.refresh()

lv_example_chart_8()

2.7. Widgets 138


LVGL Documentation 9.0

2.7.9 Checkbox

Simple Checkboxes

#include "../../lv_examples.h"
#if LV_USE_CHECKBOX && LV_BUILD_EXAMPLES

static void event_handler(lv_event_t * e)


{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_VALUE_CHANGED) {
LV_UNUSED(obj);
const char * txt = lv_checkbox_get_text(obj);
const char * state = lv_obj_get_state(obj) & LV_STATE_CHECKED ? "Checked" :
,→"Unchecked";

LV_UNUSED(txt);
LV_UNUSED(state);
LV_LOG_USER("%s: %s", txt, state);
}
}

void lv_example_checkbox_1(void)
{
lv_obj_set_flex_flow(lv_scr_act(), LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(lv_scr_act(), LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START, LV_
,→FLEX_ALIGN_CENTER);

lv_obj_t * cb;
cb = lv_checkbox_create(lv_scr_act());
lv_checkbox_set_text(cb, "Apple");
lv_obj_add_event(cb, event_handler, LV_EVENT_ALL, NULL);

cb = lv_checkbox_create(lv_scr_act());
lv_checkbox_set_text(cb, "Banana");
lv_obj_add_state(cb, LV_STATE_CHECKED);
lv_obj_add_event(cb, event_handler, LV_EVENT_ALL, NULL);

cb = lv_checkbox_create(lv_scr_act());
lv_checkbox_set_text(cb, "Lemon");
lv_obj_add_state(cb, LV_STATE_DISABLED);
lv_obj_add_event(cb, event_handler, LV_EVENT_ALL, NULL);

cb = lv_checkbox_create(lv_scr_act());
lv_obj_add_state(cb, LV_STATE_CHECKED | LV_STATE_DISABLED);
lv_checkbox_set_text(cb, "Melon\nand a new line");
lv_obj_add_event(cb, event_handler, LV_EVENT_ALL, NULL);

lv_obj_update_layout(cb);
}

#endif

def event_handler(e):
code = e.get_code()
obj = e.get_target_obj()
if code == lv.EVENT.VALUE_CHANGED:
(continues on next page)

2.7. Widgets 139


LVGL Documentation 9.0

(continued from previous page)


txt = obj.get_text()
if obj.get_state() & lv.STATE.CHECKED:
state = "Checked"
else:
state = "Unchecked"
print(txt + ":" + state)

lv.scr_act().set_flex_flow(lv.FLEX_FLOW.COLUMN)
lv.scr_act().set_flex_align(lv.FLEX_ALIGN.CENTER, lv.FLEX_ALIGN.START, lv.FLEX_ALIGN.
,→CENTER)

cb = lv.checkbox(lv.scr_act())
cb.set_text("Apple")
cb.add_event(event_handler, lv.EVENT.ALL, None)

cb = lv.checkbox(lv.scr_act())
cb.set_text("Banana")
cb.add_state(lv.STATE.CHECKED)
cb.add_event(event_handler, lv.EVENT.ALL, None)

cb = lv.checkbox(lv.scr_act())
cb.set_text("Lemon")
cb.add_state(lv.STATE.DISABLED)
cb.add_event(event_handler, lv.EVENT.ALL, None)

cb = lv.checkbox(lv.scr_act())
cb.add_state(lv.STATE.CHECKED | lv.STATE.DISABLED)
cb.set_text("Melon")
cb.add_event(event_handler, lv.EVENT.ALL, None)

cb.update_layout()

Checkboxes as radio buttons

#include "../../lv_examples.h"
#if LV_USE_CHECKBOX && LV_BUILD_EXAMPLES

static lv_style_t style_radio;


static lv_style_t style_radio_chk;
static uint32_t active_index_1 = 0;
static uint32_t active_index_2 = 0;

static void radio_event_handler(lv_event_t * e)


{
uint32_t * active_id = lv_event_get_user_data(e);
lv_obj_t * cont = lv_event_get_current_target(e);
lv_obj_t * act_cb = lv_event_get_target(e);
lv_obj_t * old_cb = lv_obj_get_child(cont, *active_id);

/*Do nothing if the container was clicked*/


if(act_cb == cont) return;

lv_obj_clear_state(old_cb, LV_STATE_CHECKED); /*Uncheck the previous radio␣


,→ button*/ (continues on next page)

2.7. Widgets 140


LVGL Documentation 9.0

(continued from previous page)


lv_obj_add_state(act_cb, LV_STATE_CHECKED); /*Uncheck the current radio␣
,→button*/

*active_id = lv_obj_get_index(act_cb);

LV_LOG_USER("Selected radio buttons: %d, %d", (int)active_index_1, (int)active_


,→ index_2);
}

static void radiobutton_create(lv_obj_t * parent, const char * txt)


{
lv_obj_t * obj = lv_checkbox_create(parent);
lv_checkbox_set_text(obj, txt);
lv_obj_add_flag(obj, LV_OBJ_FLAG_EVENT_BUBBLE);
lv_obj_add_style(obj, &style_radio, LV_PART_INDICATOR);
lv_obj_add_style(obj, &style_radio_chk, LV_PART_INDICATOR | LV_STATE_CHECKED);
}

/**
* Checkboxes as radio buttons
*/
void lv_example_checkbox_2(void)
{
/* The idea is to enable `LV_OBJ_FLAG_EVENT_BUBBLE` on checkboxes and process the
* `LV_EVENT_CLICKED` on the container.
* A variable is passed as event user data where the index of the active
* radiobutton is saved */

lv_style_init(&style_radio);
lv_style_set_radius(&style_radio, LV_RADIUS_CIRCLE);

lv_style_init(&style_radio_chk);
lv_style_set_bg_img_src(&style_radio_chk, NULL);

uint32_t i;
char buf[32];

lv_obj_t * cont1 = lv_obj_create(lv_scr_act());


lv_obj_set_flex_flow(cont1, LV_FLEX_FLOW_COLUMN);
lv_obj_set_size(cont1, lv_pct(40), lv_pct(80));
lv_obj_add_event(cont1, radio_event_handler, LV_EVENT_CLICKED, &active_index_1);

for(i = 0; i < 5; i++) {


lv_snprintf(buf, sizeof(buf), "A %d", (int)i + 1);
radiobutton_create(cont1, buf);

}
/*Make the first checkbox checked*/
lv_obj_add_state(lv_obj_get_child(cont1, 0), LV_STATE_CHECKED);

lv_obj_t * cont2 = lv_obj_create(lv_scr_act());


lv_obj_set_flex_flow(cont2, LV_FLEX_FLOW_COLUMN);
lv_obj_set_size(cont2, lv_pct(40), lv_pct(80));
lv_obj_set_x(cont2, lv_pct(50));
lv_obj_add_event(cont2, radio_event_handler, LV_EVENT_CLICKED, &active_index_2);
(continues on next page)

2.7. Widgets 141


LVGL Documentation 9.0

(continued from previous page)

for(i = 0; i < 3; i++) {


lv_snprintf(buf, sizeof(buf), "B %d", (int)i + 1);
radiobutton_create(cont2, buf);
}

/*Make the first checkbox checked*/


lv_obj_add_state(lv_obj_get_child(cont2, 0), LV_STATE_CHECKED);
}

#endif

import time

class LV_Example_Checkbox_2:
def __init__(self):
#
# Checkboxes as radio buttons
#
# The idea is to enable `LV_OBJ_FLAG_EVENT_BUBBLE` on checkboxes and process␣
,→the

#`LV.EVENT.CLICKED` on the container.


# Since user_data cannot be used to pass parameters in MicroPython I use an␣
,→instance variable to

# keep the index of the active button

self.active_index_1 = 0
self.active_index_2 = 0
self.style_radio = lv.style_t()
self.style_radio.init()
self.style_radio.set_radius(lv.RADIUS_CIRCLE)

self.style_radio_chk = lv.style_t()
self.style_radio_chk.init()
self.style_radio_chk.init()
self.style_radio_chk.set_bg_img_src(None)

self.cont1 = lv.obj(lv.scr_act())
self.cont1.set_flex_flow(lv.FLEX_FLOW.COLUMN)
self.cont1.set_size(lv.pct(40), lv.pct(80))
self.cont1.add_event(self.radio_event_handler, lv.EVENT.CLICKED, None)

for i in range(5):
txt = "A {:d}".format(i+1)
self.radiobutton_create(self.cont1,txt)

# Make the first checkbox checked


#lv_obj_add_state(lv_obj_get_child(self.cont1, 0), LV_STATE_CHECKED);
self.cont1.get_child(0).add_state(lv.STATE.CHECKED)

self.cont2 = lv.obj(lv.scr_act())
self.cont2.set_flex_flow(lv.FLEX_FLOW.COLUMN)
self.cont2.set_size(lv.pct(40), lv.pct(80))
self.cont2.set_x(lv.pct(50))
self.cont2.add_event(self.radio_event_handler, lv.EVENT.CLICKED, None)
(continues on next page)

2.7. Widgets 142


LVGL Documentation 9.0

(continued from previous page)

for i in range(3):
txt = "B {:d}".format(i+1)
self.radiobutton_create(self.cont2,txt)

# Make the first checkbox checked*/


self.cont2.get_child(0).add_state(lv.STATE.CHECKED)

def radio_event_handler(self,e):
cont = e.get_current_target_obj()
act_cb = e.get_target_obj()
if cont == self.cont1:
active_id = self.active_index_1
else:
active_id = self.active_index_2
old_cb = cont.get_child(active_id)

# Do nothing if the container was clicked


if act_cb == cont:
return

old_cb.clear_state(lv.STATE.CHECKED) # Uncheck the previous radio␣


,→ button
act_cb.add_state(lv.STATE.CHECKED) # Uncheck the current radio␣
,→ button

if cont == self.cont1:
self.active_index_1 = act_cb.get_index()
# print("active index 1: ", self.active_index_1)
else:
self.active_index_2 = act_cb.get_index()
# print("active index 2: ", self.active_index_2)

print("Selected radio buttons: {:d}, {:d}".format(self.active_index_1, self.


,→ active_index_2))

def radiobutton_create(self,parent, txt):


obj = lv.checkbox(parent)
obj.set_text(txt)
obj.add_flag(lv.obj.FLAG.EVENT_BUBBLE)
obj.add_style(self.style_radio, lv.PART.INDICATOR)
obj.add_style(self.style_radio_chk, lv.PART.INDICATOR | lv.STATE.CHECKED)

lv_example_checkbox_2 = LV_Example_Checkbox_2()

2.7. Widgets 143


LVGL Documentation 9.0

2.7.10 Colorwheel

Simple Colorwheel

#include "../../lv_examples.h"
#if LV_USE_COLORWHEEL && LV_BUILD_EXAMPLES

void lv_example_colorwheel_1(void)
{
lv_obj_t * cw;

cw = lv_colorwheel_create(lv_scr_act(), true);
lv_obj_set_size(cw, 200, 200);
lv_obj_center(cw);
}

#endif

cw = lv.colorwheel(lv.scr_act(), True)
cw.set_size(200, 200)
cw.center()

2.7.11 Dropdown

Simple Drop down list

#include "../../lv_examples.h"
#if LV_USE_DROPDOWN && LV_BUILD_EXAMPLES

static void event_handler(lv_event_t * e)


{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_VALUE_CHANGED) {
char buf[32];
lv_dropdown_get_selected_str(obj, buf, sizeof(buf));
LV_LOG_USER("Option: %s", buf);
}
}

void lv_example_dropdown_1(void)
{

/*Create a normal drop down list*/


lv_obj_t * dd = lv_dropdown_create(lv_scr_act());
lv_dropdown_set_options(dd, "Apple\n"
"Banana\n"
"Orange\n"
"Cherry\n"
"Grape\n"
"Raspberry\n"
"Melon\n"
"Orange\n"
(continues on next page)

2.7. Widgets 144


LVGL Documentation 9.0

(continued from previous page)


"Lemon\n"
"Nuts");

lv_obj_align(dd, LV_ALIGN_TOP_MID, 0, 20);


lv_obj_add_event(dd, event_handler, LV_EVENT_ALL, NULL);
}

#endif

def event_handler(e):
code = e.get_code()
obj = e.get_target_obj()
if code == lv.EVENT.VALUE_CHANGED:
option = " "*10 # should be large enough to store the option
obj.get_selected_str(option, len(option))
# .strip() removes trailing spaces
print("Option: \"%s\"" % option.strip())

# Create a normal drop down list


dd = lv.dropdown(lv.scr_act())
dd.set_options("\n".join([
"Apple",
"Banana",
"Orange",
"Cherry",
"Grape",
"Raspberry",
"Melon",
"Orange",
"Lemon",
"Nuts"]))

dd.align(lv.ALIGN.TOP_MID, 0, 20)
dd.add_event(event_handler, lv.EVENT.ALL, None)

Drop down in four directions

#include "../../lv_examples.h"
#if LV_USE_DROPDOWN && LV_BUILD_EXAMPLES

/**
* Create a drop down, up, left and right menus
*/
void lv_example_dropdown_2(void)
{
static const char * opts = "Apple\n"
"Banana\n"
"Orange\n"
"Melon";

lv_obj_t * dd;
dd = lv_dropdown_create(lv_scr_act());
(continues on next page)

2.7. Widgets 145


LVGL Documentation 9.0

(continued from previous page)


lv_dropdown_set_options_static(dd, opts);
lv_obj_align(dd, LV_ALIGN_TOP_MID, 0, 10);

dd = lv_dropdown_create(lv_scr_act());
lv_dropdown_set_options_static(dd, opts);
lv_dropdown_set_dir(dd, LV_DIR_BOTTOM);
lv_dropdown_set_symbol(dd, LV_SYMBOL_UP);
lv_obj_align(dd, LV_ALIGN_BOTTOM_MID, 0, -10);

dd = lv_dropdown_create(lv_scr_act());
lv_dropdown_set_options_static(dd, opts);
lv_dropdown_set_dir(dd, LV_DIR_RIGHT);
lv_dropdown_set_symbol(dd, LV_SYMBOL_RIGHT);
lv_obj_align(dd, LV_ALIGN_LEFT_MID, 10, 0);

dd = lv_dropdown_create(lv_scr_act());
lv_dropdown_set_options_static(dd, opts);
lv_dropdown_set_dir(dd, LV_DIR_LEFT);
lv_dropdown_set_symbol(dd, LV_SYMBOL_LEFT);
lv_obj_align(dd, LV_ALIGN_RIGHT_MID, -10, 0);
}

#endif

#
# Create a drop down, up, left and right menus
#

opts = "\n".join([
"Apple",
"Banana",
"Orange",
"Melon",
"Grape",
"Raspberry"])

dd = lv.dropdown(lv.scr_act())
dd.set_options_static(opts)
dd.align(lv.ALIGN.TOP_MID, 0, 10)
dd = lv.dropdown(lv.scr_act())
dd.set_options_static(opts)
dd.set_dir(lv.DIR.BOTTOM)
dd.set_symbol(lv.SYMBOL.UP)
dd.align(lv.ALIGN.BOTTOM_MID, 0, -10)

dd = lv.dropdown(lv.scr_act())
dd.set_options_static(opts)
dd.set_dir(lv.DIR.RIGHT)
dd.set_symbol(lv.SYMBOL.RIGHT)
dd.align(lv.ALIGN.LEFT_MID, 10, 0)

dd = lv.dropdown(lv.scr_act())
dd.set_options_static(opts)
dd.set_dir(lv.DIR.LEFT)
dd.set_symbol(lv.SYMBOL.LEFT)
dd.align(lv.ALIGN.RIGHT_MID, -10, 0)
(continues on next page)

2.7. Widgets 146


LVGL Documentation 9.0

(continued from previous page)

Menu

#include "../../lv_examples.h"
#if LV_USE_DROPDOWN && LV_BUILD_EXAMPLES

static void event_cb(lv_event_t * e)


{
lv_obj_t * dropdown = lv_event_get_target(e);
char buf[64];
lv_dropdown_get_selected_str(dropdown, buf, sizeof(buf));
LV_LOG_USER("'%s' is selected", buf);
}

/**
* Create a menu from a drop-down list and show some drop-down list features and␣
,→styling

*/
void lv_example_dropdown_3(void)
{
/*Create a drop down list*/
lv_obj_t * dropdown = lv_dropdown_create(lv_scr_act());
lv_obj_align(dropdown, LV_ALIGN_TOP_LEFT, 10, 10);
lv_dropdown_set_options(dropdown, "New project\n"
"New file\n"
"Save\n"
"Save as ...\n"
"Open project\n"
"Recent projects\n"
"Preferences\n"
"Exit");

/*Set a fixed text to display on the button of the drop-down list*/


lv_dropdown_set_text(dropdown, "Menu");

/*Use a custom image as down icon and flip it when the list is opened*/
LV_IMG_DECLARE(img_caret_down)
lv_dropdown_set_symbol(dropdown, &img_caret_down);
lv_obj_set_style_transform_angle(dropdown, 1800, LV_PART_INDICATOR | LV_STATE_
,→CHECKED);

/*In a menu we don't need to show the last clicked item*/


lv_dropdown_set_selected_highlight(dropdown, false);

lv_obj_add_event(dropdown, event_cb, LV_EVENT_VALUE_CHANGED, NULL);


}

#endif

# Create an image from the png file


try:
with open('../../assets/img_caret_down.png','rb') as f:
(continues on next page)

2.7. Widgets 147


LVGL Documentation 9.0

(continued from previous page)


png_data = f.read()
except:
print("Could not find img_caret_down.png")
sys.exit()

img_caret_down_argb = lv.img_dsc_t({
'data_size': len(png_data),
'data': png_data
})

def event_cb(e):
dropdown = e.get_target_obj()
option = " "*64 # should be large enough to store the option
dropdown.get_selected_str(option, len(option))
print(option.strip() +" is selected")
#
# Create a menu from a drop-down list and show some drop-down list features and␣
,→styling

# Create a drop down list


dropdown = lv.dropdown(lv.scr_act())
dropdown.align(lv.ALIGN.TOP_LEFT, 10, 10)
dropdown.set_options("\n".join([
"New project",
"New file",
"Open project",
"Recent projects",
"Preferences",
"Exit"]))

# Set a fixed text to display on the button of the drop-down list


dropdown.set_text("Menu")

# Use a custom image as down icon and flip it when the list is opened
# LV_IMG_DECLARE(img_caret_down)
dropdown.set_symbol(img_caret_down_argb)
dropdown.set_style_transform_angle(1800, lv.PART.INDICATOR | lv.STATE.CHECKED)

# In a menu we don't need to show the last clicked item


dropdown.set_selected_highlight(False)

dropdown.add_event(event_cb, lv.EVENT.VALUE_CHANGED, None)

2.7.12 Image

Image from variable and symbol

#include "../../lv_examples.h"
#if LV_USE_IMG && LV_BUILD_EXAMPLES

void lv_example_img_1(void)
(continues on next page)

2.7. Widgets 148


LVGL Documentation 9.0

(continued from previous page)


{
LV_IMG_DECLARE(img_cogwheel_chroma_keyed);
lv_obj_t * img1 = lv_img_create(lv_scr_act());
lv_img_set_src(img1, &img_cogwheel_chroma_keyed);
lv_obj_align(img1, LV_ALIGN_CENTER, 0, -20);
lv_obj_set_size(img1, 200, 200);

lv_obj_t * img2 = lv_img_create(lv_scr_act());


lv_img_set_src(img2, LV_SYMBOL_OK "Accept");
lv_obj_align_to(img2, img1, LV_ALIGN_OUT_BOTTOM_MID, 0, 20);
}

#endif

#!/opt/bin/lv_micropython -i
import usys as sys
import lvgl as lv
import display_driver

# Create an image from the png file


try:
with open('../../assets/img_cogwheel_argb.png','rb') as f:
png_data = f.read()
except:
print("Could not find img_cogwheel_argb.png")
sys.exit()

img_cogwheel_argb = lv.img_dsc_t({
'data_size': len(png_data),
'data': png_data
})

img1 = lv.img(lv.scr_act())
img1.set_src(img_cogwheel_argb)
img1.align(lv.ALIGN.CENTER, 0, -20)
img1.set_size(200, 200)

img2 = lv.img(lv.scr_act())
img2.set_src(lv.SYMBOL.OK + "Accept")
img2.align_to(img1, lv.ALIGN.OUT_BOTTOM_MID, 0, 20)

Image recoloring

#include "../../lv_examples.h"
#if LV_USE_IMG && LV_USE_SLIDER && LV_BUILD_EXAMPLES

static lv_obj_t * create_slider(lv_color_t color);


static void slider_event_cb(lv_event_t * e);

static lv_obj_t * red_slider, * green_slider, * blue_slider, * intense_slider;


static lv_obj_t * img1;

/**
(continues on next page)

2.7. Widgets 149


LVGL Documentation 9.0

(continued from previous page)


* Demonstrate runtime image re-coloring
*/
void lv_example_img_2(void)
{
/*Create 4 sliders to adjust RGB color and re-color intensity*/
red_slider = create_slider(lv_palette_main(LV_PALETTE_RED));
green_slider = create_slider(lv_palette_main(LV_PALETTE_GREEN));
blue_slider = create_slider(lv_palette_main(LV_PALETTE_BLUE));
intense_slider = create_slider(lv_palette_main(LV_PALETTE_GREY));

lv_slider_set_value(red_slider, LV_OPA_20, LV_ANIM_OFF);


lv_slider_set_value(green_slider, LV_OPA_90, LV_ANIM_OFF);
lv_slider_set_value(blue_slider, LV_OPA_60, LV_ANIM_OFF);
lv_slider_set_value(intense_slider, LV_OPA_50, LV_ANIM_OFF);

lv_obj_align(red_slider, LV_ALIGN_LEFT_MID, 25, 0);


lv_obj_align_to(green_slider, red_slider, LV_ALIGN_OUT_RIGHT_MID, 25, 0);
lv_obj_align_to(blue_slider, green_slider, LV_ALIGN_OUT_RIGHT_MID, 25, 0);
lv_obj_align_to(intense_slider, blue_slider, LV_ALIGN_OUT_RIGHT_MID, 25, 0);

/*Now create the actual image*/


LV_IMG_DECLARE(img_cogwheel_argb)
img1 = lv_img_create(lv_scr_act());
lv_img_set_src(img1, &img_cogwheel_argb);
lv_obj_align(img1, LV_ALIGN_RIGHT_MID, -20, 0);

lv_obj_send_event(intense_slider, LV_EVENT_VALUE_CHANGED, NULL);


}

static void slider_event_cb(lv_event_t * e)


{
LV_UNUSED(e);

/*Recolor the image based on the sliders' values*/


lv_color_t color = lv_color_make(lv_slider_get_value(red_slider), lv_slider_get_
,→value(green_slider),

lv_slider_get_value(blue_slider));
lv_opa_t intense = lv_slider_get_value(intense_slider);
lv_obj_set_style_img_recolor_opa(img1, intense, 0);
lv_obj_set_style_img_recolor(img1, color, 0);
}

static lv_obj_t * create_slider(lv_color_t color)


{
lv_obj_t * slider = lv_slider_create(lv_scr_act());
lv_slider_set_range(slider, 0, 255);
lv_obj_set_size(slider, 10, 200);
lv_obj_set_style_bg_color(slider, color, LV_PART_KNOB);
lv_obj_set_style_bg_color(slider, lv_color_darken(color, LV_OPA_40), LV_PART_
,→INDICATOR);

lv_obj_add_event(slider, slider_event_cb, LV_EVENT_VALUE_CHANGED, NULL);


return slider;
}

#endif

2.7. Widgets 150


LVGL Documentation 9.0

#!/opt/bin/lv_micropython -i
import usys as sys
import lvgl as lv
import display_driver

# Create an image from the png file


try:
with open('../../assets/img_cogwheel_argb.png','rb') as f:
png_data = f.read()
except:
print("Could not find img_cogwheel_argb.png")
sys.exit()

img_cogwheel_argb = lv.img_dsc_t({
'data_size': len(png_data),
'data': png_data
})

def create_slider(color):
slider = lv.slider(lv.scr_act())
slider.set_range(0, 255)
slider.set_size(10, 200)
slider.set_style_bg_color(color, lv.PART.KNOB)
slider.set_style_bg_color(color.color_darken(lv.OPA._40), lv.PART.INDICATOR)
slider.add_event(slider_event_cb, lv.EVENT.VALUE_CHANGED, None)
return slider

def slider_event_cb(e):
# Recolor the image based on the sliders' values
color = lv.color_make(red_slider.get_value(), green_slider.get_value(), blue_
,→slider.get_value())

intense = intense_slider.get_value()
img1.set_style_img_recolor_opa(intense, 0)
img1.set_style_img_recolor(color, 0)

#
# Demonstrate runtime image re-coloring
#
# Create 4 sliders to adjust RGB color and re-color intensity
red_slider = create_slider(lv.palette_main(lv.PALETTE.RED))
green_slider = create_slider(lv.palette_main(lv.PALETTE.GREEN))
blue_slider = create_slider(lv.palette_main(lv.PALETTE.BLUE))
intense_slider = create_slider(lv.palette_main(lv.PALETTE.GREY))

red_slider.set_value(lv.OPA._20, lv.ANIM.OFF)
green_slider.set_value(lv.OPA._90, lv.ANIM.OFF)
blue_slider.set_value(lv.OPA._60, lv.ANIM.OFF)
intense_slider.set_value(lv.OPA._50, lv.ANIM.OFF)

red_slider.align(lv.ALIGN.LEFT_MID, 25, 0)
green_slider.align_to(red_slider, lv.ALIGN.OUT_RIGHT_MID, 25, 0)
blue_slider.align_to(green_slider, lv.ALIGN.OUT_RIGHT_MID, 25, 0)
intense_slider.align_to(blue_slider, lv.ALIGN.OUT_RIGHT_MID, 25, 0)

# Now create the actual image


img1 = lv.img(lv.scr_act())
img1.set_src(img_cogwheel_argb)
(continues on next page)

2.7. Widgets 151


LVGL Documentation 9.0

(continued from previous page)


img1.align(lv.ALIGN.RIGHT_MID, -20, 0)

intense_slider.send_event(lv.EVENT.VALUE_CHANGED, None)

Rotate and zoom

#include "../../lv_examples.h"
#if LV_USE_IMG && LV_BUILD_EXAMPLES

static void set_angle(void * img, int32_t v)


{
lv_img_set_angle(img, v);
}

static void set_zoom(void * img, int32_t v)


{
lv_img_set_zoom(img, v);
}

/**
* Show transformations (zoom and rotation) using a pivot point.
*/
void lv_example_img_3(void)
{
LV_IMG_DECLARE(img_cogwheel_argb);

/*Now create the actual image*/


lv_obj_t * img = lv_img_create(lv_scr_act());
lv_img_set_src(img, &img_cogwheel_argb);
lv_obj_align(img, LV_ALIGN_CENTER, 50, 50);
lv_img_set_pivot(img, 0, 0); /*Rotate around the top left corner*/

lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, img);
lv_anim_set_exec_cb(&a, set_angle);
lv_anim_set_values(&a, 0, 3600);
lv_anim_set_time(&a, 5000);
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
lv_anim_start(&a);

lv_anim_set_exec_cb(&a, set_zoom);
lv_anim_set_values(&a, 128, 256);
lv_anim_set_playback_time(&a, 3000);
lv_anim_start(&a);
}

#endif

2.7. Widgets 152


LVGL Documentation 9.0

#!/opt/bin/lv_micropython -i
import usys as sys
import lvgl as lv
import display_driver

# Create an image from the png file


try:
with open('../../assets/img_cogwheel_argb.png','rb') as f:
png_data = f.read()
except:
print("Could not find img_cogwheel_argb.png")
sys.exit()

img_cogwheel_argb = lv.img_dsc_t({
'data_size': len(png_data),
'data': png_data
})

def set_angle(img, v):


img.set_angle(v)

def set_zoom(img, v):


img.set_zoom(v)

#
# Show transformations (zoom and rotation) using a pivot point.
#

# Now create the actual image


img = lv.img(lv.scr_act())
img.set_src(img_cogwheel_argb)
img.align(lv.ALIGN.CENTER, 50, 50)
img.set_pivot(0, 0) # Rotate around the top left corner

a1 = lv.anim_t()
a1.init()
a1.set_var(img)
a1.set_custom_exec_cb(lambda a,val: set_angle(img,val))
a1.set_values(0, 3600)
a1.set_time(5000)
a1.set_repeat_count(lv.ANIM_REPEAT_INFINITE)
lv.anim_t.start(a1)

a2 = lv.anim_t()
a2.init()
a2.set_var(img)
a2.set_custom_exec_cb(lambda a,val: set_zoom(img,val))
a2.set_values(128, 256)
a2.set_time(5000)
a2.set_playback_time(3000)
a2.set_repeat_count(lv.ANIM_REPEAT_INFINITE)
lv.anim_t.start(a2)

2.7. Widgets 153


LVGL Documentation 9.0

Image offset and styling

#include "../../lv_examples.h"
#if LV_USE_IMG && LV_BUILD_EXAMPLES

static void ofs_y_anim(void * img, int32_t v)


{
lv_img_set_offset_y(img, v);
}

/**
* Image styling and offset
*/
void lv_example_img_4(void)
{
LV_IMG_DECLARE(img_skew_strip);

static lv_style_t style;


lv_style_init(&style);
lv_style_set_bg_color(&style, lv_palette_main(LV_PALETTE_YELLOW));
lv_style_set_bg_opa(&style, LV_OPA_COVER);
lv_style_set_img_recolor_opa(&style, LV_OPA_COVER);
lv_style_set_img_recolor(&style, lv_color_black());

lv_obj_t * img = lv_img_create(lv_scr_act());


lv_obj_add_style(img, &style, 0);
lv_img_set_src(img, &img_skew_strip);
lv_obj_set_size(img, 150, 100);
lv_obj_center(img);

lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, img);
lv_anim_set_exec_cb(&a, ofs_y_anim);
lv_anim_set_values(&a, 0, 100);
lv_anim_set_time(&a, 3000);
lv_anim_set_playback_time(&a, 500);
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
lv_anim_start(&a);

#endif

def ofs_y_anim(img, v):


img.set_offset_y(v)
# print(img,v)

# Create an image from the png file


try:
with open('../../assets/img_skew_strip.png','rb') as f:
png_data = f.read()
except:
print("Could not find img_skew_strip.png")
sys.exit()

(continues on next page)

2.7. Widgets 154


LVGL Documentation 9.0

(continued from previous page)


img_skew_strip = lv.img_dsc_t({
'data_size': len(png_data),
'data': png_data
})

#
# Image styling and offset
#

style = lv.style_t()
style.init()
style.set_bg_color(lv.palette_main(lv.PALETTE.YELLOW))
style.set_bg_opa(lv.OPA.COVER)
style.set_img_recolor_opa(lv.OPA.COVER)
style.set_img_recolor(lv.color_black())

img = lv.img(lv.scr_act())
img.add_style(style, 0)
img.set_src(img_skew_strip)
img.set_size(150, 100)
img.center()

a = lv.anim_t()
a.init()
a.set_var(img)
a.set_values(0, 100)
a.set_time(3000)
a.set_playback_time(500)
a.set_repeat_count(lv.ANIM_REPEAT_INFINITE)
a.set_custom_exec_cb(lambda a,val: ofs_y_anim(img,val))
lv.anim_t.start(a)

2.7.13 Image button

Simple Image button

#include "../../lv_examples.h"
#if LV_USE_IMGBTN && LV_BUILD_EXAMPLES

void lv_example_imgbtn_1(void)
{
LV_IMG_DECLARE(imgbtn_left);
LV_IMG_DECLARE(imgbtn_right);
LV_IMG_DECLARE(imgbtn_mid);

/*Create a transition animation on width transformation and recolor.*/


static lv_style_prop_t tr_prop[] = {LV_STYLE_TRANSFORM_WIDTH, LV_STYLE_IMG_
,→RECOLOR_OPA, 0};

static lv_style_transition_dsc_t tr;


lv_style_transition_dsc_init(&tr, tr_prop, lv_anim_path_linear, 200, 0, NULL);

static lv_style_t style_def;


lv_style_init(&style_def);
(continues on next page)

2.7. Widgets 155


LVGL Documentation 9.0

(continued from previous page)


lv_style_set_text_color(&style_def, lv_color_white());
lv_style_set_transition(&style_def, &tr);

/*Darken the button when pressed and make it wider*/


static lv_style_t style_pr;
lv_style_init(&style_pr);
lv_style_set_img_recolor_opa(&style_pr, LV_OPA_30);
lv_style_set_img_recolor(&style_pr, lv_color_black());
lv_style_set_transform_width(&style_pr, 20);

/*Create an image button*/


lv_obj_t * imgbtn1 = lv_imgbtn_create(lv_scr_act());
lv_imgbtn_set_src(imgbtn1, LV_IMGBTN_STATE_RELEASED, &imgbtn_left, &imgbtn_mid, &
,→imgbtn_right);

lv_obj_add_style(imgbtn1, &style_def, 0);


lv_obj_add_style(imgbtn1, &style_pr, LV_STATE_PRESSED);

lv_obj_align(imgbtn1, LV_ALIGN_CENTER, 0, 0);

/*Create a label on the image button*/


lv_obj_t * label = lv_label_create(imgbtn1);
lv_label_set_text(label, "Button");
lv_obj_align(label, LV_ALIGN_CENTER, 0, -4);
}

#endif

# Create an image from the png file


try:
with open('../../assets/imgbtn_left.png','rb') as f:
imgbtn_left_data = f.read()
except:
print("Could not find imgbtn_left.png")
sys.exit()

imgbtn_left_dsc = lv.img_dsc_t({
'data_size': len(imgbtn_left_data),
'data': imgbtn_left_data
})

try:
with open('../../assets/imgbtn_mid.png','rb') as f:
imgbtn_mid_data = f.read()
except:
print("Could not find imgbtn_mid.png")
sys.exit()

imgbtn_mid_dsc = lv.img_dsc_t({
'data_size': len(imgbtn_mid_data),
'data': imgbtn_mid_data
})

try:
with open('../../assets/imgbtn_right.png','rb') as f:
imgbtn_right_data = f.read()
except:
(continues on next page)

2.7. Widgets 156


LVGL Documentation 9.0

(continued from previous page)


print("Could not find imgbtn_right.png")
sys.exit()

imgbtn_right_dsc = lv.img_dsc_t({
'data_size': len(imgbtn_right_data),
'data': imgbtn_right_data
})

# Create a transition animation on width transformation and recolor.


tr_prop = [lv.STYLE.TRANSFORM_WIDTH, lv.STYLE.IMG_RECOLOR_OPA, 0]
tr = lv.style_transition_dsc_t()
tr.init(tr_prop, lv.anim_t.path_linear, 200, 0, None)

style_def = lv.style_t()
style_def.init()
style_def.set_text_color(lv.color_white())
style_def.set_transition(tr)

# Darken the button when pressed and make it wider


style_pr = lv.style_t()
style_pr.init()
style_pr.set_img_recolor_opa(lv.OPA._30)
style_pr.set_img_recolor(lv.color_black())
style_pr.set_transform_width(20)

# Create an image button


imgbtn1 = lv.imgbtn(lv.scr_act())
imgbtn1.set_src(lv.imgbtn.STATE.RELEASED, imgbtn_left_dsc, imgbtn_mid_dsc, imgbtn_
,→right_dsc)

imgbtn1.add_style(style_def, 0)
imgbtn1.add_style(style_pr, lv.STATE.PRESSED)

imgbtn1.align(lv.ALIGN.CENTER, 0, 0)

# Create a label on the image button


label = lv.label(imgbtn1)
label.set_text("Button")
label.align(lv.ALIGN.CENTER, 0, -4)

2.7.14 Keyboard

Keyboard with text area

#include "../../lv_examples.h"
#if LV_USE_KEYBOARD && LV_BUILD_EXAMPLES

static void ta_event_cb(lv_event_t * e)


{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * ta = lv_event_get_target(e);
lv_obj_t * kb = lv_event_get_user_data(e);
if(code == LV_EVENT_FOCUSED) {
lv_keyboard_set_textarea(kb, ta);
(continues on next page)

2.7. Widgets 157


LVGL Documentation 9.0

(continued from previous page)


lv_obj_clear_flag(kb, LV_OBJ_FLAG_HIDDEN);
}

if(code == LV_EVENT_DEFOCUSED) {
lv_keyboard_set_textarea(kb, NULL);
lv_obj_add_flag(kb, LV_OBJ_FLAG_HIDDEN);
}
}

void lv_example_keyboard_1(void)
{
/*Create a keyboard to use it with an of the text areas*/
lv_obj_t * kb = lv_keyboard_create(lv_scr_act());

/*Create a text area. The keyboard will write here*/


lv_obj_t * ta;
ta = lv_textarea_create(lv_scr_act());
lv_obj_align(ta, LV_ALIGN_TOP_LEFT, 10, 10);
lv_obj_add_event(ta, ta_event_cb, LV_EVENT_ALL, kb);
lv_textarea_set_placeholder_text(ta, "Hello");
lv_obj_set_size(ta, 140, 80);

ta = lv_textarea_create(lv_scr_act());
lv_obj_align(ta, LV_ALIGN_TOP_RIGHT, -10, 10);
lv_obj_add_event(ta, ta_event_cb, LV_EVENT_ALL, kb);
lv_obj_set_size(ta, 140, 80);

lv_keyboard_set_textarea(kb, ta);
}
#endif

def ta_event_cb(e,kb):
code = e.get_code()
ta = e.get_target_obj()
if code == lv.EVENT.FOCUSED:
kb.set_textarea(ta)
kb.clear_flag(lv.obj.FLAG.HIDDEN)

if code == lv.EVENT.DEFOCUSED:
kb.set_textarea(None)
kb.add_flag(lv.obj.FLAG.HIDDEN)

# Create a keyboard to use it with one of the text areas


kb = lv.keyboard(lv.scr_act())

# Create a text area. The keyboard will write here


ta = lv.textarea(lv.scr_act())
ta.set_width(200)
ta.align(lv.ALIGN.TOP_LEFT, 10, 10)
ta.add_event(lambda e: ta_event_cb(e,kb), lv.EVENT.ALL, None)
ta.set_placeholder_text("Hello")

ta = lv.textarea(lv.scr_act())
ta.set_width(200)
ta.align(lv.ALIGN.TOP_RIGHT, -10, 10)
ta.add_event(lambda e: ta_event_cb(e,kb), lv.EVENT.ALL, None)
(continues on next page)

2.7. Widgets 158


LVGL Documentation 9.0

(continued from previous page)

kb.set_textarea(ta)

Keyboard with custom map

#include "../../lv_examples.h"
#if LV_USE_KEYBOARD && LV_BUILD_EXAMPLES

void lv_example_keyboard_2(void)
{
/*Create an AZERTY keyboard map*/
static const char * kb_map[] = {"A", "Z", "E", "R", "T", "Y", "U", "I", "O", "P",␣
,→LV_SYMBOL_BACKSPACE, "\n",

"Q", "S", "D", "F", "G", "J", "K", "L", "M", LV_
,→SYMBOL_NEW_LINE, "\n",

"W", "X", "C", "V", "B", "N", ",", ".", ":", "!",
,→"?", "\n",

LV_SYMBOL_CLOSE, " ", " ", " ", LV_SYMBOL_OK,␣


,→NULL

};

/*Set the relative width of the buttons and other controls*/


static const lv_btnmatrix_ctrl_t kb_ctrl[] = {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6,
4, 4, 4, 4, 4, 4, 4, 4, 4, 6,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
2, LV_BTNMATRIX_CTRL_HIDDEN | 2, 6,␣
,→LV_BTNMATRIX_CTRL_HIDDEN | 2, 2

};

/*Create a keyboard and add the new map as USER_1 mode*/


lv_obj_t * kb = lv_keyboard_create(lv_scr_act());

lv_keyboard_set_map(kb, LV_KEYBOARD_MODE_USER_1, kb_map, kb_ctrl);


lv_keyboard_set_mode(kb, LV_KEYBOARD_MODE_USER_1);

/*Create a text area. The keyboard will write here*/


lv_obj_t * ta;
ta = lv_textarea_create(lv_scr_act());
lv_obj_align(ta, LV_ALIGN_TOP_MID, 0, 10);
lv_obj_set_size(ta, lv_pct(90), 80);
lv_obj_add_state(ta, LV_STATE_FOCUSED);

lv_keyboard_set_textarea(kb, ta);
}
#endif

# Create an AZERTY keyboard map


kb_map = ["A", "Z", "E", "R", "T", "Y", "U", "I", "O", "P", lv.SYMBOL.BACKSPACE, "\n",
"Q", "S", "D", "F", "G", "J", "K", "L", "M", lv.SYMBOL.NEW_LINE, "\n",
"W", "X", "C", "V", "B", "N", ",", ".", ":", "!", "?", "\n",
lv.SYMBOL.CLOSE, " ", " ", " ", lv.SYMBOL.OK, None]

# Set the relative width of the buttons and other controls


(continues on next page)

2.7. Widgets 159


LVGL Documentation 9.0

(continued from previous page)


kb_ctrl = [4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6,
4, 4, 4, 4, 4, 4, 4, 4, 4, 6,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
2, lv.btnmatrix.CTRL.HIDDEN | 2, 6, lv.btnmatrix.CTRL.HIDDEN | 2, 2]

# Create a keyboard and add the new map as USER_1 mode


kb = lv.keyboard(lv.scr_act())

kb.set_map(lv.keyboard.MODE.USER_1, kb_map, kb_ctrl)


kb.set_mode(lv.keyboard.MODE.USER_1)

# Create a text area. The keyboard will write here


ta = lv.textarea(lv.scr_act())
ta.align(lv.ALIGN.TOP_MID, 0, 10)
ta.set_size(lv.pct(90), 80)
ta.add_state(lv.STATE.FOCUSED)

kb.set_textarea(ta)

2.7.15 Label

Line wrap, recoloring and scrolling

#include "../../lv_examples.h"
#if LV_USE_LABEL && LV_BUILD_EXAMPLES

/**
* Show line wrap, re-color, line align and text scrolling.
*/
void lv_example_label_1(void)
{
lv_obj_t * label1 = lv_label_create(lv_scr_act());
lv_label_set_long_mode(label1, LV_LABEL_LONG_WRAP); /*Break the long lines*/
lv_label_set_recolor(label1, true); /*Enable re-coloring by␣
,→commands in the text*/

lv_label_set_text(label1, "#0000ff Re-color# #ff00ff words# #ff0000 of a# label,␣


,→align the lines to the center "

"and wrap long text automatically.");


lv_obj_set_width(label1, 150); /*Set smaller width to make the lines wrap*/
lv_obj_set_style_text_align(label1, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_align(label1, LV_ALIGN_CENTER, 0, -40);

lv_obj_t * label2 = lv_label_create(lv_scr_act());


lv_label_set_long_mode(label2, LV_LABEL_LONG_SCROLL_CIRCULAR); /*Circular␣
,→scroll*/

lv_obj_set_width(label2, 150);
lv_label_set_text(label2, "It is a circularly scrolling text. ");
lv_obj_align(label2, LV_ALIGN_CENTER, 0, 40);
}

#endif

2.7. Widgets 160


LVGL Documentation 9.0

#
# Show line wrap, re-color, line align and text scrolling.
#
label1 = lv.label(lv.scr_act())
label1.set_long_mode(lv.label.LONG.WRAP) # Break the long lines*/
label1.set_recolor(True) # Enable re-coloring by commands in the␣
,→text

label1.set_text("#0000ff Re-color# #ff00ff words# #ff0000 of a# label, align the␣


,→lines to the center"

"and wrap long text automatically.")


label1.set_width(150) # Set smaller width to make the lines␣
,→wrap

label1.set_style_text_align(lv.ALIGN.CENTER, 0)
label1.align(lv.ALIGN.CENTER, 0, -40)

label2 = lv.label(lv.scr_act())
label2.set_long_mode(lv.label.LONG.SCROLL_CIRCULAR) # Circular scroll
label2.set_width(150)
label2.set_text("It is a circularly scrolling text. ")
label2.align(lv.ALIGN.CENTER, 0, 40)

Text shadow

#include "../../lv_examples.h"
#if LV_USE_LABEL && LV_BUILD_EXAMPLES

/**
* Create a fake text shadow
*/
void lv_example_label_2(void)
{
/*Create a style for the shadow*/
static lv_style_t style_shadow;
lv_style_init(&style_shadow);
lv_style_set_text_opa(&style_shadow, LV_OPA_30);
lv_style_set_text_color(&style_shadow, lv_color_black());

/*Create a label for the shadow first (it's in the background)*/


lv_obj_t * shadow_label = lv_label_create(lv_scr_act());
lv_obj_add_style(shadow_label, &style_shadow, 0);

/*Create the main label*/


lv_obj_t * main_label = lv_label_create(lv_scr_act());
lv_label_set_text(main_label, "A simple method to create\n"
"shadows on a text.\n"
"It even works with\n\n"
"newlines and spaces.");

/*Set the same text for the shadow label*/


lv_label_set_text(shadow_label, lv_label_get_text(main_label));

/*Position the main label*/


lv_obj_align(main_label, LV_ALIGN_CENTER, 0, 0);
(continues on next page)

2.7. Widgets 161


LVGL Documentation 9.0

(continued from previous page)

/*Shift the second label down and to the right by 2 pixel*/


lv_obj_align_to(shadow_label, main_label, LV_ALIGN_TOP_LEFT, 2, 2);
}

#endif

#
# Create a fake text shadow
#

# Create a style for the shadow


style_shadow = lv.style_t()
style_shadow.init()
style_shadow.set_text_opa(lv.OPA._30)
style_shadow.set_text_color(lv.color_black())

# Create a label for the shadow first (it's in the background)


shadow_label = lv.label(lv.scr_act())
shadow_label.add_style(style_shadow, 0)

# Create the main label


main_label = lv.label(lv.scr_act())
main_label.set_text("A simple method to create\n"
"shadows on a text.\n"
"It even works with\n\n"
"newlines and spaces.")

# Set the same text for the shadow label


shadow_label.set_text(lv.label.get_text(main_label))

# Position the main label


main_label.align(lv.ALIGN.CENTER, 0, 0)

# Shift the second label down and to the right by 2 pixel


shadow_label.align_to(main_label, lv.ALIGN.TOP_LEFT, 2, 2)

Show LTR, RTL and Chinese texts

#include "../../lv_examples.h"
#if LV_USE_LABEL && LV_BUILD_EXAMPLES && LV_FONT_DEJAVU_16_PERSIAN_HEBREW && LV_FONT_
,→SIMSUN_16_CJK && LV_USE_BIDI

/**
* Show mixed LTR, RTL and Chinese label
*/
void lv_example_label_3(void)
{
lv_obj_t * ltr_label = lv_label_create(lv_scr_act());
lv_label_set_text(ltr_label, "In modern terminology, a microcontroller is similar␣
,→to a system on a chip (SoC).");

lv_obj_set_style_text_font(ltr_label, &lv_font_montserrat_16, 0);


lv_obj_set_width(ltr_label, 310);
(continues on next page)

2.7. Widgets 162


LVGL Documentation 9.0

(continued from previous page)


lv_obj_align(ltr_label, LV_ALIGN_TOP_LEFT, 5, 5);

lv_obj_t * rtl_label = lv_label_create(lv_scr_act());


lv_label_set_text(rtl_label,
", : ) CPU - Central␣
,→Processing Unit).");

lv_obj_set_style_base_dir(rtl_label, LV_BASE_DIR_RTL, 0);


lv_obj_set_style_text_font(rtl_label, &lv_font_dejavu_16_persian_hebrew, 0);
lv_obj_set_width(rtl_label, 310);
lv_obj_align(rtl_label, LV_ALIGN_LEFT_MID, 5, 0);

lv_obj_t * cz_label = lv_label_create(lv_scr_act());


lv_label_set_text(cz_label,
" Embedded System \n ");
lv_obj_set_style_text_font(cz_label, &lv_font_simsun_16_cjk, 0);
lv_obj_set_width(cz_label, 310);
lv_obj_align(cz_label, LV_ALIGN_BOTTOM_LEFT, 5, -5);
}

#endif

import fs_driver
#
# Show mixed LTR, RTL and Chinese label
#

ltr_label = lv.label(lv.scr_act())
ltr_label.set_text("In modern terminology, a microcontroller is similar to a system␣
,→on a chip (SoC).")

# ltr_label.set_style_text_font(ltr_label, &lv_font_montserrat_16, 0);

fs_drv = lv.fs_drv_t()
fs_driver.fs_register(fs_drv, 'S')

try:
ltr_label.set_style_text_font(ltr_label, lv.font_montserrat_16, 0)
except:
font_montserrat_16 = lv.font_load("S:../../assets/font/montserrat-16.fnt")
ltr_label.set_style_text_font(font_montserrat_16, 0)

ltr_label.set_width(310)
ltr_label.align(lv.ALIGN.TOP_LEFT, 5, 5)

rtl_label = lv.label(lv.scr_act())
rtl_label.set_text(", : ) CPU - Central␣
,→Processing Unit).")

rtl_label.set_style_base_dir(lv.BASE_DIR.RTL, 0)
rtl_label.set_style_text_font(lv.font_dejavu_16_persian_hebrew, 0)
rtl_label.set_width(310)
rtl_label.align(lv.ALIGN.LEFT_MID, 5, 0)

font_simsun_16_cjk = lv.font_load("S:../../assets/font/lv_font_simsun_16_cjk.fnt")

cz_label = lv.label(lv.scr_act())
cz_label.set_style_text_font(font_simsun_16_cjk, 0)
cz_label.set_text(" Embedded System \n ")
(continues on next page)

2.7. Widgets 163


LVGL Documentation 9.0

(continued from previous page)


cz_label.set_width(310)
cz_label.align(lv.ALIGN.BOTTOM_LEFT, 5, -5)

Draw label with gradient color

#include "../../lv_examples.h"
#if LV_USE_LABEL && LV_USE_CANVAS && LV_BUILD_EXAMPLES && LV_USE_DRAW_MASKS

#define MASK_WIDTH 100


#define MASK_HEIGHT 45

static void add_mask_event_cb(lv_event_t * e)


{
static lv_draw_mask_map_param_t m;
static int16_t mask_id;

lv_event_code_t code = lv_event_get_code(e);


lv_obj_t * obj = lv_event_get_target(e);
lv_opa_t * mask_map = lv_event_get_user_data(e);
if(code == LV_EVENT_COVER_CHECK) {
lv_event_set_cover_res(e, LV_COVER_RES_MASKED);
}
else if(code == LV_EVENT_DRAW_MAIN_BEGIN) {
lv_draw_mask_map_init(&m, &obj->coords, mask_map);
mask_id = lv_draw_mask_add(&m, NULL);

}
else if(code == LV_EVENT_DRAW_MAIN_END) {
lv_draw_mask_free_param(&m);
lv_draw_mask_remove_id(mask_id);
}
}

/**
* Draw label with gradient color
*/
void lv_example_label_4(void)
{
/* Create the mask of a text by drawing it to a canvas*/
static lv_opa_t mask_map[MASK_WIDTH * MASK_HEIGHT];

/*Create a "8 bit alpha" canvas and clear it*/


lv_obj_t * canvas = lv_canvas_create(lv_scr_act());
lv_canvas_set_buffer(canvas, mask_map, MASK_WIDTH, MASK_HEIGHT, LV_COLOR_FORMAT_
,→L8);

lv_canvas_fill_bg(canvas, lv_color_black(), LV_OPA_TRANSP);

/*Draw a label to the canvas. The result "image" will be used as mask*/
lv_draw_label_dsc_t label_dsc;
lv_draw_label_dsc_init(&label_dsc);
label_dsc.color = lv_color_white();
label_dsc.align = LV_TEXT_ALIGN_CENTER;
lv_canvas_draw_text(canvas, 5, 5, MASK_WIDTH, &label_dsc, "Text with gradient");

(continues on next page)

2.7. Widgets 164


LVGL Documentation 9.0

(continued from previous page)


/*The mask is reads the canvas is not required anymore*/
lv_obj_del(canvas);

/* Create an object from where the text will be masked out.


* Now it's a rectangle with a gradient but it could be an image too*/
lv_obj_t * grad = lv_obj_create(lv_scr_act());
lv_obj_set_size(grad, MASK_WIDTH, MASK_HEIGHT);
lv_obj_center(grad);
lv_obj_set_style_bg_color(grad, lv_color_hex(0xff0000), 0);
lv_obj_set_style_bg_grad_color(grad, lv_color_hex(0x0000ff), 0);
lv_obj_set_style_bg_grad_dir(grad, LV_GRAD_DIR_HOR, 0);
lv_obj_add_event(grad, add_mask_event_cb, LV_EVENT_ALL, mask_map);
}

#endif

Error encountered while trying to open /home/runner/work/lvgl/lvgl/examples/widgets/


,→label/lv_example_label_4.py

Customize circular scrolling animation

#include "../../lv_examples.h"
#if LV_USE_LABEL && LV_BUILD_EXAMPLES

/**
* Show customizing the circular scrolling animation of a label with `LV_LABEL_LONG_
,→SCROLL_CIRCULAR`

* long mode.
*/
void lv_example_label_5(void)
{
static lv_anim_t animation_template;
static lv_style_t label_style;

lv_anim_init(&animation_template);
lv_anim_set_delay(&animation_template, 1000); /*Wait 1 second to start␣
,→the first scroll*/

lv_anim_set_repeat_delay(&animation_template,
3000); /*Repeat the scroll 3 seconds after the label␣
,→scrolls back to the initial position*/

/*Initialize the label style with the animation template*/


lv_style_init(&label_style);
lv_style_set_anim(&label_style, &animation_template);

lv_obj_t * label1 = lv_label_create(lv_scr_act());


lv_label_set_long_mode(label1, LV_LABEL_LONG_SCROLL_CIRCULAR); /*Circular␣
,→scroll*/

lv_obj_set_width(label1, 150);
lv_label_set_text(label1, "It is a circularly scrolling text. ");
lv_obj_align(label1, LV_ALIGN_CENTER, 0, 40);
lv_obj_add_style(label1, &label_style, LV_STATE_DEFAULT); /*Add the␣
,→style to the label*/

}
(continues on next page)

2.7. Widgets 165


LVGL Documentation 9.0

(continued from previous page)

#endif

#
# Show customizing the circular scrolling animation of a label with `LV_LABEL_LONG_
,→SCROLL_CIRCULAR` long mode.

label1 = lv.label(lv.scr_act())
label1.set_long_mode(lv.label.LONG.SCROLL_CIRCULAR) # Circular scroll
label1.set_width(150)
label1.set_text("It is a circularly scrolling text. ")
label1.align(lv.ALIGN.CENTER, 0, 40)

2.7.16 LED

LED with custom style

#include "../../lv_examples.h"
#if LV_USE_LED && LV_BUILD_EXAMPLES

/**
* Create LED's with different brightness and color
*/
void lv_example_led_1(void)
{
/*Create a LED and switch it OFF*/
lv_obj_t * led1 = lv_led_create(lv_scr_act());
lv_obj_align(led1, LV_ALIGN_CENTER, -80, 0);
lv_led_off(led1);

/*Copy the previous LED and set a brightness*/


lv_obj_t * led2 = lv_led_create(lv_scr_act());
lv_obj_align(led2, LV_ALIGN_CENTER, 0, 0);
lv_led_set_brightness(led2, 150);
lv_led_set_color(led2, lv_palette_main(LV_PALETTE_RED));

/*Copy the previous LED and switch it ON*/


lv_obj_t * led3 = lv_led_create(lv_scr_act());
lv_obj_align(led3, LV_ALIGN_CENTER, 80, 0);
lv_led_on(led3);
}

#endif

#
# Create LED's with different brightness and color
#

# Create a LED and switch it OFF


led1 = lv.led(lv.scr_act())
led1.align(lv.ALIGN.CENTER, -80, 0)
(continues on next page)

2.7. Widgets 166


LVGL Documentation 9.0

(continued from previous page)


led1.off()

# Copy the previous LED and set a brightness


led2 = lv.led(lv.scr_act())
led2.align(lv.ALIGN.CENTER, 0, 0)
led2.set_brightness(150)
led2.set_color(lv.palette_main(lv.PALETTE.RED))

# Copy the previous LED and switch it ON


led3 = lv.led(lv.scr_act())
led3.align(lv.ALIGN.CENTER, 80, 0)
led3.on()

2.7.17 Line

Simple Line

#include "../../lv_examples.h"
#if LV_USE_LINE && LV_BUILD_EXAMPLES

void lv_example_line_1(void)
{
/*Create an array for the points of the line*/
static lv_point_t line_points[] = { {5, 5}, {70, 70}, {120, 10}, {180, 60}, {240,␣
,→10} };

/*Create style*/
static lv_style_t style_line;
lv_style_init(&style_line);
lv_style_set_line_width(&style_line, 8);
lv_style_set_line_color(&style_line, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_line_rounded(&style_line, true);

/*Create a line and apply the new style*/


lv_obj_t * line1;
line1 = lv_line_create(lv_scr_act());
lv_line_set_points(line1, line_points, 5); /*Set the points*/
lv_obj_add_style(line1, &style_line, 0);
lv_obj_center(line1);
}

#endif

# Create an array for the points of the line


line_points = [ {"x":5, "y":5},
{"x":70, "y":70},
{"x":120, "y":10},
{"x":180, "y":60},
{"x":240, "y":10}]

# Create style
style_line = lv.style_t()
style_line.init()
(continues on next page)

2.7. Widgets 167


LVGL Documentation 9.0

(continued from previous page)


style_line.set_line_width(8)
style_line.set_line_color(lv.palette_main(lv.PALETTE.BLUE))
style_line.set_line_rounded(True)

# Create a line and apply the new style


line1 = lv.line(lv.scr_act())
line1.set_points(line_points, 5) # Set the points
line1.add_style(style_line, 0)
line1.center()

2.7.18 List

Simple List

#include "../../lv_examples.h"
#if LV_USE_LIST && LV_BUILD_EXAMPLES
static lv_obj_t * list1;

static void event_handler(lv_event_t * e)


{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_CLICKED) {
LV_UNUSED(obj);
LV_LOG_USER("Clicked: %s", lv_list_get_btn_text(list1, obj));
}
}
void lv_example_list_1(void)
{
/*Create a list*/
list1 = lv_list_create(lv_scr_act());
lv_obj_set_size(list1, 180, 220);
lv_obj_center(list1);

/*Add buttons to the list*/


lv_obj_t * btn;

lv_list_add_text(list1, "File");
btn = lv_list_add_btn(list1, LV_SYMBOL_FILE, "New");
lv_obj_add_event(btn, event_handler, LV_EVENT_CLICKED, NULL);
btn = lv_list_add_btn(list1, LV_SYMBOL_DIRECTORY, "Open");
lv_obj_add_event(btn, event_handler, LV_EVENT_CLICKED, NULL);
btn = lv_list_add_btn(list1, LV_SYMBOL_SAVE, "Save");
lv_obj_add_event(btn, event_handler, LV_EVENT_CLICKED, NULL);
btn = lv_list_add_btn(list1, LV_SYMBOL_CLOSE, "Delete");
lv_obj_add_event(btn, event_handler, LV_EVENT_CLICKED, NULL);
btn = lv_list_add_btn(list1, LV_SYMBOL_EDIT, "Edit");
lv_obj_add_event(btn, event_handler, LV_EVENT_CLICKED, NULL);

lv_list_add_text(list1, "Connectivity");
btn = lv_list_add_btn(list1, LV_SYMBOL_BLUETOOTH, "Bluetooth");
lv_obj_add_event(btn, event_handler, LV_EVENT_CLICKED, NULL);
btn = lv_list_add_btn(list1, LV_SYMBOL_GPS, "Navigation");
(continues on next page)

2.7. Widgets 168


LVGL Documentation 9.0

(continued from previous page)


lv_obj_add_event(btn, event_handler, LV_EVENT_CLICKED, NULL);
btn = lv_list_add_btn(list1, LV_SYMBOL_USB, "USB");
lv_obj_add_event(btn, event_handler, LV_EVENT_CLICKED, NULL);
btn = lv_list_add_btn(list1, LV_SYMBOL_BATTERY_FULL, "Battery");
lv_obj_add_event(btn, event_handler, LV_EVENT_CLICKED, NULL);

lv_list_add_text(list1, "Exit");
btn = lv_list_add_btn(list1, LV_SYMBOL_OK, "Apply");
lv_obj_add_event(btn, event_handler, LV_EVENT_CLICKED, NULL);
btn = lv_list_add_btn(list1, LV_SYMBOL_CLOSE, "Close");
lv_obj_add_event(btn, event_handler, LV_EVENT_CLICKED, NULL);
}

#endif

def event_handler(e):
code = e.get_code()
obj = e.get_target_obj()
if code == lv.EVENT.CLICKED:
print("Clicked: list1." + list1.get_btn_text(obj))

# Create a list
list1 = lv.list(lv.scr_act())
list1.set_size(180, 220)
list1.center()

# Add buttons to the list


list1.add_text("File")
btn_new = list1.add_btn(lv.SYMBOL.FILE, "New")
btn_new.add_event(event_handler,lv.EVENT.ALL, None)
btn_open = list1.add_btn(lv.SYMBOL.DIRECTORY, "Open")
btn_open.add_event(event_handler,lv.EVENT.ALL, None)
btn_save = list1.add_btn(lv.SYMBOL.SAVE, "Save")
btn_save.add_event(event_handler,lv.EVENT.ALL, None)
btn_delete = list1.add_btn(lv.SYMBOL.CLOSE, "Delete")
btn_delete.add_event(event_handler,lv.EVENT.ALL, None)
btn_edit = list1.add_btn(lv.SYMBOL.EDIT, "Edit")
btn_edit.add_event(event_handler,lv.EVENT.ALL, None)

list1.add_text("Connectivity")
btn_bluetooth = list1.add_btn(lv.SYMBOL.BLUETOOTH, "Bluetooth")
btn_bluetooth.add_event(event_handler,lv.EVENT.ALL, None)
btn_navig = list1.add_btn(lv.SYMBOL.GPS, "Navigation")
btn_navig.add_event(event_handler,lv.EVENT.ALL, None)
btn_USB = list1.add_btn(lv.SYMBOL.USB, "USB")
btn_USB.add_event(event_handler,lv.EVENT.ALL, None)
btn_battery = list1.add_btn(lv.SYMBOL.BATTERY_FULL, "Battery")
btn_battery.add_event(event_handler,lv.EVENT.ALL, None)

list1.add_text("Exit")
btn_apply = list1.add_btn(lv.SYMBOL.OK, "Apply")
btn_apply.add_event(event_handler,lv.EVENT.ALL, None)
btn_close = list1.add_btn(lv.SYMBOL.CLOSE, "Close")
btn_close.add_event(event_handler,lv.EVENT.ALL, None)

2.7. Widgets 169


LVGL Documentation 9.0

Sorting a List using up and down buttons

#include <stdlib.h>

#include "../../lv_examples.h"
#if LV_USE_LIST && LV_BUILD_EXAMPLES

static lv_obj_t * list1;


static lv_obj_t * list2;

static lv_obj_t * currentButton = NULL;

static void event_handler(lv_event_t * e)


{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_CLICKED) {
LV_LOG_USER("Clicked: %s", lv_list_get_btn_text(list1, obj));

if(currentButton == obj) {
currentButton = NULL;
}
else {
currentButton = obj;
}
lv_obj_t * parent = lv_obj_get_parent(obj);
uint32_t i;
for(i = 0; i < lv_obj_get_child_cnt(parent); i++) {
lv_obj_t * child = lv_obj_get_child(parent, i);
if(child == currentButton) {
lv_obj_add_state(child, LV_STATE_CHECKED);
}
else {
lv_obj_clear_state(child, LV_STATE_CHECKED);
}
}
}
}

static void event_handler_top(lv_event_t * e)


{
lv_event_code_t code = lv_event_get_code(e);
if(code == LV_EVENT_CLICKED) {
if(currentButton == NULL) return;
lv_obj_move_background(currentButton);
lv_obj_scroll_to_view(currentButton, LV_ANIM_ON);
}
}

static void event_handler_up(lv_event_t * e)


{
lv_event_code_t code = lv_event_get_code(e);
if((code == LV_EVENT_CLICKED) || (code == LV_EVENT_LONG_PRESSED_REPEAT)) {
if(currentButton == NULL) return;
uint32_t index = lv_obj_get_index(currentButton);
if(index <= 0) return;
(continues on next page)

2.7. Widgets 170


LVGL Documentation 9.0

(continued from previous page)


lv_obj_move_to_index(currentButton, index - 1);
lv_obj_scroll_to_view(currentButton, LV_ANIM_ON);
}
}

static void event_handler_center(lv_event_t * e)


{
const lv_event_code_t code = lv_event_get_code(e);
if((code == LV_EVENT_CLICKED) || (code == LV_EVENT_LONG_PRESSED_REPEAT)) {
if(currentButton == NULL) return;

lv_obj_t * parent = lv_obj_get_parent(currentButton);


const uint32_t pos = lv_obj_get_child_cnt(parent) / 2;

lv_obj_move_to_index(currentButton, pos);

lv_obj_scroll_to_view(currentButton, LV_ANIM_ON);
}
}

static void event_handler_dn(lv_event_t * e)


{
const lv_event_code_t code = lv_event_get_code(e);
if((code == LV_EVENT_CLICKED) || (code == LV_EVENT_LONG_PRESSED_REPEAT)) {
if(currentButton == NULL) return;
const uint32_t index = lv_obj_get_index(currentButton);

lv_obj_move_to_index(currentButton, index + 1);


lv_obj_scroll_to_view(currentButton, LV_ANIM_ON);
}
}

static void event_handler_bottom(lv_event_t * e)


{
const lv_event_code_t code = lv_event_get_code(e);
if(code == LV_EVENT_CLICKED) {
if(currentButton == NULL) return;
lv_obj_move_foreground(currentButton);
lv_obj_scroll_to_view(currentButton, LV_ANIM_ON);
}
}

static void event_handler_swap(lv_event_t * e)


{
const lv_event_code_t code = lv_event_get_code(e);
// lv_obj_t* obj = lv_event_get_target(e);
if((code == LV_EVENT_CLICKED) || (code == LV_EVENT_LONG_PRESSED_REPEAT)) {
uint32_t cnt = lv_obj_get_child_cnt(list1);
for(int i = 0; i < 100; i++)
if(cnt > 1) {
lv_obj_t * obj = lv_obj_get_child(list1, rand() % cnt);
lv_obj_move_to_index(obj, rand() % cnt);
if(currentButton != NULL) {
lv_obj_scroll_to_view(currentButton, LV_ANIM_ON);
}
}
}
(continues on next page)

2.7. Widgets 171


LVGL Documentation 9.0

(continued from previous page)


}

void lv_example_list_2(void)
{
/*Create a list*/
list1 = lv_list_create(lv_scr_act());
lv_obj_set_size(list1, lv_pct(60), lv_pct(100));
lv_obj_set_style_pad_row(list1, 5, 0);

/*Add buttons to the list*/


lv_obj_t * btn;
int i;
for(i = 0; i < 15; i++) {
btn = lv_btn_create(list1);
lv_obj_set_width(btn, lv_pct(50));
lv_obj_add_event(btn, event_handler, LV_EVENT_CLICKED, NULL);

lv_obj_t * lab = lv_label_create(btn);


lv_label_set_text_fmt(lab, "Item %d", i);
}

/*Select the first button by default*/


currentButton = lv_obj_get_child(list1, 0);
lv_obj_add_state(currentButton, LV_STATE_CHECKED);

/*Create a second list with up and down buttons*/


list2 = lv_list_create(lv_scr_act());
lv_obj_set_size(list2, lv_pct(40), lv_pct(100));
lv_obj_align(list2, LV_ALIGN_TOP_RIGHT, 0, 0);
lv_obj_set_flex_flow(list2, LV_FLEX_FLOW_COLUMN);

btn = lv_list_add_btn(list2, NULL, "Top");


lv_obj_add_event(btn, event_handler_top, LV_EVENT_ALL, NULL);
lv_group_remove_obj(btn);

btn = lv_list_add_btn(list2, LV_SYMBOL_UP, "Up");


lv_obj_add_event(btn, event_handler_up, LV_EVENT_ALL, NULL);
lv_group_remove_obj(btn);

btn = lv_list_add_btn(list2, LV_SYMBOL_LEFT, "Center");


lv_obj_add_event(btn, event_handler_center, LV_EVENT_ALL, NULL);
lv_group_remove_obj(btn);

btn = lv_list_add_btn(list2, LV_SYMBOL_DOWN, "Down");


lv_obj_add_event(btn, event_handler_dn, LV_EVENT_ALL, NULL);
lv_group_remove_obj(btn);

btn = lv_list_add_btn(list2, NULL, "Bottom");


lv_obj_add_event(btn, event_handler_bottom, LV_EVENT_ALL, NULL);
lv_group_remove_obj(btn);

btn = lv_list_add_btn(list2, LV_SYMBOL_SHUFFLE, "Shuffle");


lv_obj_add_event(btn, event_handler_swap, LV_EVENT_ALL, NULL);
lv_group_remove_obj(btn);
}

#endif

2.7. Widgets 172


LVGL Documentation 9.0

import urandom

currentButton = None
list1 = None

def event_handler(e):
global currentButton
code = e.get_code()
obj = e.get_target_obj()
if code == lv.EVENT.CLICKED:
if currentButton == obj:
currentButton = None
else:
currentButton = obj
parent = obj.get_parent()
for i in range( parent.get_child_cnt()):
child = parent.get_child(i)
if child == currentButton:
child.add_state(lv.STATE.CHECKED)
else:
child.clear_state(lv.STATE.CHECKED)

def event_handler_top(e):
global currentButton
code = e.get_code()
obj = e.get_target_obj()
if code == lv.EVENT.CLICKED:
if currentButton == None:
return
currentButton.move_background()
currentButton.scroll_to_view( lv.ANIM.ON)

def event_handler_up(e):
global currentButton
code = e.get_code()
obj = e.get_target_obj()
if code == lv.EVENT.CLICKED or code == lv.EVENT.LONG_PRESSED_REPEAT:
if currentButton == None:
return
index = currentButton.get_index()
if index <= 0:
return
currentButton.move_to_index(index - 1)
currentButton.scroll_to_view(lv.ANIM.ON)

def event_handler_center(e):
global currentButton
code = e.get_code()
obj = e.get_target_obj()
if code == lv.EVENT.CLICKED or code == lv.EVENT.LONG_PRESSED_REPEAT:
if currentButton == None:
return
parent = currentButton.get_parent()
pos = parent.get_child_cnt() // 2
currentButton.move_to_index(pos)
currentButton.scroll_to_view(lv.ANIM.ON)

(continues on next page)

2.7. Widgets 173


LVGL Documentation 9.0

(continued from previous page)


def event_handler_dn(e):
global currentButton
code = e.get_code()
obj = e.get_target_obj()
if code == lv.EVENT.CLICKED or code == lv.EVENT.LONG_PRESSED_REPEAT:
if currentButton == None:
return
index = currentButton.get_index()
currentButton.move_to_index(index + 1)
currentButton.scroll_to_view(lv.ANIM.ON)

def event_handler_bottom(e):
global currentButton
code = e.get_code()
obj = e.get_target_obj()
if code == lv.EVENT.CLICKED or code == lv.EVENT.LONG_PRESSED_REPEAT:
if currentButton == None:
return
currentButton.move_foreground()
currentButton.scroll_to_view(lv.ANIM.ON)

def event_handler_swap(e):
global currentButton
global list1
code = e.get_code()
obj = e.get_target_obj()
if code == lv.EVENT.CLICKED:
cnt = list1.get_child_cnt()
for i in range(100):
if cnt > 1:
obj = list1.get_child(urandom.getrandbits(32) % cnt )
obj.move_to_index(urandom.getrandbits(32) % cnt)
if currentButton != None:
currentButton.scroll_to_view(lv.ANIM.ON)

#Create a list with buttons that can be sorted


list1 = lv.list(lv.scr_act())
list1.set_size(lv.pct(60), lv.pct(100))
list1.set_style_pad_row( 5, 0)

for i in range(15):
btn = lv.btn(list1)
btn.set_width(lv.pct(100))
btn.add_event( event_handler, lv.EVENT.CLICKED, None)
lab = lv.label(btn)
lab.set_text("Item " + str(i))

#Select the first button by default


currentButton = list1.get_child(0)
currentButton.add_state(lv.STATE.CHECKED)

#Create a second list with up and down buttons


list2 = lv.list(lv.scr_act())
list2.set_size(lv.pct(40), lv.pct(100))
list2.align(lv.ALIGN.TOP_RIGHT, 0, 0)
list2.set_flex_flow(lv.FLEX_FLOW.COLUMN)

(continues on next page)

2.7. Widgets 174


LVGL Documentation 9.0

(continued from previous page)


btn = list2.add_btn(None, "Top")
btn.add_event(event_handler_top, lv.EVENT.ALL, None)
lv.group_remove_obj(btn)

btn = list2.add_btn(lv.SYMBOL.UP, "Up")


btn.add_event(event_handler_up, lv.EVENT.ALL, None)
lv.group_remove_obj(btn)

btn = list2.add_btn(lv.SYMBOL.LEFT, "Center")


btn.add_event(event_handler_center, lv.EVENT.ALL, None)
lv.group_remove_obj(btn)

btn = list2.add_btn(lv.SYMBOL.DOWN, "Down")


btn.add_event(event_handler_dn, lv.EVENT.ALL, None)
lv.group_remove_obj(btn)

btn = list2.add_btn(None, "Bottom")


btn.add_event(event_handler_bottom, lv.EVENT.ALL, None)
lv.group_remove_obj(btn)

btn = list2.add_btn(lv.SYMBOL.SHUFFLE, "Shuffle")


btn.add_event(event_handler_swap, lv.EVENT.ALL, None)
lv.group_remove_obj(btn)

2.7.19 Menu

Simple Menu

#include "../../lv_examples.h"
#if LV_USE_MENU && LV_BUILD_EXAMPLES

void lv_example_menu_1(void)
{
/*Create a menu object*/
lv_obj_t * menu = lv_menu_create(lv_scr_act());
lv_obj_set_size(menu, lv_disp_get_hor_res(NULL), lv_disp_get_ver_res(NULL));
lv_obj_center(menu);

lv_obj_t * cont;
lv_obj_t * label;

/*Create a sub page*/


lv_obj_t * sub_page = lv_menu_page_create(menu, NULL);

cont = lv_menu_cont_create(sub_page);
label = lv_label_create(cont);
lv_label_set_text(label, "Hello, I am hiding here");

/*Create a main page*/


lv_obj_t * main_page = lv_menu_page_create(menu, NULL);

cont = lv_menu_cont_create(main_page);
label = lv_label_create(cont);
lv_label_set_text(label, "Item 1");
(continues on next page)

2.7. Widgets 175


LVGL Documentation 9.0

(continued from previous page)

cont = lv_menu_cont_create(main_page);
label = lv_label_create(cont);
lv_label_set_text(label, "Item 2");

cont = lv_menu_cont_create(main_page);
label = lv_label_create(cont);
lv_label_set_text(label, "Item 3 (Click me!)");
lv_menu_set_load_page_event(menu, cont, sub_page);

lv_menu_set_page(menu, main_page);
}

#endif

# Create a menu object


menu = lv.menu(lv.scr_act())
menu.set_size(320, 240)
menu.center()

# Create a sub page


sub_page = lv.menu_page(menu, None)
cont = lv.menu_cont(sub_page)
label = lv.label(cont)
label.set_text("Hello, I am hiding here")

# Create a main page


main_page = lv.menu_page(menu, None)

cont = lv.menu_cont(main_page)
label = lv.label(cont)
label.set_text("Item 1")

cont = lv.menu_cont(main_page)
label = lv.label(cont)
label.set_text("Item 2")

cont = lv.menu_cont(main_page)
label = lv.label(cont)
label.set_text("Item 3 (Click me!)")
menu.set_load_page_event(cont, sub_page)

menu.set_page(main_page)

Simple Menu with root btn

#include "../../lv_examples.h"
#if LV_USE_MENU && LV_USE_MSGBOX && LV_BUILD_EXAMPLES

static void back_event_handler(lv_event_t * e)


{
lv_obj_t * obj = lv_event_get_target(e);
lv_obj_t * menu = lv_event_get_user_data(e);

(continues on next page)

2.7. Widgets 176


LVGL Documentation 9.0

(continued from previous page)


if(lv_menu_back_btn_is_root(menu, obj)) {
lv_obj_t * mbox1 = lv_msgbox_create(NULL, "Hello", "Root back btn click.",␣
,→NULL, true);

lv_obj_center(mbox1);
}
}

void lv_example_menu_2(void)
{
lv_obj_t * menu = lv_menu_create(lv_scr_act());
lv_menu_set_mode_root_back_btn(menu, LV_MENU_ROOT_BACK_BTN_ENABLED);
lv_obj_add_event(menu, back_event_handler, LV_EVENT_CLICKED, menu);
lv_obj_set_size(menu, lv_disp_get_hor_res(NULL), lv_disp_get_ver_res(NULL));
lv_obj_center(menu);

lv_obj_t * cont;
lv_obj_t * label;

/*Create a sub page*/


lv_obj_t * sub_page = lv_menu_page_create(menu, NULL);

cont = lv_menu_cont_create(sub_page);
label = lv_label_create(cont);
lv_label_set_text(label, "Hello, I am hiding here");

/*Create a main page*/


lv_obj_t * main_page = lv_menu_page_create(menu, NULL);

cont = lv_menu_cont_create(main_page);
label = lv_label_create(cont);
lv_label_set_text(label, "Item 1");

cont = lv_menu_cont_create(main_page);
label = lv_label_create(cont);
lv_label_set_text(label, "Item 2");

cont = lv_menu_cont_create(main_page);
label = lv_label_create(cont);
lv_label_set_text(label, "Item 3 (Click me!)");
lv_menu_set_load_page_event(menu, cont, sub_page);

lv_menu_set_page(menu, main_page);
}

#endif

def back_event_handler(e):
obj = e.get_target_obj()
if menu.back_btn_is_root(obj):
mbox1 = lv.msgbox(lv.scr_act(), "Hello", "Root back btn click.", None, True)
mbox1.center()

# Create a menu object


menu = lv.menu(lv.scr_act())
menu.set_mode_root_back_btn(lv.menu.ROOT_BACK_BTN.ENABLED)
menu.add_event(back_event_handler, lv.EVENT.CLICKED, None)
(continues on next page)

2.7. Widgets 177


LVGL Documentation 9.0

(continued from previous page)


menu.set_size(320, 240)
menu.center()

# Create a sub page


sub_page = lv.menu_page(menu, None)
cont = lv.menu_cont(sub_page)
label = lv.label(cont)
label.set_text("Hello, I am hiding here")

# Create a main page


main_page = lv.menu_page(menu, None)

cont = lv.menu_cont(main_page)
label = lv.label(cont)
label.set_text("Item 1")

cont = lv.menu_cont(main_page)
label = lv.label(cont)
label.set_text("Item 2")

cont = lv.menu_cont(main_page)
label = lv.label(cont)
label.set_text("Item 3 (Click me!)")
menu.set_load_page_event(cont, sub_page)

menu.set_page(main_page)

Simple Menu with custom header

#include "../../lv_examples.h"
#if LV_USE_MENU && LV_USE_USER_DATA && LV_BUILD_EXAMPLES

void lv_example_menu_3(void)
{
/*Create a menu object*/
lv_obj_t * menu = lv_menu_create(lv_scr_act());
lv_obj_set_size(menu, lv_disp_get_hor_res(NULL), lv_disp_get_ver_res(NULL));
lv_obj_center(menu);

/*Modify the header*/


lv_obj_t * back_btn = lv_menu_get_main_header_back_btn(menu);
lv_obj_t * back_btn_label = lv_label_create(back_btn);
lv_label_set_text(back_btn_label, "Back");

lv_obj_t * cont;
lv_obj_t * label;

/*Create sub pages*/


lv_obj_t * sub_1_page = lv_menu_page_create(menu, "Page 1");

cont = lv_menu_cont_create(sub_1_page);
label = lv_label_create(cont);
lv_label_set_text(label, "Hello, I am hiding here");

lv_obj_t * sub_2_page = lv_menu_page_create(menu, "Page 2");


(continues on next page)

2.7. Widgets 178


LVGL Documentation 9.0

(continued from previous page)

cont = lv_menu_cont_create(sub_2_page);
label = lv_label_create(cont);
lv_label_set_text(label, "Hello, I am hiding here");

lv_obj_t * sub_3_page = lv_menu_page_create(menu, "Page 3");

cont = lv_menu_cont_create(sub_3_page);
label = lv_label_create(cont);
lv_label_set_text(label, "Hello, I am hiding here");

/*Create a main page*/


lv_obj_t * main_page = lv_menu_page_create(menu, NULL);

cont = lv_menu_cont_create(main_page);
label = lv_label_create(cont);
lv_label_set_text(label, "Item 1 (Click me!)");
lv_menu_set_load_page_event(menu, cont, sub_1_page);

cont = lv_menu_cont_create(main_page);
label = lv_label_create(cont);
lv_label_set_text(label, "Item 2 (Click me!)");
lv_menu_set_load_page_event(menu, cont, sub_2_page);

cont = lv_menu_cont_create(main_page);
label = lv_label_create(cont);
lv_label_set_text(label, "Item 3 (Click me!)");
lv_menu_set_load_page_event(menu, cont, sub_3_page);

lv_menu_set_page(menu, main_page);
}

#endif

# Create a menu object


menu = lv.menu(lv.scr_act())
menu.set_size(320, 240)
menu.center()

# Create sub pages


sub_page_1 = lv.menu_page(menu, "Page 1")

cont = lv.menu_cont(sub_page_1)
label = lv.label(cont)
label.set_text("Hello, I am hiding here")

sub_page_2 = lv.menu_page(menu, "Page 2")

cont = lv.menu_cont(sub_page_2)
label = lv.label(cont)
label.set_text("Hello, I am hiding here")

sub_page_3 = lv.menu_page(menu, "Page 3")

cont = lv.menu_cont(sub_page_3)
label = lv.label(cont)
(continues on next page)

2.7. Widgets 179


LVGL Documentation 9.0

(continued from previous page)


label.set_text("Hello, I am hiding here")

# Create a main page


main_page = lv.menu_page(menu, None)

cont = lv.menu_cont(main_page)
label = lv.label(cont)
label.set_text("Item 1 (Click me!)")
menu.set_load_page_event(cont, sub_page_1)

cont = lv.menu_cont(main_page)
label = lv.label(cont)
label.set_text("Item 2 (Click me!)")
menu.set_load_page_event(cont, sub_page_2)

cont = lv.menu_cont(main_page)
label = lv.label(cont)
label.set_text("Item 3 (Click me!)")
menu.set_load_page_event(cont, sub_page_3)

menu.set_page(main_page)

Simple Menu with floating btn to add new menu page

#include "../../lv_examples.h"
#if LV_USE_MENU && LV_BUILD_EXAMPLES

static uint32_t btn_cnt = 1;


static lv_obj_t * main_page;
static lv_obj_t * menu;

static void float_btn_event_cb(lv_event_t * e)


{
LV_UNUSED(e);

btn_cnt++;

lv_obj_t * cont;
lv_obj_t * label;

lv_obj_t * sub_page = lv_menu_page_create(menu, NULL);

cont = lv_menu_cont_create(sub_page);
label = lv_label_create(cont);
lv_label_set_text_fmt(label, "Hello, I am hiding inside %"LV_PRIu32"", btn_cnt);

cont = lv_menu_cont_create(main_page);
label = lv_label_create(cont);
lv_label_set_text_fmt(label, "Item %"LV_PRIu32"", btn_cnt);
lv_menu_set_load_page_event(menu, cont, sub_page);

lv_obj_scroll_to_view_recursive(cont, LV_ANIM_ON);
}

void lv_example_menu_4(void)
(continues on next page)

2.7. Widgets 180


LVGL Documentation 9.0

(continued from previous page)


{
/*Create a menu object*/
menu = lv_menu_create(lv_scr_act());
lv_obj_set_size(menu, lv_disp_get_hor_res(NULL), lv_disp_get_ver_res(NULL));
lv_obj_center(menu);

lv_obj_t * cont;
lv_obj_t * label;

/*Create a sub page*/


lv_obj_t * sub_page = lv_menu_page_create(menu, NULL);

cont = lv_menu_cont_create(sub_page);
label = lv_label_create(cont);
lv_label_set_text(label, "Hello, I am hiding inside the first item");

/*Create a main page*/


main_page = lv_menu_page_create(menu, NULL);

cont = lv_menu_cont_create(main_page);
label = lv_label_create(cont);
lv_label_set_text(label, "Item 1");
lv_menu_set_load_page_event(menu, cont, sub_page);

lv_menu_set_page(menu, main_page);

/*Create floating btn*/


lv_obj_t * float_btn = lv_btn_create(lv_scr_act());
lv_obj_set_size(float_btn, 50, 50);
lv_obj_add_flag(float_btn, LV_OBJ_FLAG_FLOATING);
lv_obj_align(float_btn, LV_ALIGN_BOTTOM_RIGHT, -10, -10);
lv_obj_add_event(float_btn, float_btn_event_cb, LV_EVENT_CLICKED, menu);
lv_obj_set_style_radius(float_btn, LV_RADIUS_CIRCLE, 0);
lv_obj_set_style_bg_img_src(float_btn, LV_SYMBOL_PLUS, 0);
lv_obj_set_style_text_font(float_btn, lv_theme_get_font_large(float_btn), 0);
}

#endif

btn_cnt = 1

def float_btn_event_cb(e):
global btn_cnt
btn_cnt += 1

sub_page = lv.menu_page(menu, None)

cont = lv.menu_cont(sub_page)
label = lv.label(cont)
label.set_text("Hello, I am hiding inside {:d}".format(btn_cnt))

cont = lv.menu_cont(main_page)
label = lv.label(cont)
label.set_text("Item {:d}".format(btn_cnt))
menu.set_load_page_event(cont, sub_page)
(continues on next page)

2.7. Widgets 181


LVGL Documentation 9.0

(continued from previous page)

# Create a menu object


menu = lv.menu(lv.scr_act())
menu.set_size(320, 240)
menu.center()

# Create a sub page


sub_page = lv.menu_page(menu, None)

cont = lv.menu_cont(sub_page)
label = lv.label(cont)
label.set_text("Hello, I am hiding inside the first item")

# Create a main page


main_page = lv.menu_page(menu, None)

cont = lv.menu_cont(main_page)
label = lv.label(cont)
label.set_text("Item 1")
menu.set_load_page_event(cont, sub_page)

menu.set_page(main_page)

float_btn = lv.btn(lv.scr_act())
float_btn.set_size(50, 50)
float_btn.add_flag(lv.obj.FLAG.FLOATING)
float_btn.align(lv.ALIGN.BOTTOM_RIGHT, -10, -10)
float_btn.add_event(float_btn_event_cb, lv.EVENT.CLICKED, None)
float_btn.set_style_radius(lv.RADIUS_CIRCLE, 0)
float_btn.set_style_bg_img_src(lv.SYMBOL.PLUS, 0)
float_btn.set_style_text_font(lv.theme_get_font_large(float_btn), 0)

Complex Menu

#include "../../lv_examples.h"
#if LV_USE_MENU && LV_USE_MSGBOX && LV_BUILD_EXAMPLES

enum {
LV_MENU_ITEM_BUILDER_VARIANT_1,
LV_MENU_ITEM_BUILDER_VARIANT_2
};
typedef uint8_t lv_menu_builder_variant_t;

static void back_event_handler(lv_event_t * e);


static void switch_handler(lv_event_t * e);
lv_obj_t * root_page;
static lv_obj_t * create_text(lv_obj_t * parent, const char * icon, const char * txt,
lv_menu_builder_variant_t builder_variant);
static lv_obj_t * create_slider(lv_obj_t * parent,
const char * icon, const char * txt, int32_t min,␣
,→int32_t max, int32_t val);

static lv_obj_t * create_switch(lv_obj_t * parent,


const char * icon, const char * txt, bool chk);

void lv_example_menu_5(void)
(continues on next page)

2.7. Widgets 182


LVGL Documentation 9.0

(continued from previous page)


{
lv_obj_t * menu = lv_menu_create(lv_scr_act());

lv_color_t bg_color = lv_obj_get_style_bg_color(menu, 0);


if(lv_color_brightness(bg_color) > 127) {
lv_obj_set_style_bg_color(menu, lv_color_darken(lv_obj_get_style_bg_
,→color(menu, 0), 10), 0);

}
else {
lv_obj_set_style_bg_color(menu, lv_color_darken(lv_obj_get_style_bg_
,→color(menu, 0), 50), 0);

}
lv_menu_set_mode_root_back_btn(menu, LV_MENU_ROOT_BACK_BTN_ENABLED);
lv_obj_add_event(menu, back_event_handler, LV_EVENT_CLICKED, menu);
lv_obj_set_size(menu, lv_disp_get_hor_res(NULL), lv_disp_get_ver_res(NULL));
lv_obj_center(menu);

lv_obj_t * cont;
lv_obj_t * section;

/*Create sub pages*/


lv_obj_t * sub_mechanics_page = lv_menu_page_create(menu, NULL);
lv_obj_set_style_pad_hor(sub_mechanics_page, lv_obj_get_style_pad_left(lv_menu_
,→get_main_header(menu), 0), 0);

lv_menu_separator_create(sub_mechanics_page);
section = lv_menu_section_create(sub_mechanics_page);
create_slider(section, LV_SYMBOL_SETTINGS, "Velocity", 0, 150, 120);
create_slider(section, LV_SYMBOL_SETTINGS, "Acceleration", 0, 150, 50);
create_slider(section, LV_SYMBOL_SETTINGS, "Weight limit", 0, 150, 80);

lv_obj_t * sub_sound_page = lv_menu_page_create(menu, NULL);


lv_obj_set_style_pad_hor(sub_sound_page, lv_obj_get_style_pad_left(lv_menu_get_
,→main_header(menu), 0), 0);

lv_menu_separator_create(sub_sound_page);
section = lv_menu_section_create(sub_sound_page);
create_switch(section, LV_SYMBOL_AUDIO, "Sound", false);

lv_obj_t * sub_display_page = lv_menu_page_create(menu, NULL);


lv_obj_set_style_pad_hor(sub_display_page, lv_obj_get_style_pad_left(lv_menu_get_
,→main_header(menu), 0), 0);

lv_menu_separator_create(sub_display_page);
section = lv_menu_section_create(sub_display_page);
create_slider(section, LV_SYMBOL_SETTINGS, "Brightness", 0, 150, 100);

lv_obj_t * sub_software_info_page = lv_menu_page_create(menu, NULL);


lv_obj_set_style_pad_hor(sub_software_info_page, lv_obj_get_style_pad_left(lv_
,→menu_get_main_header(menu), 0), 0);

section = lv_menu_section_create(sub_software_info_page);
create_text(section, NULL, "Version 1.0", LV_MENU_ITEM_BUILDER_VARIANT_1);

lv_obj_t * sub_legal_info_page = lv_menu_page_create(menu, NULL);


lv_obj_set_style_pad_hor(sub_legal_info_page, lv_obj_get_style_pad_left(lv_menu_
,→get_main_header(menu), 0), 0);

section = lv_menu_section_create(sub_legal_info_page);
for(uint32_t i = 0; i < 15; i++) {
create_text(section, NULL,
"This is a long long long long long long long long long text, if␣
,→it is long enough it may scroll.", (continues on next page)

2.7. Widgets 183


LVGL Documentation 9.0

(continued from previous page)


LV_MENU_ITEM_BUILDER_VARIANT_1);
}

lv_obj_t * sub_about_page = lv_menu_page_create(menu, NULL);


lv_obj_set_style_pad_hor(sub_about_page, lv_obj_get_style_pad_left(lv_menu_get_
,→main_header(menu), 0), 0);

lv_menu_separator_create(sub_about_page);
section = lv_menu_section_create(sub_about_page);
cont = create_text(section, NULL, "Software information", LV_MENU_ITEM_BUILDER_
,→VARIANT_1);

lv_menu_set_load_page_event(menu, cont, sub_software_info_page);


cont = create_text(section, NULL, "Legal information", LV_MENU_ITEM_BUILDER_
,→VARIANT_1);

lv_menu_set_load_page_event(menu, cont, sub_legal_info_page);

lv_obj_t * sub_menu_mode_page = lv_menu_page_create(menu, NULL);


lv_obj_set_style_pad_hor(sub_menu_mode_page, lv_obj_get_style_pad_left(lv_menu_
,→get_main_header(menu), 0), 0);

lv_menu_separator_create(sub_menu_mode_page);
section = lv_menu_section_create(sub_menu_mode_page);
cont = create_switch(section, LV_SYMBOL_AUDIO, "Sidebar enable", true);
lv_obj_add_event(lv_obj_get_child(cont, 2), switch_handler, LV_EVENT_VALUE_
,→CHANGED, menu);

/*Create a root page*/


root_page = lv_menu_page_create(menu, "Settings");
lv_obj_set_style_pad_hor(root_page, lv_obj_get_style_pad_left(lv_menu_get_main_
,→header(menu), 0), 0);

section = lv_menu_section_create(root_page);
cont = create_text(section, LV_SYMBOL_SETTINGS, "Mechanics", LV_MENU_ITEM_BUILDER_
,→VARIANT_1);

lv_menu_set_load_page_event(menu, cont, sub_mechanics_page);


cont = create_text(section, LV_SYMBOL_AUDIO, "Sound", LV_MENU_ITEM_BUILDER_
,→VARIANT_1);

lv_menu_set_load_page_event(menu, cont, sub_sound_page);


cont = create_text(section, LV_SYMBOL_SETTINGS, "Display", LV_MENU_ITEM_BUILDER_
,→VARIANT_1);

lv_menu_set_load_page_event(menu, cont, sub_display_page);

create_text(root_page, NULL, "Others", LV_MENU_ITEM_BUILDER_VARIANT_1);


section = lv_menu_section_create(root_page);
cont = create_text(section, NULL, "About", LV_MENU_ITEM_BUILDER_VARIANT_1);
lv_menu_set_load_page_event(menu, cont, sub_about_page);
cont = create_text(section, LV_SYMBOL_SETTINGS, "Menu mode", LV_MENU_ITEM_BUILDER_
,→VARIANT_1);

lv_menu_set_load_page_event(menu, cont, sub_menu_mode_page);

lv_menu_set_sidebar_page(menu, root_page);

lv_obj_send_event(lv_obj_get_child(lv_obj_get_child(lv_menu_get_cur_sidebar_
,→ page(menu), 0), 0), LV_EVENT_CLICKED,
NULL);
}

static void back_event_handler(lv_event_t * e)


{
lv_obj_t * obj = lv_event_get_target(e);
(continues on next page)

2.7. Widgets 184


LVGL Documentation 9.0

(continued from previous page)


lv_obj_t * menu = lv_event_get_user_data(e);

if(lv_menu_back_btn_is_root(menu, obj)) {
lv_obj_t * mbox1 = lv_msgbox_create(NULL, "Hello", "Root back btn click.",␣
,→NULL, true);

lv_obj_center(mbox1);
}
}

static void switch_handler(lv_event_t * e)


{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * menu = lv_event_get_user_data(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_VALUE_CHANGED) {
if(lv_obj_has_state(obj, LV_STATE_CHECKED)) {
lv_menu_set_page(menu, NULL);
lv_menu_set_sidebar_page(menu, root_page);
lv_obj_send_event(lv_obj_get_child(lv_obj_get_child(lv_menu_get_cur_
,→sidebar_page(menu), 0), 0), LV_EVENT_CLICKED,

NULL);
}
else {
lv_menu_set_sidebar_page(menu, NULL);
lv_menu_clear_history(menu); /* Clear history because we will be showing␣
,→the root page later */

lv_menu_set_page(menu, root_page);
}
}
}

static lv_obj_t * create_text(lv_obj_t * parent, const char * icon, const char * txt,
lv_menu_builder_variant_t builder_variant)
{
lv_obj_t * obj = lv_menu_cont_create(parent);

lv_obj_t * img = NULL;


lv_obj_t * label = NULL;

if(icon) {
img = lv_img_create(obj);
lv_img_set_src(img, icon);
}

if(txt) {
label = lv_label_create(obj);
lv_label_set_text(label, txt);
lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR);
lv_obj_set_flex_grow(label, 1);
}

if(builder_variant == LV_MENU_ITEM_BUILDER_VARIANT_2 && icon && txt) {


lv_obj_add_flag(img, LV_OBJ_FLAG_FLEX_IN_NEW_TRACK);
lv_obj_swap(img, label);
}

return obj;
(continues on next page)

2.7. Widgets 185


LVGL Documentation 9.0

(continued from previous page)


}

static lv_obj_t * create_slider(lv_obj_t * parent, const char * icon, const char *␣


,→txt, int32_t min, int32_t max,

int32_t val)
{
lv_obj_t * obj = create_text(parent, icon, txt, LV_MENU_ITEM_BUILDER_VARIANT_2);

lv_obj_t * slider = lv_slider_create(obj);


lv_obj_set_flex_grow(slider, 1);
lv_slider_set_range(slider, min, max);
lv_slider_set_value(slider, val, LV_ANIM_OFF);

if(icon == NULL) {
lv_obj_add_flag(slider, LV_OBJ_FLAG_FLEX_IN_NEW_TRACK);
}

return obj;
}

static lv_obj_t * create_switch(lv_obj_t * parent, const char * icon, const char *␣


,→txt, bool chk)

{
lv_obj_t * obj = create_text(parent, icon, txt, LV_MENU_ITEM_BUILDER_VARIANT_1);

lv_obj_t * sw = lv_switch_create(obj);
lv_obj_add_state(sw, chk ? LV_STATE_CHECKED : 0);

return obj;
}

#endif

from micropython import const

def create_text(parent, icon, txt, builder_variant):

obj = lv.menu_cont(parent)

img = None
label = None

if icon :
img = lv.img(obj)
img.set_src(icon)

if txt :
label = lv.label(obj)
label.set_text(txt)
label.set_long_mode(lv.label.LONG.SCROLL_CIRCULAR)
label.set_flex_grow(1)

if builder_variant == LV_MENU_ITEM_BUILDER_VARIANT_2 and icon and txt :


img.add_flag(lv.OBJ_FLAG_FLEX_IN_NEW_TRACK)
img.swap(label)
(continues on next page)

2.7. Widgets 186


LVGL Documentation 9.0

(continued from previous page)

return obj

def create_slider(parent, icon, txt, min, max, val) :

obj = create_text(parent, icon, txt, LV_MENU_ITEM_BUILDER_VARIANT_2)

slider = lv.slider(obj)
slider.set_flex_grow(1)
slider.set_range(min, max)
slider.set_value(val, lv.ANIM.OFF)

if icon == None :
slider.add_flag(lv.obj.FLAG_FLEX.IN_NEW_TRACK)

return obj

def create_switch(parent, icon, txt, chk) :

obj = create_text(parent, icon, txt, LV_MENU_ITEM_BUILDER_VARIANT_1)

sw = lv.switch(obj)
if chk == lv.STATE.CHECKED:
sw.add_state(chk )
else:
sw.add_state(0)

return obj

def back_event_handler(e,menu):

obj = e.get_target_obj()
# menu = lv_event_get_user_data(e);

if menu.back_btn_is_root(obj) :
mbox1 = lv.msgbox(None, "Hello", "Root back btn click.", None, True)
mbox1.center()

def switch_handler(e,menu):

code = e.get_code()
obj = e.get_target_obj()
if code == lv.EVENT.VALUE_CHANGED :
if obj.has_state(lv.STATE.CHECKED) :
menu.set_page(None)
menu.set_sidebar_page(root_page)
menu.get_cur_sidebar_page().get_child(0).get_child(0).send_event(lv.EVENT.
,→CLICKED,None)

else :
menu.set_sidebar_page(None)
menu.clear_history() # Clear history because we will be showing the␣
,→root page later

menu.set_page(root_page)

LV_MENU_ITEM_BUILDER_VARIANT_1 = const(0)
(continues on next page)

2.7. Widgets 187


LVGL Documentation 9.0

(continued from previous page)


LV_MENU_ITEM_BUILDER_VARIANT_2 = const(1)

menu = lv.menu(lv.scr_act())

bg_color = menu.get_style_bg_color(0)
if bg_color.color_brightness() > 127 :
menu.set_style_bg_color(menu.get_style_bg_color(0).color_darken(10),0)
else :
menu.set_style_bg_color(menu.get_style_bg_color(0).color_darken(50),0)

menu.set_mode_root_back_btn(lv.menu.ROOT_BACK_BTN.ENABLED)
menu.add_event(lambda evt: back_event_handler(evt,menu), lv.EVENT.CLICKED, None)
menu.set_size(lv.pct(100), lv.pct(100))
menu.center()

# Create sub pages


sub_mechanics_page = lv.menu_page(menu, None)
sub_mechanics_page.set_style_pad_hor(menu.get_main_header().get_style_pad_left(0),0)
lv.menu_separator(sub_mechanics_page)
section = lv.menu_section(sub_mechanics_page);
create_slider(section,lv.SYMBOL.SETTINGS, "Velocity", 0, 150, 120)
create_slider(section,lv.SYMBOL.SETTINGS, "Acceleration", 0, 150, 50)
create_slider(section,lv.SYMBOL.SETTINGS, "Weight limit", 0, 150, 80)

sub_sound_page = lv.menu_page(menu, None)


sub_sound_page.set_style_pad_hor(menu.get_main_header().get_style_pad_left(0),0)
lv.menu_separator(sub_sound_page)
section = lv.menu_section(sub_sound_page)
create_switch(section,lv.SYMBOL.AUDIO, "Sound", False)

sub_display_page = lv.menu_page(menu, None)


sub_display_page.set_style_pad_hor(menu.get_main_header().get_style_pad_left(0),0)
lv.menu_separator(sub_display_page)
section = lv.menu_section(sub_display_page)
create_slider(section,lv.SYMBOL.SETTINGS, "Brightness", 0, 150, 100)

sub_software_info_page = lv.menu_page(menu, None)


sub_software_info_page.set_style_pad_hor(menu.get_main_header().get_style_pad_left(0),
,→0)

section = lv.menu_section(sub_software_info_page)
create_text(section,None, "Version 1.0", LV_MENU_ITEM_BUILDER_VARIANT_1)

sub_legal_info_page = lv.menu_page(menu, None)


sub_legal_info_page.set_style_pad_hor(menu.get_main_header().get_style_pad_left(0),0)

section = lv.menu_section(sub_legal_info_page)

for i in range(15):
create_text(section, None,
"This is a long long long long long long long long long text, if it␣
,→is long enough it may scroll.",

LV_MENU_ITEM_BUILDER_VARIANT_1)

sub_about_page = lv.menu_page(menu, None)


sub_about_page.set_style_pad_hor(menu.get_main_header().get_style_pad_left(0),0)

(continues on next page)

2.7. Widgets 188


LVGL Documentation 9.0

(continued from previous page)


lv.menu_separator(sub_about_page)
section = lv.menu_section(sub_about_page)
cont = create_text(section, None, "Software information", LV_MENU_ITEM_BUILDER_
,→VARIANT_1);

menu.set_load_page_event(cont, sub_software_info_page);
cont = create_text(section, None, "Legal information", LV_MENU_ITEM_BUILDER_VARIANT_
,→1);

menu.set_load_page_event(cont, sub_legal_info_page)

sub_menu_mode_page = lv.menu_page(menu, None)


sub_menu_mode_page.set_style_pad_hor(menu.get_main_header().get_style_pad_left(0),0)
lv.menu_separator(sub_menu_mode_page)
section = lv.menu_section(sub_menu_mode_page)
cont = create_switch(section, lv.SYMBOL.AUDIO, "Sidebar enable",True)
cont.get_child(2).add_event(lambda evt: switch_handler(evt,menu), lv.EVENT.VALUE_
,→CHANGED, None)

# Create a root page


root_page = lv.menu_page(menu, "Settings")
root_page.set_style_pad_hor(menu.get_main_header().get_style_pad_left(0),0)
section = lv.menu_section(root_page)
cont = create_text(section, lv.SYMBOL.SETTINGS, "Mechanics", LV_MENU_ITEM_BUILDER_
,→VARIANT_1)

menu.set_load_page_event(cont, sub_mechanics_page);
cont = create_text(section, lv.SYMBOL.AUDIO, "Sound", LV_MENU_ITEM_BUILDER_VARIANT_1);
menu.set_load_page_event(cont, sub_sound_page)
cont = create_text(section, lv.SYMBOL.SETTINGS, "Display", LV_MENU_ITEM_BUILDER_
,→VARIANT_1);

menu.set_load_page_event(cont, sub_display_page)

create_text(root_page, None, "Others", LV_MENU_ITEM_BUILDER_VARIANT_1);


section = lv.menu_section(root_page)
cont = create_text(section, None, "About", LV_MENU_ITEM_BUILDER_VARIANT_1);
menu.set_load_page_event(cont, sub_about_page)
cont = create_text(section, lv.SYMBOL.SETTINGS, "Menu mode", LV_MENU_ITEM_BUILDER_
,→VARIANT_1);

menu.set_load_page_event(cont, sub_menu_mode_page)

menu.set_sidebar_page(root_page)

menu.get_cur_sidebar_page().get_child(0).get_child(0).send_event(lv.EVENT.CLICKED,
,→None)

2.7. Widgets 189


LVGL Documentation 9.0

2.7.20 Meter

Simple meter

#include "../../lv_examples.h"
#if LV_USE_METER && LV_BUILD_EXAMPLES

static lv_obj_t * meter;

static void set_value(void * indic, int32_t v)


{
lv_meter_set_indicator_value(meter, indic, v);
}

/**
* A simple meter
*/
void lv_example_meter_1(void)
{
meter = lv_meter_create(lv_scr_act());
lv_obj_center(meter);
lv_obj_set_size(meter, 200, 200);

/*Add a scale first*/


lv_meter_set_scale_ticks(meter, 41, 2, 10, lv_palette_main(LV_PALETTE_GREY));
lv_meter_set_scale_major_ticks(meter, 8, 4, 15, lv_color_black(), 10);

lv_meter_indicator_t * indic;

/*Add a blue arc to the start*/


indic = lv_meter_add_arc(meter, 3, lv_palette_main(LV_PALETTE_BLUE), 0);
lv_meter_set_indicator_start_value(meter, indic, 0);
lv_meter_set_indicator_end_value(meter, indic, 20);

/*Make the tick lines blue at the start of the scale*/


indic = lv_meter_add_scale_lines(meter, lv_palette_main(LV_PALETTE_BLUE), lv_
,→palette_main(LV_PALETTE_BLUE),

false, 0);
lv_meter_set_indicator_start_value(meter, indic, 0);
lv_meter_set_indicator_end_value(meter, indic, 20);

/*Add a red arc to the end*/


indic = lv_meter_add_arc(meter, 3, lv_palette_main(LV_PALETTE_RED), 0);
lv_meter_set_indicator_start_value(meter, indic, 80);
lv_meter_set_indicator_end_value(meter, indic, 100);

/*Make the tick lines red at the end of the scale*/


indic = lv_meter_add_scale_lines(meter, lv_palette_main(LV_PALETTE_RED), lv_
,→palette_main(LV_PALETTE_RED), false,

0);
lv_meter_set_indicator_start_value(meter, indic, 80);
lv_meter_set_indicator_end_value(meter, indic, 100);

/*Add a needle line indicator*/


indic = lv_meter_add_needle_line(meter, 4, lv_palette_main(LV_PALETTE_GREY), -10);

/*Create an animation to set the value*/


(continues on next page)

2.7. Widgets 190


LVGL Documentation 9.0

(continued from previous page)


lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_exec_cb(&a, set_value);
lv_anim_set_var(&a, indic);
lv_anim_set_values(&a, 0, 100);
lv_anim_set_time(&a, 2000);
lv_anim_set_repeat_delay(&a, 100);
lv_anim_set_playback_time(&a, 500);
lv_anim_set_playback_delay(&a, 100);
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
lv_anim_start(&a);
}

#endif

#!//opt/bin/lv_micropython -i
import utime as time
import lvgl as lv
import display_driver

def set_value(indic, v):


meter.set_indicator_value(indic, v)

#
# A simple meter
#
meter = lv.meter(lv.scr_act())
meter.center()
meter.set_size(200, 200)

indic = lv.meter_indicator_t()

# Add a blue arc to the start


indic = meter.add_arc(3, lv.palette_main(lv.PALETTE.BLUE), 0)
meter.set_indicator_start_value(indic, 0)
meter.set_indicator_end_value(indic, 20)

# Make the tick lines blue at the start of the scale


indic = meter.add_scale_lines(lv.palette_main(lv.PALETTE.BLUE), lv.palette_main(lv.
,→PALETTE.BLUE), False, 0)

meter.set_indicator_start_value(indic, 0)
meter.set_indicator_end_value(indic, 20)

# Add a red arc to the end


indic = meter.add_arc(3, lv.palette_main(lv.PALETTE.RED), 0)
meter.set_indicator_start_value(indic, 80)
meter.set_indicator_end_value(indic, 100)

# Make the tick lines red at the end of the scale


indic = meter.add_scale_lines(lv.palette_main(lv.PALETTE.RED), lv.palette_main(lv.
,→PALETTE.RED), False, 0)

meter.set_indicator_start_value(indic, 80)
meter.set_indicator_end_value(indic, 100)

# Add a needle line indicator


indic = meter.add_needle_line(4, lv.palette_main(lv.PALETTE.GREY), -10)
(continues on next page)

2.7. Widgets 191


LVGL Documentation 9.0

(continued from previous page)

# Create an animation to set the value


a = lv.anim_t()
a.init()
a.set_var(indic)
a.set_values(0, 100)
a.set_time(2000)
a.set_repeat_delay(100)
a.set_playback_time(500)
a.set_playback_delay(100)
a.set_repeat_count(lv.ANIM_REPEAT_INFINITE)
a.set_custom_exec_cb(lambda a,val: set_value(indic,val))
lv.anim_t.start(a)

A meter with multiple arcs

#include "../../lv_examples.h"
#if LV_USE_METER && LV_BUILD_EXAMPLES

static lv_obj_t * meter;

static void set_value(void * indic, int32_t v)


{
lv_meter_set_indicator_end_value(meter, indic, v);
}

/**
* A meter with multiple arcs
*/
void lv_example_meter_2(void)
{
meter = lv_meter_create(lv_scr_act());
lv_obj_center(meter);
lv_obj_set_size(meter, 220, 220);

/*Remove the circle from the middle*/


lv_obj_remove_style(meter, NULL, LV_PART_INDICATOR);

/*Add a scale first*/


lv_meter_set_scale_ticks(meter, 11, 2, 10, lv_palette_main(LV_PALETTE_GREY));
lv_meter_set_scale_major_ticks(meter, 1, 2, 30, lv_color_hex3(0xeee), 15);
lv_meter_set_scale_range(meter, 0, 100, 270, 90);

/*Add a three arc indicator*/


lv_meter_indicator_t * indic1 = lv_meter_add_arc(meter, 10, lv_palette_main(LV_
,→PALETTE_RED), 0);

lv_meter_indicator_t * indic2 = lv_meter_add_arc(meter, 10, lv_palette_main(LV_


,→PALETTE_GREEN), -10);

lv_meter_indicator_t * indic3 = lv_meter_add_arc(meter, 10, lv_palette_main(LV_


,→PALETTE_BLUE), -20);

/*Create an animation to set the value*/


lv_anim_t a;
(continues on next page)

2.7. Widgets 192


LVGL Documentation 9.0

(continued from previous page)


lv_anim_init(&a);
lv_anim_set_exec_cb(&a, set_value);
lv_anim_set_values(&a, 0, 100);
lv_anim_set_repeat_delay(&a, 100);
lv_anim_set_playback_delay(&a, 100);
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);

lv_anim_set_time(&a, 2000);
lv_anim_set_playback_time(&a, 500);
lv_anim_set_var(&a, indic1);
lv_anim_start(&a);

lv_anim_set_time(&a, 1000);
lv_anim_set_playback_time(&a, 1000);
lv_anim_set_var(&a, indic2);
lv_anim_start(&a);

lv_anim_set_time(&a, 1000);
lv_anim_set_playback_time(&a, 2000);
lv_anim_set_var(&a, indic3);
lv_anim_start(&a);
}

#endif

#!//opt/bin/lv_micropython -i
import utime as time
import lvgl as lv
import display_driver

def set_value(indic,v):
meter.set_indicator_end_value(indic, v)

#
# A meter with multiple arcs
#

meter = lv.meter(lv.scr_act())
meter.center()
meter.set_size(200, 200)

# Remove the circle from the middle


meter.remove_style(None, lv.PART.INDICATOR)

# Scale settings
meter.set_scale_ticks(11, 2, 10, lv.palette_main(lv.PALETTE.GREY))
meter.set_scale_major_ticks(1, 2, 30, lv.color_hex3(0xeee), 10)
meter.set_scale_range(0, 100, 270, 90)

# Add a three arc indicator


indic1 = meter.add_arc(10, lv.palette_main(lv.PALETTE.RED), 0)
indic2 = meter.add_arc(10, lv.palette_main(lv.PALETTE.GREEN), -10)
indic3 = meter.add_arc(10, lv.palette_main(lv.PALETTE.BLUE), -20)

# Create an animation to set the value


a1 = lv.anim_t()
(continues on next page)

2.7. Widgets 193


LVGL Documentation 9.0

(continued from previous page)


a1.init()
a1.set_values(0, 100)
a1.set_time(2000)
a1.set_repeat_delay(100)
a1.set_playback_delay(100)
a1.set_playback_time(500)
a1.set_var(indic1)
a1.set_repeat_count(lv.ANIM_REPEAT_INFINITE)
a1.set_custom_exec_cb(lambda a,val: set_value(indic1,val))
lv.anim_t.start(a1)

a2 = lv.anim_t()
a2.init()
a2.set_values(0, 100)
a2.set_time(1000)
a2.set_repeat_delay(100)
a2.set_playback_delay(100)
a2.set_playback_time(1000)
a2.set_var(indic2)
a2.set_repeat_count(lv.ANIM_REPEAT_INFINITE)
a2.set_custom_exec_cb(lambda a,val: set_value(indic2,val))
lv.anim_t.start(a2)

a3 = lv.anim_t()
a3.init()
a3.set_values(0, 100)
a3.set_time(1000)
a3.set_repeat_delay(100)
a3.set_playback_delay(100)
a3.set_playback_time(2000)
a3.set_var(indic3)
a3.set_repeat_count(lv.ANIM_REPEAT_INFINITE)
a3.set_custom_exec_cb(lambda a,val: set_value(indic3,val))
lv.anim_t.start(a3)

A clock from a meter

#include "../../lv_examples.h"
#if LV_USE_METER && LV_BUILD_EXAMPLES

static lv_obj_t * meter;

static void set_value(void * indic, int32_t v)


{
lv_meter_set_indicator_end_value(meter, indic, v);
}

static void tick_label_event(lv_event_t * e)


{
lv_obj_draw_part_dsc_t * draw_part_dsc = lv_event_get_draw_part_dsc(e);

/*Be sure it's drawing meter related parts*/


(continues on next page)

2.7. Widgets 194


LVGL Documentation 9.0

(continued from previous page)


if(draw_part_dsc->class_p != &lv_meter_class) return;

/*Be sure it's drawing the ticks*/


if(draw_part_dsc->type != LV_METER_DRAW_PART_TICK) return;

/*Be sure it's a major ticks*/


if(draw_part_dsc->id % 5) return;

/*The order of numbers on the clock is tricky: 12, 1, 2, 3...*/


if(draw_part_dsc->id == 0) {
lv_strncpy(draw_part_dsc->text, "12", 4);
}
else {
lv_snprintf(draw_part_dsc->text, 4, "%d", draw_part_dsc->id / 5);
}
}

/**
* A clock from a meter
*/
void lv_example_meter_3(void)
{
meter = lv_meter_create(lv_scr_act());
lv_obj_set_size(meter, 220, 220);
lv_obj_center(meter);

/*Create a scale for the minutes*/


/*61 ticks in a 360 degrees range (the last and the first line overlaps)*/
lv_meter_set_scale_ticks(meter, 60, 1, 10, lv_palette_main(LV_PALETTE_GREY));
lv_meter_set_scale_major_ticks(meter, 5, 2, 20, lv_color_black(), 10);
lv_meter_set_scale_range(meter, 0, 59, 354, 270);

LV_IMG_DECLARE(img_hand)

/*Add a the hands from images*/


lv_meter_indicator_t * indic_min = lv_meter_add_needle_img(meter, &img_hand, 5,␣
,→5);

lv_meter_indicator_t * indic_hour = lv_meter_add_needle_img(meter, &img_hand, 5,␣


,→5);

lv_obj_add_event(meter, tick_label_event, LV_EVENT_DRAW_PART_BEGIN, NULL);

/*Create an animation to set the value*/


lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_exec_cb(&a, set_value);
lv_anim_set_values(&a, 0, 59);
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
lv_anim_set_time(&a, 5000); /*2 sec for 1 turn of the minute hand (1 hour)*/
lv_anim_set_var(&a, indic_min);
lv_anim_start(&a);

lv_anim_set_var(&a, indic_hour);
lv_anim_set_time(&a, 240000); /*24 sec for 1 turn of the hour hand*/
lv_anim_set_values(&a, 0, 59);
lv_anim_start(&a);
(continues on next page)

2.7. Widgets 195


LVGL Documentation 9.0

(continued from previous page)


}

#endif

#!//opt/bin/lv_micropython -i
import utime as time
import lvgl as lv
import display_driver

# Create an image from the png file


try:
with open('../../assets/img_hand_min.png','rb') as f:
img_hand_min_data = f.read()
except:
print("Could not find img_hand_min.png")
sys.exit()

img_hand_min_dsc = lv.img_dsc_t({
'data_size': len(img_hand_min_data),
'data': img_hand_min_data
})

# Create an image from the png file


try:
with open('../../assets/img_hand_hour.png','rb') as f:
img_hand_hour_data = f.read()
except:
print("Could not find img_hand_hour.png")
sys.exit()

img_hand_hour_dsc = lv.img_dsc_t({
'data_size': len(img_hand_hour_data),
'data': img_hand_hour_data
})

def set_value(indic, v):


meter.set_indicator_value(indic, v)
#
# A clock from a meter
#

def tick_label_event(e):
draw_part_dsc = e.get_draw_part_dsc();

# Be sure it's drawing the ticks


if draw_part_dsc.type != lv.meter.DRAW_PART.TICK: return

# Be sure it's a major ticks


if draw_part_dsc.id % 5: return

# The order of numbers on the clock is tricky: 12, 1, 2, 3...*/


txt = ["12", "1", "2as", "3", "4", "5", "6", "7", "8", "9", "10", "11"]
# dsc.text is defined char text[16], I must therefore convert the Python string␣
,→to a byte_array

idx = int(draw_part_dsc.id / 5)
(continues on next page)

2.7. Widgets 196


LVGL Documentation 9.0

(continued from previous page)


draw_part_dsc.text = bytes(txt[idx],"ascii")

meter = lv.meter(lv.scr_act())
meter.set_size(220, 220)
meter.center()

# Create a scale for the minutes


# 60 ticks in a 354 degrees range
meter.set_scale_ticks(60, 1, 10, lv.palette_main(lv.PALETTE.GREY))
meter.set_scale_major_ticks(5, 2, 20, lv.color_black(), 10) # Every tick is␣
,→major

meter.set_scale_range(0, 59, 354, 270)

# Add the hands from images


indic_min = meter.add_needle_img(img_hand_min_dsc, 5, 5)
indic_hour = meter.add_needle_img(img_hand_hour_dsc, 5, 5)

#Add an event to set the numbers of hours


meter.add_event(tick_label_event, lv.EVENT.DRAW_PART_BEGIN, None)

# Create an animation to set the value


a1 = lv.anim_t()
a1.init()
a1.set_values(0, 60)
a1.set_repeat_count(lv.ANIM_REPEAT_INFINITE)
a1.set_time(2000) # 2 sec for 1 turn of the minute hand (1 hour)
a1.set_var(indic_min)
a1.set_custom_exec_cb(lambda a1,val: set_value(indic_min,val))
lv.anim_t.start(a1)

a2 = lv.anim_t()
a2.init()
a2.set_var(indic_hour)
a2.set_time(24000) # 24 sec for 1 turn of the hour hand
a2.set_values(0, 60)
a2.set_custom_exec_cb(lambda a2,val: set_value(indic_hour,val))
lv.anim_t.start(a2)

Pie chart

#include "../../lv_examples.h"
#if LV_USE_METER && LV_BUILD_EXAMPLES

/**
* Create a pie chart
*/
void lv_example_meter_4(void)
{
lv_obj_t * meter = lv_meter_create(lv_scr_act());

/*Remove the background and the circle from the middle*/


lv_obj_remove_style(meter, NULL, LV_PART_MAIN);
lv_obj_remove_style(meter, NULL, LV_PART_INDICATOR);

(continues on next page)

2.7. Widgets 197


LVGL Documentation 9.0

(continued from previous page)


lv_obj_set_size(meter, 200, 200);
lv_obj_center(meter);

/*Add a scale first with no ticks.*/


lv_meter_set_scale_ticks(meter, 0, 0, 0, lv_color_black());
lv_meter_set_scale_range(meter, 0, 100, 360, 0);

/*Add a three arc indicator*/


lv_coord_t indic_w = 100;
lv_meter_indicator_t * indic1 = lv_meter_add_arc(meter, indic_w, lv_palette_
,→main(LV_PALETTE_ORANGE), 0);

lv_meter_set_indicator_start_value(meter, indic1, 0);


lv_meter_set_indicator_end_value(meter, indic1, 40);

lv_meter_indicator_t * indic2 = lv_meter_add_arc(meter, indic_w, lv_palette_


,→main(LV_PALETTE_YELLOW), 0);
lv_meter_set_indicator_start_value(meter, indic2, 40); /*Start from the␣
,→previous*/

lv_meter_set_indicator_end_value(meter, indic2, 80);

lv_meter_indicator_t * indic3 = lv_meter_add_arc(meter, indic_w, lv_palette_


,→ main(LV_PALETTE_DEEP_ORANGE), 0);
lv_meter_set_indicator_start_value(meter, indic3, 80); /*Start from the␣
,→previous*/

lv_meter_set_indicator_end_value(meter, indic3, 100);


}

#endif

#
# Create a pie chart
#

meter = lv.meter(lv.scr_act())

# Remove the background and the circle from the middle


meter.remove_style(None, lv.PART.MAIN)
meter.remove_style(None, lv.PART.INDICATOR)

meter.set_size(200, 200)
meter.center()

# Add a scale first with no ticks.


meter.set_scale_ticks( 0, 0, 0, lv.color_black())
meter.set_scale_range(0, 100, 360, 0)

# Add a three arc indicator*


indic_w = 100
indic1 = meter.add_arc(indic_w,lv.palette_main(lv.PALETTE.ORANGE), 0)
meter.set_indicator_start_value(indic1, 0)
meter.set_indicator_end_value(indic1, 40)

indic2 = meter.add_arc(indic_w, lv.palette_main(lv.PALETTE.YELLOW), 0)


meter.set_indicator_start_value(indic2, 40) # Start from the previous
meter.set_indicator_end_value(indic2, 80)

(continues on next page)

2.7. Widgets 198


LVGL Documentation 9.0

(continued from previous page)


indic3 = meter.add_arc(indic_w, lv.palette_main(lv.PALETTE.DEEP_ORANGE), 0)
meter.set_indicator_start_value(indic3, 80) # Start from the previous
meter.set_indicator_end_value(indic3, 100)

2.7.21 Message box

Simple Message box

#include "../../lv_examples.h"
#if LV_USE_MSGBOX && LV_BUILD_EXAMPLES

static void event_cb(lv_event_t * e)


{
lv_obj_t * obj = lv_event_get_current_target(e);
LV_UNUSED(obj);
LV_LOG_USER("Button %s clicked", lv_msgbox_get_active_btn_text(obj));
}

void lv_example_msgbox_1(void)
{
static const char * btns[] = {"Apply", "Close", ""};

lv_obj_t * mbox1 = lv_msgbox_create(NULL, "Hello", "This is a message box with␣


,→ two buttons.", btns, true);
lv_obj_add_event(mbox1, event_cb, LV_EVENT_VALUE_CHANGED, NULL);
lv_obj_center(mbox1);
}

#endif

def event_cb(e):
mbox = e.get_target_obj()
print("Button %s clicked" % mbox.get_active_btn_text())

btns = ["Apply", "Close", ""]

mbox1 = lv.msgbox(lv.scr_act(), "Hello", "This is a message box with two buttons.",␣


,→btns, True)

mbox1.add_event(event_cb, lv.EVENT.VALUE_CHANGED, None)


mbox1.center()

2.7. Widgets 199


LVGL Documentation 9.0

2.7.22 Roller

Simple Roller

#include "../../lv_examples.h"
#if LV_USE_ROLLER && LV_BUILD_EXAMPLES

static void event_handler(lv_event_t * e)


{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_VALUE_CHANGED) {
char buf[32];
lv_roller_get_selected_str(obj, buf, sizeof(buf));
LV_LOG_USER("Selected month: %s\n", buf);
}
}

/**
* An infinite roller with the name of the months
*/
void lv_example_roller_1(void)
{
lv_obj_t * roller1 = lv_roller_create(lv_scr_act());
lv_roller_set_options(roller1,
"January\n"
"February\n"
"March\n"
"April\n"
"May\n"
"June\n"
"July\n"
"August\n"
"September\n"
"October\n"
"November\n"
"December",
LV_ROLLER_MODE_INFINITE);

lv_roller_set_visible_row_count(roller1, 4);
lv_obj_center(roller1);
lv_obj_add_event(roller1, event_handler, LV_EVENT_ALL, NULL);
}

#endif

def event_handler(e):
code = e.get_code()
obj = e.get_target_obj()
if code == lv.EVENT.VALUE_CHANGED:
option = " "*10
obj.get_selected_str(option, len(option))
print("Selected month: " + option.strip())

#
# An infinite roller with the name of the months
#
(continues on next page)

2.7. Widgets 200


LVGL Documentation 9.0

(continued from previous page)

roller1 = lv.roller(lv.scr_act())
roller1.set_options("\n".join([
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"]),lv.roller.MODE.INFINITE)

roller1.set_visible_row_count(4)
roller1.center()
roller1.add_event(event_handler, lv.EVENT.ALL, None)

Styling the roller

#include "../../lv_examples.h"
#if LV_USE_ROLLER && LV_FONT_MONTSERRAT_22 && LV_BUILD_EXAMPLES

static void event_handler(lv_event_t * e)


{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_VALUE_CHANGED) {
char buf[32];
lv_roller_get_selected_str(obj, buf, sizeof(buf));
LV_LOG_USER("Selected value: %s", buf);
}
}

/**
* Roller with various alignments and larger text in the selected area
*/
void lv_example_roller_2(void)
{
/*A style to make the selected option larger*/
static lv_style_t style_sel;
lv_style_init(&style_sel);
lv_style_set_text_font(&style_sel, &lv_font_montserrat_22);
lv_style_set_bg_color(&style_sel, lv_color_hex3(0xf88));
lv_style_set_border_width(&style_sel, 2);
lv_style_set_border_color(&style_sel, lv_color_hex3(0xf00));

const char * opts = "1\n2\n3\n4\n5\n6\n7\n8\n9\n10";


lv_obj_t * roller;

/*A roller on the left with left aligned text, and custom width*/
roller = lv_roller_create(lv_scr_act());
(continues on next page)

2.7. Widgets 201


LVGL Documentation 9.0

(continued from previous page)


lv_roller_set_options(roller, opts, LV_ROLLER_MODE_NORMAL);
lv_roller_set_visible_row_count(roller, 2);
lv_obj_set_width(roller, 100);
lv_obj_add_style(roller, &style_sel, LV_PART_SELECTED);
lv_obj_set_style_text_align(roller, LV_TEXT_ALIGN_LEFT, 0);
lv_obj_set_style_bg_color(roller, lv_color_hex3(0x0f0), 0);
lv_obj_set_style_bg_grad_color(roller, lv_color_hex3(0xafa), 0);
lv_obj_set_style_bg_grad_dir(roller, LV_GRAD_DIR_VER, 0);
lv_obj_align(roller, LV_ALIGN_LEFT_MID, 10, 0);
lv_obj_add_event(roller, event_handler, LV_EVENT_ALL, NULL);
lv_roller_set_selected(roller, 2, LV_ANIM_OFF);

/*A roller on the middle with center aligned text, and auto (default) width*/
roller = lv_roller_create(lv_scr_act());
lv_roller_set_options(roller, opts, LV_ROLLER_MODE_NORMAL);
lv_roller_set_visible_row_count(roller, 3);
lv_obj_add_style(roller, &style_sel, LV_PART_SELECTED);
lv_obj_align(roller, LV_ALIGN_CENTER, 0, 0);
lv_obj_add_event(roller, event_handler, LV_EVENT_ALL, NULL);
lv_roller_set_selected(roller, 5, LV_ANIM_OFF);

/*A roller on the right with right aligned text, and custom width*/
roller = lv_roller_create(lv_scr_act());
lv_roller_set_options(roller, opts, LV_ROLLER_MODE_NORMAL);
lv_roller_set_visible_row_count(roller, 4);
lv_obj_set_width(roller, 80);
lv_obj_add_style(roller, &style_sel, LV_PART_SELECTED);
lv_obj_set_style_text_align(roller, LV_TEXT_ALIGN_RIGHT, 0);
lv_obj_align(roller, LV_ALIGN_RIGHT_MID, -10, 0);
lv_obj_add_event(roller, event_handler, LV_EVENT_ALL, NULL);
lv_roller_set_selected(roller, 8, LV_ANIM_OFF);
}

#endif

import fs_driver

def event_handler(e):
code = e.get_code()
obj = e.get_target_obj()
if code == lv.EVENT.VALUE_CHANGED:
option = " "*10
obj.get_selected_str(option, len(option))
print("Selected value: %s\n" + option.strip())

#
# Roller with various alignments and larger text in the selected area
#

# A style to make the selected option larger


style_sel = lv.style_t()
style_sel.init()

try:
style_sel.set_text_font(lv.font_montserrat_22)
(continues on next page)

2.7. Widgets 202


LVGL Documentation 9.0

(continued from previous page)


except:
fs_drv = lv.fs_drv_t()
fs_driver.fs_register(fs_drv, 'S')
print("montserrat-22 not enabled in lv_conf.h, dynamically loading the font")
font_montserrat_22 = lv.font_load("S:" + "../../assets/font/montserrat-22.fnt")
style_sel.set_text_font(font_montserrat_22)

opts = "\n".join(["1","2","3","4","5","6","7","8","9","10"])

# A roller on the left with left aligned text, and custom width
roller = lv.roller(lv.scr_act())
roller.set_options(opts, lv.roller.MODE.NORMAL)
roller.set_visible_row_count(2)
roller.set_width(100)
roller.add_style(style_sel, lv.PART.SELECTED)
roller.set_style_text_align(lv.TEXT_ALIGN.LEFT, 0)
roller.align(lv.ALIGN.LEFT_MID, 10, 0)
roller.add_event(event_handler, lv.EVENT.ALL, None)
roller.set_selected(2, lv.ANIM.OFF)

# A roller in the middle with center aligned text, and auto (default) width
roller = lv.roller(lv.scr_act())
roller.set_options(opts, lv.roller.MODE.NORMAL)
roller.set_visible_row_count(3)
roller.add_style(style_sel, lv.PART.SELECTED)
roller.align(lv.ALIGN.CENTER, 0, 0)
roller.add_event(event_handler, lv.EVENT.ALL, None)
roller.set_selected(5, lv.ANIM.OFF)

# A roller on the right with right aligned text, and custom width
roller = lv.roller(lv.scr_act())
roller.set_options(opts, lv.roller.MODE.NORMAL)
roller.set_visible_row_count(4)
roller.set_width(80)
roller.add_style(style_sel, lv.PART.SELECTED)
roller.set_style_text_align(lv.TEXT_ALIGN.RIGHT, 0)
roller.align(lv.ALIGN.RIGHT_MID, -10, 0)
roller.add_event(event_handler, lv.EVENT.ALL, None)
roller.set_selected(8, lv.ANIM.OFF)

add fade mask to roller

#include "../../lv_examples.h"
#if LV_USE_ROLLER && LV_USE_DRAW_MASKS && LV_BUILD_EXAMPLES

static void mask_event_cb(lv_event_t * e)


{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);

static int16_t mask_top_id = -1;


static int16_t mask_bottom_id = -1;

if(code == LV_EVENT_COVER_CHECK) {
lv_event_set_cover_res(e, LV_COVER_RES_MASKED);
(continues on next page)

2.7. Widgets 203


LVGL Documentation 9.0

(continued from previous page)

}
else if(code == LV_EVENT_DRAW_MAIN_BEGIN) {
/* add mask */
const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN);
lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN);
lv_coord_t font_h = lv_font_get_line_height(font);

lv_area_t roller_coords;
lv_obj_get_coords(obj, &roller_coords);

lv_area_t rect_area;
rect_area.x1 = roller_coords.x1;
rect_area.x2 = roller_coords.x2;
rect_area.y1 = roller_coords.y1;
rect_area.y2 = roller_coords.y1 + (lv_obj_get_height(obj) - font_h - line_
,→space) / 2;

lv_draw_mask_fade_param_t * fade_mask_top = lv_malloc(sizeof(lv_draw_mask_


,→fade_param_t));
lv_draw_mask_fade_init(fade_mask_top, &rect_area, LV_OPA_TRANSP, rect_area.y1,
,→ LV_OPA_COVER, rect_area.y2);

mask_top_id = lv_draw_mask_add(fade_mask_top, NULL);

rect_area.y1 = rect_area.y2 + font_h + line_space - 1;


rect_area.y2 = roller_coords.y2;

lv_draw_mask_fade_param_t * fade_mask_bottom = lv_malloc(sizeof(lv_draw_mask_


,→fade_param_t));
lv_draw_mask_fade_init(fade_mask_bottom, &rect_area, LV_OPA_COVER, rect_area.
,→y1, LV_OPA_TRANSP, rect_area.y2);

mask_bottom_id = lv_draw_mask_add(fade_mask_bottom, NULL);

}
else if(code == LV_EVENT_DRAW_POST_END) {
lv_draw_mask_fade_param_t * fade_mask_top = lv_draw_mask_remove_id(mask_top_
,→id);

lv_draw_mask_fade_param_t * fade_mask_bottom = lv_draw_mask_remove_id(mask_


,→bottom_id);

lv_draw_mask_free_param(fade_mask_top);
lv_draw_mask_free_param(fade_mask_bottom);
lv_free(fade_mask_top);
lv_free(fade_mask_bottom);
mask_top_id = -1;
mask_bottom_id = -1;
}
}

/**
* Add a fade mask to roller.
*/
void lv_example_roller_3(void)
{
static lv_style_t style;
lv_style_init(&style);
lv_style_set_bg_color(&style, lv_color_black());
lv_style_set_text_color(&style, lv_color_white());
(continues on next page)

2.7. Widgets 204


LVGL Documentation 9.0

(continued from previous page)


lv_style_set_border_width(&style, 0);
lv_style_set_pad_all(&style, 0);
lv_obj_add_style(lv_scr_act(), &style, 0);

lv_obj_t * roller1 = lv_roller_create(lv_scr_act());


lv_obj_add_style(roller1, &style, 0);
lv_obj_set_style_bg_opa(roller1, LV_OPA_TRANSP, LV_PART_SELECTED);

#if LV_FONT_MONTSERRAT_22
lv_obj_set_style_text_font(roller1, &lv_font_montserrat_22, LV_PART_SELECTED);
#endif

lv_roller_set_options(roller1,
"January\n"
"February\n"
"March\n"
"April\n"
"May\n"
"June\n"
"July\n"
"August\n"
"September\n"
"October\n"
"November\n"
"December",
LV_ROLLER_MODE_NORMAL);

lv_obj_center(roller1);
lv_roller_set_visible_row_count(roller1, 3);
lv_obj_add_event(roller1, mask_event_cb, LV_EVENT_ALL, NULL);
}

#endif

import fs_driver
import sys

class Lv_Roller_3():

def __init__(self):
self.mask_top_id = -1
self.mask_bottom_id = -1

#
# Add a fade mask to roller.
#
style = lv.style_t()
style.init()
style.set_bg_color(lv.color_black())
style.set_text_color(lv.color_white())

lv.scr_act().add_style(style, 0)

roller1 = lv.roller(lv.scr_act())
roller1.add_style(style, 0)
roller1.set_style_border_width(0, 0)
(continues on next page)

2.7. Widgets 205


LVGL Documentation 9.0

(continued from previous page)


roller1.set_style_pad_all(0, 0)
roller1.set_style_bg_opa(lv.OPA.TRANSP, lv.PART.SELECTED)

#if LV_FONT_MONTSERRAT_22
# lv_obj_set_style_text_font(roller1, &lv_font_montserrat_22, LV_PART_
,→SELECTED);

#endif
try:
roller1.set_style_text_font(lv.font_montserrat_22,lv.PART.SELECTED)
except:
fs_drv = lv.fs_drv_t()
fs_driver.fs_register(fs_drv, 'S')
print("montserrat-22 not enabled in lv_conf.h, dynamically loading the␣
,→font")

font_montserrat_22 = lv.font_load("S:" + "../../assets/font/montserrat-22.


,→fnt")

roller1.set_style_text_font(font_montserrat_22,lv.PART.SELECTED)

roller1.set_options("\n".join([
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"]),lv.roller.MODE.NORMAL)

roller1.center()
roller1.set_visible_row_count(3)
roller1.add_event(self.mask_event_cb, lv.EVENT.ALL, None)

def mask_event_cb(self,e):

code = e.get_code()
obj = e.get_target_obj()

if code == lv.EVENT.COVER_CHECK:
e.set_cover_res(lv.COVER_RES.MASKED)

elif code == lv.EVENT.DRAW_MAIN_BEGIN:


# add mask
font = obj.get_style_text_font(lv.PART.MAIN)
line_space = obj.get_style_text_line_space(lv.PART.MAIN)
font_h = font.get_line_height()

roller_coords = lv.area_t()
obj.get_coords(roller_coords)

rect_area = lv.area_t()
rect_area.x1 = roller_coords.x1
rect_area.x2 = roller_coords.x2
rect_area.y1 = roller_coords.y1
(continues on next page)

2.7. Widgets 206


LVGL Documentation 9.0

(continued from previous page)


rect_area.y2 = roller_coords.y1 + (obj.get_height() - font_h - line_
,→space) // 2

fade_mask_top = lv.draw_mask_fade_param_t()
fade_mask_top.init(rect_area, lv.OPA.TRANSP, rect_area.y1, lv.OPA.COVER,␣
,→rect_area.y2)

self.mask_top_id = lv.draw_mask_add(fade_mask_top,None)

rect_area.y1 = rect_area.y2 + font_h + line_space - 1


rect_area.y2 = roller_coords.y2

fade_mask_bottom = lv.draw_mask_fade_param_t()
fade_mask_bottom.init(rect_area, lv.OPA.COVER, rect_area.y1, lv.OPA.
,→TRANSP, rect_area.y2)

self.mask_bottom_id = lv.draw_mask_add(fade_mask_bottom, None)

elif code == lv.EVENT.DRAW_POST_END:


fade_mask_top = lv.draw_mask_remove_id(self.mask_top_id)
fade_mask_bottom = lv.draw_mask_remove_id(self.mask_bottom_id)
# Remove the masks
lv.draw_mask_remove_id(self.mask_top_id)
lv.draw_mask_remove_id(self.mask_bottom_id)
self.mask_top_id = -1
self.mask_bottom_id = -1

roller3 = Lv_Roller_3()

2.7.23 Slider

Simple Slider

#include "../../lv_examples.h"
#if LV_USE_SLIDER && LV_BUILD_EXAMPLES

static void slider_event_cb(lv_event_t * e);


static lv_obj_t * slider_label;

/**
* A default slider with a label displaying the current value
*/
void lv_example_slider_1(void)
{
/*Create a slider in the center of the display*/
lv_obj_t * slider = lv_slider_create(lv_scr_act());
lv_obj_center(slider);
lv_obj_add_event(slider, slider_event_cb, LV_EVENT_VALUE_CHANGED, NULL);

/*Create a label below the slider*/


slider_label = lv_label_create(lv_scr_act());
lv_label_set_text(slider_label, "0%");

lv_obj_align_to(slider_label, slider, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);


}

(continues on next page)

2.7. Widgets 207


LVGL Documentation 9.0

(continued from previous page)


static void slider_event_cb(lv_event_t * e)
{
lv_obj_t * slider = lv_event_get_target(e);
char buf[8];
lv_snprintf(buf, sizeof(buf), "%d%%", (int)lv_slider_get_value(slider));
lv_label_set_text(slider_label, buf);
lv_obj_align_to(slider_label, slider, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
}

#endif

#
# A default slider with a label displaying the current value
#
def slider_event_cb(e):

slider = e.get_target_obj()
slider_label.set_text("{:d}%".format(slider.get_value()))
slider_label.align_to(slider, lv.ALIGN.OUT_BOTTOM_MID, 0, 10)

# Create a slider in the center of the display


slider = lv.slider(lv.scr_act())
slider.center()
slider.add_event(slider_event_cb, lv.EVENT.VALUE_CHANGED, None)

# Create a label below the slider


slider_label = lv.label(lv.scr_act())
slider_label.set_text("0%")

slider_label.align_to(slider, lv.ALIGN.OUT_BOTTOM_MID, 0, 10)

Slider with custom style

#include "../../lv_examples.h"
#if LV_USE_SLIDER && LV_BUILD_EXAMPLES

/**
* Show how to style a slider.
*/
void lv_example_slider_2(void)
{
/*Create a transition*/
static const lv_style_prop_t props[] = {LV_STYLE_BG_COLOR, 0};
static lv_style_transition_dsc_t transition_dsc;
lv_style_transition_dsc_init(&transition_dsc, props, lv_anim_path_linear, 300, 0,␣
,→NULL);

static lv_style_t style_main;


static lv_style_t style_indicator;
static lv_style_t style_knob;
static lv_style_t style_pressed_color;
(continues on next page)

2.7. Widgets 208


LVGL Documentation 9.0

(continued from previous page)


lv_style_init(&style_main);
lv_style_set_bg_opa(&style_main, LV_OPA_COVER);
lv_style_set_bg_color(&style_main, lv_color_hex3(0xbbb));
lv_style_set_radius(&style_main, LV_RADIUS_CIRCLE);
lv_style_set_pad_ver(&style_main, -2); /*Makes the indicator larger*/

lv_style_init(&style_indicator);
lv_style_set_bg_opa(&style_indicator, LV_OPA_COVER);
lv_style_set_bg_color(&style_indicator, lv_palette_main(LV_PALETTE_CYAN));
lv_style_set_radius(&style_indicator, LV_RADIUS_CIRCLE);
lv_style_set_transition(&style_indicator, &transition_dsc);

lv_style_init(&style_knob);
lv_style_set_bg_opa(&style_knob, LV_OPA_COVER);
lv_style_set_bg_color(&style_knob, lv_palette_main(LV_PALETTE_CYAN));
lv_style_set_border_color(&style_knob, lv_palette_darken(LV_PALETTE_CYAN, 3));
lv_style_set_border_width(&style_knob, 2);
lv_style_set_radius(&style_knob, LV_RADIUS_CIRCLE);
lv_style_set_pad_all(&style_knob, 6); /*Makes the knob larger*/
lv_style_set_transition(&style_knob, &transition_dsc);

lv_style_init(&style_pressed_color);
lv_style_set_bg_color(&style_pressed_color, lv_palette_darken(LV_PALETTE_CYAN,␣
,→2));

/*Create a slider and add the style*/


lv_obj_t * slider = lv_slider_create(lv_scr_act());
lv_obj_remove_style_all(slider); /*Remove the styles coming from the␣
,→theme*/

lv_obj_add_style(slider, &style_main, LV_PART_MAIN);


lv_obj_add_style(slider, &style_indicator, LV_PART_INDICATOR);
lv_obj_add_style(slider, &style_pressed_color, LV_PART_INDICATOR | LV_STATE_
,→PRESSED);

lv_obj_add_style(slider, &style_knob, LV_PART_KNOB);


lv_obj_add_style(slider, &style_pressed_color, LV_PART_KNOB | LV_STATE_PRESSED);

lv_obj_center(slider);
}

#endif

#
# Show how to style a slider.
#
# Create a transition
props = [lv.STYLE.BG_COLOR, 0]
transition_dsc = lv.style_transition_dsc_t()
transition_dsc.init(props, lv.anim_t.path_linear, 300, 0, None)

style_main = lv.style_t()
style_indicator = lv.style_t()
style_knob = lv.style_t()
style_pressed_color = lv.style_t()
style_main.init()
style_main.set_bg_opa(lv.OPA.COVER)
(continues on next page)

2.7. Widgets 209


LVGL Documentation 9.0

(continued from previous page)


style_main.set_bg_color(lv.color_hex3(0xbbb))
style_main.set_radius(lv.RADIUS_CIRCLE)
style_main.set_pad_ver(-2) # Makes the indicator larger

style_indicator.init()
style_indicator.set_bg_opa(lv.OPA.COVER)
style_indicator.set_bg_color(lv.palette_main(lv.PALETTE.CYAN))
style_indicator.set_radius(lv.RADIUS_CIRCLE)
style_indicator.set_transition(transition_dsc)

style_knob.init()
style_knob.set_bg_opa(lv.OPA.COVER)
style_knob.set_bg_color(lv.palette_main(lv.PALETTE.CYAN))
style_knob.set_border_color(lv.palette_darken(lv.PALETTE.CYAN, 3))
style_knob.set_border_width(2)
style_knob.set_radius(lv.RADIUS_CIRCLE)
style_knob.set_pad_all(6) # Makes the knob larger
style_knob.set_transition(transition_dsc)

style_pressed_color.init()
style_pressed_color.set_bg_color(lv.palette_darken(lv.PALETTE.CYAN, 2))

# Create a slider and add the style


slider = lv.slider(lv.scr_act())
slider.remove_style_all() # Remove the styles coming from the theme

slider.add_style(style_main, lv.PART.MAIN)
slider.add_style(style_indicator, lv.PART.INDICATOR)
slider.add_style(style_pressed_color, lv.PART.INDICATOR | lv.STATE.PRESSED)
slider.add_style(style_knob, lv.PART.KNOB)
slider.add_style(style_pressed_color, lv.PART.KNOB | lv.STATE.PRESSED)

slider.center()

Slider with extended drawer

#include "../../lv_examples.h"
#if LV_USE_SLIDER && LV_BUILD_EXAMPLES

static void slider_event_cb(lv_event_t * e);

/**
* Show the current value when the slider is pressed by extending the drawer
*
*/
void lv_example_slider_3(void)
{
/*Create a slider in the center of the display*/
lv_obj_t * slider;
slider = lv_slider_create(lv_scr_act());
lv_obj_center(slider);

lv_slider_set_mode(slider, LV_SLIDER_MODE_RANGE);
lv_slider_set_value(slider, 70, LV_ANIM_OFF);
(continues on next page)

2.7. Widgets 210


LVGL Documentation 9.0

(continued from previous page)


lv_slider_set_left_value(slider, 20, LV_ANIM_OFF);

lv_obj_add_event(slider, slider_event_cb, LV_EVENT_ALL, NULL);


lv_obj_refresh_ext_draw_size(slider);
}

static void slider_event_cb(lv_event_t * e)


{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);

/*Provide some extra space for the value*/


if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
lv_event_set_ext_draw_size(e, 50);
}
else if(code == LV_EVENT_DRAW_PART_END) {
lv_obj_draw_part_dsc_t * dsc = lv_event_get_draw_part_dsc(e);
if(dsc->part == LV_PART_INDICATOR) {
char buf[16];
lv_snprintf(buf, sizeof(buf), "%d - %d", (int)lv_slider_get_left_
,→value(obj), (int)lv_slider_get_value(obj));

lv_point_t label_size;
lv_txt_get_size(&label_size, buf, LV_FONT_DEFAULT, 0, 0, LV_COORD_MAX, 0);
lv_area_t label_area;
label_area.x1 = dsc->draw_area->x1 + lv_area_get_width(dsc->draw_area) /␣
,→2 - label_size.x / 2;

label_area.x2 = label_area.x1 + label_size.x;


label_area.y2 = dsc->draw_area->y1 - 10;
label_area.y1 = label_area.y2 - label_size.y;

lv_draw_label_dsc_t label_draw_dsc;
lv_draw_label_dsc_init(&label_draw_dsc);
label_draw_dsc.color = lv_color_hex3(0x888);
lv_draw_label(dsc->draw_ctx, &label_draw_dsc, &label_area, buf, NULL);
}
}
}

#endif

def slider_event_cb(e):
code = e.get_code()
obj = e.get_target_obj()

# Provide some extra space for the value


if code == lv.EVENT.REFR_EXT_DRAW_SIZE:
e.set_ext_draw_size(50)

elif code == lv.EVENT.DRAW_PART_END:


# print("DRAW_PART_END")
dsc = lv.obj_draw_part_dsc_t.__cast__(e.get_param())
# print(dsc)
if dsc.part == lv.PART.INDICATOR:
label_text = "{:d} - {:d}".format(obj.get_left_value(),slider.get_value())
label_size = lv.point_t()
(continues on next page)

2.7. Widgets 211


LVGL Documentation 9.0

(continued from previous page)


lv.txt_get_size(label_size, label_text, lv.font_default(), 0, 0, lv.COORD.
,→ MAX, 0)
# print(label_size.x,label_size.y)
label_area = lv.area_t()
label_area.x1 = dsc.draw_area.x1 + dsc.draw_area.get_width() // 2 - label_
,→size.x // 2

label_area.x2 = label_area.x1 + label_size.x


label_area.y2 = dsc.draw_area.y1 - 10
label_area.y1 = label_area.y2 - label_size.y

label_draw_dsc = lv.draw_label_dsc_t()
label_draw_dsc.init()

dsc.draw_ctx.label(label_draw_dsc, label_area, label_text, None)


#
# Show the current value when the slider if pressed by extending the drawer
#
#
#Create a slider in the center of the display

slider = lv.slider(lv.scr_act())
slider.center()

slider.set_mode(lv.slider.MODE.RANGE)
slider.set_value(70, lv.ANIM.OFF)
slider.set_left_value(20, lv.ANIM.OFF)

slider.add_event(slider_event_cb, lv.EVENT.ALL, None)


slider.refresh_ext_draw_size()

2.7.24 Span

Span with custom styles

#include "../../lv_examples.h"
#if LV_USE_SPAN && LV_BUILD_EXAMPLES

/**
* Create span.
*/
void lv_example_span_1(void)
{
static lv_style_t style;
lv_style_init(&style);
lv_style_set_border_width(&style, 1);
lv_style_set_border_color(&style, lv_palette_main(LV_PALETTE_ORANGE));
lv_style_set_pad_all(&style, 2);

lv_obj_t * spans = lv_spangroup_create(lv_scr_act());


lv_obj_set_width(spans, 300);
lv_obj_set_height(spans, 300);
lv_obj_center(spans);
lv_obj_add_style(spans, &style, 0);
(continues on next page)

2.7. Widgets 212


LVGL Documentation 9.0

(continued from previous page)

lv_spangroup_set_align(spans, LV_TEXT_ALIGN_LEFT);
lv_spangroup_set_overflow(spans, LV_SPAN_OVERFLOW_CLIP);
lv_spangroup_set_indent(spans, 20);
lv_spangroup_set_mode(spans, LV_SPAN_MODE_BREAK);

lv_span_t * span = lv_spangroup_new_span(spans);


lv_span_set_text(span, "China is a beautiful country.");
lv_style_set_text_color(&span->style, lv_palette_main(LV_PALETTE_RED));
lv_style_set_text_decor(&span->style, LV_TEXT_DECOR_UNDERLINE);
lv_style_set_text_opa(&span->style, LV_OPA_50);

span = lv_spangroup_new_span(spans);
lv_span_set_text_static(span, "good good study, day day up.");
#if LV_FONT_MONTSERRAT_24
lv_style_set_text_font(&span->style, &lv_font_montserrat_24);
#endif
lv_style_set_text_color(&span->style, lv_palette_main(LV_PALETTE_GREEN));

span = lv_spangroup_new_span(spans);
lv_span_set_text_static(span, "LVGL is an open-source graphics library.");
lv_style_set_text_color(&span->style, lv_palette_main(LV_PALETTE_BLUE));

span = lv_spangroup_new_span(spans);
lv_span_set_text_static(span, "the boy no name.");
lv_style_set_text_color(&span->style, lv_palette_main(LV_PALETTE_GREEN));
#if LV_FONT_MONTSERRAT_20
lv_style_set_text_font(&span->style, &lv_font_montserrat_20);
#endif
lv_style_set_text_decor(&span->style, LV_TEXT_DECOR_UNDERLINE);

span = lv_spangroup_new_span(spans);
lv_span_set_text(span, "I have a dream that hope to come true.");
lv_style_set_text_decor(&span->style, LV_TEXT_DECOR_STRIKETHROUGH);

lv_spangroup_refr_mode(spans);
}

#endif

#
# Create span
#
style = lv.style_t()
style.init()
style.set_border_width(1)
style.set_border_color(lv.palette_main(lv.PALETTE.ORANGE))
style.set_pad_all(2)

spans = lv.spangroup(lv.scr_act())
spans.set_width(300)
spans.set_height(300)
spans.center()
spans.add_style(style, 0)

spans.set_align(lv.TEXT_ALIGN.LEFT)
(continues on next page)

2.7. Widgets 213


LVGL Documentation 9.0

(continued from previous page)


spans.set_overflow(lv.SPAN_OVERFLOW.CLIP)
spans.set_indent(20)
spans.set_mode(lv.SPAN_MODE.BREAK)

span = spans.new_span()
span.set_text("china is a beautiful country.")
span.style.set_text_color(lv.palette_main(lv.PALETTE.RED))
span.style.set_text_decor(lv.TEXT_DECOR.STRIKETHROUGH | lv.TEXT_DECOR.UNDERLINE)
span.style.set_text_opa(lv.OPA._30)

span = spans.new_span()
span.set_text_static("good good study, day day up.")
#if LV_FONT_MONTSERRAT_24
# lv_style_set_text_font(&span->style, &lv_font_montserrat_24);
#endif
span.style.set_text_color(lv.palette_main(lv.PALETTE.GREEN))

span = spans.new_span()
span.set_text_static("LVGL is an open-source graphics library.")
span.style.set_text_color(lv.palette_main(lv.PALETTE.BLUE))

span = spans.new_span()
span.set_text_static("the boy no name.")
span.style.set_text_color(lv.palette_main(lv.PALETTE.GREEN))
#if LV_FONT_MONTSERRAT_20
# lv_style_set_text_font(&span->style, &lv_font_montserrat_20);
#endif
span.style.set_text_decor(lv.TEXT_DECOR.UNDERLINE)

span = spans.new_span()
span.set_text("I have a dream that hope to come true.")

spans.refr_mode()

# lv_span_del(spans, span);
# lv_obj_del(spans);

2.7.25 Spinbox

Simple Spinbox

#include "../../lv_examples.h"
#if LV_USE_SPINBOX && LV_BUILD_EXAMPLES

static lv_obj_t * spinbox;

static void lv_spinbox_increment_event_cb(lv_event_t * e)


{
lv_event_code_t code = lv_event_get_code(e);
if(code == LV_EVENT_SHORT_CLICKED || code == LV_EVENT_LONG_PRESSED_REPEAT) {
lv_spinbox_increment(spinbox);
}
(continues on next page)

2.7. Widgets 214


LVGL Documentation 9.0

(continued from previous page)


}

static void lv_spinbox_decrement_event_cb(lv_event_t * e)


{
lv_event_code_t code = lv_event_get_code(e);
if(code == LV_EVENT_SHORT_CLICKED || code == LV_EVENT_LONG_PRESSED_REPEAT) {
lv_spinbox_decrement(spinbox);
}
}

void lv_example_spinbox_1(void)
{
spinbox = lv_spinbox_create(lv_scr_act());
lv_spinbox_set_range(spinbox, -1000, 25000);
lv_spinbox_set_digit_format(spinbox, 5, 2);
lv_spinbox_step_prev(spinbox);
lv_obj_set_width(spinbox, 100);
lv_obj_center(spinbox);

lv_coord_t h = lv_obj_get_height(spinbox);

lv_obj_t * btn = lv_btn_create(lv_scr_act());


lv_obj_set_size(btn, h, h);
lv_obj_align_to(btn, spinbox, LV_ALIGN_OUT_RIGHT_MID, 5, 0);
lv_obj_set_style_bg_img_src(btn, LV_SYMBOL_PLUS, 0);
lv_obj_add_event(btn, lv_spinbox_increment_event_cb, LV_EVENT_ALL, NULL);

btn = lv_btn_create(lv_scr_act());
lv_obj_set_size(btn, h, h);
lv_obj_align_to(btn, spinbox, LV_ALIGN_OUT_LEFT_MID, -5, 0);
lv_obj_set_style_bg_img_src(btn, LV_SYMBOL_MINUS, 0);
lv_obj_add_event(btn, lv_spinbox_decrement_event_cb, LV_EVENT_ALL, NULL);
}

#endif

def increment_event_cb(e):
code = e.get_code()
if code == lv.EVENT.SHORT_CLICKED or code == lv.EVENT.LONG_PRESSED_REPEAT:
spinbox.increment()

def decrement_event_cb(e):
code = e.get_code()
if code == lv.EVENT.SHORT_CLICKED or code == lv.EVENT.LONG_PRESSED_REPEAT:
spinbox.decrement()

spinbox = lv.spinbox(lv.scr_act())
spinbox.set_range(-1000, 25000)
spinbox.set_digit_format(5, 2)
spinbox.step_prev()
spinbox.set_width(100)
spinbox.center()

h = spinbox.get_height()

(continues on next page)

2.7. Widgets 215


LVGL Documentation 9.0

(continued from previous page)


btn = lv.btn(lv.scr_act())
btn.set_size(h, h)
btn.align_to(spinbox, lv.ALIGN.OUT_RIGHT_MID, 5, 0)
btn.set_style_bg_img_src(lv.SYMBOL.PLUS, 0)
btn.add_event(increment_event_cb, lv.EVENT.ALL, None)

btn = lv.btn(lv.scr_act())
btn.set_size(h, h)
btn.align_to(spinbox, lv.ALIGN.OUT_LEFT_MID, -5, 0)
btn.set_style_bg_img_src(lv.SYMBOL.MINUS, 0)
btn.add_event(decrement_event_cb, lv.EVENT.ALL, None)

2.7.26 Spinner

Simple spinner

#include "../../lv_examples.h"
#if LV_USE_SPINNER && LV_BUILD_EXAMPLES

void lv_example_spinner_1(void)
{
/*Create a spinner*/
lv_obj_t * spinner = lv_spinner_create(lv_scr_act(), 1000, 60);
lv_obj_set_size(spinner, 100, 100);
lv_obj_center(spinner);
}

#endif

# Create a spinner
spinner = lv.spinner(lv.scr_act(), 1000, 60)
spinner.set_size(100, 100)
spinner.center()

2.7.27 Switch

Simple Switch

#include "../../lv_examples.h"
#if LV_USE_SWITCH && LV_BUILD_EXAMPLES

static void event_handler(lv_event_t * e)


{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_VALUE_CHANGED) {
LV_UNUSED(obj);
LV_LOG_USER("State: %s\n", lv_obj_has_state(obj, LV_STATE_CHECKED) ? "On" :
,→"Off");

(continues on next page)

2.7. Widgets 216


LVGL Documentation 9.0

(continued from previous page)


}
}

void lv_example_switch_1(void)
{
lv_obj_set_flex_flow(lv_scr_act(), LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(lv_scr_act(), LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER,␣
,→LV_FLEX_ALIGN_CENTER);

lv_obj_t * sw;

sw = lv_switch_create(lv_scr_act());
lv_obj_add_event(sw, event_handler, LV_EVENT_ALL, NULL);

sw = lv_switch_create(lv_scr_act());
lv_obj_add_state(sw, LV_STATE_CHECKED);
lv_obj_add_event(sw, event_handler, LV_EVENT_ALL, NULL);

sw = lv_switch_create(lv_scr_act());
lv_obj_add_state(sw, LV_STATE_DISABLED);
lv_obj_add_event(sw, event_handler, LV_EVENT_ALL, NULL);

sw = lv_switch_create(lv_scr_act());
lv_obj_add_state(sw, LV_STATE_CHECKED | LV_STATE_DISABLED);
lv_obj_add_event(sw, event_handler, LV_EVENT_ALL, NULL);
}

#endif

def event_handler(e):
code = e.get_code()
obj = e.get_target_obj()
if code == lv.EVENT.VALUE_CHANGED:
if obj.has_state(lv.STATE.CHECKED):
print("State: on")
else:
print("State: off")

lv.scr_act().set_flex_flow(lv.FLEX_FLOW.COLUMN)
lv.scr_act().set_flex_align(lv.FLEX_ALIGN.CENTER, lv.FLEX_ALIGN.CENTER, lv.FLEX_ALIGN.
,→CENTER)

sw = lv.switch(lv.scr_act())
sw.add_event(event_handler,lv.EVENT.ALL, None)

sw = lv.switch(lv.scr_act())
sw.add_state(lv.STATE.CHECKED)
sw.add_event(event_handler, lv.EVENT.ALL, None)

sw = lv.switch(lv.scr_act())
sw.add_state(lv.STATE.DISABLED)
sw.add_event(event_handler, lv.EVENT.ALL, None)

sw = lv.switch(lv.scr_act())
sw.add_state(lv.STATE.CHECKED | lv.STATE.DISABLED)
(continues on next page)

2.7. Widgets 217


LVGL Documentation 9.0

(continued from previous page)


sw.add_event(event_handler, lv.EVENT.ALL, None)

2.7.28 Table

Simple table

#include "../../lv_examples.h"
#if LV_USE_TABLE && LV_BUILD_EXAMPLES

static void draw_part_event_cb(lv_event_t * e)


{
lv_obj_t * obj = lv_event_get_target(e);
lv_obj_draw_part_dsc_t * dsc = lv_event_get_draw_part_dsc(e);
/*If the cells are drawn...*/
if(dsc->part == LV_PART_ITEMS) {
uint32_t row = dsc->id / lv_table_get_col_cnt(obj);
uint32_t col = dsc->id - row * lv_table_get_col_cnt(obj);

/*Make the texts in the first cell center aligned*/


if(row == 0) {
dsc->label_dsc->align = LV_TEXT_ALIGN_CENTER;
dsc->rect_dsc->bg_color = lv_color_mix(lv_palette_main(LV_PALETTE_BLUE),␣
,→dsc->rect_dsc->bg_color, LV_OPA_20);

dsc->rect_dsc->bg_opa = LV_OPA_COVER;
}
/*In the first column align the texts to the right*/
else if(col == 0) {
dsc->label_dsc->align = LV_TEXT_ALIGN_RIGHT;
}

/*MAke every 2nd row grayish*/


if((row != 0 && row % 2) == 0) {
dsc->rect_dsc->bg_color = lv_color_mix(lv_palette_main(LV_PALETTE_GREY),␣
,→dsc->rect_dsc->bg_color, LV_OPA_10);

dsc->rect_dsc->bg_opa = LV_OPA_COVER;
}
}
}

void lv_example_table_1(void)
{
lv_obj_t * table = lv_table_create(lv_scr_act());

/*Fill the first column*/


lv_table_set_cell_value(table, 0, 0, "Name");
lv_table_set_cell_value(table, 1, 0, "Apple");
lv_table_set_cell_value(table, 2, 0, "Banana");
lv_table_set_cell_value(table, 3, 0, "Lemon");
lv_table_set_cell_value(table, 4, 0, "Grape");
lv_table_set_cell_value(table, 5, 0, "Melon");
lv_table_set_cell_value(table, 6, 0, "Peach");
lv_table_set_cell_value(table, 7, 0, "Nuts");
(continues on next page)

2.7. Widgets 218


LVGL Documentation 9.0

(continued from previous page)

/*Fill the second column*/


lv_table_set_cell_value(table, 0, 1, "Price");
lv_table_set_cell_value(table, 1, 1, "$7");
lv_table_set_cell_value(table, 2, 1, "$4");
lv_table_set_cell_value(table, 3, 1, "$6");
lv_table_set_cell_value(table, 4, 1, "$2");
lv_table_set_cell_value(table, 5, 1, "$5");
lv_table_set_cell_value(table, 6, 1, "$1");
lv_table_set_cell_value(table, 7, 1, "$9");

/*Set a smaller height to the table. It'll make it scrollable*/


lv_obj_set_height(table, 200);
lv_obj_center(table);

/*Add an event callback to to apply some custom drawing*/


lv_obj_add_event(table, draw_part_event_cb, LV_EVENT_DRAW_PART_BEGIN, NULL);
}

#endif

def draw_part_event_cb(e):
obj = e.get_target_obj()
dsc = lv.obj_draw_part_dsc_t.__cast__(e.get_param())
# If the cells are drawn../
if dsc.part == lv.PART.ITEMS:
row = dsc.id // obj.get_col_cnt()
col = dsc.id - row * obj.get_col_cnt()

# Make the texts in the first cell center aligned


if row == 0:
dsc.label_dsc.align = lv.TEXT_ALIGN.CENTER
dsc.rect_dsc.bg_color = lv.palette_main(lv.PALETTE.BLUE).color_mix(dsc.
,→rect_dsc.bg_color, lv.OPA._20)

dsc.rect_dsc.bg_opa = lv.OPA.COVER

# In the first column align the texts to the right


elif col == 0:
dsc.label_dsc.flag = lv.TEXT_ALIGN.RIGHT

# Make every 2nd row grayish


if row != 0 and (row % 2) == 0:
dsc.rect_dsc.bg_color = lv.palette_main(lv.PALETTE.GREY).color_mix(dsc.
,→rect_dsc.bg_color, lv.OPA._10)

dsc.rect_dsc.bg_opa = lv.OPA.COVER

table = lv.table(lv.scr_act())

# Fill the first column


table.set_cell_value(0, 0, "Name")
table.set_cell_value(1, 0, "Apple")
table.set_cell_value(2, 0, "Banana")
table.set_cell_value(3, 0, "Lemon")
table.set_cell_value(4, 0, "Grape")
table.set_cell_value(5, 0, "Melon")
(continues on next page)

2.7. Widgets 219


LVGL Documentation 9.0

(continued from previous page)


table.set_cell_value(6, 0, "Peach")
table.set_cell_value(7, 0, "Nuts")

# Fill the second column


table.set_cell_value(0, 1, "Price")
table.set_cell_value(1, 1, "$7")
table.set_cell_value(2, 1, "$4")
table.set_cell_value(3, 1, "$6")
table.set_cell_value(4, 1, "$2")
table.set_cell_value(5, 1, "$5")
table.set_cell_value(6, 1, "$1")
table.set_cell_value(7, 1, "$9")

# Set a smaller height to the table. It'll make it scrollable


table.set_height(200)
table.center()

# Add an event callback to apply some custom drawing


table.add_event(draw_part_event_cb, lv.EVENT.DRAW_PART_BEGIN, None)

Lightweighted list from table

#include "../../lv_examples.h"
#if LV_USE_TABLE && LV_BUILD_EXAMPLES

#define ITEM_CNT 200

static void draw_event_cb(lv_event_t * e)


{
lv_obj_t * obj = lv_event_get_target(e);
lv_obj_draw_part_dsc_t * dsc = lv_event_get_draw_part_dsc(e);
/*If the cells are drawn...*/
if(dsc->part == LV_PART_ITEMS) {
bool chk = lv_table_has_cell_ctrl(obj, dsc->id, 0, LV_TABLE_CELL_CTRL_CUSTOM_
,→1);

lv_draw_rect_dsc_t rect_dsc;
lv_draw_rect_dsc_init(&rect_dsc);
rect_dsc.bg_color = chk ? lv_theme_get_color_primary(obj) : lv_palette_
,→lighten(LV_PALETTE_GREY, 2);

rect_dsc.radius = LV_RADIUS_CIRCLE;

lv_area_t sw_area;
sw_area.x1 = dsc->draw_area->x2 - 50;
sw_area.x2 = sw_area.x1 + 40;
sw_area.y1 = dsc->draw_area->y1 + lv_area_get_height(dsc->draw_area) / 2 - 10;
sw_area.y2 = sw_area.y1 + 20;
lv_draw_rect(dsc->draw_ctx, &rect_dsc, &sw_area);

rect_dsc.bg_color = lv_color_white();
if(chk) {
sw_area.x2 -= 2;
sw_area.x1 = sw_area.x2 - 16;
}
(continues on next page)

2.7. Widgets 220


LVGL Documentation 9.0

(continued from previous page)


else {
sw_area.x1 += 2;
sw_area.x2 = sw_area.x1 + 16;
}
sw_area.y1 += 2;
sw_area.y2 -= 2;
lv_draw_rect(dsc->draw_ctx, &rect_dsc, &sw_area);
}
}

static void change_event_cb(lv_event_t * e)


{
lv_obj_t * obj = lv_event_get_target(e);
uint16_t col;
uint16_t row;
lv_table_get_selected_cell(obj, &row, &col);
bool chk = lv_table_has_cell_ctrl(obj, row, 0, LV_TABLE_CELL_CTRL_CUSTOM_1);
if(chk) lv_table_clear_cell_ctrl(obj, row, 0, LV_TABLE_CELL_CTRL_CUSTOM_1);
else lv_table_add_cell_ctrl(obj, row, 0, LV_TABLE_CELL_CTRL_CUSTOM_1);
}

/**
* A very light-weighted list created from table
*/
void lv_example_table_2(void)
{
/*Measure memory usage*/
lv_mem_monitor_t mon1;
lv_mem_monitor(&mon1);

uint32_t t = lv_tick_get();

lv_obj_t * table = lv_table_create(lv_scr_act());

/*Set a smaller height to the table. It'll make it scrollable*/


lv_obj_set_size(table, LV_SIZE_CONTENT, 200);

lv_table_set_col_width(table, 0, 150);
lv_table_set_row_cnt(table, ITEM_CNT); /*Not required but avoids a lot of memory␣
,→reallocation lv_table_set_set_value*/

lv_table_set_col_cnt(table, 1);

/*Don't make the cell pressed, we will draw something different in the event*/
lv_obj_remove_style(table, NULL, LV_PART_ITEMS | LV_STATE_PRESSED);

uint32_t i;
for(i = 0; i < ITEM_CNT; i++) {
lv_table_set_cell_value_fmt(table, i, 0, "Item %"LV_PRIu32, i + 1);
}

lv_obj_align(table, LV_ALIGN_CENTER, 0, -20);

/*Add an event callback to to apply some custom drawing*/


lv_obj_add_event(table, draw_event_cb, LV_EVENT_DRAW_PART_END, NULL);
lv_obj_add_event(table, change_event_cb, LV_EVENT_VALUE_CHANGED, NULL);

(continues on next page)

2.7. Widgets 221


LVGL Documentation 9.0

(continued from previous page)


lv_mem_monitor_t mon2;
lv_mem_monitor(&mon2);

uint32_t mem_used = mon1.free_size - mon2.free_size;

uint32_t elaps = lv_tick_elaps(t);

lv_obj_t * label = lv_label_create(lv_scr_act());


lv_label_set_text_fmt(label, "%"LV_PRIu32" items were created in %"LV_PRIu32" ms\n
,→ "
"using %"LV_PRIu32" bytes of memory",
(uint32_t)ITEM_CNT, elaps, mem_used);

lv_obj_align(label, LV_ALIGN_BOTTOM_MID, 0, -10);

#endif

from utime import ticks_ms


import gc

ITEM_CNT = 200

def draw_event_cb(e):
obj = e.get_target_obj()
dsc = lv.obj_draw_part_dsc_t.__cast__(e.get_param())
# If the cells are drawn...
if dsc.part == lv.PART.ITEMS:
chk = obj.has_cell_ctrl(dsc.id, 0, lv.table.CELL_CTRL.CUSTOM_1)

rect_dsc = lv.draw_rect_dsc_t()
rect_dsc.init()

if chk:
rect_dsc.bg_color = lv.theme_get_color_primary(obj)
else:
rect_dsc.bg_color = lv.palette_lighten(lv.PALETTE.GREY, 2)

rect_dsc.radius = lv.RADIUS_CIRCLE

sw_area = lv.area_t()
sw_area.x1 = dsc.draw_area.x2 - 50
sw_area.x2 = sw_area.x1 + 40
sw_area.y1 = dsc.draw_area.y1 + dsc.draw_area.get_height() // 2 - 10
sw_area.y2 = sw_area.y1 + 20
dsc.draw_ctx.rect(rect_dsc, sw_area)

rect_dsc.bg_color = lv.color_white()

if chk:
sw_area.x2 -= 2
sw_area.x1 = sw_area.x2 - 16
else:
sw_area.x1 += 2
sw_area.x2 = sw_area.x1 + 16
(continues on next page)

2.7. Widgets 222


LVGL Documentation 9.0

(continued from previous page)


sw_area.y1 += 2
sw_area.y2 -= 2
dsc.draw_ctx.rect(rect_dsc, sw_area)

def change_event_cb(e):
obj = e.get_target_obj()
row = lv.C_Pointer()
col = lv.C_Pointer()
table.get_selected_cell(row, col)
# print("row: ",row.uint_val)

chk = table.has_cell_ctrl(row.uint_val, 0, lv.table.CELL_CTRL.CUSTOM_1)


if chk:
table.clear_cell_ctrl(row.uint_val, 0, lv.table.CELL_CTRL.CUSTOM_1)
else:
table.add_cell_ctrl(row.uint_val, 0, lv.table.CELL_CTRL.CUSTOM_1)

#
# A very light-weighted list created from table
#

# Measure memory usage


gc.enable()
gc.collect()
mem_free = gc.mem_free()
print("mem_free: ", mem_free)
t = ticks_ms()
print("ticks: ", t)
table = lv.table(lv.scr_act())

# Set a smaller height to the table. It'll make it scrollable


table.set_size(150, 200)

table.set_col_width(0, 150)
table.set_row_cnt(ITEM_CNT) # Not required but avoids a lot of memory reallocation␣
,→lv_table_set_set_value

table.set_col_cnt(1)

# Don't make the cell pressed, we will draw something different in the event
table.remove_style(None, lv.PART.ITEMS | lv.STATE.PRESSED)

for i in range(ITEM_CNT):
table.set_cell_value(i, 0, "Item " + str(i+1))

table.align(lv.ALIGN.CENTER, 0, -20)

# Add an event callback to apply some custom drawing


table.add_event(draw_event_cb, lv.EVENT.DRAW_PART_END, None)
table.add_event(change_event_cb, lv.EVENT.VALUE_CHANGED, None)

gc.collect()
mem_used = mem_free - gc.mem_free()
elaps = ticks_ms()-t

label = lv.label(lv.scr_act())
label.set_text(str(ITEM_CNT) + " items were created in " + str(elaps) + " ms\n using
,→" + str(mem_used) + " bytes of memory")
(continues on next page)

2.7. Widgets 223


LVGL Documentation 9.0

(continued from previous page)


#label.set_text(str(ITEM_CNT) + " items were created in " + str(elaps) + " ms")

label.align(lv.ALIGN.BOTTOM_MID, 0, -10)

2.7.29 Tabview

Simple Tabview

#include "../../lv_examples.h"
#if LV_USE_TABVIEW && LV_BUILD_EXAMPLES

void lv_example_tabview_1(void)
{
/*Create a Tab view object*/
lv_obj_t * tabview;
tabview = lv_tabview_create(lv_scr_act(), LV_DIR_TOP, 50);

/*Add 3 tabs (the tabs are page (lv_page) and can be scrolled*/
lv_obj_t * tab1 = lv_tabview_add_tab(tabview, "Tab 1");
lv_obj_t * tab2 = lv_tabview_add_tab(tabview, "Tab 2");
lv_obj_t * tab3 = lv_tabview_add_tab(tabview, "Tab 3");

/*Add content to the tabs*/


lv_obj_t * label = lv_label_create(tab1);
lv_label_set_text(label, "This the first tab\n\n"
"If the content\n"
"of a tab\n"
"becomes too\n"
"longer\n"
"than the\n"
"container\n"
"then it\n"
"automatically\n"
"becomes\n"
"scrollable.\n"
"\n"
"\n"
"\n"
"Can you see it?");

label = lv_label_create(tab2);
lv_label_set_text(label, "Second tab");

label = lv_label_create(tab3);
lv_label_set_text(label, "Third tab");

lv_obj_scroll_to_view_recursive(label, LV_ANIM_ON);

}
#endif

# Create a Tab view object


tabview = lv.tabview(lv.scr_act(), lv.DIR.TOP, 50)

(continues on next page)

2.7. Widgets 224


LVGL Documentation 9.0

(continued from previous page)


# Add 3 tabs (the tabs are page (lv_page) and can be scrolled
tab1 = tabview.add_tab("Tab 1")
tab2 = tabview.add_tab("Tab 2")
tab3 = tabview.add_tab("Tab 3")

# Add content to the tabs


label = lv.label(tab1)
label.set_text("""This the first tab

If the content
of a tab
becomes too
longer
than the
container
then it
automatically
becomes
scrollable.

Can you see it?""")

label = lv.label(tab2)
label.set_text("Second tab")

label = lv.label(tab3)
label.set_text("Third tab");

label.scroll_to_view_recursive(lv.ANIM.ON)

Tabs on the left, styling and no scrolling

#include "../../lv_examples.h"
#if LV_USE_TABVIEW && LV_BUILD_EXAMPLES

static void scroll_begin_event(lv_event_t * e)


{
/*Disable the scroll animations. Triggered when a tab button is clicked */
if(lv_event_get_code(e) == LV_EVENT_SCROLL_BEGIN) {
lv_anim_t * a = lv_event_get_param(e);
if(a) a->time = 0;
}
}

void lv_example_tabview_2(void)
{
/*Create a Tab view object*/
lv_obj_t * tabview;
tabview = lv_tabview_create(lv_scr_act(), LV_DIR_LEFT, 80);
lv_obj_add_event(lv_tabview_get_content(tabview), scroll_begin_event, LV_EVENT_
,→SCROLL_BEGIN, NULL);

(continues on next page)

2.7. Widgets 225


LVGL Documentation 9.0

(continued from previous page)


lv_obj_set_style_bg_color(tabview, lv_palette_lighten(LV_PALETTE_RED, 2), 0);

lv_obj_t * tab_btns = lv_tabview_get_tab_btns(tabview);


lv_obj_set_style_bg_color(tab_btns, lv_palette_darken(LV_PALETTE_GREY, 3), 0);
lv_obj_set_style_text_color(tab_btns, lv_palette_lighten(LV_PALETTE_GREY, 5), 0);
lv_obj_set_style_border_side(tab_btns, LV_BORDER_SIDE_RIGHT, LV_PART_ITEMS | LV_
,→STATE_CHECKED);

/*Add 3 tabs (the tabs are page (lv_page) and can be scrolled*/
lv_obj_t * tab1 = lv_tabview_add_tab(tabview, "Tab 1");
lv_obj_t * tab2 = lv_tabview_add_tab(tabview, "Tab 2");
lv_obj_t * tab3 = lv_tabview_add_tab(tabview, "Tab 3");
lv_obj_t * tab4 = lv_tabview_add_tab(tabview, "Tab 4");
lv_obj_t * tab5 = lv_tabview_add_tab(tabview, "Tab 5");

lv_obj_set_style_bg_color(tab2, lv_palette_lighten(LV_PALETTE_AMBER, 3), 0);


lv_obj_set_style_bg_opa(tab2, LV_OPA_COVER, 0);

/*Add content to the tabs*/


lv_obj_t * label = lv_label_create(tab1);
lv_label_set_text(label, "First tab");

label = lv_label_create(tab2);
lv_label_set_text(label, "Second tab");

label = lv_label_create(tab3);
lv_label_set_text(label, "Third tab");

label = lv_label_create(tab4);
lv_label_set_text(label, "Forth tab");

label = lv_label_create(tab5);
lv_label_set_text(label, "Fifth tab");

lv_obj_clear_flag(lv_tabview_get_content(tabview), LV_OBJ_FLAG_SCROLLABLE);
}
#endif

def scroll_begin_event(e):

#Disable the scroll animations. Triggered when a tab button is clicked */


if e.get_code() == lv.EVENT.SCROLL_BEGIN:
a = lv.anim_t.__cast__(e.get_param())
if a:
a.time = 0

# Create a Tab view object


tabview = lv.tabview(lv.scr_act(), lv.DIR.LEFT, 80)
tabview.get_content().add_event(scroll_begin_event, lv.EVENT.SCROLL_BEGIN, None)

tabview.set_style_bg_color(lv.palette_lighten(lv.PALETTE.RED, 2), 0)

tab_btns = tabview.get_tab_btns()
tab_btns.set_style_bg_color(lv.palette_darken(lv.PALETTE.GREY, 3), 0)
tab_btns.set_style_text_color(lv.palette_lighten(lv.PALETTE.GREY, 5), 0)
(continues on next page)

2.7. Widgets 226


LVGL Documentation 9.0

(continued from previous page)


tab_btns.set_style_border_side(lv.BORDER_SIDE.RIGHT, lv.PART.ITEMS | lv.STATE.CHECKED)

# Add 3 tabs (the tabs are page (lv_page) and can be scrolled
tab1 = tabview.add_tab("Tab 1")
tab2 = tabview.add_tab("Tab 2")
tab3 = tabview.add_tab("Tab 3")
tab4 = tabview.add_tab("Tab 4")
tab5 = tabview.add_tab("Tab 5")

tab2.set_style_bg_color(lv.palette_lighten(lv.PALETTE.AMBER, 3), 0)
tab2.set_style_bg_opa(lv.OPA.COVER, 0)

# Add content to the tabs


label = lv.label(tab1)
label.set_text("First tab")

label = lv.label(tab2)
label.set_text("Second tab")

label = lv.label(tab3)
label.set_text("Third tab")

label = lv.label(tab4)
label.set_text("Forth tab")

label = lv.label(tab5)
label.set_text("Fifth tab")

tabview.get_content().clear_flag(lv.obj.FLAG.SCROLLABLE)

2.7.30 Textarea

Simple Text area

#include "../../lv_examples.h"
#if LV_USE_TEXTAREA && LV_BUILD_EXAMPLES

static void textarea_event_handler(lv_event_t * e)


{
lv_obj_t * ta = lv_event_get_target(e);
LV_UNUSED(ta);
LV_LOG_USER("Enter was pressed. The current text is: %s", lv_textarea_get_
,→text(ta));

static void btnm_event_handler(lv_event_t * e)


{
lv_obj_t * obj = lv_event_get_target(e);
lv_obj_t * ta = lv_event_get_user_data(e);
const char * txt = lv_btnmatrix_get_btn_text(obj, lv_btnmatrix_get_selected_
,→btn(obj));

(continues on next page)

2.7. Widgets 227


LVGL Documentation 9.0

(continued from previous page)


if(strcmp(txt, LV_SYMBOL_BACKSPACE) == 0) lv_textarea_del_char(ta);
else if(strcmp(txt, LV_SYMBOL_NEW_LINE) == 0) lv_obj_send_event(ta, LV_EVENT_
,→READY, NULL);

else lv_textarea_add_text(ta, txt);

void lv_example_textarea_1(void)
{
lv_obj_t * ta = lv_textarea_create(lv_scr_act());
lv_textarea_set_one_line(ta, true);
lv_obj_align(ta, LV_ALIGN_TOP_MID, 0, 10);
lv_obj_add_event(ta, textarea_event_handler, LV_EVENT_READY, ta);
lv_obj_add_state(ta, LV_STATE_FOCUSED); /*To be sure the cursor is visible*/

static const char * btnm_map[] = {"1", "2", "3", "\n",


"4", "5", "6", "\n",
"7", "8", "9", "\n",
LV_SYMBOL_BACKSPACE, "0", LV_SYMBOL_NEW_LINE, ""
};

lv_obj_t * btnm = lv_btnmatrix_create(lv_scr_act());


lv_obj_set_size(btnm, 200, 150);
lv_obj_align(btnm, LV_ALIGN_BOTTOM_MID, 0, -10);
lv_obj_add_event(btnm, btnm_event_handler, LV_EVENT_VALUE_CHANGED, ta);
lv_obj_clear_flag(btnm, LV_OBJ_FLAG_CLICK_FOCUSABLE); /*To keep the text area␣
,→focused on button clicks*/

lv_btnmatrix_set_map(btnm, btnm_map);
}

#endif

def textarea_event_handler(e, ta):


print("Enter was pressed. The current text is: " + ta.get_text())

def btnm_event_handler(e, ta):


obj = e.get_target_obj()
txt = obj.get_btn_text(obj.get_selected_btn())
if txt == lv.SYMBOL.BACKSPACE:
ta.del_char()
elif txt == lv.SYMBOL.NEW_LINE:
lv.event_send(ta, lv.EVENT.READY, None)
elif txt:
ta.add_text(txt)

ta = lv.textarea(lv.scr_act())
ta.set_one_line(True)
ta.align(lv.ALIGN.TOP_MID, 0, 10)
ta.add_event(lambda e: textarea_event_handler(e, ta), lv.EVENT.READY, None)
ta.add_state(lv.STATE.FOCUSED) # To be sure the cursor is visible

btnm_map = ["1", "2", "3", "\n",


"4", "5", "6", "\n",
"7", "8", "9", "\n",
(continues on next page)

2.7. Widgets 228


LVGL Documentation 9.0

(continued from previous page)


lv.SYMBOL.BACKSPACE, "0", lv.SYMBOL.NEW_LINE, ""]

btnm = lv.btnmatrix(lv.scr_act())
btnm.set_size(200, 150)
btnm.align(lv.ALIGN.BOTTOM_MID, 0, -10)
btnm.add_event(lambda e: btnm_event_handler(e, ta), lv.EVENT.VALUE_CHANGED, None)
btnm.clear_flag(lv.obj.FLAG.CLICK_FOCUSABLE) # To keep the text area focused on␣
,→button clicks

btnm.set_map(btnm_map)

Text area with password field

#include "../../lv_examples.h"
#if LV_USE_TEXTAREA && LV_USE_KEYBOARD && LV_BUILD_EXAMPLES

static void ta_event_cb(lv_event_t * e);

static lv_obj_t * kb;

void lv_example_textarea_2(void)
{
/*Create the password box*/
lv_obj_t * pwd_ta = lv_textarea_create(lv_scr_act());
lv_textarea_set_text(pwd_ta, "");
lv_textarea_set_password_mode(pwd_ta, true);
lv_textarea_set_one_line(pwd_ta, true);
lv_obj_set_width(pwd_ta, lv_pct(40));
lv_obj_set_pos(pwd_ta, 5, 20);
lv_obj_add_event(pwd_ta, ta_event_cb, LV_EVENT_ALL, NULL);

/*Create a label and position it above the text box*/


lv_obj_t * pwd_label = lv_label_create(lv_scr_act());
lv_label_set_text(pwd_label, "Password:");
lv_obj_align_to(pwd_label, pwd_ta, LV_ALIGN_OUT_TOP_LEFT, 0, 0);

/*Create the one-line mode text area*/


lv_obj_t * text_ta = lv_textarea_create(lv_scr_act());
lv_textarea_set_one_line(text_ta, true);
lv_textarea_set_password_mode(text_ta, false);
lv_obj_set_width(text_ta, lv_pct(40));
lv_obj_add_event(text_ta, ta_event_cb, LV_EVENT_ALL, NULL);
lv_obj_align(text_ta, LV_ALIGN_TOP_RIGHT, -5, 20);

/*Create a label and position it above the text box*/


lv_obj_t * oneline_label = lv_label_create(lv_scr_act());
lv_label_set_text(oneline_label, "Text:");
lv_obj_align_to(oneline_label, text_ta, LV_ALIGN_OUT_TOP_LEFT, 0, 0);

/*Create a keyboard*/
kb = lv_keyboard_create(lv_scr_act());
lv_obj_set_size(kb, LV_HOR_RES, LV_VER_RES / 2);

lv_keyboard_set_textarea(kb, pwd_ta); /*Focus it on one of the text areas to␣


,→ start*/
(continues on next page)

2.7. Widgets 229


LVGL Documentation 9.0

(continued from previous page)


}

static void ta_event_cb(lv_event_t * e)


{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * ta = lv_event_get_target(e);
if(code == LV_EVENT_CLICKED || code == LV_EVENT_FOCUSED) {
/*Focus on the clicked text area*/
if(kb != NULL) lv_keyboard_set_textarea(kb, ta);
}

else if(code == LV_EVENT_READY) {


LV_LOG_USER("Ready, current text: %s", lv_textarea_get_text(ta));
}
}

#endif

def ta_event_cb(e):
code = e.get_code()
ta = e.get_target_obj()
if code == lv.EVENT.CLICKED or code == lv.EVENT.FOCUSED:
# Focus on the clicked text area
if kb != None:
kb.set_textarea(ta)

elif code == lv.EVENT.READY:


print("Ready, current text: " + ta.get_text())

# Create the password box

pwd_ta = lv.textarea(lv.scr_act())
pwd_ta.set_text("")
pwd_ta.set_password_mode(True)
pwd_ta.set_one_line(True)
pwd_ta.set_width(lv.pct(45))
pwd_ta.set_pos(5, 20)
pwd_ta.add_event(ta_event_cb, lv.EVENT.ALL, None)

# Create a label and position it above the text box


pwd_label = lv.label(lv.scr_act())
pwd_label.set_text("Password:")
pwd_label.align_to(pwd_ta, lv.ALIGN.OUT_TOP_LEFT, 0, 0)

# Create the one-line mode text area


text_ta = lv.textarea(lv.scr_act())
text_ta.set_width(lv.pct(45))
text_ta.set_one_line(True)
text_ta.add_event(ta_event_cb, lv.EVENT.ALL, None)
text_ta.set_password_mode(False)

text_ta.align(lv.ALIGN.TOP_RIGHT, -5, 20)

# Create a label and position it above the text box


oneline_label = lv.label(lv.scr_act())
(continues on next page)

2.7. Widgets 230


LVGL Documentation 9.0

(continued from previous page)


oneline_label.set_text("Text:")
oneline_label.align_to(text_ta, lv.ALIGN.OUT_TOP_LEFT, 0, 0)

# Create a keyboard
kb = lv.keyboard(lv.scr_act())
kb.set_size(lv.pct(100), lv.pct(50))

kb.set_textarea(pwd_ta) # Focus it on one of the text areas to start

Text auto-formatting

#include "../../lv_examples.h"
#if LV_USE_TEXTAREA && LV_USE_KEYBOARD && LV_BUILD_EXAMPLES

static void ta_event_cb(lv_event_t * e);

static lv_obj_t * kb;

/**
* Automatically format text like a clock. E.g. "12:34"
* Add the ':' automatically.
*/
void lv_example_textarea_3(void)
{
/*Create the text area*/
lv_obj_t * ta = lv_textarea_create(lv_scr_act());
lv_obj_add_event(ta, ta_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
lv_textarea_set_accepted_chars(ta, "0123456789:");
lv_textarea_set_max_length(ta, 5);
lv_textarea_set_one_line(ta, true);
lv_textarea_set_text(ta, "");

/*Create a keyboard*/
kb = lv_keyboard_create(lv_scr_act());
lv_obj_set_size(kb, LV_HOR_RES, LV_VER_RES / 2);
lv_keyboard_set_mode(kb, LV_KEYBOARD_MODE_NUMBER);
lv_keyboard_set_textarea(kb, ta);
}

static void ta_event_cb(lv_event_t * e)


{
lv_obj_t * ta = lv_event_get_target(e);
const char * txt = lv_textarea_get_text(ta);
if(txt[0] >= '0' && txt[0] <= '9' &&
txt[1] >= '0' && txt[1] <= '9' &&
txt[2] != ':') {
lv_textarea_set_cursor_pos(ta, 2);
lv_textarea_add_char(ta, ':');
}
}

#endif

2.7. Widgets 231


LVGL Documentation 9.0

def ta_event_cb(e):
ta = e.get_target_obj()
txt = ta.get_text()
# print(txt)
pos = ta.get_cursor_pos()
# print("cursor pos: ",pos)
# find position of ":" in text
colon_pos= txt.find(":")
# if there are more than 2 digits before the colon, remove the last one entered
if colon_pos == 3:
ta.del_char()
if colon_pos != -1:
# if there are more than 3 digits after the ":" remove the last one entered
rest = txt[colon_pos:]
if len(rest) > 3:
ta.del_char()

if len(txt) < 2:
return
if ":" in txt:
return
if txt[0] >= '0' and txt[0] <= '9' and \
txt[1] >= '0' and txt[1] <= '9':
if len(txt) == 2 or txt[2] != ':' :
ta.set_cursor_pos(2)
ta.add_char(ord(':'))
#
# Automatically format text like a clock. E.g. "12:34"
# Add the ':' automatically
#
# Create the text area

ta = lv.textarea(lv.scr_act())
ta.add_event(ta_event_cb, lv.EVENT.VALUE_CHANGED, None)
ta.set_accepted_chars("0123456789:")
ta.set_max_length(5)
ta.set_one_line(True)
ta.set_text("")
ta.add_state(lv.STATE.FOCUSED)

# Create a keyboard
kb = lv.keyboard(lv.scr_act())
kb.set_size(lv.pct(100), lv.pct(50))
kb.set_mode(lv.keyboard.MODE.NUMBER)
kb.set_textarea(ta)

2.7. Widgets 232


LVGL Documentation 9.0

2.7.31 Tabview

Tileview with content

#include "../../lv_examples.h"
#if LV_USE_TILEVIEW && LV_BUILD_EXAMPLES

/**
* Create a 2x2 tile view and allow scrolling only in an "L" shape.
* Demonstrate scroll chaining with a long list that
* scrolls the tile view when it can't be scrolled further.
*/
void lv_example_tileview_1(void)
{
lv_obj_t * tv = lv_tileview_create(lv_scr_act());

/*Tile1: just a label*/


lv_obj_t * tile1 = lv_tileview_add_tile(tv, 0, 0, LV_DIR_BOTTOM);
lv_obj_t * label = lv_label_create(tile1);
lv_label_set_text(label, "Scroll down");
lv_obj_center(label);

/*Tile2: a button*/
lv_obj_t * tile2 = lv_tileview_add_tile(tv, 0, 1, LV_DIR_TOP | LV_DIR_RIGHT);

lv_obj_t * btn = lv_btn_create(tile2);

label = lv_label_create(btn);
lv_label_set_text(label, "Scroll up or right");

lv_obj_set_size(btn, LV_SIZE_CONTENT, LV_SIZE_CONTENT);


lv_obj_center(btn);

/*Tile3: a list*/
lv_obj_t * tile3 = lv_tileview_add_tile(tv, 1, 1, LV_DIR_LEFT);
lv_obj_t * list = lv_list_create(tile3);
lv_obj_set_size(list, LV_PCT(100), LV_PCT(100));

lv_list_add_btn(list, NULL, "One");


lv_list_add_btn(list, NULL, "Two");
lv_list_add_btn(list, NULL, "Three");
lv_list_add_btn(list, NULL, "Four");
lv_list_add_btn(list, NULL, "Five");
lv_list_add_btn(list, NULL, "Six");
lv_list_add_btn(list, NULL, "Seven");
lv_list_add_btn(list, NULL, "Eight");
lv_list_add_btn(list, NULL, "Nine");
lv_list_add_btn(list, NULL, "Ten");

#endif

#
# Create a 2x2 tile view and allow scrolling only in an "L" shape.
# Demonstrate scroll chaining with a long list that
(continues on next page)

2.7. Widgets 233


LVGL Documentation 9.0

(continued from previous page)


# scrolls the tile view when it can't be scrolled further.
#
tv = lv.tileview(lv.scr_act())

# Tile1: just a label


tile1 = tv.add_tile(0, 0, lv.DIR.BOTTOM)
label = lv.label(tile1)
label.set_text("Scroll down")
label.center()

# Tile2: a button
tile2 = tv.add_tile(0, 1, lv.DIR.TOP | lv.DIR.RIGHT)

btn = lv.btn(tile2)

label = lv.label(btn)
label.set_text("Scroll up or right")

btn.set_size(lv.SIZE_CONTENT, lv.SIZE_CONTENT)
btn.center()

# Tile3: a list
tile3 = tv.add_tile(1, 1, lv.DIR.LEFT)
list = lv.list(tile3)
list.set_size(lv.pct(100), lv.pct(100))

list.add_btn(None, "One")
list.add_btn(None, "Two")
list.add_btn(None, "Three")
list.add_btn(None, "Four")
list.add_btn(None, "Five")
list.add_btn(None, "Six")
list.add_btn(None, "Seven")
list.add_btn(None, "Eight")
list.add_btn(None, "Nine")
list.add_btn(None, "Ten")

2.7.32 Window

Simple window

#include "../../lv_examples.h"
#if LV_USE_WIN && LV_BUILD_EXAMPLES

static void event_handler(lv_event_t * e)


{
lv_obj_t * obj = lv_event_get_target(e);
LV_UNUSED(obj);
LV_LOG_USER("Button %d clicked", (int)lv_obj_get_index(obj));
}

void lv_example_win_1(void)
{
(continues on next page)

2.7. Widgets 234


LVGL Documentation 9.0

(continued from previous page)


lv_obj_t * win = lv_win_create(lv_scr_act(), 40);
lv_obj_t * btn;
btn = lv_win_add_btn(win, LV_SYMBOL_LEFT, 40);
lv_obj_add_event(btn, event_handler, LV_EVENT_CLICKED, NULL);

lv_win_add_title(win, "A title");

btn = lv_win_add_btn(win, LV_SYMBOL_RIGHT, 40);


lv_obj_add_event(btn, event_handler, LV_EVENT_CLICKED, NULL);

btn = lv_win_add_btn(win, LV_SYMBOL_CLOSE, 60);


lv_obj_add_event(btn, event_handler, LV_EVENT_CLICKED, NULL);

lv_obj_t * cont = lv_win_get_content(win); /*Content can be added here*/


lv_obj_t * label = lv_label_create(cont);
lv_label_set_text(label, "This is\n"
"a pretty\n"
"long text\n"
"to see how\n"
"the window\n"
"becomes\n"
"scrollable.\n"
"\n"
"\n"
"Some more\n"
"text to be\n"
"sure it\n"
"overflows. :)");

#endif

def event_handler(e):
code = e.get_code()
obj = e.get_target_obj()
if code == lv.EVENT.CLICKED:
print("Button {:d} clicked".format(obj.get_child_id()))

win = lv.win(lv.scr_act(), 60)


btn1 = win.add_btn(lv.SYMBOL.LEFT, 40)
btn1.add_event(event_handler, lv.EVENT.ALL, None)
win.add_title("A title")
btn2=win.add_btn(lv.SYMBOL.RIGHT, 40)
btn2.add_event(event_handler, lv.EVENT.ALL, None)
btn3 = win.add_btn(lv.SYMBOL.CLOSE, 60)
btn3.add_event(event_handler, lv.EVENT.ALL, None)

cont = win.get_content() # Content can be added here


label = lv.label(cont)
label.set_text("""This is
a pretty
long text
to see how
(continues on next page)

2.7. Widgets 235


LVGL Documentation 9.0

(continued from previous page)


the window
becomes
scrollable.

We need
quite some text
and we will
even put
some more
text to be
sure it
overflows.
""")

2.7. Widgets 236


CHAPTER

THREE

GET STARTED

There are several ways to get your feet wet with LVGL. Here is one recommended order of documents to read and things
to play with when you are learning to use LVGL:
1. Check the Online demos to see LVGL in action (3 minutes)
2. Read the Introduction page of the documentation (5 minutes)
3. Read the Quick overview page of the documentation (15 minutes)
4. Set up a Simulator (10 minutes)
5. Try out some Examples
6. Check out the Platform-specific tutorials. (in this section below). (10 minutes)
7. Port LVGL to a board. See the Porting guide or check the ready to use Projects
8. Read the Overview page to get a better understanding of the library. (2-3 hours)
9. Check the documentation of the Widgets to see their features and usage
10. If you have questions got to the Forum
11. Read the Contributing guide to see how you can help to improve LVGL (15 minutes)

3.1 Quick overview

Here you can learn the most important things about LVGL. You should read this first to get a general impression and read
the detailed Porting and Overview sections after that.

3.1.1 Get started in a simulator

Instead of porting LVGL to embedded hardware straight away, it's highly recommended to get started in a simulator first.
LVGL is ported to many IDEs to be sure you will find your favorite one. Go to the Simulators section to get ready-to-use
projects that can be run on your PC. This way you can save the time of porting for now and get some experience with
LVGL immediately.

237
LVGL Documentation 9.0

3.1.2 Add LVGL into your project

If you would rather try LVGL on your own project follow these steps:
• Download or clone the library from GitHub with git clone https://github.com/lvgl/lvgl.git.
• Copy the lvgl folder into your project.
• Copy lvgl/lv_conf_template.h as lv_conf.h next to the lvgl folder, change the first #if 0 to 1
to enable the file's content and set the LV_COLOR_DEPTH defines.
• Include lvgl/lvgl.h in files where you need to use LVGL related functions.
• Call lv_tick_inc(x) every x milliseconds in a Timer or Task (x should be between 1 and 10). It is required
for the internal timing of LVGL. Alternatively, configure LV_TICK_CUSTOM (see lv_conf.h) so that LVGL
can retrieve the current time directly.
• Call lv_init()
• Create a draw buffer: LVGL will render the graphics here first, and send the rendered image to the display. The
buffer size can be set freely but 1/10 screen size is a good starting point.
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf1[MY_DISP_HOR_RES * MY_DISP_VER_RES / 10]; ␣
,→ /*Declare a buffer for 1/10 screen size*/

lv_disp_draw_buf_init(&draw_buf, buf1, NULL, MY_DISP_HOR_RES * MY_DISP_VER_RES / 10);␣


,→ /*Initialize the display buffer.*/

• Implement and register a function which can copy the rendered image to an area of your display:
static lv_disp_t disp_drv; /*Descriptor of a display driver*/
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
disp_drv.flush_cb = my_disp_flush; /*Set your driver function*/
disp_drv.draw_buf = &draw_buf; /*Assign the buffer to the display*/
disp_drv.hor_res = MY_DISP_HOR_RES; /*Set the horizontal resolution of the display*/
disp_drv.ver_res = MY_DISP_VER_RES; /*Set the vertical resolution of the display*/
lv_disp_drv_register(&disp_drv); /*Finally register the driver*/

void my_disp_flush(lv_disp_t * disp, const lv_area_t * area, lv_color_t * color_p)


{
int32_t x, y;
/*It's a very slow but simple implementation.
*`set_pixel` needs to be written by you to a set pixel on the screen*/
for(y = area->y1; y <= area->y2; y++) {
for(x = area->x1; x <= area->x2; x++) {
set_pixel(x, y, *color_p);
color_p++;
}
}

lv_disp_flush_ready(disp); /* Indicate you are ready with the flushing*/


}

• Implement and register a function which can read an input device. E.g. for a touchpad:
static lv_indev_t indev_drv; /*Descriptor of a input device driver*/
lv_indev_drv_init(&indev_drv); /*Basic initialization*/
indev_drv.type = LV_INDEV_TYPE_POINTER; /*Touch pad is a pointer-like device*/
indev_drv.read_cb = my_touchpad_read; /*Set your driver function*/
lv_indev_drv_register(&indev_drv); /*Finally register the driver*/
(continues on next page)

3.1. Quick overview 238


LVGL Documentation 9.0

(continued from previous page)

void my_touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)


{
/*`touchpad_is_pressed` and `touchpad_get_xy` needs to be implemented by you*/
if(touchpad_is_pressed()) {
data->state = LV_INDEV_STATE_PRESSED;
touchpad_get_xy(&data->point.x, &data->point.y);
} else {
data->state = LV_INDEV_STATE_RELEASED;
}

• Call lv_timer_handler() periodically every few milliseconds in the main while(1) loop or in an oper-
ating system task. It will redraw the screen if required, handle input devices, animation etc.
For a more detailed guide go to the Porting section.

3.1.3 Learn the basics

Widgets

The graphical elements like Buttons, Labels, Sliders, Charts etc. are called objects or widgets. Go to Widgets to see the
full list of available widgets.
Every object has a parent object where it is created. For example, if a label is created on a button, the button is the parent
of label.
The child object moves with the parent and if the parent is deleted the children will be deleted too.
Children can be visible only within their parent's bounding area. In other words, the parts of the children outside the
parent are clipped.
A Screen is the "root" parent. You can have any number of screens.
To get the current screen call lv_scr_act(), and to load a screen use lv_scr_load(scr1).
You can create a new object with lv_<type>_create(parent). It will return an lv_obj_t * variable that can
be used as a reference to the object to set its parameters.
For example:

lv_obj_t * slider1 = lv_slider_create(lv_scr_act());

To set some basic attributes lv_obj_set_<parameter_name>(obj, <value>) functions can be used. For
example:

lv_obj_set_x(btn1, 30);
lv_obj_set_y(btn1, 10);
lv_obj_set_size(btn1, 200, 50);

Along with the basic attributes, widgets can have type specific parameters which are set by
lv_<widget_type>_set_<parameter_name>(obj, <value>) functions. For example:

lv_slider_set_value(slider1, 70, LV_ANIM_ON);

To see the full API visit the documentation of the widgets or the related header file (e.g. lvgl/src/widgets/slider/lv_slider.h).

3.1. Quick overview 239


LVGL Documentation 9.0

Events

Events are used to inform the user that something has happened with an object. You can assign one or more callbacks to
an object which will be called if the object is clicked, released, dragged, being deleted, etc.
A callback is assigned like this:

lv_obj_add_event(btn, btn_event_cb, LV_EVENT_CLICKED, NULL); /*Assign a callback to␣


,→the button*/

...

void btn_event_cb(lv_event_t * e)
{
printf("Clicked\n");
}

LV_EVENT_ALL can be used instead of LV_EVENT_CLICKED to invoke the callback for any event.
From lv_event_t * e the current event code can be retrieved with:

lv_event_code_t code = lv_event_get_code(e);

The object that triggered the event can be retrieved with:

lv_obj_t * obj = lv_event_get_target(e);

To learn all features of the events go to the Event overview section.

Parts

Widgets might be built from one or more parts. For example, a button has only one part called LV_PART_MAIN.
However, a Slider has LV_PART_MAIN, LV_PART_INDICATOR and LV_PART_KNOB.
By using parts you can apply different styles to sub-elements of a widget. (See below)
Read the widgets' documentation to learn which parts each uses.

States

LVGL objects can be in a combination of the following states:


• LV_STATE_DEFAULT Normal, released state
• LV_STATE_CHECKED Toggled or checked state
• LV_STATE_FOCUSED Focused via keypad or encoder or clicked via touchpad/mouse
• LV_STATE_FOCUS_KEY Focused via keypad or encoder but not via touchpad/mouse
• LV_STATE_EDITED Edit by an encoder
• LV_STATE_HOVERED Hovered by mouse (not supported now)
• LV_STATE_PRESSED Being pressed
• LV_STATE_SCROLLED Being scrolled
• LV_STATE_DISABLED Disabled

3.1. Quick overview 240


LVGL Documentation 9.0

For example, if you press an object it will automatically go to the LV_STATE_FOCUSED and LV_STATE_PRESSED
states and when you release it the LV_STATE_PRESSED state will be removed while focus remains active.
To check if an object is in a given state use lv_obj_has_state(obj, LV_STATE_...). It will return true if
the object is currently in that state.
To manually add or remove states use:

lv_obj_add_state(obj, LV_STATE_...);
lv_obj_clear_state(obj, LV_STATE_...);

Styles

A style instance contains properties such as background color, border width, font, etc. that describe the appearance of
objects.
Styles are represented with lv_style_t variables. Only their pointer is saved in the objects so they need to be defined
as static or global. Before using a style it needs to be initialized with lv_style_init(&style1). After that,
properties can be added to configure the style. For example:

static lv_style_t style1;


lv_style_init(&style1);
lv_style_set_bg_color(&style1, lv_color_hex(0xa03080))
lv_style_set_border_width(&style1, 2))

See the full list of properties here.


Styles are assigned using the ORed combination of an object's part and state. For example to use this style on the slider's
indicator when the slider is pressed:

lv_obj_add_style(slider1, &style1, LV_PART_INDICATOR | LV_STATE_PRESSED);

If the part is LV_PART_MAIN it can be omitted:

lv_obj_add_style(btn1, &style1, LV_STATE_PRESSED); /*Equal to LV_PART_MAIN | LV_STATE_


,→PRESSED*/

Similarly, LV_STATE_DEFAULT can be omitted too:

lv_obj_add_style(slider1, &style1, LV_PART_INDICATOR); /*Equal to LV_PART_INDICATOR |␣


,→LV_STATE_DEFAULT*/

For LV_STATE_DEFAULT and LV_PART_MAIN simply write 0:

lv_obj_add_style(btn1, &style1, 0); /*Equal to LV_PART_MAIN | LV_STATE_DEFAULT*/

Styles can be cascaded (similarly to CSS). It means you can add more styles to a part of an object. For example
style_btn can set a default button appearance, and style_btn_red can overwrite the background color to make
the button red:

lv_obj_add_style(btn1, &style_btn, 0);


lv_obj_add_style(btn1, &style1_btn_red, 0);

If a property is not set on for the current state, the style with LV_STATE_DEFAULT will be used. A default value is
used if the property is not defined in the default state.

3.1. Quick overview 241


LVGL Documentation 9.0

Some properties (typically the text-related ones) can be inherited. This means if a property is not set in an object it will
be searched for in its parents too. For example, you can set the font once in the screen's style and all text on that screen
will inherit it by default.
Local style properties also can be added to objects. This creates a style which resides inside the object and is used only
by the object:

lv_obj_set_style_bg_color(slider1, lv_color_hex(0x2080bb), LV_PART_INDICATOR | LV_


,→STATE_PRESSED);

To learn all the features of styles see the Style overview section.

Themes

Themes are the default styles for objects. Styles from a theme are applied automatically when objects are created.
The theme for your application is a compile time configuration set in lv_conf.h.

3.1.4 Examples

A very simple "hello world" label

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_LABEL

/**
* Basic example to create a "Hello world" label
*/
void lv_example_get_started_1(void)
{
/*Change the active screen's background color*/
lv_obj_set_style_bg_color(lv_scr_act(), lv_color_hex(0x003a57), LV_PART_MAIN);

/*Create a white label, set its text and align it to the center*/
lv_obj_t * label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "Hello world");
lv_obj_set_style_text_color(lv_scr_act(), lv_color_hex(0xffffff), LV_PART_MAIN);
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
}

#endif

# Change the active screen's background color


scr = lv.scr_act()
scr.set_style_bg_color(lv.color_hex(0x003a57), lv.PART.MAIN)

# Create a white label, set its text and align it to the center
label = lv.label(lv.scr_act())
label.set_text("Hello world")
label.set_style_text_color(lv.color_hex(0xffffff), lv.PART.MAIN)
label.align(lv.ALIGN.CENTER, 0, 0)

3.1. Quick overview 242


LVGL Documentation 9.0

A button with a label and react on click event

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_BTN

static void btn_event_cb(lv_event_t * e)


{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * btn = lv_event_get_target(e);
if(code == LV_EVENT_CLICKED) {
static uint8_t cnt = 0;
cnt++;

/*Get the first child of the button which is the label and change its text*/
lv_obj_t * label = lv_obj_get_child(btn, 0);
lv_label_set_text_fmt(label, "Button: %d", cnt);
}
}

/**
* Create a button with a label and react on click event.
*/
void lv_example_get_started_2(void)
{
lv_obj_t * btn = lv_btn_create(lv_scr_act()); /*Add a button the current␣
,→screen*/

lv_obj_set_pos(btn, 10, 10); /*Set its position*/


lv_obj_set_size(btn, 120, 50); /*Set its size*/
lv_obj_add_event(btn, btn_event_cb, LV_EVENT_ALL, NULL); /*Assign a␣
,→callback to the button*/

lv_obj_t * label = lv_label_create(btn); /*Add a label to the button*/


lv_label_set_text(label, "Button"); /*Set the labels text*/
lv_obj_center(label);
}

#endif

class CounterBtn():
def __init__(self):
self.cnt = 0
#
# Create a button with a label and react on click event.
#

btn = lv.btn(lv.scr_act()) # Add a button the␣


,→current screen
btn.set_pos(10, 10) # Set its position
btn.set_size(120, 50) # Set its size
btn.align(lv.ALIGN.CENTER,0,0)
btn.add_event(self.btn_event_cb, lv.EVENT.ALL, None) # Assign a callback to␣
,→the button

label = lv.label(btn) # Add a label to the␣


,→button

label.set_text("Button") # Set the labels text


label.center()
(continues on next page)

3.1. Quick overview 243


LVGL Documentation 9.0

(continued from previous page)

def btn_event_cb(self,e):
code = e.get_code()
btn = e.get_target_obj()
if code == lv.EVENT.CLICKED:
self.cnt += 1

# Get the first child of the button which is the label and change its text
label = btn.get_child(0)
label.set_text("Button: " + str(self.cnt))

counterBtn = CounterBtn()

Create styles from scratch for buttons

#include "../lv_examples.h"
#if LV_USE_BTN && LV_BUILD_EXAMPLES

static lv_style_t style_btn;


static lv_style_t style_btn_pressed;
static lv_style_t style_btn_red;

static lv_color_t darken(const lv_color_filter_dsc_t * dsc, lv_color_t color, lv_opa_


,→t opa)

{
LV_UNUSED(dsc);
return lv_color_darken(color, opa);
}

static void style_init(void)


{
/*Create a simple button style*/
lv_style_init(&style_btn);
lv_style_set_radius(&style_btn, 10);
lv_style_set_bg_opa(&style_btn, LV_OPA_COVER);
lv_style_set_bg_color(&style_btn, lv_palette_lighten(LV_PALETTE_GREY, 3));
lv_style_set_bg_grad_color(&style_btn, lv_palette_main(LV_PALETTE_GREY));
lv_style_set_bg_grad_dir(&style_btn, LV_GRAD_DIR_VER);

lv_style_set_border_color(&style_btn, lv_color_black());
lv_style_set_border_opa(&style_btn, LV_OPA_20);
lv_style_set_border_width(&style_btn, 2);

lv_style_set_text_color(&style_btn, lv_color_black());

/*Create a style for the pressed state.


*Use a color filter to simply modify all colors in this state*/
static lv_color_filter_dsc_t color_filter;
lv_color_filter_dsc_init(&color_filter, darken);
lv_style_init(&style_btn_pressed);
lv_style_set_color_filter_dsc(&style_btn_pressed, &color_filter);
lv_style_set_color_filter_opa(&style_btn_pressed, LV_OPA_20);

(continues on next page)

3.1. Quick overview 244


LVGL Documentation 9.0

(continued from previous page)


/*Create a red style. Change only some colors.*/
lv_style_init(&style_btn_red);
lv_style_set_bg_color(&style_btn_red, lv_palette_main(LV_PALETTE_RED));
lv_style_set_bg_grad_color(&style_btn_red, lv_palette_lighten(LV_PALETTE_RED, 3));
}

/**
* Create styles from scratch for buttons.
*/
void lv_example_get_started_3(void)
{
/*Initialize the style*/
style_init();

/*Create a button and use the new styles*/


lv_obj_t * btn = lv_btn_create(lv_scr_act());
/* Remove the styles coming from the theme
* Note that size and position are also stored as style properties
* so lv_obj_remove_style_all will remove the set size and position too */
lv_obj_remove_style_all(btn);
lv_obj_set_pos(btn, 10, 10);
lv_obj_set_size(btn, 120, 50);
lv_obj_add_style(btn, &style_btn, 0);
lv_obj_add_style(btn, &style_btn_pressed, LV_STATE_PRESSED);

/*Add a label to the button*/


lv_obj_t * label = lv_label_create(btn);
lv_label_set_text(label, "Button");
lv_obj_center(label);

/*Create another button and use the red style too*/


lv_obj_t * btn2 = lv_btn_create(lv_scr_act());
lv_obj_remove_style_all(btn2); /*Remove the styles coming␣
,→from the theme*/

lv_obj_set_pos(btn2, 10, 80);


lv_obj_set_size(btn2, 120, 50);
lv_obj_add_style(btn2, &style_btn, 0);
lv_obj_add_style(btn2, &style_btn_red, 0);
lv_obj_add_style(btn2, &style_btn_pressed, LV_STATE_PRESSED);
lv_obj_set_style_radius(btn2, LV_RADIUS_CIRCLE, 0); /*Add a local style too*/

label = lv_label_create(btn2);
lv_label_set_text(label, "Button 2");
lv_obj_center(label);
}

#endif

#
# Create styles from scratch for buttons.
#
style_btn = lv.style_t()
style_btn_red = lv.style_t()
style_btn_pressed = lv.style_t()

# Create a simple button style


(continues on next page)

3.1. Quick overview 245


LVGL Documentation 9.0

(continued from previous page)


style_btn.init()
style_btn.set_radius(10)
style_btn.set_bg_opa(lv.OPA.COVER)
style_btn.set_bg_color(lv.palette_lighten(lv.PALETTE.GREY, 3))
style_btn.set_bg_grad_color(lv.palette_main(lv.PALETTE.GREY))
style_btn.set_bg_grad_dir(lv.GRAD_DIR.VER)

# Add a border
style_btn.set_border_color(lv.color_white())
style_btn.set_border_opa(lv.OPA._70)
style_btn.set_border_width(2)

# Set the text style


style_btn.set_text_color(lv.color_white())

# Create a red style. Change only some colors.


style_btn_red.init()
style_btn_red.set_bg_color(lv.palette_main(lv.PALETTE.RED))
style_btn_red.set_bg_grad_color(lv.palette_lighten(lv.PALETTE.RED, 2))

# Create a style for the pressed state.


style_btn_pressed.init()
style_btn_pressed.set_bg_color(lv.palette_main(lv.PALETTE.BLUE))
style_btn_pressed.set_bg_grad_color(lv.palette_darken(lv.PALETTE.RED, 3))

# Create a button and use the new styles


btn = lv.btn(lv.scr_act()) # Add a button the current screen
# Remove the styles coming from the theme
# Note that size and position are also stored as style properties
# so lv_obj_remove_style_all will remove the set size and position too
btn.remove_style_all() # Remove the styles coming from the theme
btn.set_pos(10, 10) # Set its position
btn.set_size(120, 50) # Set its size
btn.add_style(style_btn, 0)
btn.add_style(style_btn_pressed, lv.STATE.PRESSED)

label = lv.label(btn) # Add a label to the button


label.set_text("Button") # Set the labels text
label.center()

# Create a slider in the center of the display


slider = lv.slider(lv.scr_act())
slider.set_width(200) # Set the width
slider.center() # Align to the␣
,→center of the parent (screen)

# Create another button and use the red style too


btn2 = lv.btn(lv.scr_act())
btn2.remove_style_all() # Remove the styles coming from the theme
btn2.set_pos(10, 80) # Set its position
btn2.set_size(120, 50) # Set its size
btn2.add_style(style_btn, 0)
btn2.add_style(style_btn_red, 0)
btn2.add_style(style_btn_pressed, lv.STATE.PRESSED)
btn2.set_style_radius(lv.RADIUS_CIRCLE, 0) # Add a local style

label = lv.label(btn2) # Add a label to the button


(continues on next page)

3.1. Quick overview 246


LVGL Documentation 9.0

(continued from previous page)


label.set_text("Button 2") # Set the labels text
label.center()

Create a slider and write its value on a label

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_SLIDER

static lv_obj_t * label;

static void slider_event_cb(lv_event_t * e)


{
lv_obj_t * slider = lv_event_get_target(e);

/*Refresh the text*/


lv_label_set_text_fmt(label, "%"LV_PRId32, lv_slider_get_value(slider));
lv_obj_align_to(label, slider, LV_ALIGN_OUT_TOP_MID, 0, -15); /*Align top of␣
,→the slider*/

/**
* Create a slider and write its value on a label.
*/
void lv_example_get_started_4(void)
{
/*Create a slider in the center of the display*/
lv_obj_t * slider = lv_slider_create(lv_scr_act());
lv_obj_set_width(slider, 200); /*Set the width*/
lv_obj_center(slider); /*Align to the center of␣
,→the parent (screen)*/

lv_obj_add_event(slider, slider_event_cb, LV_EVENT_VALUE_CHANGED, NULL); /


,→*Assign an event function*/

/*Create a label above the slider*/


label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "0");
lv_obj_align_to(label, slider, LV_ALIGN_OUT_TOP_MID, 0, -15); /*Align top of␣
,→the slider*/

#endif

def slider_event_cb(e):
slider = e.get_target_obj()

# Refresh the text


label.set_text(str(slider.get_value()))

#
# Create a slider and write its value on a label.
#

(continues on next page)

3.1. Quick overview 247


LVGL Documentation 9.0

(continued from previous page)


# Create a slider in the center of the display
slider = lv.slider(lv.scr_act())
slider.set_width(200) # Set the width
slider.center() # Align to the␣
,→center of the parent (screen)

slider.add_event(slider_event_cb, lv.EVENT.VALUE_CHANGED, None) # Assign an event␣


,→function

# Create a label above the slider


label = lv.label(lv.scr_act())
label.set_text("0")
label.align_to(slider, lv.ALIGN.OUT_TOP_MID, 0, -15) # Align below the␣
,→slider

3.1.5 Micropython

Learn more about Micropython.

# Create a Button and a Label


scr = lv.obj()
btn = lv.btn(scr)
btn.align(lv.scr_act(), lv.ALIGN.CENTER, 0, 0)
label = lv.label(btn)
label.set_text("Button")

# Load the screen


lv.scr_load(scr)

3.2 Platforms

3.2.1 Simulator on PC

You can try out LVGL using only your PC (i.e. without any development boards). LVGL will run on a simulator
environment on the PC where anyone can write and experiment with real LVGL applications.
Using the simulator on a PC has the following advantages:
• Hardware independent - Write code, run it on the PC and see the result on a monitor.
• Cross-platform - Any Windows, Linux or macOS system can run the PC simulator.
• Portability - The written code is portable, which means you can simply copy it when migrating to embedded hard-
ware.
• Easy Validation - The simulator is also very useful to report bugs because it provides a common platform for every
user. So it's a good idea to reproduce a bug in the simulator and use that code snippet in the Forum.

3.2. Platforms 248


LVGL Documentation 9.0

Select an IDE

The simulator is ported to various IDEs (Integrated Development Environments). Choose your favorite IDE, read its
README on GitHub, download the project, and load it to the IDE.
• Eclipse with SDL driver: Recommended on Linux and Mac
• CodeBlocks: Recommended on Windows
• VisualStudio with SDL driver: For Windows
• VSCode with SDL driver: Recommended on Linux and Mac
• PlatformIO with SDL driver: Recommended on Linux and Mac
• MDK with FastModel: For Windows
External project not maintained by the LVGL organization:
• QT Creator: Cross platform
You can use any IDE for development but, for simplicity, the configuration for Eclipse CDT is what we'll focus on in this
tutorial. The following section describes the set-up guide of Eclipse CDT in more detail.
Note: If you are on Windows, it's usually better to use the Visual Studio or CodeBlocks projects instead. They
work out of the box without requiring extra steps.

Set-up Eclipse CDT

Install Eclipse CDT

Eclipse CDT is a C/C++ IDE.


Eclipse is a Java-based tool so be sure Java Runtime Environment is installed on your system.
On Debian-based distros (e.g. Ubuntu): sudo apt-get install default-jre
Note: If you are using other distros, then please install a 'Java Runtime Environment' suitable to your distro. Note: If you
are using macOS and get a "Failed to create the Java Virtual Machine" error, uninstall any other Java JDK installs and
install Java JDK 8u. This should fix the problem.
You can download Eclipse's CDT from: https://www.eclipse.org/cdt/downloads.php. Start the installer and choose
Eclipse CDT from the list.

Install SDL 2

The PC simulator uses the SDL 2 cross-platform library to simulate a TFT display and a touchpad.

Linux

On Linux you can easily install SDL2 using a terminal:


1. Find the current version of SDL2: apt-cache search libsdl2 (e.g. libsdl2-2.0-0)
2. Install SDL2: sudo apt-get install libsdl2-2.0-0 (replace with the found version)
3. Install SDL2 development package: sudo apt-get install libsdl2-dev
4. If build essentials are not installed yet: sudo apt-get install build-essential

3.2. Platforms 249


LVGL Documentation 9.0

Windows

If you are using Windows firstly you need to install MinGW (64 bit version). After installing MinGW, do the following
steps to add SDL2:
1. Download the development libraries of SDL. Go to https://www.libsdl.org/download-2.0.php and download De-
velopment Libraries: SDL2-devel-2.0.5-mingw.tar.gz
2. Decompress the file and go to x86_64-w64-mingw32 directory (for 64 bit MinGW) or to i686-w64-mingw32 (for
32 bit MinGW)
3. Copy _...mingw32/include/SDL2 folder to C:/MinGW/.../x86_64-w64-mingw32/include
4. Copy _...mingw32/lib/ content to C:/MinGW/.../x86_64-w64-mingw32/lib
5. Copy _...mingw32/bin/SDL2.dll to {eclipse_workspace}/pc_simulator/Debug/. Do it later when Eclipse is installed.
Note: If you are using Microsoft Visual Studio instead of Eclipse then you don't have to install MinGW.

OSX

On OSX you can easily install SDL2 with brew: brew install sdl2
If something is not working, then please refer this tutorial to get started with SDL.

Pre-configured project

A pre-configured graphics library project (based on the latest release) is always available to get started easily. You can
find the latest one on GitHub. (Please note that, the project is configured for Eclipse CDT).

Add the pre-configured project to Eclipse CDT

Run Eclipse CDT. It will show a dialogue about the workspace path. Before accepting the path, check that path and
copy (and unzip) the downloaded pre-configured project there. After that, you can accept the workspace path. Of course
you can modify this path but in that case copy the project to the corresponding location.
Close the start-up window and go to File->Import and choose General->Existing project into Workspace. Browse
the root directory of the project and click Finish
On Windows you have to do two additional things:
• Copy the SDL2.dll into the project's Debug folder
• Right-click on the project -> Project properties -> C/C++ Build -> Settings -> Libraries -> Add ... and add mingw32
above SDLmain and SDL. (The order is important: mingw32, SDLmain, SDL)

3.2. Platforms 250


LVGL Documentation 9.0

Compile and Run

Now you are ready to run LVGL on your PC. Click on the Hammer Icon on the top menu bar to Build the project. If you
have done everything right, then you will not get any errors. Note that on some systems additional steps might be required
to "see" SDL 2 from Eclipse but in most cases the configuration in the downloaded project is enough.
After a successful build, click on the Play button on the top menu bar to run the project. Now a window should appear in
the middle of your screen.
Now you are ready to use LVGL and begin development on your PC.

3.2.2 NXP

NXP has integrated LVGL into the MCUXpresso SDK packages for general purpose and crossover microcontrollers,
allowing easy evaluation and migration into your product design. Download an SDK for a supported board today and get
started with your next GUI application.

Creating new project with LVGL

Downloading the MCU SDK example project is recommended as a starting point. It comes fully configured with LVGL
(and with PXP/VGLite support if the modules are present), no additional integration work is required.

HW acceleration for NXP iMX RT platforms

Depending on the RT platform used, the acceleration can be done by NXP PXP (PiXel Pipeline) and/or the Verisilicon
GPU through an API named VGLite. Each accelerator has its own context that allows them to be used individually as
well simultaneously (in LVGL multithreading mode).

PXP accelerator

Several drawing features in LVGL can be offloaded to the PXP engine. The CPU is available for other operations while
the PXP is running. RTOS is required to block the LVGL drawing thread and switch to another task or suspend the CPU
for power savings.
Supported draw callbacks are available in "src/draw/nxp/pxp/lv_draw_pxp.c":

pxp_draw_ctx->base_draw.draw_img_decoded = lv_draw_pxp_img_decoded;
pxp_draw_ctx->blend = lv_draw_pxp_blend;
pxp_draw_ctx->base_draw.wait_for_finish = lv_draw_pxp_wait_for_finish;

Features supported:

All operations can be used in conjunction with optional transparency.

• RGB565 and ARGB8888 color formats


• Area fill with color
• BLIT (BLock Image Transfer)
• Screen Rotation (90, 180, 270 degree)

3.2. Platforms 251


LVGL Documentation 9.0

• Color keying
• Recoloring (color tint)
• Image Rotation (90, 180, 270 degree)
• RTOS integration layer
• Default FreeRTOS and bare metal code provided
• Combination of recolor and/or rotation + color key/alpha blend/transparency is supported. That is achieved by PXP
in two steps:
– First step is to recolor/rotate the image to a temporary buffer (statically allocated)
– Second step is required to handle color keying, alpha channel or to apply transparency

Known limitations:

• Rotation is not supported for images unaligned to blocks of 16x16 pixels. PXP is set to process 16x16 blocks to
optimize the system for memory bandwidth and image processing time. The output engine essentially truncates
any output pixels after the desired number of pixels has been written. When rotating a source image and the output
is not divisible by the block size, the incorrect pixels could be truncated and the final output image can look shifted.

Basic configuration:

• Select NXP PXP engine in lv_conf.h: Set LV_USE_GPU_NXP_PXP to 1


• Enable default implementation for interrupt handling, PXP start function and automatic initialization: Set
LV_USE_GPU_NXP_PXP_AUTO_INIT to 1
• If SDK_OS_FREE_RTOS symbol is defined, FreeRTOS implementation will be used, otherwise bare metal code
will be included

Basic initialization:

• If LV_USE_GPU_NXP_PXP_AUTO_INIT is enabled, no user code is required; PXP is initialized automatically


in lv_init()
• For manual PXP initialization, default configuration structure for callbacks can be used. Initialize PXP before
calling lv_init()

#if LV_USE_GPU_NXP_PXP
#include "src/draw/nxp/pxp/lv_gpu_nxp_pxp.h"
#endif
. . .
#if LV_USE_GPU_NXP_PXP
PXP_COND_STOP(!lv_gpu_nxp_pxp_init(), "PXP init failed.");
#endif

3.2. Platforms 252


LVGL Documentation 9.0

Project setup:

• Add PXP related files to project:


– src/draw/nxp/pxp/lv_draw_pxp.c[.h]: draw context callbacks
– src/draw/nxp/pxp/lv_draw_pxp_blend.c[.h]: fill and blit (with optional transformation)
– src/draw/nxp/pxp/lv_gpu_nxp_pxp.c[.h]: init, uninit, run/wait PXP device
– src/draw/nxp/pxp/lv_gpu_nxp_pxp_osa.c[.h]: OS abstraction (FreeRTOS or bare metal)
∗ optional, required only if LV_USE_GPU_NXP_PXP_AUTO_INIT is set to 1
• PXP related code depends on two drivers provided by MCU SDK. These drivers need to be added to project:
– fsl_pxp.c[.h]: PXP driver
– fsl_cache.c[.h]: CPU cache handling functions

Logging:

• By default, LV_GPU_NXP_PXP_LOG_ERRORS is enabled so that any PXP error will be seen on SDK debug
console
• By default, LV_GPU_NXP_PXP_LOG_TRACES is disabled. Enable it for tracing logs (like PXP limitations)

Advanced configuration:

• Implementation depends on multiple OS-specific functions. The struct lv_nxp_pxp_cfg_t with callback
pointers is used as a parameter for the lv_gpu_nxp_pxp_init() function. Default implementation for
FreeRTOS and bare metal is provided in lv_gpu_nxp_pxp_osa.c
– pxp_interrupt_init(): Initialize PXP interrupt (HW setup, OS setup)
– pxp_interrupt_deinit(): Deinitialize PXP interrupt (HW setup, OS setup)
– pxp_run(): Start PXP job. Use OS-specific mechanism to block drawing thread. PXP must finish drawing
before leaving this function.
• Area threshold (size limit) is configurable and used to decide whether the area will be processed by PXP or not.
Areas smaller than the defined value will be processed by CPU and those bigger than the threshold will be processed
by PXP. The threshold is defined as a macro in lv_draw_pxp.c
– LV_GPU_NXP_PXP_SIZE_LIMIT: size threshold for fill/blit (with optional transformation)

VGLite accelerator

Extra drawing features in LVGL can be handled by the VGLite engine. The CPU is available for other operations while
the VGLite is running. An RTOS is required to block the LVGL drawing thread and switch to another task or suspend
the CPU for power savings.
Supported draw callbacks are available in "src/draw/nxp/vglite/lv_draw_vglite.c":
vglite_draw_ctx->base_draw.init_buf = lv_draw_vglite_init_buf;
vglite_draw_ctx->base_draw.draw_line = lv_draw_vglite_line;
vglite_draw_ctx->base_draw.draw_arc = lv_draw_vglite_arc;
vglite_draw_ctx->base_draw.draw_rect = lv_draw_vglite_rect;
(continues on next page)

3.2. Platforms 253


LVGL Documentation 9.0

(continued from previous page)


vglite_draw_ctx->base_draw.draw_img_decoded = lv_draw_vglite_img_decoded;
vglite_draw_ctx->blend = lv_draw_vglite_blend;
vglite_draw_ctx->base_draw.wait_for_finish = lv_draw_vglite_wait_for_finish;

Features supported:

All operations can be used in conjunction with optional transparency.

• RGB565 and ARGB8888 color formats


• Area fill with color
• BLIT (BLock Image Transfer)
• Image Rotation (any degree with decimal)
• Image Scale
• Draw rectangle background with optional radius or gradient
• Blit rectangle background image
• Draw rectangle border/outline with optional rounded corners
• Draw arc with optional rounded ending
• Draw line or dashed line with optional rounded ending

Known limitations:

• Source image alignment: The byte alignment requirement for a pixel depends on the specific pixel format. Both
buffer address and buffer stride must be aligned. As general rule, the alignment is set to 16 pixels. This makes the
buffer address alignment to be 32 bytes for RGB565 and 64 bytes for ARGB8888.
• For pixel engine (PE) destination, the alignment should be 64 bytes for all tiled (4x4) buffer layouts. The pixel
engine has no additional alignment requirement for linear buffer layouts (VG_LITE_LINEAR).

Basic configuration:

• Select NXP VGLite engine in lv_conf.h: Set LV_USE_GPU_NXP_VG_LITE to 1


• SDK_OS_FREE_RTOS symbol needs to be defined so that the FreeRTOS implementation will be used

Basic initialization:

• Initialize VGLite before calling lv_init() by specifying the width/height of tessellation window. Value should
be a multiple of 16; minimum value is 16 pixels, maximum cannot be greater than the frame width. If less than or
equal to 0, then no tessellation buffer is created, in which case VGLite is initialized only for blitting.

#if LV_USE_GPU_NXP_VG_LITE
#include "vg_lite.h"
#endif
. . .
(continues on next page)

3.2. Platforms 254


LVGL Documentation 9.0

(continued from previous page)


#if LV_USE_GPU_NXP_VG_LITE
VG_LITE_COND_STOP(vg_lite_init(64, 64) != VG_LITE_SUCCESS, "VGLite init␣
,→failed.");

#endif

Project setup:

• Add VGLite related files to project:


– src/draw/nxp/vglite/lv_draw_vglite.c[.h]: draw context callbacks
– src/draw/nxp/vglite/lv_draw_vglite_blend.c[.h]: fill and blit (with optional transformation)
– src/draw/nxp/vglite/lv_draw_vglite_rect.c[.h]: draw rectangle
– src/draw/nxp/vglite/lv_draw_vglite_arc.c[.h]: draw arc
– src/draw/nxp/vglite/lv_draw_vglite_line.c[.h]: draw line
– src/draw/nxp/vglite/lv_vglite_buf.c[.h]: init/get vglite buffer
– src/draw/nxp/vglite/lv_vglite_utils.c[.h]: function helpers

Logging:

• By default, LV_GPU_NXP_VG_LITE_LOG_ERRORS is enabled so that any VGLite error will be seen on SDK
debug console
• By default, LV_GPU_NXP_VG_LITE_LOG_TRACES is disabled. Enable it for tracing logs (like blit split
workaround or VGLite fallback to CPU due to any error on the driver)

Advanced configuration:

• Area threshold (size limit) is configurable and used to decide whether the area will be processed by VGLite or
not. Areas smaller than the defined value will be processed by CPU and those bigger than the threshold will be
processed by VGLite. The threshold is defined as a macro in lv_draw_vglite.c
– LV_GPU_NXP_VG_LITE_SIZE_LIMIT: size threshold for fill/blit (with optional transformation)

3.2.3 STM32

LVGL Can be added to STM32CubeIDE in a similar fashion to any other Eclipse-based IDE.

3.2. Platforms 255


LVGL Documentation 9.0

Including LVGL in a Project

• Create or open a project in STM32CubeIDE.


• Copy the entire LVGL folder to [project_folder]/Drivers/lvgl.
• In the STM32CubeIDE Project Explorer pane: right click on the LVGL folder that you copied (you may need
to refresh the view first before it will appear), and select Add/remove include path.... If this doesn't appear, or
doesn't work, you can review your project include paths under the Project - Properties menu, and then navigating
to C/C++ Build - Settings - Include paths, and ensuring that the LVGL directory is listed.
Now that the source files are included in your project, follow the instructions for Porting your project to create the
lv_conf.h file, and initialise the display.

Bare Metal Example

A minimal example using STM32CubeIDE, and HAL.


• When setting up Pinout and Configuration using the Device Configuration Tool, select System Core - SYS
and ensure that Timebase Source is set to SysTick.
• Configure any other peripherals (including the LCD panel), and initialise them in main.c.
• #include "lvgl.h" in the main.c file.
• Create some frame buffer(s) as global variables:

//Frame buffers
/*A static or global variable to store the buffers*/
static lv_disp_draw_buf_t disp_buf;

/*Static or global buffer(s). The second buffer is optional*/


static lv_color_t buf_1[BUFF_SIZE]; //TODO: Chose a buffer size. DISPLAY_WIDTH * 10␣
,→is one suggestion.

static lv_color_t buf_2[BUFF_SIZE];

• In your main() function, after initialising your CPU, peripherals, and LCD panel, call lv_init(); to initialise
LVGL. You can then register the frame buffers using lv_disp_draw_buf_init(), and create the display
driver using lv_disp_drv_init().

//Initialise LVGL UI library


lv_init();
lv_disp_draw_buf_init(&disp_buf, buf_1, NULL, BUFF_SIZE);

static lv_disp_drv_t disp_drv; /*A variable to hold the drivers. Must be␣
,→static or global.*/

lv_disp_drv_init(&disp_drv); /*Basic initialization*/


disp_drv.draw_buf = &disp_buf; /*Set an initialized buffer*/
disp_drv.flush_cb = my_flush_cb; /*Set a flush callback to draw to the␣
,→display*/

disp_drv.hor_res = WIDTH; /*Set the horizontal resolution in pixels*/


disp_drv.ver_res = HEIGHT; /*Set the vertical resolution in pixels*/

lv_disp_t * disp;
disp = lv_disp_drv_register(&disp_drv); /*Register the driver and save the created␣
,→display objects*/

• Create some dummy objects to test the output:

3.2. Platforms 256


LVGL Documentation 9.0

// Change the active screen's background color


lv_obj_set_style_bg_color(lv_scr_act(), lv_color_hex(0x003a57), LV_PART_MAIN);
lv_obj_set_style_text_color(lv_scr_act(), lv_color_hex(0xffffff), LV_PART_MAIN);

/*Create a spinner*/
lv_obj_t * spinner = lv_spinner_create(lv_scr_act(), 1000, 60);
lv_obj_set_size(spinner, 64, 64);
lv_obj_align(spinner, LV_ALIGN_BOTTOM_MID, 0, 0);

• Add a call to lv_timer_handler() inside your while(1) loop:

/* Infinite loop */
while (1)
{
lv_timer_handler();
HAL_Delay(5);
}

• Add a call to lv_tick_inc() inside the SysTick_Handler() function. Open the stm32xxxx_it.c file (the
name will depend on your specific MCU), and update the SysTick_Handler() function:

void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */

HAL_SYSTICK_IRQHandler();
lv_tick_inc(1);
#ifdef USE_RTOS_SYSTICK
osSystickHandler();
#endif

/* USER CODE END SysTick_IRQn 0 */


HAL_IncTick();
/* USER CODE BEGIN SysTick_IRQn 1 */

/* USER CODE END SysTick_IRQn 1 */


}

• Finally, write the callback function, my_flush_cb(), which will send the display buffer to your LCD panel.
Below is one example, but it will vary depending on your setup.

void my_flush_cb(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_


,→p)

{
//Set the drawing region
set_draw_window(area->x1, area->y1, area->x2, area->y2);

int height = area->y2 - area->y1 + 1;


int width = area->x2 - area->x1 + 1;

//We will do the SPI write manually here for speed


HAL_GPIO_WritePin(DC_PORT, DC_PIN, GPIO_PIN_SET);
//CS low to begin data
HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_RESET);

//Write colour to each pixel


for (int i = 0; i < width * height; i++) {
(continues on next page)

3.2. Platforms 257


LVGL Documentation 9.0

(continued from previous page)


parallel_write(color_p->full);
color_p++;
}

//Return CS to high
HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_SET);

/* IMPORTANT!!!
* Inform the graphics library that you are ready with the flushing*/
lv_disp_flush_ready(disp_drv);
}

FreeRTOS Example

A minimal example using STM32CubeIDE, HAL, and CMSISv1 (FreeRTOS). Note that we have not used Mutexes in
this example, however LVGL is NOT thread safe and so Mutexes should be used. See: Operating system and interrupts
• #include "lvgl.h"
• Create your frame buffer(s) as global variables:

//Frame buffers
/*A static or global variable to store the buffers*/
static lv_disp_draw_buf_t disp_buf;

/*Static or global buffer(s). The second buffer is optional*/


static lv_color_t buf_1[BUFF_SIZE]; //TODO: Declare your own BUFF_SIZE appropriate to␣
,→your system.

static lv_color_t buf_2[BUFF_SIZE];

• In your main() function, after your peripherals (SPI, GPIOs, LCD etc) have been initialised, initialise LVGL
using lv_init();, register the frame buffers using lv_disp_draw_buf_init(), and create a new display
driver using lv_disp_drv_init().

//Initialise LVGL UI library


lv_init();
lv_disp_draw_buf_init(&disp_buf, buf_1, buf_2, BUFF_SIZE);

static lv_disp_drv_t disp_drv; /*A variable to hold the drivers. Must be␣
,→static or global.*/

lv_disp_drv_init(&disp_drv); /*Basic initialization*/


disp_drv.draw_buf = &disp_buf; /*Set an initialized buffer*/
disp_drv.flush_cb = my_flush_cb; /*Set a flush callback to draw to the␣
,→display*/

disp_drv.hor_res = WIDTH; /*Set the horizontal resolution in pixels*/


disp_drv.ver_res = HEIGHT; /*Set the vertical resolution in pixels*/

lv_disp_t * disp;
disp = lv_disp_drv_register(&disp_drv); /*Register the driver and save the created␣
,→display objects*/

// Register the touch controller with LVGL - Not included here for brevity.

• Create some dummy objects to test the output:

3.2. Platforms 258


LVGL Documentation 9.0

// Change the active screen's background color


lv_obj_set_style_bg_color(lv_scr_act(), lv_color_hex(0x003a57), LV_PART_MAIN);
lv_obj_set_style_text_color(lv_scr_act(), lv_color_hex(0xffffff), LV_PART_MAIN);

/*Create a spinner*/
lv_obj_t * spinner = lv_spinner_create(lv_scr_act(), 1000, 60);
lv_obj_set_size(spinner, 64, 64);
lv_obj_align(spinner, LV_ALIGN_BOTTOM_MID, 0, 0);

• Create two threads to call lv_timer_handler(), and lv_tick_inc().You will need two osThreadId
handles for CMSISv1. These don't strictly have to be globally accessible in this case, however STM32Cube code
generation does by default. If you are using CMSIS and STM32Cube code generation it should look something
like this:

//Thread Handles
osThreadId lvgl_tickHandle;
osThreadId lvgl_timerHandle;

/* definition and creation of lvgl_tick */


osThreadDef(lvgl_tick, LGVLTick, osPriorityNormal, 0, 1024);
lvgl_tickHandle = osThreadCreate(osThread(lvgl_tick), NULL);

//LVGL update timer


osThreadDef(lvgl_timer, LVGLTimer, osPriorityNormal, 0, 1024);
lvgl_timerHandle = osThreadCreate(osThread(lvgl_timer), NULL);

And create the thread functions:

/* LVGL timer for tasks. */


void LVGLTimer(void const * argument)
{
for(;;)
{
lv_timer_handler();
osDelay(20);
}
}
/* LVGL tick source */
void LGVLTick(void const * argument)
{
for(;;)
{
lv_tick_inc(10);
osDelay(10);
}
}

• Finally, create the my_flush_cb() function to output the frame buffer to your LCD. The specifics of this
function will vary depending on which MCU features you are using. Below is an example for a typical MCU
interface.

void my_flush_cb(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_


,→p)

{
//Set the drawing region
set_draw_window(area->x1, area->y1, area->x2, area->y2);
(continues on next page)

3.2. Platforms 259


LVGL Documentation 9.0

(continued from previous page)

int height = area->y2 - area->y1 + 1;


int width = area->x2 - area->x1 + 1;

//Begin SPI Write for DATA


HAL_GPIO_WritePin(DC_PORT, DC_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_RESET);

//Write colour to each pixel


for (int i = 0; i < width * height; i++) {
parallel_write(color_p->full);
color_p++;
}

//Return CS to high
HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_SET);

/* IMPORTANT!!!
* Inform the graphics library that you are ready with the flushing*/
lv_disp_flush_ready(disp_drv);
}

3.2.4 Espressif (ESP32 chip series)

LVGL can be used and configured as a standard ESP-IDF component.


More information about ESP-IDF build system can be found here.

LVGL demo project for ESP32

We've created lv_port_esp32, a project using ESP-IDF and LVGL to show one of the demos from demos. You can
configure the project to use one of the many supported display controllers and targets (chips).
See lvgl_esp32_drivers repository for a complete list of supported display and indev (touch) controllers and targets.

Using LVGL in your ESP-IDF project

Prerequisites

• ESP-IDF v4.1 and above


• ESP evaluation board with a display

3.2. Platforms 260


LVGL Documentation 9.0

Obtaining LVGL

Option 1: git submodule


Simply clone LVGL into your project_root/components directory and it will be automatically integrated into
the project. If the project is a git repository you can include LVGL as a git submodule:

git submodule add https://github.com/lvgl/lvgl.git components/lvgl

The above command will clone LVGL's main repository into the components/lvgl directory. LVGL includes a
CMakeLists.txt file that sets some configuration options so you can use LVGL right away.
Option 2: IDF Component Manager
LVGL is also distributed through IDF Component Manager. It allows users to seamlessly integrate LVGL component
into their project with following command:

idf.py add-dependency lvgl/lvgl>=8.*

During next project build, LVGL component will be fetched from the component registry and added to project build.

Configuration

When you are ready to configure LVGL, launch the configuration menu with idf.py menuconfig in your project
root directory, go to Component config and then LVGL configuration.

Using lvgl_esp32_drivers in ESP-IDF project

You can also add lvgl_esp32_drivers as a "component". This component should be located inside a directory
named "components" in your project root directory.
When your project is a git repository you can include lvgl_esp32_drivers as a git submodule:

git submodule add https://github.com/lvgl/lvgl_esp32_drivers.git components/lvgl_


,→esp32_drivers

3.2.5 Arduino

The LVGL library is directly available as Arduino libraries.


Note that you need to choose a board powerful enough to run LVGL and your GUI. See the requirements of LVGL.
For example ESP32 is a good candidate to create UI's with LVGL.

3.2. Platforms 261


LVGL Documentation 9.0

Get the LVGL Arduino library

LVGL can be installed via the Arduino IDE Library Manager or as a .ZIP library.
You can Download the latest version of LVGL from GitHub and simply copy it to Arduino's library folder.

Set up drivers

To get started it's recommended to use TFT_eSPI library as a TFT driver to simplify testing. To make it work, setup
TFT_eSPI according to your TFT display type via editing either
• User_Setup.h
• or by selecting a configuration in the User_Setup_Select.h
Both files are located in TFT_eSPI library's folder.

Configure LVGL

LVGL has its own configuration file called lv_conf.h. When LVGL is installed, follow these configuration steps:
1. Go to the directory of the installed Arduino libraries
2. Go to lvgl and copy lv_conf_template.h as lv_conf.h into the Arduino Libraries directory next to
the lvgl library folder.
3. Open lv_conf.h and change the first #if 0 to #if 1 to enable the content of the file
4. Set the color depth of you display in LV_COLOR_DEPTH
5. Set LV_TICK_CUSTOM 1
Finally the layout with lv_conf.h should look like this:

arduino
|-libraries
|-lvgl
|-other_lib_1
|-other_lib_2
|-lv_conf.h

Initialize and run LVGL

Take a look at LVGL_Arduino.ino to see how to initialize LVGL. TFT_eSPI is used as the display driver.
In the INO file you can see how to register a display and a touchpad for LVGL and call an example.

3.2. Platforms 262


LVGL Documentation 9.0

Use the examples and demos

Note that, there is no dedicated INO file for every example. Instead, you can load an example by calling an
lv_example_... function. For example lv_example_btn_1().
IMPORTANT Due to some the limitations of Arduino's build system you need to copy lvgl/examples to lvgl/
src/examples. Similarly for the demos lvgl/demos to lvgl/src/demos.

Debugging and logging

LVGL can display debug information in case of trouble. In the LVGL_Arduino.ino example there is a my_print
method, which sends this debug information to the serial interface. To enable this feature you have to edit the lv_conf.
h file and enable logging in the section log settings:

/*Log settings*/
#define USE_LV_LOG 1 /*Enable/disable the log module*/
#if LV_USE_LOG
/* How important log should be added:
* LV_LOG_LEVEL_TRACE A lot of logs to give detailed information
* LV_LOG_LEVEL_INFO Log important events
* LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't cause a␣
,→problem

* LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail


* LV_LOG_LEVEL_NONE Do not log anything
*/
# define LV_LOG_LEVEL LV_LOG_LEVEL_WARN

After enabling the log module and setting LV_LOG_LEVEL accordingly, the output log is sent to the Serial port @
115200 bps.

3.2.6 Tasmota and berry

What is Tasmota?

Tasmota is a widely used open-source firmware for ESP8266 and EPS32 based devices. It supports a wide variety of
devices, sensors and integrations to Home Automation and Cloud services. Tasmota firmware is downloaded more than
200,000 times each month, and has an active and growing community.
Tasmota provides access to hundreds of supported devices, full support of MQTT, HTTP(S), integration with major
Home Automation systems, myriad of sensors, IR, RF, Zigbee, Bluetooth, AWS IoT, Azure IoT, Alexa and many more.

What is Berry?

Berry is a ultra-lightweight dynamically typed embedded scripting language. It is designed for lower-performance em-
bedded devices. The interpreter of Berry include a one-pass compiler and register-based VM, all the code is written in
ANSI C99. Berry offers a syntax very similar to Python, and is inspired from LUA VM. It is fully integrated in Tasmota

3.2. Platforms 263


LVGL Documentation 9.0

Highlights of Berry

Berry has the following advantages:


• Lightweight: A well-optimized interpreter with very little resources. Ideal for use in microprocessors.
• Fast: optimized one-pass bytecode compiler and register-based virtual machine.
• Powerful: supports imperative programming, object-oriented programming, functional programming.
• Flexible: Berry is a dynamic type script, and it's intended for embedding in applications. It can provide good
dynamic scalability for the host system.
• Simple: simple and natural syntax, support garbage collection, and easy to use FFI (foreign function interface).
• RAM saving: With compile-time object construction, most of the constant objects are stored in read-only code
data segments, so the RAM usage of the interpreter is very low when it starts.
All features are detailed in the Berry Reference Manual

Why LVGL + Tasmota + Berry?

In 2021, Tasmota added full support of LVGL for ESP32 based devices. It also introduced the Berry scripting language,
a small-footprint language similar to Python and fully integrated in Tasmota.
A comprehensive mapping of LVGL in Berry language is now available, similar to the mapping of Micropython. It allows
to use +98% of all LVGL features. It is also possible to write custom widgets in Berry.
Versions supported: LVGL v8.0.2, LodePNG v20201017, Freetype 2.10.4

Tasmota + Berry + LVGL could be used for:

• Fast prototyping GUI.


• Shortening the cycle of changing and fine-tuning the GUI.
• Modelling the GUI in a more abstract way by defining reusable composite objects, taking advantage of Berry's
language features such as Inheritance, Closures, Exception Handling...
• Make LVGL accessible to a larger audience. No need to know C to create a nice GUI on an embedded system.
A higher level interface compatible with OpenHASP is also under development.

So what does it look like?

TL;DR: Similar to MicroPython, it's very much like the C API, but Object-Oriented for LVGL components.
Let's dive right into an example!

3.2. Platforms 264


LVGL Documentation 9.0

A simple example

lv.start() # start LVGL


scr = lv.scr_act() # get default screen
btn = lv.btn(scr) # create button
btn.center()
label = lv.label(btn) # create a label in the button
label.set_text("Button") # set a label to the button

How can I use it?

You can start in less than 10 minutes on a M5Stack or equivalent device in less than 10 minutes in this short tutorial

Where can I find more information?

3.2.7 CMake

LVGL supports integrating with CMake. It comes with preconfigured targets for:
On top of the preconfigured targets you can also use "plain" CMake to integrate LVGL into any custom C/C++ project.

Prerequisites

• CMake ( >= 3.12.4 )


• Compatible build tool e.g.

Building LVGL with CMake

There are many ways to include external CMake projects into your own. A modern one also used in this example is the
CMake FetchContent module. This module conveniently allows us to download dependencies directly at configure time
from e.g. GitHub. Here is an example how we might include LVGL into our own project.

cmake_minimum_required(VERSION 3.14)
include(FetchContent)

project(MyProject LANGUAGES C CXX)

# Build an executable called "MyFirmware"


add_executable(MyFirmware src/main.c)

# Specify path to own LVGL config header


set(LV_CONF_PATH
${CMAKE_CURRENT_SOURCE_DIR}/src/lv_conf.h
CACHE STRING "" FORCE)

# Fetch LVGL from GitHub


FetchContent_Declare(lvgl GIT_REPOSITORY https://github.com/lvgl/lvgl.git)
FetchContent_MakeAvailable(lvgl)

# The target "MyFirmware" depends on LVGL


target_link_libraries(MyFirmware PRIVATE lvgl::lvgl)

3.2. Platforms 265


LVGL Documentation 9.0

This configuration declares a dependency between the two targets MyFirmware and lvgl. Upon building the target
MyFirmware this dependency will be resolved and lvgl will be built and linked with it. Since LVGL requires a config
header called lv_conf.h to be includable by its sources we also set the option LV_CONF_PATH to point to our own copy
of it.

Additional CMake options

Besides LV_CONF_PATH there are few additional CMake options available.

Include paths options

• LV_LVGL_H_INCLUDE_SIMPLE: which specifies whether to #include "lvgl.h" absolut or relative


• LV_CONF_INCLUDE_SIMPLE: which specifies whether to #include "lv_conf.h" and
"lv_drv_conf.h" absolut or relative
We do not recommend disabling those options unless your folder layout makes it absolutely necessary.

Examples/demos options

LVGL examples and demos are built by default in the main CMake file.To disable their built, use:
• LV_CONF_BUILD_DISABLE_EXAMPLES: Set to 1 to disable examples build
• LV_CONF_BUILD_DISABLE_DEMOS: Set to 1 to disable demos build

Building LVGL drivers

To build LVGL drivers, you can use:

FetchContent_Declare(lv_drivers
GIT_REPOSITORY https://github.com/lvgl/lv_drivers)
FetchContent_MakeAvailable(lv_drivers)

# The target "MyFirmware" depends on LVGL and drivers


target_link_libraries(MyFirmware PRIVATE lvgl::lvgl lvgl::drivers)

3.2.8 Build shared libraries with CMake

By default, LVGL will be built as a static library (archive). CMake can instead be instructed to build LVGL as shared
library (.so/.dll/etc.):

set(BUILD_SHARED_LIBS ON)

OR

$ cmake "-DBUILD_SHARED_LIBS=ON" .

3.2. Platforms 266


LVGL Documentation 9.0

3.2.9 MDK

TODO

3.3 (RT)OS

3.3.1 NuttX RTOS

What is NuttX?

NuttX is a mature and secure real-time operating system (RTOS) with an emphasis on technical standards compliance
and small size. It is scalable from 8-bit to 64-bit microcontrollers and microprocessors and compliant with the Portable
Operating System Interface (POSIX) and the American National Standards Institute (ANSI) standards and with many
Linux-like subsystems. The best way to think about NuttX is to think of it as a small Unix/Linux for microcontrollers.

Highlights of NuttX

• Small - Fits and runs in microcontrollers as small as 32 kB Flash and 8 kB of RAM.


• Compliant - Strives to be as compatible as possible with POSIX and Linux.
• Versatile - Supports many architectures (ARM, ARM Thumb, AVR, MIPS, OpenRISC, RISC-V 32-bit and 64-bit,
RX65N, x86-64, Xtensa, Z80/Z180, etc.).
• Modular - Its modular design allows developers to select only what really matters and use modules to include new
features.
• Popular - NuttX is used by many companies around the world. Probably you already used a product with NuttX
without knowing it was running NuttX.
• Predictable - NuttX is a preemptible Realtime kernel, so you can use it to create predictable applications for
realtime control.

Why NuttX + LVGL?

Although NuttX has its own graphic library called NX, LVGL is a good alternative because users could find more eye-
candy demos and they can reuse code from previous projects. LVGL is an Object-Oriented Component Based high-level
GUI library, that could fit very well for a RTOS with advanced features like NuttX. LVGL is implemented in C and its
APIs are in C.

Here are some advantages of using LVGL in NuttX

• Develop GUI in Linux first and when it is done just compile it for NuttX. Nothing more, no wasting of time.
• Usually, GUI development for low level RTOS requires multiple iterations to get things right, where each iteration
consists of Change code > Build > Flash > Run. Using LVGL, Linux and NuttX you can reduce this
process and just test everything on your computer and when it is done, compile it on NuttX and that is it.

3.3. (RT)OS 267


LVGL Documentation 9.0

NuttX + LVGL could be used for

• GUI demos to demonstrate your board graphics capacities.


• Fast prototyping GUI for MVP (Minimum Viable Product) presentation.
• visualize sensor data directly and easily on the board without using a computer.
• Final products with a GUI without a touchscreen (i.e. 3D Printer Interface using Rotary Encoder to Input data).
• Final products with a touchscreen (and all sorts of bells and whistles).

How to get started with NuttX and LVGL?

There are many boards in the NuttX mainline with support for LVGL. Let's use the STM32F429IDISCOVERY as an
example because it is a very popular board.

First you need to install the pre-requisites on your system

Let's use the Windows Subsystem for Linux

$ sudo apt-get install automake bison build-essential flex gcc-arm-none-eabi gperf␣


,→git libncurses5-dev libtool libusb-dev libusb-1.0.0-dev pkg-config kconfig-

,→frontends openocd

Now let's create a workspace to save our files

$ mkdir ~/nuttxspace
$ cd ~/nuttxspace

Clone the NuttX and Apps repositories:

$ git clone https://github.com/apache/incubator-nuttx nuttx


$ git clone https://github.com/apache/incubator-nuttx-apps apps

Configure NuttX to use the stm32f429i-disco board and the LVGL Demo

$ ./tools/configure.sh stm32f429i-disco:lvgl
$ make

If everything went fine you should have now the file nuttx.bin to flash on your board:

$ ls -l nuttx.bin
-rwxrwxr-x 1 alan alan 287144 Jun 27 09:26 nuttx.bin

3.3. (RT)OS 268


LVGL Documentation 9.0

Flashing the firmware in the board using OpenOCD:

$ sudo openocd -f interface/stlink-v2.cfg -f target/stm32f4x.cfg -c init -c "reset␣


,→halt" -c "flash write_image erase nuttx.bin 0x08000000"

Reset the board and using the 'NSH>' terminal start the LVGL demo:

nsh> lvgldemo

Where can I find more information?

• This blog post: LVGL on LPCXpresso54628


• NuttX mailing list: Apache NuttX Mailing List

3.3.2 RT-Thread RTOS

What is RT-Thread?

RT-Thread is an open source, neutral, and community-based real-time operating system (RTOS). RT-Thread has Stan-
dard version and Nano version. For resource-constrained microcontroller (MCU) systems, the Nano version that re-
quires only 3 KB Flash and 1.2 KB RAM memory resources can be tailored with easy-to-use tools. For resource-rich IoT
devices, RT-Thread can use the online software package management tool, together with system configuration tools, to
achieve intuitive and rapid modular cutting, seamlessly import rich software packages; thus, achieving complex functions
like Android's graphical interface and touch sliding effects, smart voice interaction effects, and so on.

Key features

• Designed for resource-constrained devices, the minimum kernel requires only 1.2KB of RAM and 3 KB of Flash.
• A variety of standard interfaces, such as POSIX, CMSIS, C++ application environment.
• Has rich components and a prosperous and fast growing package ecosystem
• Elegant code style, easy to use, read and master.
• High Scalability. RT-Thread has high-quality scalable software architecture, loose coupling, modularity, is easy to
tailor and expand.
• Supports high-performance applications.
• Supports all mainstream compiling tools such as GCC, Keil and IAR.
• Supports a wide range of architectures and chips.

3.3. (RT)OS 269


LVGL Documentation 9.0

How to run LVGL on RT-Thread?

2/72/72/72/7
LVGL has registered as a software package of RT-Thread. By using Env tool or RT-Thread Studio IDE, RT-Thread users
can easily download LVGL source code and combine with RT-Thread project. RT-Thread community has port LVGL to
several BSPs:

Tutorials

3.3.3 FreeRTOS

TODO

3.3.4 Zephyr

TODO

3.4 Bindings

3.4.1 Micropython

What is Micropython?

Micropython is Python for microcontrollers. Using Micropython, you can write Python3 code and run it even on a bare
metal architecture with limited resources.

Highlights of Micropython

• Compact - Fits and runs within just 256k of code space and 16k of RAM. No OS is needed, although you can also
run it with an OS, if you want.
• Compatible - Strives to be as compatible as possible with normal Python (known as CPython).
• Versatile - Supports many architectures (x86, x86-64, ARM, ARM Thumb, Xtensa).
• Interactive - No need for the compile-flash-boot cycle. With the REPL (interactive prompt) you can type com-
mands and execute them immediately, run scripts, etc.
• Popular - Many platforms are supported. The user base is growing bigger. Notable forks: MicroPython, Circuit-
Python, MicroPython_ESP32_psRAM_LoBo
• Embedded Oriented - Comes with modules specifically for embedded systems, such as the machine module for
accessing low-level hardware (I/O pins, ADC, UART, SPI, I2C, RTC, Timers etc.)

3.4. Bindings 270


LVGL Documentation 9.0

Why Micropython + LVGL?

Micropython does not have a good native high-level GUI library. LVGL is an Object-Oriented Component Based high-
level GUI library, which seems to be a natural candidate to map into a higher level language, such as Python. LVGL is
implemented in C and its APIs are in C.

Here are some advantages of using LVGL in Micropython:

• Develop GUI in Python, a very popular high level language. Use paradigms such as Object-Oriented Programming.
• Usually, GUI development requires multiple iterations to get things right. With C, each iteration consists of
Change code > Build > Flash > Run. In Micropython it's just Change code > Run ! You can
even run commands interactively using the REPL (the interactive prompt)

Micropython + LVGL could be used for:

• Fast prototyping GUI.


• Shortening the cycle of changing and fine-tuning the GUI.
• Modelling the GUI in a more abstract way by defining reusable composite objects, taking advantage of Python's
language features such as Inheritance, Closures, List Comprehension, Generators, Exception Handling, Arbitrary
Precision Integers and others.
• Make LVGL accessible to a larger audience. No need to know C to create a nice GUI on an embedded system.
This goes well with CircuitPython vision. CircuitPython was designed with education in mind, to make it easier for
new or inexperienced users to get started with embedded development.
• Creating tools to work with LVGL at a higher level (e.g. drag-and-drop designer).

So what does it look like?

TL;DR: It's very much like the C API, but Object-Oriented for LVGL components.
Let's dive right into an example!

A simple example

import lvgl as lv
lv.init()
scr = lv.obj()
btn = lv.btn(scr)
btn.align(lv.ALIGN.CENTER, 0, 0)
label = lv.label(btn)
label.set_text('Hello World!')
lv.scr_load(scr)

3.4. Bindings 271


LVGL Documentation 9.0

How can I use it?

Online Simulator

If you want to experiment with LVGL + Micropython without downloading anything - you can use our online simulator!
It's a fully functional LVGL + Micropython that runs entirely in the browser and allows you to edit a python script and
run it.
Click here to experiment on the online simulator
Many LVGL examples are available also for Micropython.Just click the link!

PC Simulator

Micropython is ported to many platforms. One notable port is "unix", which allows you to build and run Micropython
(+LVGL) on a Linux machine. (On a Windows machine you might need Virtual Box or WSL or MinGW or Cygwin etc.)
Click here to know more information about building and running the unix port

Embedded Platforms

In the end, the goal is to run it all on an embedded platform. Both Micropython and LVGL can be used on many embedded
architectures. lv_micropython is a fork of Micropython+LVGL and currently supports Linux, ESP32, STM32 and RP2.
It can be ported to any other platform supported by Micropython.
You would also need display and input drivers. You can either use one of the existing drivers provided with
lv_micropython, or you can create your own input/display drivers for your specific hardware.Drivers can be implemented
either in C as a Micropython module, or in pure Python!
lv_micropython already contains these drivers:
• Display drivers:
– SDL on Linux
– ESP32 specific: ILI9341, ILI9488, GC9A01, ST7789, ST7735
– Generic (pure Python): ILI9341, ST7789, ST7735
• Input drivers:
– SDL, XPT2046, FT6X36, ESP32 ADC with resistive touch

Where can I find more information?

• lv_micropython README
• lv_binding_micropython README
• The LVGL micropython forum (Feel free to ask anything!)
• At Micropython: docs and forum
• Blog Post, a little outdated.

3.4. Bindings 272


LVGL Documentation 9.0

The Micropython Binding is auto generated!

LVGL is a git submodule inside lv_micropython (LVGL is a git submodule of lv_binding_micropython which is itself
a submodule of lv_micropython).When building lv_micropython, the public LVGL C API is scanned and Micropython
API is auto-generated. That means that lv_micropython provides LVGL API for any LVGL version, and generally does
not require code changes as LVGL evolves.

LVGL C API Coding Conventions

To support the auto-generation of the Python API, the LVGL C API must follow some coding conventions:
• Use enums instead of macros. If inevitable to use defines export them with
LV_EXPORT_CONST_INT(defined_value) right after the define.
• In function arguments use type name[] declaration for array parameters instead of type * name
• Use typed pointers instead of void * pointers
• Widget constructor must follow the lv_<widget_name>_create(lv_obj_t * parent) pattern.
• Widget members function must start with lv_<modul_name> and should receive lv_obj_t * as first argu-
ment which is a pointer to widget object itself.
• struct APIs should follow the widgets' conventions. That is to receive a pointer to the struct as the first
argument, and the prefix of the struct name should be used as the prefix of the function name too (e.g.
lv_disp_set_default(lv_disp_t * disp))
• Functions and structs which are not part of the public API must begin with underscore in order to mark them
as "private".
• Argument must be named in H files too.
• Do not malloc into a static or global variables. Instead declare the variable in LV_ITERATE_ROOTS list in
lv_gc.h and mark the variable with GC_ROOT(variable) when it's used.See Memory Management
• To register and use callbacks one of the followings needs to be followed. See Callbacks
– Pass a pointer to a struct as the first argument of both the registration function and the callback. That
struct must contain void * user_data field.
– The last argument of the registration function must be void * user_data and the same user_data
needs to be passed as the last argument of the callback.
Most of these rules are simple and straightforward but there are two related concepts that worth a deeper look: Memory
Management and Callbacks.

Memory Management

When LVGL runs in Micropython, all dynamic memory allocations (lv_malloc) are handled by Micropython's mem-
ory manager which is garbage-collected (GC).To prevent GC from collecting memory prematurely, all dynamic allocated
RAM must be reachable by GC.GC is aware of most allocations, except from pointers on the Data Segment:
• Pointers which are global variables
• Pointers which are static global variables
• Pointers which are static local variables
Such pointers need to be defined in a special way to make them reachable by GC

3.4. Bindings 273


LVGL Documentation 9.0

Identify The Problem

Problem happens when an allocated memory's pointer (return value of lv_malloc) is stored only in either global,
static global or static local pointer variable and not as part of a previously allocated struct or other variable.

Solve The Problem

• Replace the global/static local var with LV_GC_ROOT(_var)


• Include lv_gc.h on files that use LV_GC_ROOT
• Add _var to LV_ITERATE_ROOTS on lv_gc.h

Example

https://github.com/lvgl/lvgl/commit/adced46eccfa0437f84aa51aedca4895cc3c679c

More Information

Callbacks

In C a callback is just a function pointer. But in Micropython we need to register a Micropython callable object for each
callback. Therefore in the Micropython binding we need to register both a function pointer and a Micropython object for
every callback.
Therefore we defined a callback convention for the LVGL C API that expects lvgl headers to be defined in a certain way.
Callbacks that are declared according to the convention would allow the binding to register a Micropython object next to
the function pointer when registering a callback, and access that object when the callback is called.
The basic idea is that we have void * user_data field that is used automatically by the Micropython Binding to
save the Micropython callable object for a callback. This field must be provided when registering the function pointer,
and provided to the callback function itself.Although called "user_data", the user is not expectd to read/write that field.
Instead, the Micropython glue code uses user_data to automatically keep track of the Micropython callable object.
The glue code updates it when the callback is registered, and uses it when the callback is called in order to invoke a call
to the original callable object.
There are a few options for defining a callback in LVGL C API:
• Option 1: user_data in a struct
– There's a struct that contains a field called void * user_data
– A pointer to that struct is provided as the first argument of a callback registration function
– A pointer to that struct is provided as the first argument of the callback itself
• Option 2: user_data as a function argument
– A parameter called void * user_data is provided to the registration function as the last argument
– The callback itself recieves void * as the last argument
• Option 3: both callback and user_data are struct fields
– The API exposes a struct with both function pointer member and user_data member
– The function pointer member receives the same struct as its first argument

3.4. Bindings 274


LVGL Documentation 9.0

In practice it's also possible to mix these options, for example provide a struct pointer when registering a callback (option
1) and provide user_data argument when calling the callback (options 2), as long as the same user_data that
was registered is passed to the callback when it's called.

Examples

• lv_anim_t contains user_data field. lv_anim_set_path_cb registers path_cb callback. Both


lv_anim_set_path_cb and lv_anim_path_cb_t recieve lv_anim_t as their first argument
• path_cb field can also be assigned directly in the Python code because it's a member of lv_anim_t which
contains user_data field, and lv_anim_path_cb_t recieve lv_anim_t as its first argument.
• lv_imgfont_create registers path_cb and recieves user_data as the last argument. The callback
lv_imgfont_get_path_cb_t also receieves the user_data as the last argument.

More Information

• In the Blog and in the README


• [v6.0] Callback conventions #1036
• Various discussions: here and here and here

3.4.2 Cpp

In progress: https://github.com/lvgl/lv_binding_cpp

3.4.3 PikaScript

What is PikaScript ?

PikaScript is a Python interpreter designed specifically for microcontrollers, and it supports a subset of the common
Python3 syntax.
It's lighter, requiring only 32k of code space and 4k of RAM, which means it can run on stm32f103c8 (blue-pill) or even
stm32g030c8, on the other hand, you can leave valuable space for more material or larger buffer areas.
It is simpler, out of the box, runs with no porting and configuration at all, does not depend on OS or file system, has good
support for popular IDEs for Windows platforms like Keil, IAR, RT-Thread-Studio, and of course, supports linux-gcc
development platforms.
It's smarter, with a unique C module mechanism that allows you to generate bindings automatically by simply writing the
API for the C module in Python, and you don't need to deal with the headache of writing any macros or global tables
manually. On the other hand, all C modules have sophisticated smart hints, even hinting at the types of your arguments .

3.4. Bindings 275


LVGL Documentation 9.0

Why PikaScript + LVGL ?

PikaScript now supports the main features of LVGL8, and these APIs are fully compatible with Micropython!
This means that you can continue to use already written code from Micropython, and then use less code space and RAM.
Enjoy detailed code hints down to the parameter type for a better programming experience
Use a more convenient IDE, such as vs-based simulation projects

So how does it look like?

Here are some examples of lvgl that PikaScript can already run, they are mainly from the lvgl documentation examples

LV_ARC

import pika_lvgl as lv
import PikaStdLib
mem = PikaStdLib.MemChecker()
# Create an Arc
arc = lv.arc(lv.scr_act())
arc.set_end_angle(200)
arc.set_size(150, 150)
arc.center()
print('mem used max: %0.2f kB' % (mem.getMax()))
print('mem used now: %0.2f kB' % (mem.getNow()))

LV_BAR

import pika_lvgl as lv
import PikaStdLib
mem = PikaStdLib.MemChecker()
bar1 = lv.bar(lv.scr_act())
bar1.set_size(200, 20)
bar1.center()
bar1.set_value(70, lv.ANIM.OFF)
print('mem used max: %0.2f kB' % (mem.getMax()))
print('mem used now: %0.2f kB' % (mem.getNow()))

LV_BTN

import pika_lvgl as lv
import PikaStdLib
mem = PikaStdLib.MemChecker()
def event_cb_1(evt):
print('in evt1')
print('mem used now: %0.2f kB' % (mem.getNow()))
def event_cb_2(evt):
print('in evt2')
print('mem used now: %0.2f kB' % (mem.getNow()))
btn1 = lv.btn(lv.scr_act())
(continues on next page)

3.4. Bindings 276


LVGL Documentation 9.0

(continued from previous page)


btn1.align(lv.ALIGN.TOP_MID, 0, 10)
btn2 = lv.btn(lv.scr_act())
btn2.align(lv.ALIGN.TOP_MID, 0, 50)
btn1.add_event(event_cb_1, lv.EVENT.CLICKED, 0)
btn2.add_event(event_cb_2, lv.EVENT.CLICKED, 0)
print('mem used max: %0.2f kB' % (mem.getMax()))
print('mem used now: %0.2f kB' % (mem.getNow()))

LV_CHECKBOX

import pika_lvgl as lv
import PikaStdLib
mem = PikaStdLib.MemChecker()
cb = lv.checkbox(lv.scr_act())
cb.set_text("Apple")
cb.align(lv.ALIGN.TOP_LEFT, 0 ,0)
cb = lv.checkbox(lv.scr_act())
cb.set_text("Banana")
cb.add_state(lv.STATE.CHECKED)
cb.align(lv.ALIGN.TOP_LEFT, 0 ,30)
cb = lv.checkbox(lv.scr_act())
cb.set_text("Lemon")
cb.add_state(lv.STATE.DISABLED)
cb.align(lv.ALIGN.TOP_LEFT, 0 ,60)
cb = lv.checkbox(lv.scr_act())
cb.add_state(lv.STATE.CHECKED | lv.STATE.DISABLED)
cb.set_text("Melon")
cb.align(lv.ALIGN.TOP_LEFT, 0 ,90)
print('mem used max: %0.2f kB' % (mem.getMax()))
print('mem used now: %0.2f kB' % (mem.getNow()))

How does it work?

PikaScript has a unique C module smart binding tool


Just write the Python interface in pika_lvgl.pyi (.pyi is the python interface file)

# pika_lvgl.pyi
class arc(lv_obj):
def set_end_angle(self, angle: int): ...
def set_bg_angles(self, start: int, end: int): ...
def set_angles(self, start: int, end: int): ...

Then PikaScript's pre-compiler can automatically bind the following C functions, simply by naming the functions in the
module_class_method format, without any additional work, and all binding and registration is done automatically.

/* pika_lvgl_arc.c */
void pika_lvgl_arc_set_end_angle(PikaObj* self, int angle) {
lv_obj_t* lv_obj = obj_getPtr(self, "lv_obj");
lv_arc_set_end_angle(lv_obj, angle);
}
(continues on next page)

3.4. Bindings 277


LVGL Documentation 9.0

(continued from previous page)


void pika_lvgl_arc_set_bg_angles(PikaObj *self, int start, int end){
lv_obj_t* lv_obj = obj_getPtr(self, "lv_obj");
lv_arc_set_bg_angles(lv_obj, start, end);
}
void pika_lvgl_arc_set_angles(PikaObj *self, int start, int end){
lv_obj_t* lv_obj = obj_getPtr(self, "lv_obj");
lv_arc_set_angles(lv_obj, start, end);
}

To use the module, just import pika_lvgl and the precompiler will automatically scan main.py and bind the
pika_lvgl module

$ ./rust-msc-latest-win10.exe
(pikascript) packages installed:
pikascript-core==v1.10.0
PikaStdLib==v1.10.0
PikaStdDevice==v1.10.0
(pikascript) pika compiler:
scaning main.py...
binding pika_lvgl.pyi...

The precompiler is written in Rust, runs on windows and linux, and is completely open source.
In addition to binding C modules, the precompiler compiles Python scripts to bytecode in the PC, reducing the size of
the script and increasing its speed.

How can I use it?

The simulation repo on vs is available on https://github.com/pikasTech/lv_pikascript

3.4.4 JavaScript

With lv_binding_js you can write lvgl with JavaScript.


It uses React's virtual DOM concept to manipulate lvgl UI components, providing a familiar React-like experience to
users.
Code
Code Runing on Real Device

Table of Contents

• Features
• Demo
• Building
• Components
• Font

3.4. Bindings 278


LVGL Documentation 9.0

• Animation
• Style
• JSAPI
• Thanks

Features

• Support all lvgl built-in components


• Fully suport lvgl flex and grid style
• support most lvgl style2/7just write like html5 css
• support dynamic load image
• Fully support lvgl animation

Demo

See the demo folder

Building

The following are developer notes on how to build lvgljs on your native platform. They are not complete guides, but
include notes on the necessary libraries, compile flags, etc.

lvgljs

JS Bundle

Components

Font

Buitin-Symbol

Animation

Animation

3.4. Bindings 279


LVGL Documentation 9.0

Style

JSAPI

Thanks

lvgljs depends on following excellent work


lvgl: Create beautiful UIs for any MCU, MPU and display type QuickJS: JavaScript engine libuv: platform abstraction
layer curl: HTTP client txiki.js: Tiny JavaScript runtime

3.4. Bindings 280


CHAPTER

FOUR

PORTING

4.1 Set up a project

4.1.1 Get the library

LVGL is available on GitHub: https://github.com/lvgl/lvgl.


You can clone it or Download the latest version of the library from GitHub.

4.1.2 Add lvgl to your project

The graphics library itself is the lvgl directory. It contains a couple of folders but to use lvgl you only need .c and
.h files from the src folder.

Automatically add files

If your IDE automatically adds the files from the folders copied to the project folder (as Eclipse or VSCode does), you
can simply copy the lvgl folder as it is into your project.

Make and CMake

LVGL also supports make and CMake build systems out of the box. To add LVGL to your Makefile based build system
add these lines to your main Makefile:

LVGL_DIR_NAME ?= lvgl #The name of the lvgl folder (change this if you have␣
,→renamed it)

LVGL_DIR ?= ${shell pwd} #The path where the lvgl folder is


include $(LVGL_DIR)/$(LVGL_DIR_NAME)/lvgl.mk

For integration with CMake take a look this section of the Documentation.

281
LVGL Documentation 9.0

Other platforms and tools

The Get started section contains many platform specific descriptions e.g. for ESP32, Arduino, NXP, RT-Thread, NuttX,
etc.

Demos and Examples

The lvgl folder also contains an examples and a demos folder. If you needed to add the source files manually to
your project, you can do the same with the source files of these two folders too. make and CMake handles the examples
and demos, so no extra action required in these cases.

4.1.3 Configuration file

There is a configuration header file for LVGL called lv_conf.h. You modify this header to set the library's basic behavior,
disable unused modules and features, adjust the size of memory buffers in compile-time, etc.
To get lv_conf.h copy lvgl/lv_conf_template.h next to the lvgl directory and rename it to lv_conf.h. Open the file
and change the #if 0 at the beginning to #if 1 to enable its content. So the layout of the files should look like this:

|-lvgl
|-lv_conf.h
|-other files and folders

Comments in the config file explain the meaning of the options. Be sure to set at least LV_COLOR_DEPTH according to
your display's color depth. Note that, the examples and demos explicitly need to be enabled in lv_conf.h.
Alternatively, lv_conf.h can be copied to another place but then you should add the LV_CONF_INCLUDE_SIMPLE
define to your compiler options (e.g. -DLV_CONF_INCLUDE_SIMPLE for GCC compiler) and set the include path
manually (e.g. -I../include/gui). In this case LVGL will attempt to include lv_conf.h simply with #in-
clude "lv_conf.h".
You can even use a different name for lv_conf.h. The custom path can be set via the LV_CONF_PATH define. For
example -DLV_CONF_PATH="/home/joe/my_project/my_custom_conf.h"
If LV_CONF_SKIP is defined, LVGL will not try to include lv_conf.h. Instead you can pass the config defines using
build options. For example "-DLV_COLOR_DEPTH=32 -DLV_USE_BTN=1". The unset options will get a default
value which is the same as the ones in lv_conf_template.h.
LVGL also can be used via Kconfig and menuconfig. You can use lv_conf.h together with Kconfig, but keep
in mind that the value from lv_conf.h or build settings (-D...) overwrite the values set in Kconfig. To ignore the
configs from lv_conf.h simply remove its content, or define LV_CONF_SKIP.

4.1.4 Initialization

To use the graphics library you have to initialize it and setup required components. The order of the initialization is:
1. Call lv_init().
2. Initialize your drivers.
3. Register the display and input devices drivers in LVGL. Learn more about Display and Input device registration.
4. Call lv_tick_inc(x) every x milliseconds in an interrupt to report the elapsed time to LVGL. Learn more.
5. Call lv_timer_handler() every few milliseconds to handle LVGL related tasks. Learn more.

4.1. Set up a project 282


LVGL Documentation 9.0

4.2 Display interface

To create a display for LVGL call lv_disp_t * disp = lv_disp_create(hor_res, ver_res). You
can create a multiple displays and a different driver for each (see below),

4.2.1 Basic setup

Draw buffer(s) are simple array(s) that LVGL uses to render the screen's content. Once rendering is ready the content of
the draw buffer is sent to the display using the flush_cb function.

flush_cb

An example flush_cb looks like this:

void my_flush_cb(lv_disp_t * disp, const lv_area_t * area, lv_color_t * buf)


{
/*The most simple case (but also the slowest) to put all pixels to the screen one-
,→by-one

*`put_px` is just an example, it needs to be implemented by you.*/


int32_t x, y;
for(y = area->y1; y <= area->y2; y++) {
for(x = area->x1; x <= area->x2; x++) {
put_px(x, y, *color_p);
color_p++;
}
}

/* IMPORTANT!!!
* Inform LVGL that you are ready with the flushing and buf is not used anymore*/
lv_disp_flush_ready(disp);
}

Use lv_disp_set_flush_cb(disp, my_flush_cb) to set a new flush_cb.


lv_disp_flush_ready(disp) needs to be called when flushing is ready to inform LVGL the buffer is not used
anymore by the driver and it can render new content into it.
LVGL might render the screen in multiple chunks and therefore call flush_cb multiple times. To see if the current
one is the last chunk of rendering use lv_disp_flush_is_last(disp).

Draw buffers

The draw buffers can be set with lv_disp_set_draw_buffers(disp, buf1, buf2, buf_size_px,
render_mode);
• buf1 a bufer where LVGL can render
• buf2 a second optional buffer (see more details below)
• buf_size_byte size of the buffer(s) in bytes
• render_mode
– LV_DISP_RENDER_MODE_PARTIAL Use the buffer(s) to render the screen is smaller parts. This way
the buffers can be smaller then the display to save RAM. At least 1/10 sceen size buffer(s) are recommended.
In flush_cb the rendered images needs to be copied to the given area of the display.

4.2. Display interface 283


LVGL Documentation 9.0

– LV_DISP_RENDER_MODE_DIRECT The buffer(s) has to be screen sized and LVGL will render into the
correct location of the buffer. This way the buffer always contain the whole image. If two buffer are used the
rendered ares are automatically copied to the other buffer after flushing. Due to this in flush_cb typically
only a frame buffer address needs to be changed and always the changed areas will be redrawn.
– LV_DISP_RENDER_MODE_FULL The buffer can smaller or screen sized but LVGL will always redraw the
whole screen even is only 1 pixel has been changed. If two screen sized draw buffers are provided, LVGL's
display handling works like "traditional" double buffering. This means the flush_cb callback only has to
update the address of the framebuffer (color_p parameter).
Example:

static lv_color_t buf[LCD_HOR_RES * LCD_VER_RES / 10];


lv_disp_set_draw_buffers(disp, buf, NULL, sizeof(buf), LV_DISP_RENDER_MODE_PARTIAL);

One buffer

If only one buffer is used LVGL draws the content of the screen into that draw buffer and sends it to the display via the
flush_cb. LVGL then needs to wait until the content of the buffer is sent to the display before drawing something new
into it.

Two buffers

If two buffers are used LVGL can draw into one buffer while the content of the other buffer is sent to the display in the
background. DMA or other hardware should be used to transfer data to the display so the MCU can continue drawing.
This way, the rendering and refreshing of the display become parallel operations.

4.2.2 Advnaced options

Resoltion

To set the resolution of the display after creation use lv_disp_set_res(disp, hor_res, ver_res);
It's not mandatory to use the whole display for LVGL, however in some cases the physical resolution is important. For
example the touchpad still sees the whole resolution and the values needs to be converted to the active LVGL display area.
So the physical resoltution and the offset of the active area can be set with lv_disp_set_physical_res(disp,
hor_res, ver_res);and lv_disp_set_offset(disp, x, y);

Rotation

LVGL supports rotation of the display in 90 degree increments. You can select whether you'd like software rotation or
hardware rotation.
The orientation of the display can be changed with lv_disp_set_rotation(disp, LV_DISP_ROTATION_0/
90/180/270, true/false). LVGL will swap the horizontal and vertical resolutions internally according to the
set degree. IF the last paramter is true LVGL will rotate the rendered image. If it's false the display driver should
rotate the rendered image.

4.2. Display interface 284


LVGL Documentation 9.0

Color format

Set the color format of the display. The default is LV_COLOR_FORMAT_NATIVE which means LVGL render with the
follow formats dpeneding on LV_COLOR_DEPTH:
• LV_COLOR_DEPTH 32 XRGB8888 (4 bytes/pixel)
• LV_COLOR_DEPTH 24 RGB888 (3 bytes/pixel)
• LV_COLOR_DEPTH 16 RGB565 (2 bytes/pixel)
• LV_COLOR_DEPTH 8 L8 (1 bytes/pixel)
The color_format can be changed with lv_disp_set_color_depth(disp, LV_COLOR_FORMAT_...
) to the following values:
• LV_COLOR_FORMAT_NATIVE_ALPHA Append an alpha byte to the native format resulting in A8L8,
ARGB8565, ARGB8888 formats.
• LV_COLOR_FORMAT_NATIVE_REVERSE Reverse the byte order of the native format. Useful if the rendered
image is sent to the disply via SPI and the display needs the bytes in the opposite order.
• LV_COLOR_FORMAT_L8 Lightness only on 8 bit
• LV_COLOR_FORMAT_A8 Alpha only on 8 bit
• LV_COLOR_FORMAT_I8 Indexed (palette) 8 bit
• LV_COLOR_FORMAT_A8L8 Lightness on 8 bit with 8 bit alpha
• LV_COLOR_FORMAT_ARGB2222 ARGB with 2 bit for each channel
• LV_COLOR_FORMAT_RGB565 16 bit RGB565 format without alpha channel
• LV_COLOR_FORMAT_ARGB8565 16 bit RGB565 format and 8 bit alpha channel
• LV_COLOR_FORMAT_ARGB1555 5 bit for each color channel and 1 bit for alpha
• LV_COLOR_FORMAT_ARGB4444 4 bit for each channel
• LV_COLOR_FORMAT_RGB888 8 bit for each color channel with out alpha channel
• LV_COLOR_FORMAT_ARGB8888 8 bit for each channel
• LV_COLOR_FORMAT_XRGB8888 8 bit for each color channel and 8 bit placholder for the alpha cannel
If the color fotmat is set to non-native draw_ctx->buffer_convert function will be called before calling
flush_cb to convert the native color format to the desired, therfore rendering in non-native formats has a negative
effect on peroformance. Learn more about draw_ctx here.
It's very important that draw buffer(s) should be large enough for both the native format and the target color format. For
example if LV_COLOR_DEPTH == 16 and LV_COLOR_FORMAT_XRGB8888 is selected LVGL will choosoe the
larger to figure out how many pixel can be rendered at once. Therefore with LV_DISP_RENDER_MODE_FULL and the
larger pixel size needs to choosen.
LV_DISP_RENDER_MODE_DIRECT supports only the LV_COLOR_FORMAT_NATIVE format.

4.2. Display interface 285


LVGL Documentation 9.0

Antialiasing

lv_disp_set_antialiasing(disp, true/false) enables/disables the antialiasing (edge smoothing) on


the given display.

User data

With lv_disp_set_user_data(disp, p) a pointer to a custom data can be stored in display object.

4.2.3 Events

lv_disp_add_event(disp, event_cb, LV_DISP_EVENT_..., user_data) adds an event handler


to a display. The following events are sent:
• LV_DISP_EVENT_INVALIDATE_AREA An area is invalidated (marked for redraw).
lv_event_get_param(e) returns a pointer to an lv_area_t varaible with the coordinates of the
area to be invalidated. The ara can be freely modified is needed to adopt it the specialrequirement of the display.
Usually needed with monoschrome displays to invalidate Nx8 lines at once.
• LV_DISP_EVENT_RENDER_START Called when rendering starts.
• LV_DISP_EVENT_RENDER_READY Called when rendering is ready
• LV_DISP_EVENT_RESOLUTION_CHANGED CAlled when the resolution changes due to
lv_disp_set_resolution() or lv_disp_set_rotation().

4.2.4 Other options

Decoupling the display refresh timer

Normally the dirty (a.k.a invalid) areas are checked and redrawn in every LV_DEF_REFR_PERIOD milliseconds (set
in lv_hal_disp.h). However, in some cases you might need more control on when the display refreshing happen,
for example to synchronize rendering with VSYNC or the TE signal.
You can do this in the following way:

/*Delete the original display refresh timer*/


lv_timer_del(disp->refr_timer);
disp->refr_timer = NULL;

/*Call this anywhere you want to refresh the dirty areas*/


_lv_disp_refr_timer(NULL);

If you have multiple displays call lv_disp_set_deafult(disp1); to select the display to refresh before
_lv_disp_refr_timer(NULL);.
Note that lv_timer_handler() and _lv_disp_refr_timer() can not run at the same time.
If the performance monitor is enabled, the value of LV_DEF_REFR_PERIOD needs to be set to be consistent with the
refresh period of the display to ensure that the statistical results are correct.

4.2. Display interface 286


LVGL Documentation 9.0

4.2.5 Further reading

• lv_port_disp_template.c for a template for your own driver.


• Drawing to learn more about how rendering works in LVGL.
• Display features to learn more about higher level display features.

4.2.6 API

Typedefs

typedef struct _lv_disp_t lv_disp_t

Enums

enum lv_disp_rotation_t
Values:

enumerator LV_DISP_ROTATION_0

enumerator LV_DISP_ROTATION_90

enumerator LV_DISP_ROTATION_180

enumerator LV_DISP_ROTATION_270

enum lv_disp_render_mode_t
Values:

enumerator LV_DISP_RENDER_MODE_PARTIAL
Use the buffer(s) to render the screen is smaller parts. This way the buffers can be smaller then the display to
save RAM. At least 1/10 sceen size buffer(s) are recommended.

enumerator LV_DISP_RENDER_MODE_DIRECT
The buffer(s) has to be screen sized and LVGL will render into the correct location of the buffer. This way
the buffer always contain the whole image. Only the changed ares will be updated. With 2 buffers the buffers'
content are kept in sync automatically and in flush_cb only address change is required.

enumerator LV_DISP_RENDER_MODE_FULL
Always redraw the whole screen even if only one pixel has been changed. With 2 buffers in flush_cb only and
address change is required.

enum lv_scr_load_anim_t
Values:

4.2. Display interface 287


LVGL Documentation 9.0

enumerator LV_SCR_LOAD_ANIM_NONE

enumerator LV_SCR_LOAD_ANIM_OVER_LEFT

enumerator LV_SCR_LOAD_ANIM_OVER_RIGHT

enumerator LV_SCR_LOAD_ANIM_OVER_TOP

enumerator LV_SCR_LOAD_ANIM_OVER_BOTTOM

enumerator LV_SCR_LOAD_ANIM_MOVE_LEFT

enumerator LV_SCR_LOAD_ANIM_MOVE_RIGHT

enumerator LV_SCR_LOAD_ANIM_MOVE_TOP

enumerator LV_SCR_LOAD_ANIM_MOVE_BOTTOM

enumerator LV_SCR_LOAD_ANIM_FADE_IN

enumerator LV_SCR_LOAD_ANIM_FADE_ON

enumerator LV_SCR_LOAD_ANIM_FADE_OUT

enumerator LV_SCR_LOAD_ANIM_OUT_LEFT

enumerator LV_SCR_LOAD_ANIM_OUT_RIGHT

enumerator LV_SCR_LOAD_ANIM_OUT_TOP

enumerator LV_SCR_LOAD_ANIM_OUT_BOTTOM

Functions

lv_disp_t *lv_disp_create(lv_coord_t hor_res, lv_coord_t ver_res)


Create a new display with the given resolution
Parameters
• hor_res -- horizontal resolution in pixels
• ver_res -- vertical resolution in pixels
Returns pointer to a display object or NULL on error

4.2. Display interface 288


LVGL Documentation 9.0

void lv_disp_remove(lv_disp_t *disp)


Remove a display
Parameters disp -- pointer to display
void lv_disp_set_default(lv_disp_t *disp)
Set a default display. The new screens will be created on it by default.
Parameters disp -- pointer to a display
lv_disp_t *lv_disp_get_default(void)
Get the default display
Returns pointer to the default display
lv_disp_t *lv_disp_get_next(lv_disp_t *disp)
Get the next display.
Parameters disp -- pointer to the current display. NULL to initialize.
Returns the next display or NULL if no more. Gives the first display when the parameter is NULL.
void lv_disp_set_res(lv_disp_t *disp, lv_coord_t hor_res, lv_coord_t ver_res)
Sets the resolution of a display. LV_EVENT_RESOLUTION_CHANGED event will be sent. Here the native
resolution of the device should be set. If the display will be rotated later with lv_disp_set_rotation
LVGL will swap the hor. and ver. resolution automatically.
Parameters
• disp -- pointer to a display
• hor_res -- the new horizontal resolution
• ver_res -- the new vertical resolution
void lv_disp_set_physical_res(lv_disp_t *disp, lv_coord_t hor_res, lv_coord_t ver_res)
It's not mandatory to use the whole display for LVGL, however in some cases physical resolution is important. For
example the touchpad still sees whole resolution and the values needs to be converted to the active LVGL display
area.
Parameters
• disp -- pointer to a display
• hor_res -- the new physical horizontal resolution, or -1 to assume it's the same as the normal
hor. res.
• ver_res -- the new physical vertical resolution, or -1 to assume it's the same as the normal
hor. res.
void lv_disp_set_offset(lv_disp_t *disp, lv_coord_t x, lv_coord_t y)
If physical resolution is not the same as the normal resolution the offset of the active display area can be set here.
Parameters
• disp -- pointer to a display
• x -- X offset
• y -- Y offset

4.2. Display interface 289


LVGL Documentation 9.0

void lv_disp_set_rotation(lv_disp_t *disp, lv_disp_rotation_t rotation, bool sw_rotate)


Set the rotation of this display. LVGL will swap the horizontal and vertical resolutions internally.
Parameters
• disp -- pointer to a display (NULL to use the default display)
• rotation -- LV_DISP_ROTATION_0/90/180/270
• sw_rotate -- true: make LVGL rotate the rendered image; false: the display driver should
rotate the rendered image
void lv_disp_set_dpi(lv_disp_t *disp, lv_coord_t dpi)
Set the DPI (dot per inch) of the display. dpi = sqrt(hor_res^2 + ver_res^2) / diagonal"
Parameters
• disp -- pointer to a display
• dpi -- the new DPI
lv_coord_t lv_disp_get_hor_res(const lv_disp_t *disp)
Get the horizontal resolution of a display.
Parameters disp -- pointer to a display (NULL to use the default display)
Returns the horizontal resolution of the display.
lv_coord_t lv_disp_get_ver_res(const lv_disp_t *disp)
Get the vertical resolution of a display
Parameters disp -- pointer to a display (NULL to use the default display)
Returns the vertical resolution of the display
lv_coord_t lv_disp_get_physical_hor_res(const lv_disp_t *disp)
Get the physical horizontal resolution of a display
Parameters disp -- pointer to a display (NULL to use the default display)
Returns the physical horizontal resolution of the display
lv_coord_t lv_disp_get_physical_ver_res(const lv_disp_t *disp)
Get the physical vertical resolution of a display
Parameters disp -- pointer to a display (NULL to use the default display)
Returns the physical vertical resolution of the display
lv_coord_t lv_disp_get_offset_x(const lv_disp_t *disp)
Get the horizontal offset from the full / physical display
Parameters disp -- pointer to a display (NULL to use the default display)
Returns the horizontal offset from the physical display
lv_coord_t lv_disp_get_offset_y(const lv_disp_t *disp)
Get the vertical offset from the full / physical display
Parameters disp -- pointer to a display (NULL to use the default display)
Returns the horizontal offset from the physical display

4.2. Display interface 290


LVGL Documentation 9.0

lv_disp_rotation_t lv_disp_get_rotation(lv_disp_t *disp)


Get the current rotation of this display.
Parameters disp -- pointer to a display (NULL to use the default display)
Returns the current rotation
lv_coord_t lv_disp_get_dpi(const lv_disp_t *disp)
Get the DPI of the display
Parameters disp -- pointer to a display (NULL to use the default display)
Returns dpi of the display
void lv_disp_set_draw_buffers(lv_disp_t *disp, void *buf1, void *buf2, uint32_t buf_size_px,
lv_disp_render_mode_t render_mode)
Set the buffers for a display
Parameters
• disp -- pointer to a display
• buf1 -- first buffer
• buf2 -- second buffer (can be NULL)
• buf_size_px -- size of the buffer in pixels
• render_mode -- LV_DISP_RENDER_MODE_PARTIAL/DIRECT/FULL
void lv_disp_set_flush_cb(lv_disp_t *disp, void (*flush_cb)(struct _lv_disp_t *disp, const lv_area_t *area,
lv_color_t *color_p))
Set the flush callback whcih will be called to copy the rendered image to the display.
Parameters
• disp -- pointer to a display
• flush_cb -- the flush callback
void lv_disp_set_color_format(lv_disp_t *disp, lv_color_format_t color_format)
Set the color format of the display. If set to other than LV_COLOR_FORMAT_NATIVE the draw_ctx's
buffer_convert function will be used to convert the rendered content to the desired color format.
Parameters
• disp -- pointer to a display
• color_format -- By default LV_COLOR_FORMAT_NATIVE to render with L8,
RGB565, RGB888 or ARGB8888. LV_COLOR_FORMAT_NATIVE_REVERSE to change
endianess.
lv_color_format_t lv_disp_get_color_format(lv_disp_t *disp)
Get the color format of the display
Parameters disp -- pointer to a display
Returns the color format
void lv_disp_set_antialaising(lv_disp_t *disp, bool en)
Enable anti-aliasing for the render engine
Parameters
• disp -- pointer to a display

4.2. Display interface 291


LVGL Documentation 9.0

• en -- true/false
bool lv_disp_get_antialiasing(lv_disp_t *disp)
Get if anti-aliasing is enabled for a display or not
Parameters disp -- pointer to a display (NULL to use the default display)
Returns true/false
bool lv_disp_is_double_buffered(lv_disp_t *disp)

void lv_disp_set_draw_ctx(lv_disp_t *disp, void (*draw_ctx_init)(lv_disp_t *disp, struct _lv_draw_ctx_t


*draw_ctx), void (*draw_ctx_deinit)(lv_disp_t *disp, struct _lv_draw_ctx_t
*draw_ctx), size_t draw_ctx_size)
Initialize a new draw context for the display
Parameters
• disp -- pointer to a display
• draw_ctx_init -- init callback
• draw_ctx_deinit -- deinit callback
• draw_ctx_size -- size of the draw context instance
struct _lv_obj_t *lv_disp_get_scr_act(lv_disp_t *disp)
Return a pointer to the active screen on a display
Parameters disp -- pointer to display which active screen should be get. (NULL to use the default
screen)
Returns pointer to the active screen object (loaded by 'lv_scr_load()')
struct _lv_obj_t *lv_disp_get_scr_prev(lv_disp_t *disp)
Return with a pointer to the previous screen. Only used during screen transitions.
Parameters disp -- pointer to display which previous screen should be get. (NULL to use the default
screen)
Returns pointer to the previous screen object or NULL if not used now
void lv_disp_load_scr(struct _lv_obj_t *scr)
Make a screen active
Parameters scr -- pointer to a screen
struct _lv_obj_t *lv_disp_get_layer_top(lv_disp_t *disp)
Return the top layer. The top layer is the same on all screens and it is above the normal screen layer.
Parameters disp -- pointer to display which top layer should be get. (NULL to use the default screen)
Returns pointer to the top layer object
struct _lv_obj_t *lv_disp_get_layer_sys(lv_disp_t *disp)
Return the sys. layer. The system layer is the same on all screen and it is above the normal screen and the top layer.
Parameters disp -- pointer to display which sys. layer should be retrieved. (NULL to use the default
screen)
Returns pointer to the sys layer object

4.2. Display interface 292


LVGL Documentation 9.0

struct _lv_obj_t *lv_disp_get_layer_bottom(lv_disp_t *disp)


Return the bottom layer. The bottom layer is the same on all screen and it is under the normal screen layer. It's
visble only if the the screen is transparent.
Parameters disp -- pointer to display (NULL to use the default screen)
Returns pointer to the bottom layer object
void lv_scr_load_anim(struct _lv_obj_t *scr, lv_scr_load_anim_t anim_type, uint32_t time, uint32_t delay, bool
auto_del)
Switch screen with animation
Parameters
• scr -- pointer to the new screen to load
• anim_type -- type of the animation from lv_scr_load_anim_t, e.g.
LV_SCR_LOAD_ANIM_MOVE_LEFT
• time -- time of the animation
• delay -- delay before the transition
• auto_del -- true: automatically delete the old screen
static inline struct _lv_obj_t *lv_scr_act(void)
Get the active screen of the default display
Returns pointer to the active screen
static inline struct _lv_obj_t *lv_layer_top(void)
Get the top layer of the default display
Returns pointer to the top layer
static inline struct _lv_obj_t *lv_layer_sys(void)
Get the system layer of the default display
Returns pointer to the sys layer
static inline struct _lv_obj_t *lv_layer_bottom(void)
Get the bottom layer of the default display
Returns pointer to the bottom layer
static inline void lv_scr_load(struct _lv_obj_t *scr)
Load a screen on the default display
Parameters scr -- pointer to a screen
void lv_disp_add_event(lv_disp_t *disp, lv_event_cb_t event_cb, lv_event_code_t filter, void *user_data)
Add an event handler to the display
Parameters
• disp -- pointer to a display
• event_cb -- an event callback
• filter -- event code to react or LV_EVENT_ALL
• user_data -- optional user_data
uint32_t lv_disp_get_event_count(lv_disp_t *disp)

4.2. Display interface 293


LVGL Documentation 9.0

lv_event_dsc_t *lv_disp_get_event_dsc(lv_disp_t *disp, uint32_t index)

bool lv_disp_remove_event(lv_disp_t *disp, uint32_t index)

lv_res_t lv_disp_send_event(lv_disp_t *disp, lv_event_code_t code, void *user_data)


Send amn event to a display
Parameters
• disp -- pointer to a display
• code -- an event code. LV_EVENT_...
• user_data -- optional user_data
Returns LV_RES_OK: disp wasn't deleted in the event.
void lv_disp_set_theme(lv_disp_t *disp, struct _lv_theme_t *th)
Set the theme of a display. If there are no user created widgets yet the screens' theme will be updated
Parameters
• disp -- pointer to a display
• th -- pointer to a theme
struct _lv_theme_t *lv_disp_get_theme(lv_disp_t *disp)
Get the theme of a display
Parameters disp -- pointer to a display
Returns the display's theme (can be NULL)
uint32_t lv_disp_get_inactive_time(const lv_disp_t *disp)
Get elapsed time since last user activity on a display (e.g. click)
Parameters disp -- pointer to a display (NULL to get the overall smallest inactivity)
Returns elapsed ticks (milliseconds) since the last activity
void lv_disp_trig_activity(lv_disp_t *disp)
Manually trigger an activity on a display
Parameters disp -- pointer to a display (NULL to use the default display)
void lv_disp_enable_invalidation(lv_disp_t *disp, bool en)
Temporarily enable and disable the invalidation of the display.
Parameters
• disp -- pointer to a display (NULL to use the default display)
• en -- true: enable invalidation; false: invalidation
bool lv_disp_is_invalidation_enabled(lv_disp_t *disp)
Get display invalidation is enabled.
Parameters disp -- pointer to a display (NULL to use the default display)
Returns return true if invalidation is enabled
lv_timer_t *_lv_disp_get_refr_timer(lv_disp_t *disp)
Get a pointer to the screen refresher timer to modify its parameters with lv_timer_... functions.
Parameters disp -- pointer to a display

4.2. Display interface 294


LVGL Documentation 9.0

Returns pointer to the display refresher timer. (NULL on error)


lv_color_t lv_disp_get_chroma_key_color(lv_disp_t *disp)

void lv_disp_set_user_data(lv_disp_t *disp, void *user_data)

void lv_disp_set_driver_data(lv_disp_t *disp, void *driver_data)

void *lv_disp_get_user_data(lv_disp_t *disp)

void *lv_disp_get_driver_data(lv_disp_t *disp)

static inline lv_coord_t lv_dpx(lv_coord_t n)


Scale the given number of pixels (a distance or size) relative to a 160 DPI display considering the DPI of the default
display. It ensures that e.g. lv_dpx(100) will have the same physical size regardless to the DPI of the display.
Parameters n -- the number of pixels to scale
Returns n x current_dpi/160
static inline lv_coord_t lv_disp_dpx(const lv_disp_t *disp, lv_coord_t n)
Scale the given number of pixels (a distance or size) relative to a 160 DPI display considering the DPI of the given
display. It ensures that e.g. lv_dpx(100) will have the same physical size regardless to the DPI of the display.
Parameters
• obj -- a display whose dpi should be considered
• n -- the number of pixels to scale
Returns n x current_dpi/160

4.3 Input device interface

4.3.1 Types of input devices

To create an input device use

/*Register at least one display before you register any input devices*/
lv_indev_t * indev = lv_indev_create();
lv_indev_set_type(indev, LV_INDEV_TYPE_...); /*See below.*/
lv_indev_set_read_cb(indev, read_cb); /*See below.*/

The type member can be:


• LV_INDEV_TYPE_POINTER touchpad or mouse
• LV_INDEV_TYPE_KEYPAD keyboard or keypad
• LV_INDEV_TYPE_ENCODER encoder with left/right turn and push options
• LV_INDEV_TYPE_BUTTON external buttons virtually pressing the screen
read_cb is a function pointer which will be called periodically to report the current state of an input device.
Visit Input devices to learn more about input devices in general.

4.3. Input device interface 295


LVGL Documentation 9.0

Touchpad, mouse or any pointer

Input devices that can click points on the screen belong to this category.

lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
...

void my_input_read(lv_indev_t * indev, lv_indev_data_t*data)


{
if(touchpad_pressed) {
data->point.x = touchpad_x;
data->point.y = touchpad_y;
data->state = LV_INDEV_STATE_PRESSED;
} else {
data->state = LV_INDEV_STATE_RELEASED;
}
}

To set a mouse cursor use lv_indev_set_cursor(indev, &img_cursor).

Keypad or keyboard

Full keyboards with all the letters or simple keypads with a few navigation buttons belong here.
To use a keyboard/keypad:
• Register a read_cb function and use LV_INDEV_TYPE_KEYPAD type.
• An object group has to be created: lv_group_t * g = lv_group_create() and objects have to be
added to it with lv_group_add_obj(g, obj)
• The created group has to be assigned to an input device: lv_indev_set_group(indev, g)
• Use LV_KEY_... to navigate among the objects in the group. See lv_core/lv_group.h for the available
keys.

lv_indev_set_type(indev, LV_INDEV_TYPE_KEYPAD);

...

void keyboard_read(lv_indev_t * indev, lv_indev_data_t*data){


data->key = last_key(); /*Get the last pressed or released key*/

if(key_pressed()) data->state = LV_INDEV_STATE_PRESSED;


else data->state = LV_INDEV_STATE_RELEASED;
}

4.3. Input device interface 296


LVGL Documentation 9.0

Encoder

With an encoder you can do the following:


1. Press its button
2. Long-press its button
3. Turn left
4. Turn right
In short, the Encoder input devices work like this:
• By turning the encoder you can focus on the next/previous object.
• When you press the encoder on a simple object (like a button), it will be clicked.
• If you press the encoder on a complex object (like a list, message box, etc.) the object will go to edit mode whereby
you can navigate inside the object by turning the encoder.
• To leave edit mode, long press the button.
To use an Encoder (similarly to the Keypads) the objects should be added to groups.

lv_indev_set_type(indev, LV_INDEV_TYPE_ENCODER);

...

void encoder_read(lv_indev_t * indev, lv_indev_data_t*data){


data->enc_diff = enc_get_new_moves();

if(enc_pressed()) data->state = LV_INDEV_STATE_PRESSED;


else data->state = LV_INDEV_STATE_RELEASED;
}

Using buttons with Encoder logic

In addition to standard encoder behavior, you can also utilize its logic to navigate(focus) and edit widgets using buttons.
This is especially handy if you have only few buttons available, or you want to use other buttons in addition to encoder
wheel.
You need to have 3 buttons available:
• LV_KEY_ENTER will simulate press or pushing of the encoder button
• LV_KEY_LEFT will simulate turning encoder left
• LV_KEY_RIGHT will simulate turning encoder right
• other keys will be passed to the focused widget
If you hold the keys it will simulate an encoder advance with period specified in indev_drv.
long_press_repeat_time.

lv_indev_set_type(indev, LV_INDEV_TYPE_ENCODER);

...

void encoder_with_keys_read(lv_indev_t * indev, lv_indev_data_t*data){


(continues on next page)

4.3. Input device interface 297


LVGL Documentation 9.0

(continued from previous page)


data->key = last_key(); /*Get the last pressed or released key*/
/* use LV_KEY_ENTER for encoder press */
if(key_pressed()) data->state = LV_INDEV_STATE_PRESSED;
else {
data->state = LV_INDEV_STATE_RELEASED;
/* Optionally you can also use enc_diff, if you have encoder*/
data->enc_diff = enc_get_new_moves();
}
}

Button

Buttons mean external "hardware" buttons next to the screen which are assigned to specific coordinates of the screen. If
a button is pressed it will simulate the pressing on the assigned coordinate. (Similarly to a touchpad)
To assign buttons to coordinates use lv_indev_set_button_points(my_indev, points_array).
points_array should look like const lv_point_t points_array[] = { {12,30},{60,90},
...}

Important: The points_array can't go out of scope. Either declare it as a global variable or as a static variable inside a
function.

lv_indev_set_type(indev, LV_INDEV_TYPE_BUTTON);

...

void button_read(lv_indev_t * indev, lv_indev_data_t*data){


static uint32_t last_btn = 0; /*Store the last pressed button*/
int btn_pr = my_btn_read(); /*Get the ID (0,1,2...) of the pressed button*/
if(btn_pr >= 0) { /*Is there a button press? (E.g. -1 indicated no␣
,→button was pressed)*/

last_btn = btn_pr; /*Save the ID of the pressed button*/


data->state = LV_INDEV_STATE_PRESSED; /*Set the pressed state*/
} else {
data->state = LV_INDEV_STATE_RELEASED; /*Set the released state*/
}

data->btn = last_btn; /*Save the last button*/


}

4.3.2 Other features

Parameters

The default value of the following parameters can be changed in lv_indev_t:


• scroll_limit Number of pixels to slide before actually scrolling the object.
• scroll_throw Scroll throw (momentum) slow-down in [%]. Greater value means faster slow-down.
• long_press_time Press time to send LV_EVENT_LONG_PRESSED (in milliseconds)

4.3. Input device interface 298


LVGL Documentation 9.0

• long_press_repeat_time Interval of sending LV_EVENT_LONG_PRESSED_REPEAT (in milliseconds)


• read_timer pointer to the lv_timer which reads the input device. Its parameters can be changed by
lv_timer_...() functions. LV_DEF_REFR_PERIOD in lv_hal_disp.h sets the default read period.

Feedback

Besides read_cb a feedback_cb callback can be also specified in lv_indev_t. feedback_cb is called when
any type of event is sent by the input devices (independently of its type). This allows generating feedback for the user,
e.g. to play a sound on LV_EVENT_CLICKED.

Associating with a display

Every input device is associated with a display. By default, a new input device is added to the last display created or
explicitly selected (using lv_disp_set_default()). The associated display is stored and can be changed in disp
field of the driver.

Buffered reading

By default, LVGL calls read_cb periodically. Because of this intermittent polling there is a chance that some user
gestures are missed.
To solve this you can write an event driven driver for your input device that buffers measured data. In read_cb you can
report the buffered data instead of directly reading the input device. Setting the data->continue_reading flag
will tell LVGL there is more data to read and it should call read_cb again.

4.3.3 Further reading

• lv_port_indev_template.c for a template for your own driver.


• INdev features to learn more about higher level input device features.

4.3.4 API

Typedefs

typedef struct _lv_indev_t lv_indev_t

Enums

enum lv_indev_type_t
Possible input device types
Values:

enumerator LV_INDEV_TYPE_NONE
Uninitialized state

4.3. Input device interface 299


LVGL Documentation 9.0

enumerator LV_INDEV_TYPE_POINTER
Touch pad, mouse, external button

enumerator LV_INDEV_TYPE_KEYPAD
Keypad or keyboard

enumerator LV_INDEV_TYPE_BUTTON
External (hardware button) which is assigned to a specific point of the screen

enumerator LV_INDEV_TYPE_ENCODER
Encoder with only Left, Right turn and a Button

enum lv_indev_state_t
States for input devices
Values:

enumerator LV_INDEV_STATE_RELEASED

enumerator LV_INDEV_STATE_PRESSED

Functions

lv_indev_t *lv_indev_create(void)

void lv_indev_delete(lv_indev_t *indev)


Remove the provided input device. Make sure not to use the provided input device afterwards anymore.
Parameters indev -- pointer to delete
lv_indev_t *lv_indev_get_next(lv_indev_t *indev)
Get the next input device.
Parameters indev -- pointer to the current input device. NULL to initialize.
Returns the next input device or NULL if there are no more. Provide the first input device when the
parameter is NULL
void _lv_indev_read(lv_indev_t *indev, lv_indev_data_t *data)
Read data from an input device.
Parameters
• indev -- pointer to an input device
• data -- input device will write its data here
void lv_indev_read_timer_cb(lv_timer_t *timer)
Called periodically to read the input devices
Parameters timer -- pointer to a timer to read

4.3. Input device interface 300


LVGL Documentation 9.0

void lv_indev_enable(lv_indev_t *indev, bool en)


Enable or disable one or all input devices (default enabled)
Parameters
• indev -- pointer to an input device or NULL to enable/disable all of them
• en -- true to enable, false to disable
lv_indev_t *lv_indev_get_act(void)
Get the currently processed input device. Can be used in action functions too.
Returns pointer to the currently processed input device or NULL if no input device processing right
now
void lv_indev_set_type(lv_indev_t *indev, lv_indev_type_t indev_type)
Set the type of an input device
Parameters
• indev -- pointer to an input device
• indev_type -- the type of the input device from lv_indev_type_t
(LV_INDEV_TYPE_...)
void lv_indev_set_read_cb(lv_indev_t *indev, void (*read_cb)(struct _lv_indev_t *indev, lv_indev_data_t
*data))

void lv_indev_set_user_data(lv_indev_t *indev, void *user_data)

void lv_indev_set_driver_data(lv_indev_t *indev, void *driver_data)

lv_indev_type_t lv_indev_get_type(const lv_indev_t *indev)


Get the type of an input device
Parameters indev -- pointer to an input device
Returns the type of the input device from lv_hal_indev_type_t (LV_INDEV_TYPE_...)
lv_indev_state_t lv_indev_get_state(const lv_indev_t *indev)

lv_group_t *lv_indev_get_group(const lv_indev_t *indev)

struct _lv_disp_t *lv_indev_get_disp(const lv_indev_t *indev)

void lv_indev_set_disp(lv_indev_t *indev, struct _lv_disp_t *disp)

void *lv_indev_get_user_data(const lv_indev_t *indev)

void *lv_indev_get_driver_data(const lv_indev_t *indev)

void lv_indev_reset(lv_indev_t *indev, struct _lv_obj_t *obj)


Reset one or all input devices
Parameters
• indev -- pointer to an input device to reset or NULL to reset all of them
• obj -- pointer to an object which triggers the reset.
void lv_indev_reset_long_press(lv_indev_t *indev)
Reset the long press state of an input device
Parameters indev -- pointer to an input device

4.3. Input device interface 301


LVGL Documentation 9.0

void lv_indev_set_cursor(lv_indev_t *indev, struct _lv_obj_t *cur_obj)


Set a cursor for a pointer input device (for LV_INPUT_TYPE_POINTER and LV_INPUT_TYPE_BUTTON)
Parameters
• indev -- pointer to an input device
• cur_obj -- pointer to an object to be used as cursor
void lv_indev_set_group(lv_indev_t *indev, lv_group_t *group)
Set a destination group for a keypad input device (for LV_INDEV_TYPE_KEYPAD)
Parameters
• indev -- pointer to an input device
• group -- point to a group
void lv_indev_set_button_points(lv_indev_t *indev, const lv_point_t points[])
Set the an array of points for LV_INDEV_TYPE_BUTTON. These points will be assigned to the buttons to press
a specific point on the screen
Parameters
• indev -- pointer to an input device
• group -- point to a group
void lv_indev_get_point(const lv_indev_t *indev, lv_point_t *point)
Get the last point of an input device (for LV_INDEV_TYPE_POINTER and LV_INDEV_TYPE_BUTTON)
Parameters
• indev -- pointer to an input device
• point -- pointer to a point to store the result
lv_dir_t lv_indev_get_gesture_dir(const lv_indev_t *indev)
Get the current gesture direct
Parameters indev -- pointer to an input device
Returns current gesture direct
uint32_t lv_indev_get_key(const lv_indev_t *indev)
Get the last pressed key of an input device (for LV_INDEV_TYPE_KEYPAD)
Parameters indev -- pointer to an input device
Returns the last pressed key (0 on error)
lv_dir_t lv_indev_get_scroll_dir(const lv_indev_t *indev)
Check the current scroll direction of an input device (for LV_INDEV_TYPE_POINTER and
LV_INDEV_TYPE_BUTTON)
Parameters indev -- pointer to an input device
Returns LV_DIR_NONE: no scrolling now LV_DIR_HOR/VER
struct _lv_obj_t *lv_indev_get_scroll_obj(const lv_indev_t *indev)
Get the currently scrolled object (for LV_INDEV_TYPE_POINTER and LV_INDEV_TYPE_BUTTON)
Parameters indev -- pointer to an input device
Returns pointer to the currently scrolled object or NULL if no scrolling by this indev

4.3. Input device interface 302


LVGL Documentation 9.0

void lv_indev_get_vect(const lv_indev_t *indev, lv_point_t *point)


Get the movement vector of an input device (for LV_INDEV_TYPE_POINTER and
LV_INDEV_TYPE_BUTTON)
Parameters
• indev -- pointer to an input device
• point -- pointer to a point to store the types.pointer.vector
void lv_indev_wait_release(lv_indev_t *indev)
Do nothing until the next release
Parameters indev -- pointer to an input device
struct _lv_obj_t *lv_indev_get_obj_act(void)
Gets a pointer to the currently active object in the currently processed input device.
Returns pointer to currently active object or NULL if no active object
lv_timer_t *lv_indev_get_read_timer(lv_indev_t *indev)
Get a pointer to the indev read timer to modify its parameters with lv_timer_... functions.
Parameters indev -- pointer to an input device
Returns pointer to the indev read refresher timer. (NULL on error)
struct _lv_obj_t *lv_indev_search_obj(struct _lv_obj_t *obj, lv_point_t *point)
Search the most top, clickable object by a point
Parameters
• obj -- pointer to a start object, typically the screen
• point -- pointer to a point for searching the most top child
Returns pointer to the found object or NULL if there was no suitable object

struct lv_indev_data_t
#include <lv_indev.h> Data structure passed to an input driver to fill

Public Members

lv_point_t point
For LV_INDEV_TYPE_POINTER the currently pressed point

uint32_t key
For LV_INDEV_TYPE_KEYPAD the currently pressed key

uint32_t btn_id
For LV_INDEV_TYPE_BUTTON the currently pressed button

int16_t enc_diff
For LV_INDEV_TYPE_ENCODER number of steps since the previous read

4.3. Input device interface 303


LVGL Documentation 9.0

lv_indev_state_t state
LV_INDEV_STATE_REL or LV_INDEV_STATE_PR

bool continue_reading
If set to true, the read callback is invoked again

4.4 Tick interface

LVGL needs a system tick to know elapsed time for animations and other tasks.
You need to call the lv_tick_inc(tick_period) function periodically and provide the call period in milliseconds.
For example, lv_tick_inc(1) when calling every millisecond.
lv_tick_inc should be called in a higher priority routine than lv_task_handler() (e.g. in an interrupt) to
precisely know the elapsed milliseconds even if the execution of lv_task_handler takes more time.
With FreeRTOS lv_tick_inc can be called in vApplicationTickHook.
On Linux based operating systems (e.g. on Raspberry Pi) lv_tick_inc can be called in a thread like below:

void * tick_thread (void *args)


{
while(1) {
usleep(5*1000); /*Sleep for 5 millisecond*/
lv_tick_inc(5); /*Tell LVGL that 5 milliseconds were elapsed*/
}
}

4.4.1 API

Provide access to the system tick with 1 millisecond resolution

Functions

uint32_t lv_tick_get(void)
Get the elapsed milliseconds since start up
Returns the elapsed milliseconds
uint32_t lv_tick_elaps(uint32_t prev_tick)
Get the elapsed milliseconds since a previous time stamp
Parameters prev_tick -- a previous time stamp (return value of lv_tick_get() )
Returns the elapsed milliseconds since 'prev_tick'

4.4. Tick interface 304


LVGL Documentation 9.0

4.5 Timer Handler

To handle the tasks of LVGL you need to call lv_timer_handler() periodically in one of the following:
• while(1) of main() function
• timer interrupt periodically (lower priority than lv_tick_inc())
• an OS task periodically
The timing is not critical but it should be about 5 milliseconds to keep the system responsive.
Example:

while(1) {
lv_timer_handler();
my_delay_ms(5);
}

If you want to use lv_timer_handler() in a super-loop, a helper func-


tionlv_timer_handler_run_in_period() is provided to simplify the porting:

while(1) {
...
lv_timer_handler_run_in_period(5); /* run lv_timer_handler() every 5ms */
...
}

In an OS environment, you can use it together with the delay or sleep provided by OS to release CPU whenever possible:

while (1) {
lv_timer_handler_run_in_period(5); /* run lv_timer_handler() every 5ms */
my_delay_ms(5); /* delay 5ms to avoid unnecessary polling */
}

To learn more about timers visit the Timer section.

4.6 Sleep management

The MCU can go to sleep when no user input happens. In this case, the main while(1) should look like this:

while(1) {
/*Normal operation (no sleep) in < 1 sec inactivity*/
if(lv_disp_get_inactive_time(NULL) < 1000) {
lv_task_handler();
}
/*Sleep after 1 sec inactivity*/
else {
timer_stop(); /*Stop the timer where lv_tick_inc() is called*/
sleep(); /*Sleep the MCU*/
}
my_delay_ms(5);
}

You should also add the following lines to your input device read function to signal a wake-up (press, touch or click etc.)
has happened:

4.5. Timer Handler 305


LVGL Documentation 9.0

lv_tick_inc(LV_DEF_REFR_PERIOD); /*Force task execution on wake-up*/


timer_start(); /*Restart the timer where lv_tick_inc() is called*/
lv_task_handler(); /*Call `lv_task_handler()` manually to process the␣
,→wake-up event*/

In addition to lv_disp_get_inactive_time() you can check lv_anim_count_running() to see if all


animations have finished.

4.7 Operating system and interrupts

LVGL is not thread-safe by default.


However, in the following conditions it's valid to call LVGL related functions:
• In events. Learn more in Events.
• In lv_timer. Learn more in Timers.

4.7.1 Tasks and threads

If you need to use real tasks or threads, you need a mutex which should be invoked before the call of
lv_timer_handler and released after it. Also, you have to use the same mutex in other tasks and threads around
every LVGL (lv_...) related function call and code. This way you can use LVGL in a real multitasking environment.
Just make use of a mutex to avoid the concurrent calling of LVGL functions.
Here is some pseudocode to illustrate the concept:

static mutex_t lvgl_mutex;

void lvgl_thread(void)
{
while(1) {
mutex_lock(&lvgl_mutex);
lv_task_handler();
mutex_unlock(&lvgl_mutex);
thread_sleep(10); /* sleep for 10 ms */
}
}

void other_thread(void)
{
/* You must always hold the mutex while using LVGL APIs */
mutex_lock(&lvgl_mutex);
lv_obj_t *img = lv_img_create(lv_scr_act());
mutex_unlock(&lvgl_mutex);

while(1) {
mutex_lock(&lvgl_mutex);
/* change to the next image */
lv_img_set_src(img, next_image);
mutex_unlock(&lvgl_mutex);
thread_sleep(2000);
}
}

4.7. Operating system and interrupts 306


LVGL Documentation 9.0

4.7.2 Interrupts

Try to avoid calling LVGL functions from interrupt handlers (except lv_tick_inc() and
lv_disp_flush_ready()). But if you need to do this you have to disable the interrupt which uses LVGL
functions while lv_timer_handler is running.
It's a better approach to simply set a flag or some value in the interrupt, and periodically check it in an LVGL timer (which
is run by lv_timer_handler).

4.8 Logging

LVGL has a built-in Log module to inform the user about what is happening in the library.

4.8.1 Log level

To enable logging, set LV_USE_LOG 1 in lv_conf.h and set LV_LOG_LEVEL to one of the following values:
• LV_LOG_LEVEL_TRACE A lot of logs to give detailed information
• LV_LOG_LEVEL_INFO Log important events
• LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't cause a problem
• LV_LOG_LEVEL_ERROR Only critical issues, where the system may fail
• LV_LOG_LEVEL_USER Only user messages
• LV_LOG_LEVEL_NONE Do not log anything
The events which have a higher level than the set log level will be logged too. E.g. if you LV_LOG_LEVEL_WARN,
errors will be also logged.

4.8.2 Printing logs

Logging with printf

If your system supports printf, you just need to enable LV_LOG_PRINTF in lv_conf.h to send the logs with
printf.

Custom log function

If you can't use printf or want to use a custom function to log, you can register a "logger" callback with
lv_log_register_print_cb().
For example:

void my_log_cb(lv_log_level_t level, const char * buf)


{
serial_send(buf, strlen(buf));
}

...

(continues on next page)

4.8. Logging 307


LVGL Documentation 9.0

(continued from previous page)

lv_log_register_print_cb(my_log_cb);

4.8.3 Add logs

You can also use the log module via the LV_LOG_TRACE/INFO/WARN/ERROR/USER(text) or LV_LOG(text)
functions. Here:
• LV_LOG_TRACE/INFO/WARN/ERROR/USER(text) append following information to your text
• Log Level
• __FILE__
• __LINE__
• __func__
• LV_LOG(text) is similar to LV_LOG_USER but has no extra information attached.

4.9 Add custom GPU

LVGL has a flexible and extendable draw pipeline. You can hook it to do some rendering with a GPU or even completely
replace the built-in software renderer.

4.9.1 Draw context

The core structure of drawing is lv_draw_ctx_t. It contains a pointer to a buffer where drawing should happen and
a couple of callbacks to draw rectangles, texts, and other primitives.

Fields

lv_draw_ctx_t has the following fields:


• void * buf Pointer to a buffer to draw into
• lv_area_t * buf_area The position and size of buf (absolute coordinates)
• const lv_area_t * clip_area The current clip area with absolute coordinates, always the same or
smaller than buf_area. All drawings should be clipped to this area.
• void (*draw_rect)() Draw a rectangle with shadow, gradient, border, etc.
• void (*draw_arc)() Draw an arc
• void (*draw_img_decoded)() Draw an (A)RGB image that is already decoded by LVGL.
• lv_res_t (*draw_img)() Draw an image before decoding it (it bypasses LVGL's internal image decoders)
• void (*draw_letter)() Draw a letter
• void (*draw_line)() Draw a line
• void (*draw_polygon)() Draw a polygon
• void (*draw_bg)() Replace the buffer with a rect without decoration like radius or borders.

4.9. Add custom GPU 308


LVGL Documentation 9.0

• void (*wait_for_finish)() Wait until all background operation are finished. (E.g. GPU operations)
• void * user_data Custom user data for arbitrary purpose
(For the sake of simplicity the parameters of the callbacks are not shown here.)
All draw_* callbacks receive a pointer to the current draw_ctx as their first parameter. Among the other parameters
there is a descriptor that tells what to draw, e.g. for draw_rect it's called lv_draw_rect_dsc_t, for lv_draw_line
it's called lv_draw_line_dsc_t, etc.
To correctly render according to a draw_dsc you need to be familiar with the Boxing model of LVGL and the meanings
of the fields. The name and meaning of the fields are identical to name and meaning of the Style properties.

Initialization

The lv_disp_t has 4 fields related to the draw context:


• lv_draw_ctx_t * draw_ctx Pointer to the draw_ctx of this display
• void (*draw_ctx_init)(struct _lv_disp_t * disp_drv, lv_draw_ctx_t *
draw_ctx) Callback to initialize a draw_ctx
• void (*draw_ctx_deinit)(struct _lv_disp_t * disp_drv, lv_draw_ctx_t *
draw_ctx) Callback to de-initialize a draw_ctx
• size_t draw_ctx_size Size of the draw context structure. E.g. sizeof(lv_draw_sw_ctx_t)
When you ignore these fields, LVGL will set default values for callbacks and size in lv_disp_drv_init()
based on the configuration in lv_conf.h. lv_disp_drv_register() will allocate a draw_ctx based on
draw_ctx_size and call draw_ctx_init() on it.
However, you can overwrite the callbacks and the size values before calling lv_disp_drv_register(). It makes
it possible to use your own draw_ctx with your own callbacks.

4.9.2 Software renderer

LVGL's built in software renderer extends the basic lv_draw_ctx_t structure and sets the draw callbacks. It looks
like this:

typedef struct {
/** Include the basic draw_ctx type*/
lv_draw_ctx_t base_draw;

/** Blend a color or image to an area*/


void (*blend)(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc);
} lv_draw_sw_ctx_t;

Set the draw callbacks in draw_ctx_init() like:

draw_sw_ctx->base_draw.draw_rect = lv_draw_sw_rect;
draw_sw_ctx->base_draw.draw_letter = lv_draw_sw_letter;
...

4.9. Add custom GPU 309


LVGL Documentation 9.0

Blend callback

As you saw above the software renderer adds the blend callback field. It's a special callback related to how the software
renderer works. All draw operations end up in the blend callback which can either fill an area or copy an image to an
area by considering an optional mask.
The lv_draw_sw_blend_dsc_t parameter describes what and how to blend. It has the following fields:
• const lv_area_t * blend_area The area with absolute coordinates to draw on draw_ctx->buf. If
src_buf is set, it's the coordinates of the image to blend.
• const lv_color_t * src_buf Pointer to an image to blend. If set, color is ignored. If not set fill
blend_area with color
• lv_color_t color Fill color. Used only if src_buf == NULL
• lv_opa_t * mask_buf NULL if ignored, or an alpha mask to apply on blend_area
• lv_draw_mask_res_t mask_res The result of the previous mask operation. (LV_DRAW_MASK_RES_.
..)
• const lv_area_t * mask_area The area of mask_buf with absolute coordinates
• lv_opa_t opa The overall opacity
• lv_blend_mode_t blend_mode E.g. LV_BLEND_MODE_ADDITIVE

4.9.3 Extend the software renderer

New blend callback

Let's take a practical example: you would like to use your MCUs GPU for color fill operations only.
As all draw callbacks call blend callback to fill an area in the end only the blend callback needs to be overwritten.
First extend lv_draw_sw_ctx_t:

/*We don't add new fields, so just for clarity add new type*/
typedef lv_draw_sw_ctx_t my_draw_ctx_t;

void my_draw_ctx_init(lv_disp_t * drv, lv_draw_ctx_t * draw_ctx)


{
/*Initialize the parent type first */
lv_draw_sw_init_ctx(drv, draw_ctx);

/*Change some callbacks*/


my_draw_ctx_t * my_draw_ctx = (my_draw_ctx_t *)draw_ctx;

my_draw_ctx->blend = my_draw_blend;
my_draw_ctx->base_draw.wait_for_finish = my_gpu_wait;
}

After calling lv_disp_draw_init(&drv) you can assign the new draw_ctx_init callback and set
draw_ctx_size to overwrite the defaults:

static lv_disp_t drv;


lv_disp_draw_init(&drv);
drv->hor_res = my_hor_res;
(continues on next page)

4.9. Add custom GPU 310


LVGL Documentation 9.0

(continued from previous page)


drv->ver_res = my_ver_res;
drv->flush_cb = my_flush_cb;

/*New draw ctx settings*/


drv->draw_ctx_init = my_draw_ctx_init;
drv->draw_ctx_size = sizeof(my_draw_ctx_t);

lv_disp_drv_register(&drv);

This way when LVGL calls blend it will call my_draw_blend and we can do custom GPU operations. Here is a
complete example:

void my_draw_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc)


{
/*Let's get the blend area which is the intersection of the area to fill and the␣
,→clip area.*/

lv_area_t blend_area;
if(!_lv_area_intersect(&blend_area, dsc->blend_area, draw_ctx->clip_area)) return;
,→ /*Fully clipped, nothing to do*/

/*Fill only non masked, fully opaque, normal blended and not too small areas*/
if(dsc->src_buf == NULL && dsc->mask == NULL && dsc->opa >= LV_OPA_MAX &&
dsc->blend_mode == LV_BLEND_MODE_NORMAL && lv_area_get_size(&blend_area) >␣
,→100) {

/*Got the first pixel on the buffer*/


lv_coord_t dest_stride = lv_area_get_width(draw_ctx->buf_area); /*Width of␣
,→the destination buffer*/

lv_color_t * dest_buf = draw_ctx->buf;


dest_buf += dest_stride * (blend_area.y1 - draw_ctx->buf_area->y1) + (blend_
,→area.x1 - draw_ctx->buf_area->x1);

/*Make the blend area relative to the buffer*/


lv_area_move(&blend_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);

/*Call your custom gou fill function to fill blend_area, on dest_buf with dsc-
,→ >color*/
my_gpu_fill(dest_buf, dest_stride, &blend_area, dsc->color);
}
/*Fallback: the GPU doesn't support these settings. Call the SW renderer.*/
else {
lv_draw_sw_blend_basic(draw_ctx, dsc);
}
}

The implementation of wait callback is much simpler:

void my_gpu_wait(lv_draw_ctx_t * draw_ctx)


{
while(my_gpu_is_working());

/*Call SW renderer's wait callback too*/


lv_draw_sw_wait_for_finish(draw_ctx);
}

4.9. Add custom GPU 311


LVGL Documentation 9.0

New rectangle drawer

If your MCU has a more powerful GPU that can draw e.g. rounded rectangles you can replace the original software
drawer too. A custom draw_rect callback might look like this:

void my_draw_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_


,→area_t * coords)

{
if(lv_draw_mask_is_any(coords) == false && dsc->grad == NULL && dsc->bg_img_src ==␣
,→NULL &&

dsc->shadow_width == 0 && dsc->blend_mode = LV_BLEND_MODE_NORMAL)


{
/*Draw the background*/
my_bg_drawer(draw_ctx, coords, dsc->bg_color, dsc->radius);

/*Draw the border if any*/


if(dsc->border_width) {
my_border_drawer(draw_ctx, coords, dsc->border_width, dsc->border_color, dsc->
,→border_opa)

/*Draw the outline if any*/


if(dsc->outline_width) {
my_outline_drawer(draw_ctx, coords, dsc->outline_width, dsc->outline_color, dsc-
,→>outline_opa, dsc->outline_pad)

}
}
/*Fallback*/
else {
lv_draw_sw_rect(draw_ctx, dsc, coords);
}
}

my_draw_rect can fully bypass the use of blend callback if needed.

4.9.4 Fully custom draw engine

For example if your MCU/MPU supports a powerful vector graphics engine you might use only that instead of LVGL's SW
renderer. In this case, you need to base the renderer on the basic lv_draw_ctx_t (instead of lv_draw_sw_ctx_t)
and extend/initialize it as you wish.

4.9. Add custom GPU 312


CHAPTER

FIVE

OVERVIEW

5.1 Objects

In LVGL the basic building blocks of a user interface are the objects, also called Widgets. For example a Button, Label,
Image, List, Chart or Text area.
You can see all the Object types here.
All objects are referenced using an lv_obj_t pointer as a handle. This pointer can later be used to set or get the
attributes of the object.

5.1.1 Attributes

Basic attributes

All object types share some basic attributes:


• Position
• Size
• Parent
• Styles
• Event handlers
• Etc
You can set/get these attributes with lv_obj_set_... and lv_obj_get_... functions. For example:

/*Set basic object attributes*/


lv_obj_set_size(btn1, 100, 50); /*Set a button's size*/
lv_obj_set_pos(btn1, 20,30); /*Set a button's position*/

To see all the available functions visit the Base object's documentation.

313
LVGL Documentation 9.0

Specific attributes

The object types have special attributes too. For example, a slider has
• Minimum and maximum values
• Current value
For these special attributes, every object type may have unique API functions. For example for a slider:

/*Set slider specific attributes*/


lv_slider_set_range(slider1, 0, 100); /*Set␣
,→the min. and max. values*/

lv_slider_set_value(slider1, 40, LV_ANIM_ON); /*Set the current value␣


,→(position)*/

The API of the widgets is described in their Documentation but you can also check the respective header files (e.g.
widgets/lv_slider.h)

5.1.2 Working mechanisms

Parent-child structure

A parent object can be considered as the container of its children. Every object has exactly one parent object (except
screens), but a parent can have any number of children. There is no limitation for the type of the parent but there are
objects which are typically a parent (e.g. button) or a child (e.g. label).

Moving together

If the position of a parent changes, the children will move along with it. Therefore, all positions are relative to the parent.

5.1. Objects 314


LVGL Documentation 9.0

lv_obj_t * parent = lv_obj_create(lv_scr_act()); /*Create a parent object on the␣


,→current screen*/

lv_obj_set_size(parent, 100, 80); /*Set the size of the␣


,→parent*/

lv_obj_t * obj1 = lv_obj_create(parent); /*Create an object on the␣


,→previously created parent object*/

lv_obj_set_pos(obj1, 10, 10); /*Set the position of the␣


,→new object*/

Modify the position of the parent:

lv_obj_set_pos(parent, 50, 50); /*Move the parent. The child will move with it.
,→*/

(For simplicity the adjusting of colors of the objects is not shown in the example.)

Visibility only on the parent

If a child is partially or fully outside its parent then the parts outside will not be visible.

5.1. Objects 315


LVGL Documentation 9.0

lv_obj_set_x(obj1, -30); /*Move the child a little bit off the parent*/

This behavior can be overwritten with lv_obj_add_flag(obj, LV_OBJ_FLAG_OVERFLOW_VISIBLE);


which allow the children to be drawn out of the parent.

Create and delete objects

In LVGL, objects can be created and deleted dynamically at run time. It means only the currently created (existing)
objects consume RAM.
This allows for the creation of a screen just when a button is clicked to open it, and for deletion of screens when a new
screen is loaded.
UIs can be created based on the current environment of the device. For example one can create meters, charts, bars and
sliders based on the currently attached sensors.
Every widget has its own create function with a prototype like this:

lv_obj_t * lv_<widget>_create(lv_obj_t * parent, <other parameters if any>);

Typically, the create functions only have a parent parameter telling them on which object to create the new widget.
The return value is a pointer to the created object with lv_obj_t * type.
There is a common delete function for all object types. It deletes the object and all of its children.

void lv_obj_del(lv_obj_t * obj);

lv_obj_del will delete the object immediately. If for any reason you can't delete the object immediately you can use
lv_obj_del_async(obj) which will perform the deletion on the next call of lv_timer_handler(). This is
useful e.g. if you want to delete the parent of an object in the child's LV_EVENT_DELETE handler.
You can remove all the children of an object (but not the object itself) using lv_obj_clean(obj).
You can use lv_obj_del_delayed(obj, 1000) to delete an object after some time. The delay is expressed in
milliseconds.

5.1. Objects 316


LVGL Documentation 9.0

5.1.3 Screens

Create screens

The screens are special objects which have no parent object. So they can be created like:

lv_obj_t * scr1 = lv_obj_create(NULL);

Screens can be created with any object type. For example, a Base object or an image to make a wallpaper.

Get the active screen

There is always an active screen on each display. By default, the library creates and loads a "Base object" as a screen for
each display.
To get the currently active screen use the lv_scr_act() function.

Load screens

To load a new screen, use lv_scr_load(scr1).

Layers

There are two automatically generated layers:


• top layer
• system layer
They are independent of the screens and they will be shown on every screen. The top layer is above every object on the
screen and the system layer is above the top layer. You can add any pop-up windows to the top layer freely. But, the system
layer is restricted to system-level things (e.g. mouse cursor will be placed there with lv_indev_set_cursor()).
The lv_layer_top() and lv_layer_sys() functions return pointers to the top and system layers respectively.
Read the Layer overview section to learn more about layers.

Load screen with animation

A new screen can be loaded with animation by using lv_scr_load_anim(scr, transition_type, time,
delay, auto_del). The following transition types exist:
• LV_SCR_LOAD_ANIM_NONE Switch immediately after delay milliseconds
• LV_SCR_LOAD_ANIM_OVER_LEFT/RIGHT/TOP/BOTTOM Move the new screen over the current towards
the given direction
• LV_SCR_LOAD_ANIM_OUT_LEFT/RIGHT/TOP/BOTTOM Move out the old screen over the current towards
the given direction
• LV_SCR_LOAD_ANIM_MOVE_LEFT/RIGHT/TOP/BOTTOM Move both the current and new screens towards
the given direction
• LV_SCR_LOAD_ANIM_FADE_IN/OUT Fade the new screen over the old screen, or vice versa

5.1. Objects 317


LVGL Documentation 9.0

Setting auto_del to true will automatically delete the old screen when the animation is finished.
The new screen will become active (returned by lv_scr_act()) when the animation starts after delay time. All
inputs are disabled during the screen animation.

Handling multiple displays

Screens are created on the currently selected default display. The default display is the last regis-
tered display with lv_disp_drv_register. You can also explicitly select a new default display using
lv_disp_set_default(disp).
lv_scr_act(), lv_scr_load() and lv_scr_load_anim() operate on the default screen.
Visit Multi-display support to learn more.

5.1.4 Parts

The widgets are built from multiple parts. For example a Base object uses the main and scrollbar parts but a Slider uses
the main, indicator and knob parts. Parts are similar to pseudo-elements in CSS.
The following predefined parts exist in LVGL:
• LV_PART_MAIN A background like rectangle
• LV_PART_SCROLLBAR The scrollbar(s)
• LV_PART_INDICATOR Indicator, e.g. for slider, bar, switch, or the tick box of the checkbox
• LV_PART_KNOB Like a handle to grab to adjust the value
• LV_PART_SELECTED Indicate the currently selected option or section
• LV_PART_ITEMS Used if the widget has multiple similar elements (e.g. table cells)
• LV_PART_TICKS Ticks on scales e.g. for a chart or meter
• LV_PART_CURSOR Mark a specific place e.g. text area's or chart's cursor
• LV_PART_CUSTOM_FIRST Custom parts can be added from here.
The main purpose of parts is to allow styling the "components" of the widgets. They are described in more detail in the
Style overview section.

5.1.5 States

The object can be in a combination of the following states:


• LV_STATE_DEFAULT Normal, released state
• LV_STATE_CHECKED Toggled or checked state
• LV_STATE_FOCUSED Focused via keypad or encoder or clicked via touchpad/mouse
• LV_STATE_FOCUS_KEY Focused via keypad or encoder but not via touchpad/mouse
• LV_STATE_EDITED Edit by an encoder
• LV_STATE_HOVERED Hovered by mouse (not supported now)
• LV_STATE_PRESSED Being pressed
• LV_STATE_SCROLLED Being scrolled

5.1. Objects 318


LVGL Documentation 9.0

• LV_STATE_DISABLED Disabled state


• LV_STATE_USER_1 Custom state
• LV_STATE_USER_2 Custom state
• LV_STATE_USER_3 Custom state
• LV_STATE_USER_4 Custom state
The states are usually automatically changed by the library as the user interacts with an object (presses, releases, focuses,
etc.). However, the states can be changed manually too. To set or clear given state (but leave the other states untouched)
use lv_obj_add/clear_state(obj, LV_STATE_...) In both cases OR-ed state values can be used as well.
E.g. lv_obj_add_state(obj, part, LV_STATE_PRESSED | LV_PRESSED_CHECKED).
To learn more about the states read the related section of the Style overview.

5.1.6 Snapshot

A snapshot image can be generated for an object together with its children. Check details in Snapshot.

5.2 Positions, sizes, and layouts

5.2.1 Overview

Similarly to many other parts of LVGL, the concept of setting the coordinates was inspired by CSS. LVGL has by no
means a complete implementation of CSS but a comparable subset is implemented (sometimes with minor adjustments).
In short this means:
• Explicitly set coordinates are stored in styles (size, position, layouts, etc.)
• support min-width, max-width, min-height, max-height
• have pixel, percentage, and "content" units
• x=0; y=0 coordinate means the top-left corner of the parent plus the left/top padding plus border width
• width/height means the full size, the "content area" is smaller with padding and border width
• a subset of flexbox and grid layouts are supported

Units

• pixel: Simply a position in pixels. An integer always means pixels. E.g. lv_obj_set_x(btn, 10)
• percentage: The percentage of the size of the object or its parent (depending on the property). lv_pct(value)
converts a value to percentage. E.g. lv_obj_set_width(btn, lv_pct(50))
• LV_SIZE_CONTENT: Special value to set the width/height of an object to involve all the children. It's similar to
auto in CSS. E.g. lv_obj_set_width(btn, LV_SIZE_CONTENT).

5.2. Positions, sizes, and layouts 319


LVGL Documentation 9.0

Boxing model

LVGL follows CSS's border-box model. An object's "box" is built from the following parts:
• bounding box: the width/height of the elements.
• border width: the width of the border.
• padding: space between the sides of the object and its children.
• content: the content area which is the size of the bounding box reduced by the border width and padding.

The border is drawn inside the bounding box. Inside the border LVGL keeps a "padding margin" when placing an object's
children.
The outline is drawn outside the bounding box.

Important notes

This section describes special cases in which LVGL's behavior might be unexpected.

Postponed coordinate calculation

LVGL doesn't recalculate all the coordinate changes immediately. This is done to improve performance. Instead, the
objects are marked as "dirty" and before redrawing the screen LVGL checks if there are any "dirty" objects. If so it
refreshes their position, size and layout.
In other words, if you need to get the coordinate of an object and the coordinates were just changed, LVGL needs to be
forced to recalculate the coordinates. To do this call lv_obj_update_layout(obj).
The size and position might depend on the parent or layout. Therefore lv_obj_update_layout recalculates the
coordinates of all objects on the screen of obj.

5.2. Positions, sizes, and layouts 320


LVGL Documentation 9.0

Removing styles

As it's described in the Using styles section, coordinates can also be set via style properties. To be more precise, under
the hood every style coordinate related property is stored as a style property. If you use lv_obj_set_x(obj, 20)
LVGL saves x=20 in the local style of the object.
This is an internal mechanism and doesn't matter much as you use LVGL. However, there is one case in which you need
to be aware of the implementation. If the style(s) of an object are removed by

lv_obj_remove_style_all(obj)

or

lv_obj_remove_style(obj, NULL, LV_PART_MAIN);

the earlier set coordinates will be removed as well.


For example:

/*The size of obj1 will be set back to the default in the end*/
lv_obj_set_size(obj1, 200, 100); /*Now obj1 has 200;100 size*/
lv_obj_remove_style_all(obj1); /*It removes the set sizes*/

/*obj2 will have 200;100 size in the end */


lv_obj_remove_style_all(obj2);
lv_obj_set_size(obj2, 200, 100);

5.2.2 Position

Simple way

To simply set the x and y coordinates of an object use:

lv_obj_set_x(obj, 10); //Separate...


lv_obj_set_y(obj, 20);
lv_obj_set_pos(obj, 10, 20); //Or in one function

By default, the x and y coordinates are measured from the top left corner of the parent's content area. For example if the
parent has five pixels of padding on every side the above code will place obj at (15, 25) because the content area starts
after the padding.
Percentage values are calculated from the parent's content area size.

lv_obj_set_x(btn, lv_pct(10)); //x = 10 % of parent content area width

5.2. Positions, sizes, and layouts 321


LVGL Documentation 9.0

Align

In some cases it's convenient to change the origin of the positioning from the default top left. If the origin is changed e.g.
to bottom-right, the (0,0) position means: align to the bottom-right corner. To change the origin use:

lv_obj_set_align(obj, align);

To change the alignment and set new coordinates:

lv_obj_align(obj, align, x, y);

The following alignment options can be used:


• LV_ALIGN_TOP_LEFT
• LV_ALIGN_TOP_MID
• LV_ALIGN_TOP_RIGHT
• LV_ALIGN_BOTTOM_LEFT
• LV_ALIGN_BOTTOM_MID
• LV_ALIGN_BOTTOM_RIGHT
• LV_ALIGN_LEFT_MID
• LV_ALIGN_RIGHT_MID
• LV_ALIGN_CENTER
It's quite common to align a child to the center of its parent, therefore a dedicated function exists:

lv_obj_center(obj);

//Has the same effect


lv_obj_align(obj, LV_ALIGN_CENTER, 0, 0);

If the parent's size changes, the set alignment and position of the children is updated automatically.
The functions introduced above align the object to its parent. However, it's also possible to align an object to an arbitrary
reference object.

lv_obj_align_to(obj_to_align, reference_obj, align, x, y);

Besides the alignments options above, the following can be used to align an object outside the reference object:
• LV_ALIGN_OUT_TOP_LEFT
• LV_ALIGN_OUT_TOP_MID
• LV_ALIGN_OUT_TOP_RIGHT
• LV_ALIGN_OUT_BOTTOM_LEFT
• LV_ALIGN_OUT_BOTTOM_MID
• LV_ALIGN_OUT_BOTTOM_RIGHT
• LV_ALIGN_OUT_LEFT_TOP
• LV_ALIGN_OUT_LEFT_MID
• LV_ALIGN_OUT_LEFT_BOTTOM
• LV_ALIGN_OUT_RIGHT_TOP

5.2. Positions, sizes, and layouts 322


LVGL Documentation 9.0

• LV_ALIGN_OUT_RIGHT_MID
• LV_ALIGN_OUT_RIGHT_BOTTOM
For example to align a label above a button and center the label horizontally:

lv_obj_align_to(label, btn, LV_ALIGN_OUT_TOP_MID, 0, -10);

Note that, unlike with lv_obj_align(), lv_obj_align_to() can not realign the object if its coordinates or the
reference object's coordinates change.

5.2.3 Size

Simple way

The width and the height of an object can be set easily as well:

lv_obj_set_width(obj, 200); //Separate...


lv_obj_set_height(obj, 100);
lv_obj_set_size(obj, 200, 100); //Or in one function

Percentage values are calculated based on the parent's content area size. For example to set the object's height to the
screen height:

lv_obj_set_height(obj, lv_pct(100));

The size settings support a special value: LV_SIZE_CONTENT. It means the object's size in the respective direction will
be set to the size of its children. Note that only children on the right and bottom sides will be considered and children on
the top and left remain cropped. This limitation makes the behavior more predictable.
Objects with LV_OBJ_FLAG_HIDDEN or LV_OBJ_FLAG_FLOATING will be ignored by the LV_SIZE_CONTENT
calculation.
The above functions set the size of an object's bounding box but the size of the content area can be set as well. This means
an object's bounding box will be enlarged with the addition of padding.

lv_obj_set_content_width(obj, 50); //The actual width: padding left + 50 + padding␣


,→right

lv_obj_set_content_height(obj, 30); //The actual width: padding top + 30 + padding␣


,→bottom

The size of the bounding box and the content area can be retrieved with the following functions:

lv_coord_t w = lv_obj_get_width(obj);
lv_coord_t h = lv_obj_get_height(obj);
lv_coord_t content_w = lv_obj_get_content_width(obj);
lv_coord_t content_h = lv_obj_get_content_height(obj);

5.2. Positions, sizes, and layouts 323


LVGL Documentation 9.0

5.2.4 Using styles

Under the hood the position, size and alignment properties are style properties. The above described "simple functions"
hide the style related code for the sake of simplicity and set the position, size, and alignment properties in the local styles
of the object.
However, using styles to set the coordinates has some great advantages:
• It makes it easy to set the width/height/etc. for several objects together. E.g. make all the sliders 100x10 pixels
sized.
• It also makes possible to modify the values in one place.
• The values can be partially overwritten by other styles. For example style_btn makes the object 100x50 by
default but adding style_full_width overwrites only the width of the object.
• The object can have different position or size depending on state. E.g. 100 px wide in LV_STATE_DEFAULT but
120 px in LV_STATE_PRESSED.
• Style transitions can be used to make the coordinate changes smooth.
Here are some examples to set an object's size using a style:

static lv_style_t style;


lv_style_init(&style);
lv_style_set_width(&style, 100);

lv_obj_t * btn = lv_btn_create(lv_scr_act());


lv_obj_add_style(btn, &style, LV_PART_MAIN);

As you will see below there are some other great features of size and position setting. However, to keep the LVGL API
lean, only the most common coordinate setting features have a "simple" version and the more complex features can be
used via styles.

5.2.5 Translation

Let's say the there are 3 buttons next to each other. Their position is set as described above. Now you want to move a
button up a little when it's pressed.
One way to achieve this is by setting a new Y coordinate for the pressed state:

static lv_style_t style_normal;


lv_style_init(&style_normal);
lv_style_set_y(&style_normal, 100);

static lv_style_t style_pressed;


lv_style_init(&style_pressed);
lv_style_set_y(&style_pressed, 80);

lv_obj_add_style(btn1, &style_normal, LV_STATE_DEFAULT);


lv_obj_add_style(btn1, &style_pressed, LV_STATE_PRESSED);

lv_obj_add_style(btn2, &style_normal, LV_STATE_DEFAULT);


lv_obj_add_style(btn2, &style_pressed, LV_STATE_PRESSED);

lv_obj_add_style(btn3, &style_normal, LV_STATE_DEFAULT);


lv_obj_add_style(btn3, &style_pressed, LV_STATE_PRESSED);

5.2. Positions, sizes, and layouts 324


LVGL Documentation 9.0

This works, but it's not really flexible because the pressed coordinate is hard-coded. If the buttons are not at y=100,
style_pressed won't work as expected. Translations can be used to solve this:

static lv_style_t style_normal;


lv_style_init(&style_normal);
lv_style_set_y(&style_normal, 100);

static lv_style_t style_pressed;


lv_style_init(&style_pressed);
lv_style_set_translate_y(&style_pressed, -20);

lv_obj_add_style(btn1, &style_normal, LV_STATE_DEFAULT);


lv_obj_add_style(btn1, &style_pressed, LV_STATE_PRESSED);

lv_obj_add_style(btn2, &style_normal, LV_STATE_DEFAULT);


lv_obj_add_style(btn2, &style_pressed, LV_STATE_PRESSED);

lv_obj_add_style(btn3, &style_normal, LV_STATE_DEFAULT);


lv_obj_add_style(btn3, &style_pressed, LV_STATE_PRESSED);

Translation is applied from the current position of the object.


Percentage values can be used in translations as well. The percentage is relative to the size of the object (and not to the
size of the parent). For example lv_pct(50) will move the object with half of its width/height.
The translation is applied after the layouts are calculated. Therefore, even laid out objects' position can be translated.
The translation actually moves the object. That means it makes the scrollbars and LV_SIZE_CONTENT sized objects
react to the position change.

5.2.6 Transformation

Similarly to position, an object's size can be changed relative to the current size as well. The transformed width and height
are added on both sides of the object. This means a 10 px transformed width makes the object 2x10 pixels wider.
Unlike position translation, the size transformation doesn't make the object "really" larger. In other words scrollbars,
layouts, and LV_SIZE_CONTENT will not react to the transformed size. Hence, size transformation is "only" a visual
effect.
This code enlarges a button when it's pressed:

static lv_style_t style_pressed;


lv_style_init(&style_pressed);
lv_style_set_transform_width(&style_pressed, 10);
lv_style_set_transform_height(&style_pressed, 10);

lv_obj_add_style(btn, &style_pressed, LV_STATE_PRESSED);

5.2. Positions, sizes, and layouts 325


LVGL Documentation 9.0

Min and Max size

Similarly to CSS, LVGL also supports min-width, max-width, min-height and max-height. These are
limits preventing an object's size from becoming smaller/larger than these values. They are especially useful if the size is
set by percentage or LV_SIZE_CONTENT.

static lv_style_t style_max_height;


lv_style_init(&style_max_height);
lv_style_set_y(&style_max_height, 200);

lv_obj_set_height(obj, lv_pct(100));
lv_obj_add_style(obj, &style_max_height, LV_STATE_DEFAULT); //Limit the height to␣
,→200 px

Percentage values can be used as well which are relative to the size of the parent's content area.

static lv_style_t style_max_height;


lv_style_init(&style_max_height);
lv_style_set_y(&style_max_height, lv_pct(50));

lv_obj_set_height(obj, lv_pct(100));
lv_obj_add_style(obj, &style_max_height, LV_STATE_DEFAULT); //Limit the height to␣
,→half parent height

5.2.7 Layout

Overview

Layouts can update the position and size of an object's children. They can be used to automatically arrange the children
into a line or column, or in much more complicated forms.
The position and size set by the layout overwrites the "normal" x, y, width, and height settings.
There is only one function that is the same for every layout: lv_obj_set_layout(obj, <LAYOUT_NAME>) sets
the layout on an object. For further settings of the parent and children see the documentation of the given layout.

Built-in layout

LVGL comes with two very powerful layouts:


• Flexbox
• Grid
Both are heavily inspired by the CSS layouts with the same name.

5.2. Positions, sizes, and layouts 326


LVGL Documentation 9.0

Flags

There are some flags that can be used on objects to affect how they behave with layouts:
• LV_OBJ_FLAG_HIDDEN Hidden objects are ignored in layout calculations.
• LV_OBJ_FLAG_IGNORE_LAYOUT The object is simply ignored by the layouts. Its coordinates can be set as
usual.
• LV_OBJ_FLAG_FLOATING Same as LV_OBJ_FLAG_IGNORE_LAYOUT but the object with
LV_OBJ_FLAG_FLOATING will be ignored in LV_SIZE_CONTENT calculations.
These flags can be added/removed with lv_obj_add/clear_flag(obj, FLAG);

Adding new layouts

LVGL can be freely extended by a custom layout like this:

uint32_t MY_LAYOUT;

...

MY_LAYOUT = lv_layout_register(my_layout_update, &user_data);

...

void my_layout_update(lv_obj_t * obj, void * user_data)


{
/*Will be called automatically if it's required to reposition/resize the␣
,→children of "obj" */

Custom style properties can be added which can be retrieved and used in the update callback. For example:

uint32_t MY_PROP;
...

LV_STYLE_MY_PROP = lv_style_register_prop();

...
static inline void lv_style_set_my_prop(lv_style_t * style, uint32_t value)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_style_set_prop(style, LV_STYLE_MY_PROP, v);
}

5.2. Positions, sizes, and layouts 327


LVGL Documentation 9.0

5.2.8 Examples

5.3 Styles

Styles are used to set the appearance of objects. Styles in lvgl are heavily inspired by CSS. The concept in a nutshell is as
follows:
• A style is an lv_style_t variable which can hold properties like border width, text color and so on. It's similar
to a class in CSS.
• Styles can be assigned to objects to change their appearance. Upon assignment, the target part (pseudo-element in
CSS) and target state (pseudo class) can be specified. For example one can add style_blue to the knob of a
slider when it's in pressed state.
• The same style can be used by any number of objects.
• Styles can be cascaded which means multiple styles may be assigned to an object and each style can have different
properties. Therefore, not all properties have to be specified in a style. LVGL will search for a property until a style
defines it or use a default if it's not specified by any of the styles. For example style_btn can result in a default
gray button and style_btn_red can add only a background-color=red to overwrite the background
color.
• The most recently added style has higher precedence. This means if a property is specified in two styles the newest
style in the object will be used.
• Some properties (e.g. text color) can be inherited from a parent(s) if it's not specified in an object.
• Objects can also have local styles with higher precedence than "normal" styles.
• Unlike CSS (where pseudo-classes describe different states, e.g. :focus), in LVGL a property is assigned to a
given state.
• Transitions can be applied when the object changes state.

5.3.1 States

The objects can be in the combination of the following states:


• LV_STATE_DEFAULT (0x0000) Normal, released state
• LV_STATE_CHECKED (0x0001) Toggled or checked state
• LV_STATE_FOCUSED (0x0002) Focused via keypad or encoder or clicked via touchpad/mouse
• LV_STATE_FOCUS_KEY (0x0004) Focused via keypad or encoder but not via touchpad/mouse
• LV_STATE_EDITED (0x0008) Edit by an encoder
• LV_STATE_HOVERED (0x0010) Hovered by mouse (not supported now)
• LV_STATE_PRESSED (0x0020) Being pressed
• LV_STATE_SCROLLED (0x0040) Being scrolled
• LV_STATE_DISABLED (0x0080) Disabled state
• LV_STATE_USER_1 (0x1000) Custom state
• LV_STATE_USER_2 (0x2000) Custom state
• LV_STATE_USER_3 (0x4000) Custom state
• LV_STATE_USER_4 (0x8000) Custom state

5.3. Styles 328


LVGL Documentation 9.0

An object can be in a combination of states such as being focused and pressed at the same time. This is represented as
LV_STATE_FOCUSED | LV_STATE_PRESSED.
A style can be added to any state or state combination. For example, setting a different background color for the default
and pressed states. If a property is not defined in a state the best matching state's property will be used. Typically this
means the property with LV_STATE_DEFAULT is used.˛ If the property is not set even for the default state the default
value will be used. (See later)
But what does the "best matching state's property" really mean? States have a precedence which is shown by their value
(see in the above list). A higher value means higher precedence. To determine which state's property to use let's take an
example. Imagine the background color is defined like this:
• LV_STATE_DEFAULT: white
• LV_STATE_PRESSED: gray
• LV_STATE_FOCUSED: red
1. Initially the object is in the default state, so it's a simple case: the property is perfectly defined in the object's current
state as white.
2. When the object is pressed there are 2 related properties: default with white (default is related to every state)
and pressed with gray. The pressed state has 0x0020 precedence which is higher than the default state's 0x0000
precedence, so gray color will be used.
3. When the object is focused the same thing happens as in pressed state and red color will be used. (Focused state
has higher precedence than default state).
4. When the object is focused and pressed both gray and red would work, but the pressed state has higher precedence
than focused so gray color will be used.
5. It's possible to set e.g. rose color for LV_STATE_PRESSED | LV_STATE_FOCUSED. In this case, this
combined state has 0x0020 + 0x0002 = 0x0022 precedence, which is higher than the pressed state's precedence so
rose color would be used.
6. When the object is in the checked state there is no property to set the background color for this state. So for lack
of a better option, the object remains white from the default state's property.
Some practical notes:
• The precedence (value) of states is quite intuitive, and it's something the user would expect naturally. E.g. if an
object is focused the user will still want to see if it's pressed, therefore the pressed state has a higher precedence.
If the focused state had a higher precedence it would overwrite the pressed color.
• If you want to set a property for all states (e.g. red background color) just set it for the default state. If the object
can't find a property for its current state it will fall back to the default state's property.
• Use ORed states to describe the properties for complex cases. (E.g. pressed + checked + focused)
• It might be a good idea to use different style elements for different states. For example, finding background colors
for released, pressed, checked + pressed, focused, focused + pressed, focused + pressed + checked, etc. states
is quite difficult. Instead, for example, use the background color for pressed and checked states and indicate the
focused state with a different border color.

5.3. Styles 329


LVGL Documentation 9.0

5.3.2 Cascading styles

It's not required to set all the properties in one style. It's possible to add more styles to an object and have the latter added
style modify or extend appearance. For example, create a general gray button style and create a new one for red buttons
where only the new background color is set.
This is much like in CSS when used classes are listed like <div class=".btn .btn-red">.
Styles added later have precedence over ones set earlier. So in the gray/red button example above, the normal button style
should be added first and the red style second. However, the precedence of the states are still taken into account. So let's
examine the following case:
• the basic button style defines dark-gray color for the default state and light-gray color for the pressed state
• the red button style defines the background color as red only in the default state
In this case, when the button is released (it's in default state) it will be red because a perfect match is found in the most
recently added style (red). When the button is pressed the light-gray color is a better match because it describes the
current state perfectly, so the button will be light-gray.

5.3.3 Inheritance

Some properties (typically those related to text) can be inherited from the parent object's styles. Inheritance is applied
only if the given property is not set in the object's styles (even in default state). In this case, if the property is inheritable,
the property's value will be searched in the parents until an object specifies a value for the property. The parents will use
their own state to determine the value. So if a button is pressed, and the text color comes from here, the pressed text color
will be used.

5.3.4 Forced value inheritance/default value

Sometimes you may want to force a child object to use the parent's value for a given style property. To do this you can
use one of the following (depending on what type of style you're using):

/* regular style */
lv_style_set_prop_meta(&style, LV_STYLE_TEXT_COLOR, LV_STYLE_PROP_META_INHERIT);
/* local style */
lv_obj_set_local_style_prop_meta(child, LV_STYLE_TEXT_COLOR, LV_STYLE_PROP_META_
,→INHERIT, LV_PART_MAIN);

This acts like a value has been set on the style, so setting the value of the property afterwards will remove the flag.
You may also want to force the default value of a property to be used, without needing to hardcode it in your application.
To do this you can use the same API but with LV_STYLE_PROP_META_INITIAL instead. In future versions of
LVGL, this will use the value based upon the current theme, but for now it just selects the internal default regardless of
theme.

5.3. Styles 330


LVGL Documentation 9.0

5.3.5 Parts

Objects can be composed of parts which may each have their own styles.
The following predefined parts exist in LVGL:
• LV_PART_MAIN A background like rectangle
• LV_PART_SCROLLBAR The scrollbar(s)
• LV_PART_INDICATOR Indicator, e.g. for slider, bar, switch, or the tick box of the checkbox
• LV_PART_KNOB Like a handle to grab to adjust a value
• LV_PART_SELECTED Indicate the currently selected option or section
• LV_PART_ITEMS Used if the widget has multiple similar elements (e.g. table cells)
• LV_PART_TICKS Ticks on scales e.g. for a chart or meter
• LV_PART_CURSOR Mark a specific place e.g. text area's or chart's cursor
• LV_PART_CUSTOM_FIRST Custom part identifiers can be added starting from here.
For example a Slider has three parts:
• Background
• Indicator
• Knob
This means all three parts of the slider can have their own styles. See later how to add styles to objects and parts.

5.3.6 Initialize styles and set/get properties

Styles are stored in lv_style_t variables. Style variables should be static, global or dynamically allocated. In
other words they cannot be local variables in functions which are destroyed when the function exits. Before using a style
it should be initialized with lv_style_init(&my_style). After initializing a style, properties can be added or
changed.
Property set functions looks like this: lv_style_set_<property_name>(&style, <value>); For exam-
ple:

static lv_style_t style_btn;


lv_style_init(&style_btn);
lv_style_set_bg_color(&style_btn, lv_color_hex(0x115588));
lv_style_set_bg_opa(&style_btn, LV_OPA_50);
lv_style_set_border_width(&style_btn, 2);
lv_style_set_border_color(&style_btn, lv_color_black());

static lv_style_t style_btn_red;


lv_style_init(&style_btn_red);
lv_style_set_bg_color(&style_btn_red, lv_plaette_main(LV_PALETTE_RED));
lv_style_set_bg_opa(&style_btn_red, LV_OPA_COVER);

To remove a property use:

lv_style_remove_prop(&style, LV_STYLE_BG_COLOR);

To get a property's value from a style:

5.3. Styles 331


LVGL Documentation 9.0

lv_style_value_t v;
lv_res_t res = lv_style_get_prop(&style, LV_STYLE_BG_COLOR, &v);
if(res == LV_RES_OK) { /*Found*/
do_something(v.color);
}

lv_style_value_t has 3 fields:


• num for integer, boolean and opacity properties
• color for color properties
• ptr for pointer properties
To reset a style (free all its data) use:

lv_style_reset(&style);

Styles can be built as const too to save RAM:

const lv_style_const_prop_t style1_props[] = {


LV_STYLE_CONST_WIDTH(50),
LV_STYLE_CONST_HEIGHT(50),
LV_STYLE_CONST_PROPS_END
};

LV_STYLE_CONST_INIT(style1, style1_props);

Later const style can be used like any other style but (obviously) new properties can not be added.

5.3.7 Add and remove styles to a widget

A style on its own is not that useful. It must be assigned to an object to take effect.

Add styles

To add a style to an object use lv_obj_add_style(obj, &style, <selector>). <selector> is an


OR-ed value of parts and state to which the style should be added. Some examples:
• LV_PART_MAIN | LV_STATE_DEFAULT
• LV_STATE_PRESSED: The main part in pressed state. LV_PART_MAIN can be omitted
• LV_PART_SCROLLBAR: The scrollbar part in the default state. LV_STATE_DEFAULT can be omitted.
• LV_PART_SCROLLBAR | LV_STATE_SCROLLED: The scrollbar part when the object is being scrolled
• 0 Same as LV_PART_MAIN | LV_STATE_DEFAULT.
• LV_PART_INDICATOR | LV_STATE_PRESSED | LV_STATE_CHECKED The indicator part when the
object is pressed and checked at the same time.
Using lv_obj_add_style:

lv_obj_add_style(btn, &style_btn, 0); /


,→*Default button style*/

lv_obj_add_style(btn, &btn_red, LV_STATE_PRESSED); /*Overwrite only some colors to␣


,→red when pressed*/

5.3. Styles 332


LVGL Documentation 9.0

Replace styles

To replace a specific style of an object use lv_obj_replace_style(obj, old_style, new_style, se-


lector). This function will only replace old_style with new_style if the selector matches the selector
used in lv_obj_add_style. Both styles, i.e. old_style and new_style, must not be NULL (for adding and
removing separate functions exist). If the combination of old_style and selector exists multiple times in obj's
styles, all occurrences will be replaced. The return value of the function indicates whether at least one successful replace-
ment took place.
Using lv_obj_replace_style:

lv_obj_add_style(btn, &style_btn, 0); /*Add a button style*/


lv_obj_replace_style(btn, &style_btn, &new_style_btn, 0); /*Replace the button style␣
,→with a different one*/

Remove styles

To remove all styles from an object use lv_obj_remove_style_all(obj).


To remove specific styles use lv_obj_remove_style(obj, style, selector). This function will remove
style only if the selector matches with the selector used in lv_obj_add_style. style can be NULL
to check only the selector and remove all matching styles. The selector can use the LV_STATE_ANY and
LV_PART_ANY values to remove the style from any state or part.

Report style changes

If a style which is already assigned to an object changes (i.e. a property is added or changed), the objects using that style
should be notified. There are 3 options to do this:
1. If you know that the changed properties can be applied by a simple redraw (e.g. color or opacity changes) just call
lv_obj_invalidate(obj) or lv_obj_invalidate(lv_scr_act()).
2. If more complex style properties were changed or added, and you know which object(s) are affected by that
style call lv_obj_refresh_style(obj, part, property). To refresh all parts and properties use
lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY).
3. To make LVGL check all objects to see if they use a style and refresh them when needed, call
lv_obj_report_style_change(&style). If style is NULL all objects will be notified about a style
change.

Get a property's value on an object

To get a final value of property - considering cascading, inheritance, local styles and transitions (see below) - property get
functions like this can be used: lv_obj_get_style_<property_name>(obj, <part>). These functions
use the object's current state and if no better candidate exists they return a default value. For example:

lv_color_t color = lv_obj_get_style_bg_color(btn, LV_PART_MAIN);

5.3. Styles 333


LVGL Documentation 9.0

5.3.8 Local styles

In addition to "normal" styles, objects can also store local styles. This concept is similar to inline styles in CSS (e.g. <div
style="color:red">) with some modification.
Local styles are like normal styles, but they can't be shared among other objects. If used, local styles are allocated
automatically, and freed when the object is deleted. They are useful to add local customization to an object.
Unlike in CSS, LVGL local styles can be assigned to states (pseudo-classes) and parts (pseudo-elements).
To set a local property use functions like lv_obj_set_style_<property_name>(obj, <value>,
<selector>); For example:

lv_obj_set_style_bg_color(slider, lv_color_red(), LV_PART_INDICATOR | LV_STATE_


,→FOCUSED);

5.3.9 Properties

For the full list of style properties click here.

Typical background properties

In the documentation of the widgets you will see sentences like "The widget uses the typical background properties".
These "typical background properties" are the ones related to:
• Background
• Border
• Outline
• Shadow
• Padding
• Width and height transformation
• X and Y translation

5.3.10 Transitions

By default, when an object changes state (e.g. it's pressed) the new properties from the new state are set immediately.
However, with transitions it's possible to play an animation on state change. For example, on pressing a button its back-
ground color can be animated to the pressed color over 300 ms.
The parameters of the transitions are stored in the styles. It's possible to set
• the time of the transition
• the delay before starting the transition
• the animation path (also known as the timing or easing function)
• the properties to animate
The transition properties can be defined for each state. For example, setting a 500 ms transition time in the default state
means that when the object goes to the default state a 500 ms transition time is applied. Setting a 100 ms transition time in
the pressed state causes a 100 ms transition when going to the pressed state. This example configuration results in going
to the pressed state quickly and then going back to default slowly.

5.3. Styles 334


LVGL Documentation 9.0

To describe a transition an lv_transition_dsc_t variable needs to be initialized and added to a style:

/*Only its pointer is saved so must static, global or dynamically allocated */


static const lv_style_prop_t trans_props[] = {
LV_
,→ STYLE_BG_OPA, LV_STYLE_BG_COLOR,
0,
,→ /*End marker*/
};

static lv_style_transition_dsc_t trans1;


lv_style_transition_dsc_init(&trans1, trans_props, lv_anim_path_ease_out, duration_ms,
,→ delay_ms);

lv_style_set_transition(&style1, &trans1);

5.3.11 Opacity, Blend modes and Transformations

If the opa, blend_mode, transform_angle, or transform_zoom properties are set to their non-default value
LVGL creates a snapshot about the widget and all its children in order to blend the whole widget with the set opacity,
blend mode and transformation properties.
These properties have this effect only on the MAIN part of the widget.
The created snapshot is called "intermediate layer" or simply "layer". If only opa and/or blend_mode is set to a non-
default value LVGL can build the layer from smaller chunks. The size of these chunks can be configured by the following
properties in lv_conf.h:
• LV_LAYER_SIMPLE_BUF_SIZE: [bytes] the optimal target buffer size. LVGL will try to allocate this size of
memory.
• LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE: [bytes] used if LV_LAYER_SIMPLE_BUF_SIZE couldn't
be allocated.
If transformation properties were also used the layer can not be rendered in chunks, but one larger memory needs to be
allocated. The required memory depends on the angle, zoom and pivot parameters, and the size of the area to redraw,
but it's never larger than the size of the widget (including the extra draw size used for shadow, outline, etc).
If the widget can fully cover the area to redraw, LVGL creates an RGB layer (which is faster to render and uses less
memory). If the opposite case ARGB rendering needs to be used. A widget might not cover its area if it has radius,
bg_opa != 255, has shadow, outline, etc.
The click area of the widget is also transformed accordingly.

5.3.12 Color filter

TODO

5.3. Styles 335


LVGL Documentation 9.0

5.3.13 Themes

Themes are a collection of styles. If there is an active theme LVGL applies it on every created widget. This will give a
default appearance to the UI which can then be modified by adding further styles.
Every display can have a different theme. For example, you could have a colorful theme on a TFT and monochrome
theme on a secondary monochrome display.
To set a theme for a display, two steps are required:
1. Initialize a theme
2. Assign the initialized theme to a display.
Theme i