目录
一、前言
awtk-widget-chart-view 是 AWTK 提供的图表自定义控件,该控件包含:曲线图、柱状图和饼图。最近需要让该控件支持 AWTK-MVVM,但受限于原有的接口,实现起来有点麻烦,因此重构了 chart-view 控件序列点数据(先进先出队列 FIFO)的代码。
AWTK是 ZLG 开发的开源 GUI 引擎,官网地址:https://www.zlg.cn/index/pub/awtk.html。
AWTK GitHub 仓库:http://github.com/zlgopen/awtk-mvvm
AWTK-MVVM是一套用C语言开发的,专门为嵌入式平台优化的MVVM框架。它实现了数据绑定、命令绑定和窗口导航等基本功能。
AWTK-MVVM GitHub 仓库:http://github.com/zlgopen/awtk-mvvm
二、序列点数据结构(FIFO)
在 chart-view 控件中,以折线图为例,折线数据储存在一个先进先出队列 FIFO(First in,First out )结构中,绘制控件时从该 FIFO 中读取数据,添加序列点时,将点数据推送到 FIFO 中,若队列已满,则删除最旧的序列点数据。
适配 MVVM 前 chart-view 控件中的 FIFO 结构代码如下:
FIFO 完整的实现代码请前往 GitHub 控件仓库下载: http://github.com/zlgopen/awtk-widget-store
/**
* @class fifo_t
* FIFO,先进先出队列,环形缓存。
*
* 用fifo\_init初始化时,用fifo\_deinit释放。如:
*
* ```c
* fifo_t fifo;
* fifo_init(&fifo, 10, 10, compare);
* ...
* fifo_deinit(&fifo);
* ```
*
* 用fifo\_create创建时,用fifo\_destroy销毁。如:
*
* ```c
* fifo_t* fifo = fifo_create(10, 10, compare);
* ...
* fifo_destroy(fifo);
* ```
*
*/
typedef struct _fifo_t {
/**
* @property {uint32_t} capacity
* @annotation ["readable"]
* FIFO的容量大小。
*/
uint32_t capacity;
/**
* @property {uint32_t} size
* @annotation ["readable"]
* FIFO中元素的个数。
*/
uint32_t size;
/**
* @property {uint32_t} cursor
* @annotation ["readable"]
* FIFO中最后一个元素的索引。
*/
uint32_t cursor;
/**
* @property {uint32_t} unit_size
* @annotation ["readable"]
* FIFO中单个元素的大小。
*/
uint32_t unit_size;
/**
* @property {uint8_t*} buffer
* @annotation ["readable"]
* FIFO中的数据缓存。
*/
uint8_t* buffer;
/**
* @property {tk_destroy_t} destroy
* @annotation ["readable"]
* 元素销毁函数。
*/
tk_destroy_t destroy;
/**
* @property {tk_compare_t} compare
* @annotation ["readable"]
* 元素过滤函数。
*/
tk_compare_t compare;
} fifo_t;
三、重构FIFO
要让 chart-view 支持 AWTK-MVVM,其关键点就是在 MVVM 绑定数据时,能够替换该控件中的FIFO数据结构,而根据目前 FIFO 的结构是无法实现的,因此,这里定义一个继承 object_t 的抽象类 object_fifo_t ,并实现一个默认类 object_fifo_default_t。
object_t 是 AWTK 中的一个对象基类,其实现详见 AWTK 源码。
这样一来,使用 MVVM 时即可在 Model 层通过 object_fifo_default_t 实例化一个 object_fifo_t 对象,并将其替换到控件中达到数据和界面分离的目的。
3.1 object_fifo_t
抽象类 object_fifo_t 的接口如下:
#ifndef TK_OBJECT_FIFO_H
#define TK_OBJECT_FIFO_H
#include "tkc/object.h"
#include "object_fifo_data.h"
#include "object_fifo_event.h"
BEGIN_C_DECLS
struct _object_fifo_t;
typedef struct _object_fifo_t object_fifo_t;
typedef object_t* (*object_fifo_clone_t)(object_t* obj);
typedef object_t* (*object_fifo_part_clone_t)(object_t* obj, uint32_t index, uint32_t nr);
typedef void* (*object_fifo_at_t)(object_t* obj, uint32_t index);
typedef ret_t (*object_fifo_set_t)(object_t* obj, uint32_t index, const void* data, uint32_t nr);
typedef ret_t (*object_fifo_get_t)(object_t* obj, uint32_t index, uint32_t nr, uint8_t* buffer);
typedef int (*object_fifo_index_of_t)(object_t* obj, void* data);
typedef int (*object_fifo_compare_t)(object_t* obj, const void* a, const void* b);
typedef ret_t (*object_fifo_npush_t)(object_t* obj, const void* data, uint32_t nr);
typedef ret_t (*object_fifo_npop_t)(object_t* obj, uint32_t nr);
typedef ret_t (*object_fifo_clear_t)(object_t* obj);
typedef ret_t (*object_fifo_foreach_t)(object_t* obj, tk_visit_t visit, void* ctx);
typedef ret_t (*object_fifo_reset_t)(object_t* obj, uint32_t capacity, fifo_data_type_t type);
typedef struct _object_fifo_vtable_t {
object_fifo_clone_t clone;
object_fifo_part_clone_t part_clone;
object_fifo_at_t at;
object_fifo_set_t set;
object_fifo_get_t get;
object_fifo_index_of_t index_of;
object_fifo_compare_t compare;
object_fifo_npush_t npush;
object_fifo_npop_t npop;
object_fifo_clear_t clear;
object_fifo_foreach_t foreach;
object_fifo_reset_t reset;
} object_fifo_vtable_t;
/**
* @class object_fifo_t
* @parent object_t
*
* FIFO,先进先出队列,环形缓存。
* 可以使用 object_fifo_default_t 实例化该类。
*
*/
struct _object_fifo_t {
object_t obj;
/**
* @property {fifo_data_type_t} type
* @annotation ["readable"]
* FIFO类型(决定FIFO中元素的单位大小)。
*/
fifo_data_type_t type;
/**
* @property {object_fifo_vtable_t} vt
* @annotation ["readable"]
* 虚函数表。
*/
const object_fifo_vtable_t* vt;
/*private*/
object_fifo_event_t e;
};
/**
* @method object_fifo_clone
* clone。
*
* @param {object_t*} obj object_fifo对象。
*
* @return {object_t*} 返回clone的obj对象。
*/
object_t* object_fifo_clone(object_t* obj);
/**
* @method object_fifo_part_clone
* clone部分。
*
* @param {object_t*} obj object_fifo对象。
* @param {uint32_t*} index 被clone元素在FIFO中的位置。
* @param {uint32_t*} nr 被clone元素的数量。
*
* @return {object_t*} 返回clone的对象。
*/
object_t* object_fifo_part_clone(object_t* obj, uint32_t index, uint32_t nr);
/**
* @method object_fifo_at
* 返回特定位置的元素。
*
* @param {object_t*} obj object_fifo对象。
* @param {uint32_t} index 元素在FIFO中的位置。
*
* @return {void*} 如果找到,返回特定位置的元素,否则返回NULL。
*/
void* object_fifo_at(object_t* obj, uint32_t index);
/**
* @method object_fifo_set
* 设置特定位置开始的多个元素。
*
* @param {object_t*} obj object_fifo对象。
* @param {uint32_t} index 元素在FIFO中的位置。
* @param {const void*} data 元素数据。
* @param {uint32_t} nr 元素数量。
*
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
*/
ret_t object_fifo_set(object_t* obj, uint32_t index, const void* data, uint32_t nr);
/**
* @method object_fifo_set_reverse
* 设置特定位置开始的多个元素(反向)。
*
* @param {object_t*} obj object_fifo对象。
* @param {uint32_t} index 元素在FIFO中的位置。
* @param {const void*} data 元素数据。
* @param {uint32_t} nr 元素数量。
*
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
*/
ret_t object_fifo_set_reverse(object_t* obj, uint32_t index, const void* data, uint32_t nr);
/**
* @method object_fifo_get
* 返回特定位置开始的多个元素。
*
* @param {object_t*} obj object_fifo对象。
* @param {uint32_t} index 元素在FIFO中的位置。
* @param {uint32_t} nr 元素数量。
* @param {uint8_t*} buffer 返回元素的缓存。
*
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
*/
ret_t object_fifo_get(object_t* obj, uint32_t index, uint32_t nr, uint8_t* buffer);
/**
* @method object_fifo_prev
* 返回上一个元素。
*
* @param {object_t*} obj object_fifo对象。
* @param {void*} data 当前元素。
*
* @return {void*} 如果找到,返回上一个元素,否则返回NULL。
*/
void* object_fifo_prev(object_t* obj, void* data);
/**
* @method object_fifo_next
* 返回下一个元素。
*
* @param {object_t*} obj object_fifo对象。
* @param {void*} data 当前元素。
*
* @return {void*} 如果找到,返回下一个元素,否则返回NULL。
*/
void* object_fifo_next(object_t* obj, void* data);
/**
* @method object_fifo_tail
* 返回最后一个元素。
*
* @param {object_t*} obj object_fifo对象。
*
* @return {void*} 成功返回最后一个元素,失败返回NULL。
*/
void* object_fifo_tail(object_t* obj);
/**
* @method object_fifo_head
* 返回第一个元素。
*
* @param {object_t*} obj object_fifo对象。
*
* @return {void*} 成功返回最后一个元素,失败返回NULL。
*/
void* object_fifo_head(object_t* obj);
/**
* @method object_fifo_index_of
* 返回元素的位置。
*
* @param {object_t*} obj object_fifo对象。
* @param {void*} data 元素。
*
* @return {int} 如果找到,返回元素的位置,否则返回-1。
*/
int object_fifo_index_of(object_t* obj, void* data);
/**
* @method object_fifo_compare
* 比较两个元素。
*
* @param {object_t*} obj object_fifo对象。
* @param {const void*} a 元素a。
* @param {const void*} b 元素b。
*
* @return {int} 两元素相等返回0。
*/
int object_fifo_compare(object_t* obj, const void* a, const void* b);
/**
* @method object_fifo_count
* 返回满足条件元素的个数。
*
* @param {object_t*} obj object_fifo对象。
* @param {void*} ctx 比较函数的上下文。
*
* @return {int32_t} 返回元素个数。
*/
int32_t object_fifo_count(object_t* obj, void* ctx);
/**
* @method object_fifo_find
* 查找第一个满足条件的元素。
*
* @param {object_t*} obj object_fifo对象。
* @param {void*} ctx 比较函数的上下文。
*
* @return {void*} 如果找到,返回满足条件的对象,否则返回NULL。
*/
void* object_fifo_find(object_t* obj, void* ctx);
/**
* @method object_fifo_find_index
* 查找第一个满足条件的元素,并返回位置。
*
* @param {object_t*} obj object_fifo对象。
* @param {void*} ctx 比较函数的上下文。
*
* @return {int} 如果找到,返回满足条件的对象的位置,否则返回-1。
*/
int object_fifo_find_index(object_t* obj, void* ctx);
/**
* @method object_fifo_push
* 在尾巴追加一个元素。
*
* @param {object_t*} obj object_fifo对象。
* @param {const void*} data 待追加的元素。
*
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
*/
ret_t object_fifo_push(object_t* obj, const void* data);
/**
* @method object_fifo_pop
* 弹出第一个元素。
*
* @param {object_t*} obj object_fifo对象。
*
* @return {void*} 成功返回第一个元素,失败返回NULL。
*/
void* object_fifo_pop(object_t* obj);
/**
* @method object_fifo_npush
* 在尾巴追加多个元素。
*
* @param {object_t*} obj object_fifo对象。
* @param {const void*} data 待追加的元素。
* @param {uint32_t} nr 待追加的元素个数。
*
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
*/
ret_t object_fifo_npush(object_t* obj, const void* data, uint32_t nr);
/**
* @method object_fifo_npop
* 弹出开头多个元素。
*
* @param {object_t*} obj object_fifo对象。
* @param {uint32_t} nr 待弹出的元素个数。
*
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
*/
ret_t object_fifo_npop(object_t* obj, uint32_t nr);
/**
* @method object_fifo_clear
* 清除全部元素。
*
* @param {object_t*} obj object_fifo对象。
*
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
*/
ret_t object_fifo_clear(object_t* obj);
/**
* @method object_fifo_foreach
* 遍历元素。
*
* @param {object_t*} obj object_fifo对象。
* @param {tk_visit_t} visit 遍历函数。
* @param {void*} ctx 遍历函数的上下文。
*
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
*/
ret_t object_fifo_foreach(object_t* obj, tk_visit_t visit, void* ctx);
/**
* @method object_fifo_reset
* 重置fifo。
*
* @param {object_t*} obj object_fifo对象。
* @param {uint32_t} capacity 容量。
* @param {fifo_data_type_t} type FIFO类型。
*
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
*/
ret_t object_fifo_reset(object_t* obj, uint32_t capacity, fifo_data_type_t type);
/*helper functions*/
ret_t object_fifo_set_raw(object_t* obj, uint32_t index, const void* data, uint32_t nr);
#define OBJECT_FIFO(obj) ((object_fifo_t*)(obj))
#define OBJECT_FIFO_PROP_TYPE "type"
#define OBJECT_FIFO_PROP_CAPACITY "capacity"
#define OBJECT_FIFO_PROP_SIZE "size"
#define OBJECT_FIFO_PROP_CURSOR "cursor"
#define OBJECT_FIFO_PROP_UNIT_SIZE "unit_size"
#define OBJECT_FIFO_PROP_BUFFER "buffer"
#define OBJECT_FIFO_GET_TYPE(obj) object_get_prop_uint32(obj, OBJECT_FIFO_PROP_TYPE, 0)
#define OBJECT_FIFO_GET_CAPACITY(obj) object_get_prop_uint32(obj, OBJECT_FIFO_PROP_CAPACITY, 0)
#define OBJECT_FIFO_GET_SIZE(obj) object_get_prop_uint32(obj, OBJECT_FIFO_PROP_SIZE, 0)
#define OBJECT_FIFO_GET_CURSOR(obj) object_get_prop_uint32(obj, OBJECT_FIFO_PROP_CURSOR, 0)
#define OBJECT_FIFO_GET_UNIT_SIZE(obj) object_get_prop_uint32(obj, OBJECT_FIFO_PROP_UNIT_SIZE, 0)
#define OBJECT_FIFO_GET_BUFFER(obj) (uint8_t*)object_get_prop_pointer(obj, OBJECT_FIFO_PROP_BUFFER)
END_C_DECLS
#endif /*TK_OBJECT_FIFO_H*/
3.2 object_fifo_default_t
子类 object_fifo_default_t 的接口如下:
#ifndef TK_OBJECT_FIFO_DEFAULT_H
#define TK_OBJECT_FIFO_DEFAULT_H
#include "object_fifo.h"
BEGIN_C_DECLS
struct _object_fifo_default_t;
typedef struct _object_fifo_default_t object_fifo_default_t;
/**
* @class object_fifo_default_t
* @parent object_fifo_t
*
* object_fifo的缺省实现。
*
*/
struct _object_fifo_default_t {
object_fifo_t obj_fifo;
/**
* @property {uint32_t} capacity
* @annotation ["readable"]
* FIFO的容量大小。
*/
uint32_t capacity;
/**
* @property {uint32_t} size
* @annotation ["readable"]
* FIFO中元素的个数。
*/
uint32_t size;
/**
* @property {uint32_t} cursor
* @annotation ["readable"]
* FIFO中最后一个元素的索引。
*/
uint32_t cursor;
/**
* @property {uint32_t} unit_size
* @annotation ["readable"]
* FIFO中单个元素的大小。
*/
uint32_t unit_size;
/**
* @property {uint8_t*} buffer
* @annotation ["readable"]
* FIFO中的数据缓存。
*/
uint8_t* buffer;
};
/**
* @method object_fifo_default_create
* 创建object_fifo对象。
*
* @annotation ["constructor"]
* @param {uint32_t} capacity FIFO的初始容量。
* @param {fifo_data_type_t} type FIFO数据类型。
*
* @return {object_t} 返回object_fifo对象。
*/
object_t* object_fifo_default_create(uint32_t capacity, fifo_data_type_t type);
#define OBJECT_FIFO_DEFAULT(obj) ((object_fifo_default_t*)(obj))
END_C_DECLS
#endif /*TK_OBJECT_FIFO_DEFAULT_H*/
子类 object_fifo_default_t 的实现代码如下:
#include "tkc/mem.h"
#include "object_fifo_default.h"
#define FIFO_IS_VALID(fifo, index) ((index) >= 0 && (index) < fifo->size)
#define FIFO_TO_ABS_INDEX(fifo, index) \
((fifo->cursor - fifo->size + 1 + fifo->capacity + (index)) % fifo->capacity)
#define FIFO_ABS_ELM(fifo, index) ((void*)(fifo->buffer + fifo->unit_size * (index)))
#define FIFO_TO_INDEX(fifo, index) (((index)-fifo->cursor + fifo->size - 1) % fifo->capacity)
static int object_fifo_default_compare(object_t* obj, const void* a, const void* b) {
object_fifo_default_t* fifo = OBJECT_FIFO_DEFAULT(obj);
return_value_if_fail(fifo != NULL, RET_BAD_PARAMS);
return memcmp(a, b, fifo->unit_size);
}
static ret_t object_fifo_default_set_internal(object_t* obj, uint32_t index, const void* data,
uint32_t nr) {
object_fifo_default_t* fifo = OBJECT_FIFO_DEFAULT(obj);
uint8_t* start = (uint8_t*)(data);
uint8_t* elm = (uint8_t*)FIFO_ABS_ELM(fifo, index);
if (nr > fifo->capacity) {
if (start != NULL) {
start += (nr - fifo->capacity) * fifo->unit_size;