图表¶
例程¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | import lvgl as lv
import lvgl_helper as lv_h
import lcd
import time
import touchscreen as ts
lcd.init()
ts.init()
lv.init()
disp_buf1 = lv.disp_buf_t()
buf1_1 = bytearray(320*10)
lv.disp_buf_init(disp_buf1,buf1_1, None, len(buf1_1)//4)
disp_drv = lv.disp_drv_t()
lv.disp_drv_init(disp_drv)
disp_drv.buffer = disp_buf1
disp_drv.flush_cb = lv_h.flush
disp_drv.hor_res = 320
disp_drv.ver_res = 240
lv.disp_drv_register(disp_drv)
indev_drv = lv.indev_drv_t()
lv.indev_drv_init(indev_drv)
indev_drv.type = lv.INDEV_TYPE.POINTER
indev_drv.read_cb = lv_h.read
lv.indev_drv_register(indev_drv)
class Anim(lv.anim_t):
def __init__(self, obj, val, size, exec_cb, path_cb, time=500, playback = False, ready_cb=None):
super().__init__()
lv.anim_init(self)
lv.anim_set_time(self, time, 0)
lv.anim_set_values(self, val, val+size)
if callable(exec_cb):
lv.anim_set_custom_exec_cb(self, exec_cb)
else:
lv.anim_set_exec_cb(self, obj, exec_cb)
lv.anim_set_path_cb(self, path_cb )
if playback: lv.anim_set_playback(self, 0)
if ready_cb: lv.anim_set_ready_cb(self, ready_cb)
lv.anim_create(self)
class AnimatedChart(lv.chart):
def __init__(self, parent, val, size):
super().__init__(parent)
self.val = val
self.size = size
self.max = 2000
self.min = 500
self.factor = 100
self.anim_phase1()
def anim_phase1(self):
Anim(
self,
self.val,
self.size,
lambda a, val: self.set_range(0, val),
lv.anim_path_ease_in,
ready_cb=lambda a:self.anim_phase2(),
time=(self.max * self.factor) // 100)
def anim_phase2(self):
Anim(
self,
self.val+self.size,
-self.size,
lambda a, val: self.set_range(0, val),
lv.anim_path_ease_out,
ready_cb=lambda a:self.anim_phase1(),
time=(self.min * self.factor) // 100)
scr = lv.obj()
chart = AnimatedChart(scr, 100, 1000)
chart.set_width(scr.get_width() - 100)
chart.set_height(scr.get_height() - 60)
chart.align(scr, lv.ALIGN.CENTER, 0, 0)
series1 = chart.add_series(lv.color_hex(0xFF0000))
chart.set_type(chart.TYPE.POINT | chart.TYPE.LINE)
chart.set_series_width(3)
chart.set_range(0,100)
chart.init_points(series1, 10)
chart.set_points(series1, [10,20,35,20,10,40,51,90,95,80])
chart.set_x_tick_texts('a\nb\nc\nd\ne', 2, lv.chart.AXIS.DRAW_LAST_TICK)
chart.set_x_tick_length(10, 5)
chart.set_y_tick_texts('1\n2\n3\n4\n5', 2, lv.chart.AXIS.DRAW_LAST_TICK)
chart.set_y_tick_length(10, 5)
chart.set_div_line_count(3, 3)
chart.set_margin(30)
def on_slider_changed(self, obj=None, event=-1):
chart.factor = slider.get_value()
slider = lv.slider(scr)
slider.align(chart, lv.ALIGN.OUT_RIGHT_TOP, 10, 0)
slider.set_width(30)
slider.set_height(chart.get_height())
slider.set_range(10, 200)
slider.set_value(chart.factor, 0)
slider.set_event_cb(on_slider_changed)
lv.scr_load(scr)
tim = time.ticks_ms()
while True:
if time.ticks_ms()-tim > 5:
tim = time.ticks_ms()
lv.task_handler()
lv.tick_inc(5)
|
实验准备¶
将
野火K210 AI视觉相机
连接到CanMV IDE
执行程序
运行结果¶
1.运行程序后,可以看到 野火K210 AI视觉相机
会显示下面内容,此时图表里面的内容会上下运动

2.按住右边的小滑块,往上推,可以看到图表里的变化趋势变得很慢

3.按住右边的小滑块,往下拉,可以看到图表里的变化趋势变得很快

讲解¶
1 2 3 4 5 | import lvgl as lv
import lvgl_helper as lv_h
import lcd
import time
import touchscreen as ts
|
这些库提供了LVGL框架、LCD显示、触摸屏支持和一些辅助函数。
1 2 3 | lcd.init()
ts.init()
lv.init()
|
初始化了LCD显示,触摸屏和LVGL库。
1 2 3 | disp_buf1 = lv.disp_buf_t()
buf1_1 = bytearray(320*10)
lv.disp_buf_init(disp_buf1,buf1_1, None, len(buf1_1)//4)
|
这里创建了一个显示缓冲区disp_buf1,它使用buf1_1作为缓冲区内存,大小为320*10字节。lv.disp_buf_init函数初始化这个缓冲区,最后一个参数是缓冲区大小的四分之一,因为每个像素点通常由4个字节表示(RGBA)
1 2 | disp_drv = lv.disp_drv_t()
lv.disp_drv_init(disp_drv)
|
这两行代码创建并初始化了一个显示驱动程序对象disp_drv。
1 2 3 4 | disp_drv.buffer = disp_buf1
disp_drv.flush_cb = lv_h.flush
disp_drv.hor_res = 320
disp_drv.ver_res = 240
|
这里设置显示驱动程序的缓冲区、刷新回调函数(用于将缓冲区内容刷新到LCD)以及水平分辨率和垂直分辨率。
1 | lv.disp_drv_register(disp_drv)
|
这行代码将显示驱动程序注册到LVGL中。
1 2 | indev_drv = lv.indev_drv_t()
lv.indev_drv_init(indev_drv)
|
这里创建并初始化了一个输入设备驱动程序对象indev_drv。
1 2 3 | indev_drv.type = lv.INDEV_TYPE.POINTER
indev_drv.read_cb = lv_h.read
lv.indev_drv_register(indev_drv)
|
这里设置输入设备驱动程序的类型为指针(即触摸屏),并设置读取回调函数(用于读取触摸屏输入)。
1 | lv.indev_drv_register(indev_drv)
|
这行代码将输入设备驱动程序注册到LVGL中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | class Anim(lv.anim_t):
def __init__(self, obj, val, size, exec_cb, path_cb, time=500, playback = False, ready_cb=None):
super().__init__()
lv.anim_init(self)
lv.anim_set_time(self, time, 0)
lv.anim_set_values(self, val, val+size)
if callable(exec_cb):
lv.anim_set_custom_exec_cb(self, exec_cb)
else:
lv.anim_set_exec_cb(self, obj, exec_cb)
lv.anim_set_path_cb(self, path_cb )
if playback: lv.anim_set_playback(self, 0)
if ready_cb: lv.anim_set_ready_cb(self, ready_cb)
lv.anim_create(self)
class AnimatedChart(lv.chart):
def __init__(self, parent, val, size):
super().__init__(parent)
self.val = val
self.size = size
self.max = 2000
self.min = 500
self.factor = 100
self.anim_phase1()
def anim_phase1(self):
Anim(
self,
self.val,
self.size,
lambda a, val: self.set_range(0, val),
lv.anim_path_ease_in,
ready_cb=lambda a:self.anim_phase2(),
time=(self.max * self.factor) // 100)
def anim_phase2(self):
Anim(
self,
self.val+self.size,
-self.size,
lambda a, val: self.set_range(0, val),
lv.anim_path_ease_out,
ready_cb=lambda a:self.anim_phase1(),
time=(self.min * self.factor) // 100)
|
这段代码定义了两个类:Anim和AnimatedChart,它们都是基于LVGL的动画功能的封装
1 2 | scr = lv.obj()
chart = AnimatedChart(scr, 100, 1000)
|
这里创建了一个LVGL对象作为屏幕,并在这个屏幕上创建了一个AnimatedChart对象,初始值为100,大小变化为1000。
1 2 3 | chart.set_width(scr.get_width() - 100)
chart.set_height(scr.get_height() - 60)
chart.align(scr, lv.ALIGN.CENTER, 0, 0)
|
这些代码设置图表的大小,使其比屏幕宽度少100像素,高度少60像素,并将图表在屏幕上居中显示。
1 2 3 4 5 6 | series1 = chart.add_series(lv.color_hex(0xFF0000))
chart.set_type(chart.TYPE.POINT | chart.TYPE.LINE)
chart.set_series_width(3)
chart.set_range(0,100)
chart.init_points(series1, 10)
chart.set_points(series1, [10,20,35,20,10,40,51,90,95,80])
|
这些代码添加了一个系列到图表中,设置了图表的类型为点和线,设置了系列的宽度,初始化了点的数量,并设置了点的值。
1 2 3 4 5 6 | chart.set_x_tick_texts('a\nb\nc\nd\ne', 2, lv.chart.AXIS.DRAW_LAST_TICK)
chart.set_x_tick_length(10, 5)
chart.set_y_tick_texts('1\n2\n3\n4\n5', 2, lv.chart.AXIS.DRAW_LAST_TICK)
chart.set_y_tick_length(10, 5)
chart.set_div_line_count(3, 3)
chart.set_margin(30)
|
这些代码设置了x轴和y轴的刻度文本、长度和网格线的数量,以及图表的边距。
1 2 3 4 5 6 7 8 9 10 | def on_slider_changed(self, obj=None, event=-1):
chart.factor = slider.get_value()
slider = lv.slider(scr)
slider.align(chart, lv.ALIGN.OUT_RIGHT_TOP, 10, 0)
slider.set_width(30)
slider.set_height(chart.get_height())
slider.set_range(10, 200)
slider.set_value(chart.factor, 0)
slider.set_event_cb(on_slider_changed)
|
这些代码创建了一个滑动条,并将其放置在图表的右侧。滑动条的高度与图表相同,范围为10到200。当滑动条的值发生变化时,会调用on_slider_changed函数,该函数会更新图表的factor属性,从而影响动画的速度。
1 2 3 4 5 6 7 | lv.scr_load(scr)
tim = time.ticks_ms()
while True:
if time.ticks_ms()-tim > 5:
tim = time.ticks_ms()
lv.task_handler()
lv.tick_inc(5)
|
这段代码加载了屏幕,并进入了一个无限循环。循环中,它每隔5毫秒调用lv.task_handler()来处理LVGL的任务,并使用lv.tick_inc(5)来通知LVGL时间已经过去了5毫秒,这样可以更新动画和其他LVGL相关的任务。