No matter how long you have been using LVGL, you have probably wonder at some point how to draw shapes like rectangles or circles on a screen. While creating things like buttons and lists is easy, drawing basic shapes is a bit more complicated because you have to go through LVGL’s drawing engine.
Today I will explain how you can use draw descriptors to draw a rectangle any where on your screen. In LVGL the basic principles are the same for all shapes but we will start with the rectangle.
Cautions about Drawing
When you use drawing descriptors you are directly manipulating the draw engine. Doing this can sometimes result in glitches: buttons covering your text, things disappearing, etc. This should not stop you from using them, but it is something to keep in mind.
To get the code below to work with your screen you will need to add your screen setup code to the beginning of it. If you want more information, you can try How to get a LVGL Micropython screen to work.
Here is some example code
import lvgl
scr = lvgl.obj(lvgl.scr_act())
scr.set_size(200,200)
scr.center()
def draw(data):
rect = lvgl.draw_rect_dsc_t()
rect.init()
rect.bg_color = lvgl.color_hex(0x00ff00)
size_and_spot = lvgl.area_t()
size_and_spot.x1 = 0
size_and_spot.y1 = 20
size_and_spot.x2 = 260
size_and_spot.y2 = 100
draw_ctx = data.get_draw_ctx()
draw_ctx.rect(rect,size_and_spot)
def extend(data):
data.set_ext_draw_size(60)
scr.add_event_cb(draw,lvgl.EVENT.DRAW_MAIN_END,None)
scr.add_event_cb(extend,lvgl.EVENT.REFR_EXT_DRAW_SIZE,None)
scr.refresh_ext_draw_size()
scr.invalidate()
Understanding This LVGL Code
Ok, now that you have seen all the code, let’s step through each section, so you can understand exactly what each part does. This will make it much easier to actually use descriptors in your projects…
The Setup
We need to import the LVGL module to be able to use it. This would also be a good place to add your screen setup code.
import lvgl
Creating the Canvas
To be able to draw we need something to draw on. You can draw on the background but because of the some of the things we are going to do latter we will create a new object to draw on.
scr = lvgl.obj(lvgl.scr_act())
Then we will set it’s size and put it in the center of the screen.
scr.set_size(200,200)
scr.center()
If you wanted to draw on the background you could replace the three lines above with the line below.
scr = lvgl.scr_act()
The Draw Function
If you draw a rectangle on your screen and just leave it, the next time LVGL refreshes the screen your drawing will disappear. To fix that, we will make a system that redraws the rectangle after every refresh.
First thing we need is a new function. Inside this function we will draw the rectangle.
def draw(data):
Next we need to create a rectangle descriptor this is essentially a special type of style.
rect = lvgl.draw_rect_dsc_t()
And just like styles we need to initialize it first.
rect.init()
The only setting we will edit is the background color of the rectangle.
rect.bg_color = lvgl.color_hex(0x00ff00)
The way you set the size and position of a rectangle is with an area variable so lets create a new one.
size_and_spot = lvgl.area_t()
Now we will set the starting position of our rectangle.
size_and_spot.x1 = 0
size_and_spot.y1 = 20
Then we set the end position. If you subtract the starting position from the end you will get the size of the rectangle.
(x2 – x1, y2 – y1) == (260 – 0, 100 – 20) == (260,80)
size_and_spot.x2 = 260
size_and_spot.y2 = 100
Latter we are going to use this function that we are creating as a handler, and handler functions are always passed an event variable. From that data ,we can get the draw_ctx class which is how we access the draw engine.
draw_ctx = data.get_draw_ctx()
Now that we have the draw_ctx, we can use it to draw a rectangle all we have to give it is our rectangle descriptor and the area its supposed to be put in.
draw_ctx.rect(rect,size_and_spot)
Extending The Draw Area
We are drawing on a object that we created earlier called scr. Currently the rectangle we have made is partially out side of scr and LVGL will crop anything that goes out side of an object.
Normally that is what we want ,but sometimes its not, so I will show how you can extend the drawing area.
To begin we need to create another function.
def extend(data):
Then inside that function we add the command to extend the draw area by 60 pixels.
data.set_ext_draw_size(60)
Latter we will hook up this function to the scr object so it has the correct timing.
Adding the Handlers
We now have created two handler functions and its time to connect them to the scr object.
Lets start with the draw function. We could run this function after every post drawing and that would make it be drawn on top of everything else.
But running the function after the main drawing is more practical. Things like scroll bars would be draw on top of our rectangle which is how they are supposed to work.
scr.add_event_cb(draw,lvgl.EVENT.DRAW_MAIN_END,None)
Next we will connect the function that extends the draw area.
scr.add_event_cb(extend,lvgl.EVENT.REFR_EXT_DRAW_SIZE,None)
Triggering Some Events
Everything is ready to go but because nothing has append to our screen it has not updated anything. We are going to manually trigger some of the events ,so the screen actually shows the changes we’ve made.
To start we need to call the extend area function by using this command on our object.
scr.refresh_ext_draw_size()
Lastly we need to tell LVGL to just redraw our object. The invalidate command tell LVGL that an object needs updating so we run it on our scr object.
scr.invalidate()
What Should Happen
You have seen the code. If you run it, a large green rectangles should appear near the top of your screen. Over all your screen should look like the picture of mine.
The most useful feature of knowing how to use drawing descriptors is that you can modify the ones that already exist. LVGL widgets use drawing descriptors, which means you can edit those descriptors to change how any widget looks.
Now that you understand how to draw a rectangle, learning to draw the rest of LVGL’s shapes will be easy. I would suggest learning how to use LVGL Polygons and LVGL Arcs and Circles
Drawing descriptors are powerful but they are hard to get working and a can be glitchy. If you have a project that needs custom drawing Micropython LVGL Canvases can be a simpler and more reliable way to reach your goal.