目录
一、前言
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;
}
nr = fifo->capacity;
}
if (index + nr <= fifo->capacity) {
if (start != NULL) {
memcpy(elm, start, fifo->unit_size * nr);
} else {
memset(elm, 0x00, fifo->unit_size * nr);
}
} else {
uint32_t part = fifo->capacity - index;
if (start != NULL) {
memcpy(elm, start, fifo->unit_size * part);
memcpy(fifo->buffer, start + fifo->unit_size * part, fifo->unit_size * (nr - part));
} else {
memset(elm, 0x00, fifo->unit_size * part);
memset(fifo->buffer, 0x00, fifo->unit_size * (nr - part));
}
}
return RET_OK;
}
static ret_t object_fifo_default_get_internal(object_t* obj, uint32_t index, uint32_t nr,
uint8_t* buffer) {
object_fifo_default_t* fifo = OBJECT_FIFO_DEFAULT(obj);
uint8_t* start = (uint8_t*)FIFO_ABS_ELM(fifo, index);
if (index + nr <= fifo->capacity) {
memcpy(buffer, start, fifo->unit_size * nr);
} else {
uint32_t part = fifo->capacity - index;
memcpy(buffer, start, fifo->unit_size * part);
memcpy(buffer + fifo->unit_size * part, fifo->buffer, fifo->unit_size * (nr - part));
}
return RET_OK;
}
static ret_t object_fifo_default_get(object_t* obj, uint32_t index, uint32_t nr, uint8_t* buffer) {
object_fifo_default_t* fifo = OBJECT_FIFO_DEFAULT(obj);
return_value_if_fail(fifo != NULL && fifo->buffer != NULL && nr > 0 && buffer != NULL,
RET_BAD_PARAMS);
return_value_if_fail(FIFO_IS_VALID(fifo, index), RET_BAD_PARAMS);
return_value_if_fail(FIFO_IS_VALID(fifo, index + nr - 1), RET_BAD_PARAMS);
return object_fifo_default_get_internal(obj, FIFO_TO_ABS_INDEX(fifo, index), nr, buffer);
}
static object_t* object_fifo_default_clone(object_t* obj) {
object_t* clone = NULL;
object_fifo_default_t* fifo = OBJECT_FIFO_DEFAULT(obj);
return_value_if_fail(fifo != NULL, NULL);
clone = object_fifo_default_create(fifo->capacity, OBJECT_FIFO(fifo)->type);
object_fifo_default_t* fifo_clone = OBJECT_FIFO_DEFAULT(clone);
if (fifo_clone) {
if (fifo->size > 0) {
if (fifo->size >= fifo->capacity) {
memcpy(fifo_clone->buffer, fifo->buffer, fifo->unit_size * fifo->capacity);
} else {
uint32_t head_index = FIFO_TO_ABS_INDEX(fifo, 0);
uint8_t* head = FIFO_ABS_ELM(fifo, head_index);
if (head_index + fifo->size <= fifo->capacity) {
memcpy(fifo_clone->buffer, head, fifo->unit_size * fifo->size);
} else {
uint32_t part = fifo->capacity - head_index;
memcpy(FIFO_ABS_ELM(fifo_clone, head_index), head, fifo->unit_size * part);
memcpy(fifo_clone->buffer, fifo->buffer, fifo->unit_size * (fifo->size - part));
}
}
fifo_clone->size = fifo->size;
fifo_clone->cursor = fifo->cursor;
}
}
return clone;
}
static object_t* object_fifo_default_part_clone(object_t* obj, uint32_t index, uint32_t nr) {
object_t* clone = NULL;
object_fifo_default_t* fifo = OBJECT_FIFO_DEFAULT(obj);
return_value_if_fail(fifo != NULL && fifo->buffer != NULL && nr > 0, NULL);
return_value_if_fail(FIFO_IS_VALID(fifo, index), NULL);
return_value_if_fail(FIFO_IS_VALID(fifo, index + nr - 1), NULL);
clone = object_fifo_default_create(fifo->capacity, OBJECT_FIFO(fifo)->type);
object_fifo_default_t* fifo_clone = OBJECT_FIFO_DEFAULT(clone);
if (fifo_clone) {
object_fifo_default_get(obj, index, nr, fifo_clone->buffer);
fifo_clone->size = nr;
fifo_clone->cursor = fifo_clone->cursor + nr - 1;
}
return clone;
}
static void* object_fifo_default_at(object_t* obj, uint32_t index) {
object_fifo_default_t* fifo = OBJECT_FIFO_DEFAULT(obj);
return_value_if_fail(fifo != NULL && fifo->buffer != NULL && fifo->size > 0, NULL);
return_value_if_fail(FIFO_IS_VALID(fifo, index), NULL);
return FIFO_ABS_ELM(fifo, FIFO_TO_ABS_INDEX(fifo, index));
}
static ret_t object_fifo_default_set(object_t* obj, uint32_t index, const void* data, uint32_t nr) {
object_fifo_default_t* fifo = OBJECT_FIFO_DEFAULT(obj);
return_value_if_fail(fifo != NULL && fifo->buffer != NULL && nr > 0, RET_BAD_PARAMS);
return_value_if_fail(FIFO_IS_VALID(fifo, index), RET_BAD_PARAMS);
return_value_if_fail(FIFO_IS_VALID(fifo, index + nr - 1), RET_BAD_PARAMS);
return object_fifo_default_set_internal(obj, FIFO_TO_ABS_INDEX(fifo, index), data, nr);
}
static int object_fifo_default_index_of(object_t* obj, void* data) {
bool_t valid;
object_fifo_default_t* fifo = OBJECT_FIFO_DEFAULT(obj);
return_value_if_fail(fifo != NULL && fifo->buffer != NULL && fifo->size > 0, -1);
valid = ((uint8_t*)data - fifo->buffer) % fifo->unit_size == 0;
return_value_if_fail(valid, -1);
return FIFO_TO_INDEX(fifo, ((uint8_t*)data - fifo->buffer) / fifo->unit_size);
}
static ret_t object_fifo_default_npush(object_t* obj, const void* data, uint32_t nr) {
uint32_t head_index;
object_fifo_default_t* fifo = OBJECT_FIFO_DEFAULT(obj);
return_value_if_fail(fifo != NULL && fifo->buffer != NULL && nr > 0, RET_BAD_PARAMS);
if (fifo->size != 0) {
fifo->cursor = (fifo->cursor + nr) % fifo->capacity;
} else {
fifo->cursor = (fifo->cursor + nr - 1) % fifo->capacity;
}
fifo->size = tk_min(fifo->size + nr, fifo->capacity);
head_index = (fifo->cursor - tk_min(nr, fifo->capacity) + 1 + fifo->capacity) % fifo->capacity;
return object_fifo_default_set_internal(obj, head_index, data, nr);
}
static ret_t object_fifo_default_npop(object_t* obj, uint32_t nr) {
object_fifo_default_t* fifo = OBJECT_FIFO_DEFAULT(obj);
return_value_if_fail(fifo != NULL && fifo->buffer != NULL && nr > 0 && nr <= fifo->size,
RET_BAD_PARAMS);
fifo->size -= nr;
return RET_OK;
}
static ret_t object_fifo_default_clear(object_t* obj) {
object_fifo_default_t* fifo = OBJECT_FIFO_DEFAULT(obj);
return_value_if_fail(fifo != NULL && fifo->buffer != NULL, RET_BAD_PARAMS);
fifo->size = 0;
fifo->cursor = 0;
return RET_OK;
}
static ret_t object_fifo_default_foreach(object_t* obj, tk_visit_t visit, void* ctx) {
uint32_t i = 0;
object_fifo_default_t* fifo = OBJECT_FIFO_DEFAULT(obj);
return_value_if_fail(fifo != NULL && fifo->buffer != NULL && visit != NULL, RET_BAD_PARAMS);
for (i = 0; i < fifo->size; i++) {
void* iter = object_fifo_default_at(obj, i);
ret_t ret = visit(ctx, iter);
if (ret != RET_OK) {
return ret;
}
}
return RET_OK;
}
static ret_t object_fifo_default_reset(object_t* obj, uint32_t capacity, fifo_data_type_t type) {
object_fifo_default_t* fifo = OBJECT_FIFO_DEFAULT(obj);
uint32_t unit_size = object_fifo_data_get_unit_size(type);
return_value_if_fail(fifo != NULL && fifo->buffer != NULL && unit_size > 0, RET_BAD_PARAMS);
TKMEM_FREE(fifo->buffer);
fifo->buffer = TKMEM_ZALLOCN(uint8_t, (unit_size * capacity));
return_value_if_fail(fifo->buffer != NULL, RET_OOM);
fifo->size = 0;
fifo->cursor = 0;
fifo->capacity = capacity;
fifo->unit_size = unit_size;
return RET_OK;
}
static ret_t object_fifo_default_on_destroy(object_t* obj) {
object_fifo_t* obj_fifo = OBJECT_FIFO(obj);
object_fifo_default_t* fifo = OBJECT_FIFO_DEFAULT(obj);
return_value_if_fail(obj_fifo != NULL && fifo != NULL, RET_BAD_PARAMS);
if (obj_fifo->e.old_fifo != NULL) {
OBJECT_UNREF(obj_fifo->e.old_fifo)
}
object_fifo_default_clear(obj);
fifo->capacity = 0;
fifo->size = 0;
fifo->cursor = 0;
fifo->unit_size = 0;
TKMEM_FREE(fifo->buffer);
return RET_OK;
}
static ret_t object_fifo_default_get_prop(object_t* obj, const char* name, value_t* v) {
object_fifo_default_t* fifo = OBJECT_FIFO_DEFAULT(obj);
return_value_if_fail(fifo != NULL, RET_BAD_PARAMS);
if (tk_str_eq(name, OBJECT_FIFO_PROP_TYPE)) {
value_set_uint32(v, OBJECT_FIFO(fifo)->type);
return RET_OK;
} else if (tk_str_eq(name, OBJECT_FIFO_PROP_CAPACITY)) {
value_set_uint32(v, fifo->capacity);
return RET_OK;
} else if (tk_str_eq(name, OBJECT_FIFO_PROP_SIZE)) {
value_set_uint32(v, fifo->size);
return RET_OK;
} else if (tk_str_eq(name, OBJECT_FIFO_PROP_CURSOR)) {
value_set_uint32(v, fifo->cursor);
return RET_OK;
} else if (tk_str_eq(name, OBJECT_FIFO_PROP_UNIT_SIZE)) {
value_set_uint32(v, fifo->unit_size);
return RET_OK;
} else if (tk_str_eq(name, OBJECT_FIFO_PROP_BUFFER)) {
value_set_pointer(v, (void*)fifo->buffer);
return RET_OK;
}
return RET_NOT_FOUND;
}
static ret_t object_fifo_default_set_prop(object_t* obj, const char* name, const value_t* v) {
return RET_NOT_FOUND;
}
static const object_vtable_t s_object_vtable = {.type = "object_fifo_default",
.desc = "object_fifo_default",
.size = sizeof(object_fifo_default_t),
.is_collection = FALSE,
.on_destroy = object_fifo_default_on_destroy,
.get_prop = object_fifo_default_get_prop,
.set_prop = object_fifo_default_set_prop};
static const object_fifo_vtable_t s_object_fifo_vtable = {
.clone = object_fifo_default_clone,
.part_clone = object_fifo_default_part_clone,
.at = object_fifo_default_at,
.set = object_fifo_default_set,
.get = object_fifo_default_get,
.index_of = object_fifo_default_index_of,
.compare = object_fifo_default_compare,
.npush = object_fifo_default_npush,
.npop = object_fifo_default_npop,
.clear = object_fifo_default_clear,
.foreach = object_fifo_default_foreach,
.reset = object_fifo_default_reset};
object_t* object_fifo_default_create(uint32_t capacity, fifo_data_type_t type) {
object_t* obj = NULL;
object_fifo_t* obj_fifo = NULL;
object_fifo_default_t* fifo = NULL;
uint32_t unit_size = object_fifo_data_get_unit_size(type);
obj = object_create(&s_object_vtable);
return_value_if_fail(unit_size > 0 && obj != NULL, NULL);
obj_fifo = OBJECT_FIFO(obj);
fifo = OBJECT_FIFO_DEFAULT(obj);
return_value_if_fail(obj != NULL && fifo != NULL, NULL);
obj_fifo->type = type;
obj_fifo->vt = &s_object_fifo_vtable;
fifo->buffer = TKMEM_ZALLOCN(uint8_t, (unit_size * capacity));
return_value_if_fail(fifo->buffer != NULL, NULL);
fifo->capacity = capacity;
fifo->unit_size = unit_size;
fifo->cursor = 0;
fifo->size = 0;
return obj;
}
四、添加替换FIFO的接口
完成 FIFO 数据结构的重构后,需要给控件增加一个替换 FIFO 的接口。该接口在 MVVM 中的 View_Model 绑定控件属性(set_prop)时使用。
接口声明如下:
//series.h
/**
* @method series_set_object_fifo
* 设置序列fifo。
* @annotation ["scriptable"]
* @param {widget_t*} widget widget对象。
* @param {object_t*} obj fifo的object对象。
*
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
*/
ret_t series_set_object_fifo(widget_t* widget, object_t* obj);
实现如下:
//series.c
ret_t series_set_object_fifo(widget_t* widget, object_t* obj) {
object_t* obj_fifo = obj;
series_t* series = SERIES(widget);
if (series->prepare_object_fifo != NULL) {
obj_fifo = series->prepare_object_fifo(series->prepare_object_fifo_ctx, obj);
}
return series_p_set_object_fifo(widget, obj_fifo);
}
//series_p.c
ret_t series_p_set_object_fifo(widget_t* widget, object_t* obj_fifo) {
series_t* series = SERIES(widget);
return_value_if_fail(series != NULL && obj_fifo != NULL, RET_BAD_PARAMS);
if (series->obj_fifo != NULL && series->obj_fifo != obj_fifo) {
OBJECT_UNREF(series->obj_fifo);
}
if (series->obj_fifo == NULL) {
series->obj_fifo = obj_fifo;
series_p_object_fifo_emitter_on(widget);
}
return RET_OK;
}
设置控件属性的回调函数:
//series_p.c
ret_t series_p_set_prop(widget_t* widget, const char* name, const value_t* v) {
series_t* series = SERIES(widget);
return_value_if_fail(series != NULL && name != NULL && v != NULL, RET_BAD_PARAMS);
if (tk_str_eq(name, SERIES_PROP_OFFSET)) {
return series_p_set_offset(widget, value_uint32(v));
} else if (tk_str_eq(name, SERIES_PROP_CLIP_RANGE)) {
series->clip_range = value_float(v);
return RET_OK;
} else if (tk_str_eq(name, SERIES_PROP_CAPACITY)) {
return series_set_capacity(widget, value_uint32(v));
} else if (tk_str_eq(name, SERIES_PROP_FIFO_TYPE)) {
return series_set_fifo_type(widget, value_uint32(v));
} else if (tk_str_eq(name, SERIES_PROP_COVERAGE)) {
series->coverage = value_uint32(v);
return RET_OK;
} else if (tk_str_eq(name, SERIES_PROP_DISPLAY_MODE)) {
if (v->type == VALUE_TYPE_STRING) {
series->display_mode = series_dispaly_mode_from_str(value_str(v));
} else {
series->display_mode = (series_dispaly_mode_t)value_int(v);
}
return RET_OK;
} else if (tk_str_eq(name, SERIES_PROP_VALUE_ANIMATION)) {
series->value_animation = value_uint32(v);
return RET_OK;
} else if (tk_str_eq(name, SERIES_PROP_NEW_PERIOD)) {
return series_p_set_new_period_internal(widget, value_uint32(v), FALSE);
} else if (tk_str_eq(name, SERIES_PROP_TITLE)) {
return series_set_title(widget, value_str(v));
} else if (tk_str_eq(name, SERIES_PROP_FIFO)) {
return series_set_object_fifo(widget, (object_t*)value_object(v));
}
return RET_NOT_FOUND;
}
五、实现C版本的MVVM示例
5.1 数据绑定
首先,在 UI文件(xml)中进行数据绑定,比如将 FIFO 容量与变量 capacity 绑定,将 object_fifo 对象与变量 fifo1 绑定,代码如下:
<line_series v-data:capacity="capacity" v-data:fifo="{fifo1}"/>
5.2 Model 层
Model 层声明如下:
#ifndef SERIES_MODEL_H
#define SERIES_MODEL_H
#include "tkc/types_def.h"
#include "base/object_fifo_default.h"
BEGIN_C_DECLS
/**
* @enum series_type_t
* 图表类型。
*/
typedef enum _series_type_t {
/**
* @const SERIES_TYPE_LINE_NORMAL
* 折线图。
*/
SERIES_TYPE_LINE_NORMAL = 0,
/**
* @const SERIES_TYPE_LINE_COLORFUL
* 彩色折线图。
*/
SERIES_TYPE_LINE_COLORFUL,
/**
* @const SERIES_TYPE_LINE_CATEGORY
* 类型折线图。
*/
SERIES_TYPE_LINE_CATEGORY,
/**
* @const SERIES_TYPE_BAR_NORMAL
* 柱状图。
*/
SERIES_TYPE_BAR_NORMAL,
/**
* @const SERIES_TYPE_BAR
* 最大最小值柱状图。
*/
SERIES_TYPE_BAR_MINMAX,
} series_type_t;
/**
* @class series_model_t
*
* @annotation ["model"]
* 图表Model。
*
*/
typedef struct _series_model_t {
/**
* @property {object_t*} fifo
* @annotation ["readable"]
* 序列点fifo数据。
*/
object_t* fifo[3];
/**
* @property {uint32_t} data2
* @annotation ["readable", "writable"]
* 序列点fifo的容量。
*/
uint32_t capacity;
/**
* @property {uint64_t} recent_time
* @annotation ["readable", "writable"]
* 最近时间。
*/
uint64_t recent_time;
/**
* @property {uint32_t} div_time
* @annotation ["readable", "writable"]
* 间隔时间(毫秒)。
*/
uint32_t div_time;
/**
* @property {uint32_t} div_count
* @annotation ["readable", "writable"]
* 间隔时间内的序列点数。
*/
uint32_t div_count;
/* private */
series_type_t type;
} series_model_t;
/**
* @method series_model_create
* 创建series_model对象。
*
* @annotation ["constructor"]
* @param {uint32_t} capacity 序列点fifo的容量。
* @param {series_type_t} type 图表类型。
*
* @return {series_model_t*} 返回series_model对象。
*/
series_model_t* series_model_create(uint32_t capacity, series_type_t type);
/**
* @method series_model_destroy
* 销毁series_mode对象。
*
* @annotation ["destructor"]
* @param {series_model_t*} series_model series_model对象。
*
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
*/
ret_t series_model_destroy(series_model_t* series_model);
/**
* @method series_model_new_graph
* 生成新图表。
*
* @annotation ["command"]
* @param {series_model_t*} series_model series_model对象。
*
* @return {ret_t} 返回RET_OBJECT_CHANGED表示模型有变化,View需要刷新;返回其它表示失败。
*/
ret_t series_model_new_graph(series_model_t* series_model);
END_C_DECLS
#endif /*SERIES_MODEL_H*/
实现代码如下:
#include "tkc/mem.h"
#include "tkc/date_time.h"
#include "series_model.h"
#include "series_data.h"
#define TIME_PER_DIV 2000 //每个DIV的持续时间
#define SAMPLE_COUNT_PER_DIV 10 //每个DIV的采样点数
series_model_t* series_model_create(uint32_t capacity, series_type_t type) {
series_model_t* series_model = TKMEM_ZALLOC(series_model_t);
return_value_if_fail(series_model != NULL, NULL);
int32_t i = 0;
int32_t nr = 0;
fifo_data_type_t fifo_type;
date_time_t dt;
date_time_init(&dt);
series_model->capacity = capacity;
series_model->type = type;
series_model->recent_time = date_time_to_time(&dt);
series_model->div_time = TIME_PER_DIV;
series_model->div_count = SAMPLE_COUNT_PER_DIV;
nr = type < SERIES_TYPE_BAR_NORMAL ? 2 : 3;
fifo_type = FIFO_DATA_TYPE_NORMAL;
if (type == SERIES_TYPE_LINE_COLORFUL) {
nr = 1;
fifo_type = FIFO_DATA_TYPE_COLORFUL;
} else if (type == SERIES_TYPE_BAR_MINMAX) {
nr = 1;
fifo_type = FIFO_DATA_TYPE_MINMAX;
}
return_value_if_fail(capacity > 0, NULL);
/* fifo用于替换series控件中的obj_fifo,由控件管理,无需另外释放。 */
for (i = 0; i < nr; i++) {
series_model->fifo[i] = object_fifo_default_create(series_model->capacity, fifo_type);
}
return series_model;
}
ret_t series_model_destroy(series_model_t* series_model) {
return_value_if_fail(series_model != NULL, RET_BAD_PARAMS);
TKMEM_FREE(series_model);
return RET_OK;
}
ret_t series_model_new_graph(series_model_t* series_model) {
return_value_if_fail(series_model != NULL, RET_BAD_PARAMS);
series_data_rset_data(series_model, series_model->capacity);
return RET_OBJECT_CHANGED;
}
5.3 ViewModel层
ViewModel层声明如下:
#include "mvvm/base/view_model.h"
#include "../common/series_model.h"
#ifndef SERIES_VIEW_MODEL_H
#define SERIES_VIEW_MODEL_H
BEGIN_C_DECLS
/**
* @class series_view_model_t
*
* view model of series
*
*/
typedef struct _series_view_model_t {
view_model_t view_model;
/*model object*/
series_model_t* aseries_model;
bool_t line_show; /* 是否显示曲线轮廓 */
bool_t area_show; /* 是否显示区域。 */
bool_t symbol_show; /* 是否点标记 */
bool_t line_smooth; /* 是否平滑曲线 */
bool_t s1_show; /* 是否显示s1数据 */
bool_t s2_show; /* 是否显示s2数据 */
bool_t s3_show; /* 是否显示s3数据 */
uint32_t timer_id;
} series_view_model_t;
/**
* @method series_view_model_create
* 创建series view model对象。
*
* @annotation ["constructor"]
* @param {navigator_request_t*} req 请求参数。
*
* @return {view_model_t} 返回view_model_t对象。
*/
view_model_t* series_view_model_create(navigator_request_t* req);
/**
* @method series_view_model_create_with
* 创建series view model对象。
*
* @annotation ["constructor"]
* @param {series_model_t*} aseries_model series_model对象。
*
* @return {view_model_t} 返回view_model_t对象。
*/
view_model_t* series_view_model_create_with(series_model_t* aseries_model);
/**
* @method series_view_model_attach
* 关联到series对象。
*
* @param {view_model_t*} view_model view_model对象。
* @param {series_model_t*} aseries_model series_model对象。
*
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
*/
ret_t series_view_model_attach(view_model_t* vm, series_model_t* aseries_model);
END_C_DECLS
#endif /*SERIES_VIEW_MODEL_H*/
实现代码如下:
#include "tkc/utils.h"
#include "tkc/object_default.h"
#include "mvvm/base/utils.h"
#include "series_view_model.h"
static ret_t series_view_model_set_prop(object_t* obj, const char* name, const value_t* v) {
series_view_model_t* vm = (series_view_model_t*)(obj);
series_model_t* aseries_model = vm->aseries_model;
if (tk_str_ieq("fifo1", name)) {
return RET_OK;
} else if (tk_str_ieq("fifo2", name)) {
return RET_OK;
} else if (tk_str_ieq("fifo3", name)) {
return RET_OK;
} else if (tk_str_ieq("capacity", name)) {
aseries_model->capacity = value_uint32(v);
return RET_OK;
} else if (tk_str_ieq("recent_time", name)) {
aseries_model->recent_time = value_uint64(v);
return RET_OK;
} else if (tk_str_ieq("div_time", name)) {
aseries_model->div_time = value_uint32(v);
return RET_OK;
} else if (tk_str_ieq("div_count", name)) {
aseries_model->div_count = value_uint32(v);
return RET_OK;
} else if (tk_str_ieq("line_show", name)) {
vm->line_show = value_bool(v);
return RET_OK;
} else if (tk_str_ieq("area_show", name)) {
vm->area_show = value_bool(v);
return RET_OK;
} else if (tk_str_ieq("symbol_show", name)) {
vm->symbol_show = value_bool(v);
return RET_OK;
} else if (tk_str_ieq("line_smooth", name)) {
vm->line_smooth = value_bool(v);
return RET_OK;
} else if (tk_str_ieq("s1_show", name)) {
vm->s1_show = value_bool(v);
return RET_OK;
} else if (tk_str_ieq("s2_show", name)) {
vm->s2_show = value_bool(v);
return RET_OK;
} else if (tk_str_ieq("s3_show", name)) {
vm->s3_show = value_bool(v);
return RET_OK;
}
return RET_NOT_FOUND;
}
static ret_t series_view_model_get_prop(object_t* obj, const char* name, value_t* v) {
series_view_model_t* vm = (series_view_model_t*)(obj);
series_model_t* aseries_model = vm->aseries_model;
if (tk_str_ieq("fifo1", name)) {
value_set_object(v, aseries_model->fifo[0]);
return RET_OK;
} else if (tk_str_ieq("fifo2", name)) {
value_set_object(v, aseries_model->fifo[1]);
return RET_OK;
} else if (tk_str_ieq("fifo3", name)) {
value_set_object(v, aseries_model->fifo[2]);
return RET_OK;
} else if (tk_str_ieq("capacity", name)) {
value_set_uint32(v, aseries_model->capacity);
return RET_OK;
} else if (tk_str_ieq("recent_time", name)) {
value_set_uint64(v, aseries_model->recent_time);
return RET_OK;
} else if (tk_str_ieq("div_time", name)) {
value_set_uint32(v, aseries_model->div_time);
return RET_OK;
} else if (tk_str_ieq("div_count", name)) {
value_set_uint32(v, aseries_model->div_count);
return RET_OK;
} else if (tk_str_ieq("line_show", name)) {
value_set_bool(v, vm->line_show);
return RET_OK;
} else if (tk_str_ieq("area_show", name)) {
value_set_bool(v, vm->area_show);
return RET_OK;
} else if (tk_str_ieq("symbol_show", name)) {
value_set_bool(v, vm->symbol_show);
return RET_OK;
} else if (tk_str_ieq("line_smooth", name)) {
value_set_bool(v, vm->line_smooth);
return RET_OK;
} else if (tk_str_ieq("s1_show", name)) {
value_set_bool(v, vm->s1_show);
return RET_OK;
} else if (tk_str_ieq("s2_show", name)) {
value_set_bool(v, vm->s2_show);
return RET_OK;
} else if (tk_str_ieq("s3_show", name)) {
value_set_bool(v, vm->s3_show);
return RET_OK;
}
return RET_NOT_FOUND;
}
static bool_t series_view_model_can_exec(object_t* obj, const char* name, const char* args) {
series_view_model_t* vm = (series_view_model_t*)(obj);
series_model_t* aseries_model = vm->aseries_model;
if (tk_str_ieq("new_graph", name)) {
return TRUE;
} else if (tk_str_ieq("line", name)) {
return TRUE;
} else if (tk_str_ieq("area", name)) {
return TRUE;
} else if (tk_str_ieq("symbol", name)) {
return TRUE;
} else if (tk_str_ieq("smooth", name)) {
return TRUE;
} else if (tk_str_ieq("show_series", name)) {
return TRUE;
}
return FALSE;
}
static ret_t series_view_model_exec(object_t* obj, const char* name, const char* args) {
uint32_t index = 0;
series_view_model_t* vm = (series_view_model_t*)(obj);
series_model_t* aseries_model = vm->aseries_model;
if (args != NULL) {
object_t* a = object_default_create();
tk_command_arguments_to_object(args, a);
index = object_get_prop_int32(a, "index", -1);
OBJECT_UNREF(a);
}
if (tk_str_ieq("new_graph", name)) {
return series_model_new_graph(aseries_model);
} else if (tk_str_ieq("line", name)) {
vm->line_show = !vm->line_show;
return RET_OBJECT_CHANGED;
} else if (tk_str_ieq("area", name)) {
vm->area_show = !vm->area_show;
return RET_OBJECT_CHANGED;
} else if (tk_str_ieq("symbol", name)) {
vm->symbol_show = !vm->symbol_show;
return RET_OBJECT_CHANGED;
} else if (tk_str_ieq("smooth", name)) {
vm->line_smooth = !vm->line_smooth;
return RET_OBJECT_CHANGED;
} else if (tk_str_ieq("show_series", name)) {
if (index == 0) {
vm->s1_show = !vm->s1_show;
} else if (index == 1) {
vm->s2_show = !vm->s2_show;
} else if (index == 2) {
vm->s3_show = !vm->s3_show;
}
return RET_OBJECT_CHANGED;
}
return RET_NOT_FOUND;
}
static ret_t series_view_model_on_destroy(object_t* obj) {
series_view_model_t* vm = (series_view_model_t*)(obj);
return_value_if_fail(vm != NULL, RET_BAD_PARAMS);
series_model_destroy(vm->aseries_model);
if (vm->timer_id != 0) {
timer_remove(vm->timer_id);
}
return view_model_deinit(VIEW_MODEL(obj));
}
static const object_vtable_t s_series_view_model_vtable = {"series_view_model_t",
"series_view_model_t",
sizeof(series_view_model_t),
FALSE,
series_view_model_on_destroy,
NULL,
series_view_model_get_prop,
series_view_model_set_prop,
NULL,
NULL,
series_view_model_can_exec,
series_view_model_exec};
view_model_t* series_view_model_create_with(series_model_t* aseries_model) {
object_t* obj = object_create(&s_series_view_model_vtable);
view_model_t* vm = view_model_init(VIEW_MODEL(obj));
series_view_model_t* series_view_model = (series_view_model_t*)(vm);
return_value_if_fail(vm != NULL, NULL);
series_view_model->aseries_model = aseries_model;
series_view_model->line_show = TRUE;
series_view_model->area_show = TRUE;
series_view_model->symbol_show = TRUE;
series_view_model->line_smooth = TRUE;
series_view_model->s1_show = TRUE;
series_view_model->s2_show = TRUE;
series_view_model->s3_show = TRUE;
return vm;
}
ret_t series_view_model_attach(view_model_t* vm, series_model_t* aseries_model) {
series_view_model_t* series_view_model = (series_view_model_t*)(vm);
return_value_if_fail(vm != NULL, RET_BAD_PARAMS);
series_view_model->aseries_model = aseries_model;
return RET_OK;
}
view_model_t* series_view_model_create(navigator_request_t* req) {
int32_t capacity = object_get_prop_int32(req->args, "capacity", 0);
const char* type_str = object_get_prop_str(req->args, "series_type");
series_type_t type = SERIES_TYPE_LINE_NORMAL;
if (tk_str_eq(type_str, "line_colorful")) {
type = SERIES_TYPE_LINE_COLORFUL;
} else if (tk_str_eq(type_str, "line_category")) {
type = SERIES_TYPE_LINE_CATEGORY;
} else if (tk_str_eq(type_str, "bar_normal")) {
type = SERIES_TYPE_BAR_NORMAL;
} else if (tk_str_eq(type_str, "bar_minmax")) {
type = SERIES_TYPE_BAR_MINMAX;
}
series_model_t* aseries_model = series_model_create(capacity, type);
return_value_if_fail(aseries_model != NULL, NULL);
return series_view_model_create_with(aseries_model);
}
显示效果如图: