图表

例程

  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)

实验准备

  1. 野火K210 AI视觉相机 连接到 CanMV IDE

  2. 执行程序

运行结果

1.运行程序后,可以看到 野火K210 AI视觉相机 会显示下面内容,此时图表里面的内容会上下运动

野火logo

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

野火logo

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

野火logo

讲解

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相关的任务。