How to use draw_XXX function with micropython

Hi:
image
There have some function with draw ,can we use it?
eg:


According to the draw_line C prototype,we need provide 4 args.

import lvgl as lv

scr = lv.obj()
lv.scr_load(scr)

p1 = lv.point_t()
p2 = lv.point_t()
p1.x = 10
p1.y = 10
p2.x = 50
p2.y = 50

area = lv.area_t()
line_dsc = lv.draw_line_dsc_t()
'''
area:
__class__       copy            set             __dereference__
SIZE            cast            cast_instance   get_height
get_size        get_width       set_height      set_width
x1              x2              y1              y2

line_dsc:
__class__       __dereference__                 SIZE
blend_mode      cast            cast_instance   color
dash_gap        dash_width      init            opa
raw_end         round_end       round_start     width
'''
area.set_width(100)
area.set_height(100)

color = lv.color_make(0xFF, 0xA5, 0x00)
line_dsc.init()
line_dsc.color = color
line_dsc.width = 5

line = lv.draw_line(p1, p2, area, line_dsc)

lv.scr_load(scr)
'''
DIDN'T SHOW ANY THING.
any else things need to do? like: lv.scr_load(scr)
'''

Is my usage wrong or am I missing something?I need use draw some very basic shape(like: line, triangle, rect, polygon…)

Widgets display very good!

You shouldn’t use the draw functions directly, except inside a custom object implementation. Instead, you should make use of widgets like lv_line or lv_obj for drawing basic shapes.

Alternatively, you can draw custom graphics on a canvas, but this is the most expensive method in terms of memory.

Thanks for reply so quickly!

How to do that?Can you give a basic code?

It looks no function like lv_obj(lv_line, lv_rect) can use…

But I don’t how to use canvas with micropython, I thought draw_line it’s more simple than use canvas.

So you just want to draw a line?

Here is an example (on v.7).
You can also try this online with the simulator.

scr = lv.obj()

style = lv.style_t()
style.init()
style.set_line_width(lv.STATE.DEFAULT, 8)
style.set_line_color(lv.STATE.DEFAULT, lv.color_hex3(0x00F))
style.set_line_rounded(lv.STATE.DEFAULT, True)

points = [
    {'x':50, 'y':50}, 
    {'x':100, 'y':100},
    {'x':0, 'y':100},
    {'x':50, 'y':50},
]
line = lv.line(scr)
line.set_points(points, len(points))
line.add_style(line.PART.MAIN, style)
line.align(None, lv.ALIGN.CENTER, 0, 0)

lv.scr_load(scr)

Hi,
Thanks for help.
It works.I want draw triangle or rectangle like this.But there no API like lv.XXXX() can use…
Can I add by myself and how?

As @embeddedt suggested, you can use canvas if you have enough RAM.

Example (online simulator)

scr = lv.obj()

draw_desc = lv.draw_rect_dsc_t()
draw_desc.init()
draw_desc.bg_opa = lv.OPA.COVER;
draw_desc.bg_grad_dir = lv.GRAD_DIR.HOR;
draw_desc.bg_color = lv.color_hex3(0xF00);
draw_desc.bg_grad_color = lv.color_hex3(0x00F);

CANVAS_WIDTH = 200
CANVAS_HEIGHT = 150

points = [
    {'x':100, 'y':50}, 
    {'x':150, 'y':100},
    {'x':50, 'y':100},
    {'x':100, 'y':50},
]

canvas = lv.canvas(scr)
buf = bytearray(CANVAS_WIDTH * CANVAS_HEIGHT * lv.color_t.SIZE)
canvas.set_buffer(buf, CANVAS_WIDTH, CANVAS_HEIGHT, lv.img.CF.TRUE_COLOR)
canvas.draw_polygon(points, len(points), draw_desc)
canvas.align(None, lv.ALIGN.CENTER, 0, 0)

lv.scr_load(scr)

