Applying individual styles to every object on your screen is a lot of work. Because of how annoying this process is LVGL has super styles aka themes. You apply a theme to your display, and it styles every object on your screen.
Today we will work through how to create a custom theme and modify an existing theme.
How Themes Work
Themes are actually a function. Essentially a you create a function, and every time an object is created your function is called. Inside that function, you check what type of object was created and then apply a style to it.
Setting Up a Micropython Screen
Because there are so many different screens out there, I can’t add support for all of them to the example, so to make the example work, you will need to add your screen’s setup code to the beginning of it.
There is a little more information on this process at How to get a LVGL Micropython Screen to Work if you aren’t familiar on how it’s done.
Here is the example
import lvgl
display = lvgl.disp_get_default()
defualt_theme = lvgl.theme_get_from_obj(lvgl.scr_act())
def apply(th,obj):
if obj.get_class() == lvgl.btn_class:
obj.set_style_bg_color(lvgl.color_hex(0x009900),0)
theme = lvgl.theme_t()
theme.set_parent(defualt_theme)
theme.set_apply_cb(apply)
display.set_theme(theme)
btn = lvgl.btn(lvgl.scr_act())
btn.center()
btn.set_size(60,40)
gen_theme = lvgl.theme_default_init(display,lvgl.color_hex(0x990000),lvgl.color_hex(0x00ff00),False,lvgl.font_montserrat_16)
display.set_theme(gen_theme)
btn = lvgl.btn(lvgl.scr_act())
btn.align(lvgl.ALIGN.CENTER,0,60)
btn.set_size(60,40)
Understanding This Code
Just looking at a block of code is not always very informational. To make how this micropython code works more clear, lets walk through the lines of code above and see what they do.
The Setup
Of course before we can use LVGL we need to import it. This is also a good spot to put your screen setup code.
import lvgl
We also need to retrieve the active screen object. Themes are applied to physical screens, so if you have more than one screen connected to your board, your are going to need to apply a theme to each one.
display = lvgl.disp_get_default()
Custom Themes
Lets create a custom theme. To make this theme simple, it will only target LVGL buttons, so we will use the default themes settings for all the other widgets. First we are going to retrieve the default theme.
defualt_theme = lvgl.theme_get_from_obj(lvgl.scr_act())
Next we create the theme’s function.
def apply(th,obj):
Inside that function we check if the current object is a button.
if obj.get_class() == lvgl.btn_class:
If it is, we change the buttons color to green.
obj.set_style_bg_color(lvgl.color_hex(0x009900),0)
Now we create a empty theme object.
theme = lvgl.theme_t()
Then we set its parent to the default theme any thing our theme does not deal with the parent theme takes care of.
theme.set_parent(defualt_theme)
After that we set the themes function to the function we created earlier.
theme.set_apply_cb(apply)
Lastly we add our theme to the the screen.
display.set_theme(theme)
The Button
Our new theme is applied but nothing is actually using it. Lets create button to show its working.
btn = lvgl.btn(lvgl.scr_act())
btn.center()
btn.set_size(60,40)
Generating a Theme
Manually creating a theme that styles every widget takes a lot of work, so LVGL comes with some pre-made themes that you can modify to your liking.
Making a new theme, only takes one line of code.
gen_theme = lvgl.theme_default_init(display,lvgl.color_hex(0x990000),lvgl.color_hex(0x00ff00),False,lvgl.font_montserrat_16)
The theme_default_init() function takes five arguments. The first argument is the active display. The other four arguments are explained below.
Argument | Value | Controls |
---|---|---|
Primary Color | Any LVGL Color | the color of most objects |
Secondary Color | Any LVGL Color | the color of a few objects |
Dark/Light Mode | True or False | whether LVGL uses dark or light colors |
Font | Any LVGL Font | the default font of LVGL |
With that one line of code we have generated a complete theme that works for all widgets, but we still need to apply it to the screen to see it work.
display.set_theme(gen_theme)
One Last Button
Of course, we need to create one more button to show that our theme is working.
btn = lvgl.btn(lvgl.scr_act())
btn.align(lvgl.ALIGN.CENTER,0,60)
btn.set_size(60,40)
Things To Think About
One obvious question that comes up, if we change the theme why doesn’t every object on the screen get that new theme applied to it. The answer to that question is I don’t now. Sometimes it seems that adding a theme does overrides the current theme and some time it seems to let the old one exist in the background.
We looked at two ways to create themes today. The last one was clearly easier and is the best option for normal use. The first method should really only be used, if you need an extremely custom theme.
If you like LVGL Themes then you might like LVGL Drawing Descriptors. They are another great way to customize your projects.