007-让你的 LVGL 折线图支持浮点数显示

今天在做显示屏的时候发现,LVGL V8.2 的折线图 y 轴居然不能支持显示浮点数,于是研究了一下,发现还是有一些奇技淫巧来实现的。

原始代码

1
2
3
4
5
6
7
8
9
10
11
12
13
int main(int tiks) {
float real = json_object_get_double(val);
lv_chart_set_next_value(ui.chart_data, ui.chart_data_dot, real);

lv_chart_set_axis_tick(ui.chart_data, LV_CHART_AXIS_PRIMARY_Y, 10, 5, tiks, 2,
true, 100);
lv_chart_set_range(ui.chart_data, LV_CHART_AXIS_PRIMARY_Y, min_data,
max_data);
lv_obj_add_event_cb(ui.chart_data, chart_event_cb, LV_EVENT_ALL, NULL);
lv_obj_refresh_ext_draw_size(ui.chart_data);
lv_chart_set_update_mode(ui.chart_data, LV_CHART_UPDATE_MODE_CIRCULAR);
lv_chart_set_zoom_x(ui.chart_data, 700);
}

查看图标设置函数定义可以发现,只接受整数

1
2
3
4
5
6
7
8
9
10
11
12
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;
invalidate_point(obj, ser->start_point);
ser->start_point = (ser->start_point + 1) % chart->point_cnt;
invalidate_point(obj, ser->start_point);
lv_chart_refresh(obj);
}
1
2
3
4
5
#if LV_USE_LARGE_COORD
typedef int32_t lv_coord_t;
#else
typedef int16_t lv_coord_t;
#endifc

在网上搜了一下,LVGL 似乎不支持图标直接存放浮点数,但是我们可以通过乘以 100 来存入,读出来的时候再除以 100。

实现浮点数显示

首先,将 lv_conf.h 中的浮点数支持开启

1
#define LV_SPRINTF_USE_FLOAT 1

这样 lv_snprintf 函数才能支持%f,存入图表的时候乘以 100 来存入。

1
2
3
4
5
6
7
8
9
10
11
12
13
int main(int tiks) {
float real = json_object_get_double(val) * 100;
lv_chart_set_next_value(ui.chart_data, ui.chart_data_dot, real);

lv_chart_set_axis_tick(ui.chart_data, LV_CHART_AXIS_PRIMARY_Y, 10, 5, tiks, 2,
true, 100);
lv_chart_set_range(ui.chart_data, LV_CHART_AXIS_PRIMARY_Y, min_data,
max_data);
lv_obj_add_event_cb(ui.chart_data, chart_event_cb, LV_EVENT_ALL, NULL);
lv_obj_refresh_ext_draw_size(ui.chart_data);
lv_chart_set_update_mode(ui.chart_data, LV_CHART_UPDATE_MODE_CIRCULAR);
lv_chart_set_zoom_x(ui.chart_data, 700);
}

lv_chart_set_axis_tick 中,label_en 表示是否显示坐标轴,我们需要将坐标轴以浮点数形式显示。

修改 lvgl/src/extra/widgets/chart/lv_chart.c 的 draw_y_ticks 函数,你的路径可能跟我不一样,但最终要改的函数都是 draw_y_ticks 这个函数。

这个函数用于绘制 y 轴坐标,我们需要将存入的乘以 100 的坐标再改为正常值。

1
2
3
4

lv_snprintf(buf, sizeof(buf), "%" LV_PRId32, tick_value);
改为
lv_snprintf(buf, sizeof(buf), "%.2f", (float)tick_value / 100.0f);

现在显示的页面就是正常的了,但是我还有一个点击事件的需要修改,这是为了让用户在点击或者拖动折线图的时候,点位的上方可以出现一个标签显示该点位的值。先看一下我之前的写法:

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
/**
* @description: 折线图点击、拖动事件
* @param {lv_event_t} *e
* @return {*}
*/
static void chart_event_cb(lv_event_t *e) {
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t *chart = lv_event_get_target(e);

if (code == LV_EVENT_VALUE_CHANGED) {
lv_obj_invalidate(chart);
}
if (code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
lv_coord_t *s = lv_event_get_param(e);
*s = LV_MAX(*s, 20);
} else if (code == LV_EVENT_DRAW_POST_END) {
int32_t id = lv_chart_get_pressed_point(chart);
if (id == LV_CHART_POINT_NONE) return;

LV_LOG_USER("Selected point %d", (int)id);

lv_chart_series_t *ser = lv_chart_get_series_next(chart, NULL);
while (ser) {
lv_point_t p;
lv_chart_get_point_pos_by_id(chart, ser, id, &p);

lv_coord_t *y_array = lv_chart_get_y_array(chart, ser);
lv_coord_t value = y_array[id];
if (value == LV_CHART_POINT_NONE) return;

char buf[32];
lv_snprintf(buf, sizeof(buf), LV_SYMBOL_DUMMY "%d", value);

lv_draw_rect_dsc_t draw_rect_dsc;
lv_draw_rect_dsc_init(&draw_rect_dsc);
draw_rect_dsc.bg_color = lv_color_black();
draw_rect_dsc.bg_opa = LV_OPA_50;
draw_rect_dsc.radius = 3;
draw_rect_dsc.bg_img_src = buf;
draw_rect_dsc.bg_img_recolor = lv_color_white();

lv_area_t a;
a.x1 = chart->coords.x1 + p.x - 20;
a.x2 = chart->coords.x1 + p.x + 20;
a.y1 = chart->coords.y1 + p.y - 30;
a.y2 = chart->coords.y1 + p.y - 10;

lv_draw_ctx_t *draw_ctx = lv_event_get_draw_ctx(e);
lv_draw_rect(draw_ctx, &draw_rect_dsc, &a);

ser = lv_chart_get_series_next(chart, ser);
}
} else if (code == LV_EVENT_RELEASED) {
lv_obj_invalidate(chart);
}
}

现在也是一样的,要先除以 100 获取到原本的值再显示:

1
2
3
4

lv_snprintf(buf, sizeof(buf), LV_SYMBOL_DUMMY "%d", value);
改为
lv_snprintf(buf, sizeof(buf), LV_SYMBOL_DUMMY "%.2f", (float)value / 100.0f);

说在最后

查资料的时候发现 LVGL 9 要推出了,希望新版本能提供原生支持吧。

参考链接

https://forum.lvgl.io/t/chart-display-floats-temperature-values-with-decimal-places/9097/2


007-让你的 LVGL 折线图支持浮点数显示
https://kydins.com/posts/f21ee470.html
作者
Kydin
发布于
2024年1月13日
许可协议