今天在做显示屏的时候发现,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
|
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