awtk-widget-chart-view-mvvm C版本适配笔记

3 篇文章 2 订阅

一、前言

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);
}

显示效果如图:
在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
AWTK开发手册-AWTK开发实践指南-中文手册.pdf AWTK = Toolkit AnyWhere 随着手机、智能手表等便携式设备的普及,用户对 GUI 的要求越来越高,嵌入式系统对高性能、高可靠性、低功耗、美观炫酷的 GUI 的需求也越来越迫切,ZLG开源 GUI 引擎 AWTK 应运而生。AWTK 全称为 Toolkit AnyWhere,是 ZLG 倾心打造的一套基于 C 语言开发的 GUI 框架。旨在为用户提供一个功能强大、高效可靠、简单易用、可轻松做出炫酷效果的 GUI 引擎,并支持跨平台同步开发,一次编程,终生使用。 最终目标: 支持开发嵌入式软件。 支持开发Linux应用程序。 支持开发MacOS应用程序。 支持开发Windows应用程序。 支持开发Android应用程序。 支持开发iOS应用程序。 支持开发2D游戏。 其主要特色有: 小巧。在精简置下,不依赖第三方软件包,仅需要32K RAM + 256K FLASH即可开发一些简单的图形应用程序。 高效。采用脏矩形裁剪算法,每次只绘制和更新变化的部分,极大提高运行效率和能源利用率。 稳定。通过良好的架构设计和编程风格、单元测试、动态(valgrind)检查和Code Review保证其运行的稳定性。 丰富的GUI组件。提供窗口、对话框和各种常用的组件(用户可以置自己需要的组件,降低对运行环境的要求)。 支持多种字体格式。内置位图字体(并提供转换工具),也可以使用stb_truetype或freetype加载ttf字体。 支持多种图片格式。内置位图图片(并提供转换工具),也可以使用stb_image加载png/jpg等格式的图片。 紧凑的二进制界面描述格式。可以手工编辑的XML格式的界面描述文件,也可以使用Qt Designer设计界面,然后转换成紧凑的二进制界面描述格式,提高运行效率,减小内存开销。 支持主题并采用紧凑的二进制格式。开发时使用XML格式描述主题,然后转换成紧凑的二进制格式,提高运行效率,减小内存开销。 支持裸系统,无需OS和文件系统。字体、图片、主题和界面描述数据都编译到代码中,以常量数据的形式存放,运行时无需加载到内存。 内置nanovg实现高质量的矢量动画,并支持SVG矢量图。 支持窗口动画、控件动画、滑动动画和高清LCD等现代GUI常见特性。 支持国际化(Unicode、字符串翻译和输入法等)。 可移植。支持移植到各种RTOS和嵌入式Linux系统,并通过SDL在各种流行的PC/手机系统上运行。 脚本化。从API注释中提取API的描述信息,通过这些信息可以自动生成各种脚本的绑定代码。 支持硬件2D加速(目前支持STM32的DMA2D和NXP的PXP)和GPU加速(OpenGL/OpenGLES/DirectX/Metal),充分挖掘硬件潜能。 丰富的文档和示例代码。 采用LGPL协议开源发布,在商业软件中使用时无需付费。 目前核心功能已经完成,内部开始在实际项目中使用了,欢迎有兴趣的朋友评估和尝试,期待您的反馈。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值