LVGL how to draw a rectangle


#1

Hello, I was wandering how can I draw a rectangle in lvgl 7 with a cyberpi,
have a great day :slight_smile:


#2

Considering lvgl 7 does not have a dedicated polygon function, my best bet would be a connected line:

try:    
    # Create an array for the points of the line
    line_points = [ {"x":10, "y":10}, 
                    {"x":100, "y":10}, 
                    {"x":100, "y":50},
                    {"x":10, "y":50},
                    {"x":10, "y":10}]
    
    # Create new style (thick dark blue)
    style_line = lv.style_t()
    lv.style_copy(style_line, lv.style_plain)
    style_line.line.color = lv.color_make(0x00, 0x3b, 0x75)
    style_line.line.width = 5
    style_line.line.rounded = 0
    
    # Copy the previous line and apply the new style
    scr = lv.obj()
    line1 = lv.line(scr)
    line1.set_points(line_points, len(line_points))      # Set the points
    line1.set_style(lv.line.STYLE.MAIN, style_line)
    line1.align(None, lv.ALIGN.IN_TOP_MID, 0, 0)
    lv.scr_load(scr)
except Exception as err:
    pass
    cyberpi.console.print(err)

Otherwise, the button object is a rectangle with lots of adjustments.
The canvas object allows you to draw anything, but canvas requires buffer management:
https://docs.lvgl.io/6.1/object-types/canvas.html#example
BTW, I found that the 6.1 documentation is half-decent to work with the LGVL 7 in the MicroPython environment.


#3

In the cyberpi, it does have the polygone and the draw_triangle func, i know it because i search using cyberpi commands


#4

also sry I didn’t specify it but I’m looking for full filled custom rectangles (like I can chose 4 random points to make a rectangle (for 3D))


#5

draw_ functions are not exposed in our version of micropython and are not meant to be accessed directly. They are accessible through the canvas object as I wrote above, but canvas in our version does not accept a rectangle descriptor (no polygons either).
Another approach would be to draw rectangles using lvgl base object, which is a rectangle by itself.
# Create a new style
r_style = lv.style_t(lv.style_plain)
r_style.body.main_color = lv.color_make(255, 0, 0) # Red
r_style.body.grad_color = lv.color_make(155, 0, 0) # Gradient other color is red too
r_style.body.radius = 2

# Create a screen and load it
scr = lv.obj()
lv.scr_load(scr)

# Create a rectangle (lv.obj is rectangular by default)
rect = lv.obj(scr)
rect.set_size(100, 50)
rect.align(scr, lv.ALIGN.CENTER, 0, 0)

# Apply style using legacy API
rect.set_style(r_style)

#6

or by-pixel rendering.
here’s an e.g with pentagon shape:

import lvgl as lv
import gc
import math

gc.collect()

# Canvas setup
WIDTH = 127
HEIGHT = 127
buf = bytearray(WIDTH * HEIGHT * 4)
screen = lv.obj()
lv.scr_load(screen)
canvas = lv.canvas(screen)
canvas.set_buffer(buf, WIDTH, HEIGHT, lv.img.CF.TRUE_COLOR)
canvas.fill_bg(lv.color_make(255, 255, 255))  # White background

# Helper: set pixel safely
def set_pixel_safe(x, y, color):
    if 0 <= x < WIDTH and 0 <= y < HEIGHT:
        canvas.set_px(x, y, color)

# Helper: draw filled triangle using scanline fill
def draw_filled_triangle(p1, p2, p3, color):
    points = sorted([p1, p2, p3], key=lambda p: p[1])  # Sort by Y
    x1, y1 = points[0]
    x2, y2 = points[1]
    x3, y3 = points[2]

    def interpolate(y, x0, y0, x1, y1):
        if y1 == y0:
            return x0
        return int(x0 + (x1 - x0) * (y - y0) / (y1 - y0))

    y_start = max(y1, 0)
    y_end = min(y3, HEIGHT - 1)

    for y in range(y_start, y_end + 1):
        if y < y2:
            xa = interpolate(y, x1, y1, x2, y2)
            xb = interpolate(y, x1, y1, x3, y3)
        else:
            xa = interpolate(y, x2, y2, x3, y3)
            xb = interpolate(y, x1, y1, x3, y3)
        if xa > xb:
            xa, xb = xb, xa
        for x in range(max(xa, 0), min(xb + 1, WIDTH)):
            set_pixel_safe(x, y, color)

# Draw filled pentagon at center
def draw_filled_pentagon(cx, cy, r, rotation_deg, color):
    points = []
    for i in range(5):
        angle_deg = rotation_deg + i * 72  # 360 / 5
        angle_rad = math.radians(angle_deg)
        x = int(cx + r * math.cos(angle_rad))
        y = int(cy + r * math.sin(angle_rad))
        points.append((x, y))

    # Triangle fan: center to edges
    for i in range(5):
        p1 = (cx, cy)
        p2 = points[i]
        p3 = points[(i + 1) % 5]
        draw_filled_triangle(p1, p2, p3, color)

# Parameters: center x, y, radius, rotation, color
draw_filled_pentagon(
    cx=64,
    cy=64,
    r=40,
    rotation_deg=-70,
    color=lv.color_make(0, 255, 0)
)