1、代码仓库
学习的LVGL代码来自下面的链接,感谢开源的大佬lv_demo_hub: - LVGL DEMO 仓库 - 目的:可快速评估LVGL组件,了解LVGL能做些什么 - 支持:ubuntu(gcc)、windows(X64 vs2022)、linux arm(sigmastar SSD2XX) (gitee.com)
2、创建简单折线图
int app_chart_1(lv_obj_t *parent)
{
/*Create a chart*/
lv_obj_t *chart;
chart = lv_chart_create(parent);
lv_obj_set_size(chart, 200, 150);
lv_obj_center(chart);
lv_chart_set_type(chart, LV_CHART_TYPE_LINE); /*Show lines and points too*/
/*Add two data series*/
lv_chart_series_t *ser1 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_RED), LV_CHART_AXIS_PRIMARY_Y);
lv_chart_series_t *ser2 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_GREEN), LV_CHART_AXIS_SECONDARY_Y);
/*Set the next points on 'ser1'*/
lv_chart_set_next_value(chart, ser1, 10);
lv_chart_set_next_value(chart, ser1, 10);
lv_chart_set_next_value(chart, ser1, 10);
lv_chart_set_next_value(chart, ser1, 10);
lv_chart_set_next_value(chart, ser1, 10);
lv_chart_set_next_value(chart, ser1, 10);
lv_chart_set_next_value(chart, ser1, 10);
lv_chart_set_next_value(chart, ser1, 30);
lv_chart_set_next_value(chart, ser1, 70);
lv_chart_set_next_value(chart, ser1, 90);
/*Directly set points on 'ser2'*/
ser2->y_points[0] = 90;
ser2->y_points[1] = 70;
ser2->y_points[2] = 65;
ser2->y_points[3] = 65;
ser2->y_points[4] = 65;
ser2->y_points[5] = 65;
ser2->y_points[6] = 65;
ser2->y_points[7] = 65;
ser2->y_points[8] = 65;
ser2->y_points[9] = 65;
lv_chart_refresh(chart); /*Required after direct set*/
return 0;
}
3、代码解析
我们可以将折线图的实现分为两个步骤
3.1 创建图表并设置图表的大小和位置
lv_obj_t *chart;
chart = lv_chart_create(parent);//创建图表
lv_obj_set_size(chart, 200, 150);//设置图表的大小
lv_obj_center(chart);//使图表处于屏幕中央
lv_chart_set_type(chart, LV_CHART_TYPE_LINE);//设置图表的类型为折线型
前面几句都是经常用到的,很好理解,lv_chart_set_type(chart, LV_CHART_TYPE_LINE);这句顾名思义,就是设置图表的类型,我们可以跳进去看看它有什么类型
enum {
LV_CHART_TYPE_NONE, /**< Don't draw the series*/
LV_CHART_TYPE_LINE, /**< Connect the points with lines*/
LV_CHART_TYPE_BAR, /**< Draw columns*/
LV_CHART_TYPE_SCATTER, /**< Draw points and lines in 2D (x,y coordinates)*/
};
第一个是什么都不干,肯定不选它,第二个就是我们所需要的折线图,
第三个是BAR,是柱状图
第四个是画一些单独的点的图
3.2 往图表里面加入我们需要的数据
我们看到这两条函数,很明显就是往图表里面添加数据的
lv_chart_series_t *ser1 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_RED), LV_CHART_AXIS_PRIMARY_Y);
lv_chart_series_t *ser2 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_GREEN), LV_CHART_AXIS_SECONDARY_Y);
我们看一下函数原型
lv_chart_series_t * lv_chart_add_series(lv_obj_t * obj, lv_color_t color, lv_chart_axis_t axis)
{
LV_LOG_INFO("begin");//打印一句开始
LV_ASSERT_OBJ(obj, MY_CLASS);//先确定一下是不是我们现在绘制的图表类型,ASSERT类似于断言
lv_chart_t * chart = (lv_chart_t *)obj;//强转一下
lv_chart_series_t * ser = _lv_ll_ins_head(&chart->series_ll);//创建一个lv_chart_series_t的变量插入到图表的链表头部
LV_ASSERT_MALLOC(ser);//判断分配内存是否成功
if(ser == NULL) return NULL;//如果分频失败就退出
lv_coord_t def = LV_CHART_POINT_NONE;
ser->color = color;//设置颜色
ser->y_points = lv_mem_alloc(sizeof(lv_coord_t) * chart->point_cnt);//分配y轴点的内存(默认是10个)
LV_ASSERT_MALLOC(ser->y_points);//判断分配内存是否成功
if(chart->type == LV_CHART_TYPE_SCATTER) {//如果图表类型是散点图
ser->x_points = lv_mem_alloc(sizeof(lv_coord_t) * chart->point_cnt);
LV_ASSERT_MALLOC(ser->x_points);
}
if(ser->y_points == NULL) {//如果分配失败就从链表中删除,并释放内存
_lv_ll_remove(&chart->series_ll, ser);
lv_mem_free(ser);
return NULL;
}
/*感觉是做一些初始化*/
ser->start_point = 0;
ser->y_ext_buf_assigned = false;
ser->hidden = 0;
ser->x_axis_sec = axis & LV_CHART_AXIS_SECONDARY_X ? 1 : 0;
ser->y_axis_sec = axis & LV_CHART_AXIS_SECONDARY_Y ? 1 : 0;
uint16_t i;
lv_coord_t * p_tmp = ser->y_points;//
for(i = 0; i < chart->point_cnt; i++) {
*p_tmp = def;
p_tmp++;
}
return ser;
}
第一个参数是一个lv_obj_t对象,第二个是设置颜色,第三个是什么呢?
enum {
LV_CHART_AXIS_PRIMARY_Y = 0x00,
LV_CHART_AXIS_SECONDARY_Y = 0x01,
LV_CHART_AXIS_PRIMARY_X = 0x02,
LV_CHART_AXIS_SECONDARY_X = 0x04,
_LV_CHART_AXIS_LAST
};
typedef uint8_t lv_chart_axis_t;
进入后,我们发现它是个枚举类型,可以通过这个图来理解
继续往下看到有两种给折线赋值的方式,我们先看到这个函数
lv_chart_set_next_value(chart, ser1, 10);
猜测是往第一个折线加入一个数值为10的点,看一下函数原型,大概就是这个意思,具体的我也看不懂。。。
void lv_chart_set_next_value(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t value)
{
LV_ASSERT_OBJ(obj, MY_CLASS);//判断一下是不是对应的图表类
LV_ASSERT_NULL(ser);//判断是否为空
lv_chart_t * chart = (lv_chart_t *)obj;
ser->y_points[ser->start_point] = value;//设置点的y轴坐标
invalidate_point(obj, ser->start_point);//绘制点
ser->start_point = (ser->start_point + 1) % chart->point_cnt;//为下一个点的绘制做准备
invalidate_point(obj, ser->start_point);//有点没看懂
}
另外一种就更简单粗暴,ser2->y_points[0] = 90;直接给点赋值了,最后调用
lv_chart_refresh(chart);对图表进行刷新,其实就是对图表进行重绘制,就完成了绘制。
void lv_chart_refresh(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_invalidate(obj);//告诉底层我要重绘这个对象
}