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.

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.

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[DISP_HOR_RES * 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_SER / 10);  /*Initialize the display buffer.*/
  • Implement and register a function which can copy the rendered image to an area of your display:

lv_disp_drv_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.buffer = &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.hor_res = MY_DISP_VER_RES;   /*Set the verizontal resolution of the display*/
lv_disp_drv_register(&disp_drv);      /*Finally register the driver*/

void my_disp_flush(lv_disp_drv_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 touch pad:

lv_indev_drv_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*/

bool my_touchpad_read(lv_indev_t * indev, 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 operating system task. It will redraw the screen if required, handle input devices, animation etc.

For a more detailed guide go to the Porting section.

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 on their parent. It other words, the parts of the children outside of 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);

The widgets have type specific parameters too which can be 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/lv_slider.h).

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_cb(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");
}

Instead of LV_EVENT_CLICKED LV_EVENT_ALL can be used too to call the callback for any event.

From lv_event_t * e the current event code can be get 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 different parts. (See below)

To learn which parts are used by which object read the widgets' documentation.

States

The 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

For example, if you press an object it will automatically go to LV_STATE_FOCUSED and LV_STATE_PRESSED state and when you release it, the LV_STATE_PRESSED state will be removed.

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 in that state at that time.

To manually add or remove states use

lv_obj_add_state(obj, LV_STATE_...);
lv_obj_clear_state(obj, LV_STATE_...);

Styles

Styles contains properties such as background color, border width, font, etc to describe the appearance of the objects.

The styles are lv_style_t variables. Only their pointer is saved in the objects so they need to be static or global. Before using a style it needs to be initialized with lv_style_init(&style1). After that properties can be added. 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.

The styles are assigned to 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*/

The 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. If the property is not defined even in the default state a default value is used.

Some properties (typically the text-related ones) can be inherited. It means if a property is not set in an object it will be searched 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 the objects. It creates a style which resides inside the object and which 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 of the objects. The styles from the themes are applied automatically when the objects are created.

You can select the theme to use in lv_conf.h.

Examples

A button with a label and react on click event

Click to try in the MicroPython simulator!
#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_1(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_cb(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

MicroPython code     view on GitHub

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_cb(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()

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

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


counterBtn = CounterBtn()


Create styles from scratch for buttons

Click to try in the MicroPython simulator!
#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);

    /*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_2(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 an other 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

MicroPython code     view on GitHub

#
# 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
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 an other 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
label.set_text("Button 2");                 # Set the labels text
label.center()


Create a slider and write its value on a label

Click to try in the MicroPython simulator!
#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, "%d", 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_3(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_cb(slider, slider_event_cb, LV_EVENT_VALUE_CHANGED, NULL);     /*Assign an event function*/

    /*Create a label below 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

MicroPython code     view on GitHub

def slider_event_cb(evt):
    slider = evt.get_target()

    # Refresh the text
    label.set_text(str(slider.get_value()))

#
# Create a slider and write its value on a label.
#
            
# 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_cb(slider_event_cb, lv.EVENT.VALUE_CHANGED, None) # Assign an event function

# Create a label below 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



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)