I thought it could be a nice exercise to show how to create a custom widget in Micropython.

Here is a new widget “MyLine” which is just a diagonal line (drawn with the lv.draw_line function).
It implements both signal and design callbacks.
design draws the line, and signal responses to press/release events and changes the diagonal line accordingly.

Online demo available

import lvgl as lv

class MyLine(lv.obj):

    def __init__(self, par):
        super().__init__(par) 
        self.ancestor_signal = self.get_signal_cb()        
        self.set_design_cb(self.design)
        self.set_signal_cb(self.signal)
        self.pressed = False

    def signal(self, line, sign, param):
        if sign == lv.SIGNAL.PRESSED:
            self.pressed = True
        elif sign == lv.SIGNAL.RELEASED or sign == lv.SIGNAL.PRESS_LOST:
            self.pressed = False
        saved_signal = self.get_signal_cb()
        self.set_signal_cb(self.ancestor_signal)
        res = lv.signal_send(self, sign, param)
        self.set_signal_cb(saved_signal)        
        return res

    def design(self, line, clip_area, mode):        
        if mode == lv.DESIGN.COVER_CHK:
            return lv.DESIGN_RES.NOT_COVER
        if mode == lv.DESIGN.DRAW_MAIN:         
            area = lv.area_t()
            self.get_coords(area)
            h = self.get_height()
            x_ofs = area.x1
            y_ofs = area.y1
            if self.pressed:            
                p1 = lv.point_t({'x':x_ofs, 'y': y_ofs})
                p2 = lv.point_t({'x':x_ofs+h, 'y': y_ofs+h})
            else:
                p1 = lv.point_t({'x':x_ofs+h, 'y': y_ofs})
                p2 = lv.point_t({'x':x_ofs, 'y': y_ofs+h})
            dsc = lv.draw_line_dsc_t()
            dsc.init()
            lv.draw_line(p1, p2, clip_area, dsc)                        
        return lv.DESIGN_RES.OK
        

scr = lv.obj()

line = MyLine(scr)
line.align(None, lv.ALIGN.CENTER, 0, 0)
line.set_drag(True)

lv.scr_load(scr)

That’s a lot of code just for calling the old signal function. I assume it’s because the function can’t be called directly from MicroPython. Could we add a utility function that makes this kind of thing easier?

Currently the Micropython Binding does not support calling C function pointers, so using lv.signal_send is a workaround.
I don’t think there is a need to support that generically, since it’s very rare.
What utility function did you have in mind @embeddedt ? A function that receives a signal function pointer and calls it? My opinion is that for now we can keep current workaround.

Wow,this is what I needed!VERY GOOD EXAMPLE!

So,if I want create a custom widget,just need to implement two functions:

  • signal
  • design

But I got one question, the parameters how to passed?In your example 2 points of the line is inside of the design function,but how can we by passed the points to it?

You can add setter member functions to your widget class, and update properties on self.
You can access self from signal and design, because they are member functions of the widget.
You can also pass arguments to the widget’s constructor. Now it receives only par but you can add others.

In C it would be possible to call self.ancestor_signal(sign, param) directly. This is a lot more natural to the user than saving the previous signal function and attaching a new one. We can leave the current behavior for now though.

I tried, but fail…Maybe I need review the C function how to and do it.
The most important thing is I don’t know the constructor function how it works in micropython.

My bad try…

This explains constructors on Python. Micropython is a variation of Python, the core language is almost the same.

I fixed your try.

  • I’ve added a call to invalidate at the end of setPoints. It tells lvgl it needs to redraw the widget.
  • I’ve used the new p1/p2 properties in design function.

Hi @amirgon,
It works!Thank you for your help.You gave me a direction to solve the problem!
I did some change(maybe it was not perfect,but it works!), it has more function to change the line.
simple coustom widget example

I will continue to do it.But I think it is more convenient to create custom widgets with C code?