在大型嵌入式系统开发中,通常不宜将大量信息直接写入源代码。为便于后期维护和测试,需要实现低成本调整页面和控件等元素。因此,资源管理系统显得尤为重要,不仅要管理图片、脚本和参数等常规资源,还需要以文本形式加载每个页面的具体信息。
1.底层数据
页面包含控件名称、坐标位置及尺寸等文本信息
LABEL,x:12,y:20,width:45,height:28,paraname:para,color:blue
TEXT,x:34,height:77,paraname:para,y:44,width:55,color:blue
BUTTON,y:45,width:347,height:14,paraname:para,color:blue,x:14
...
2.模型
将数据填充到预定义的结构体中
// 控件的通用属性
typedef struct{
QWidget* h;
int32_t x;
int32_t y;
int32_t xsize;
int32_t ysize;
}ctrl_common_t;
// 具体某控件属性 例如TEXT
typedef struct{
ctrl_common_t comm;
...
}ctrl_text_t;
2.1 设置文本格式
采用单字符标识属性值以确保唯一性,便于字符指针操作,同时保障属性顺序混乱时仍能准确匹配提取
LABEL,{x:12},{y:20},{w:45},{h:28},{p:para},{c:blue}
TEXT,{x:34},{h:77},{p:para},{y:44},{w:55},{c:blue}
BUTTON,{y:45},{w:347},{h:14},{p:para},{c:blue},{x:14}
//信息结构体
typedef struct{
char type;
int data_off;//结构体偏移量
char fmt[16];//数据类型
}ctrl_attrs_t;
2.2 属性加载函数
为将文本属性值准确加载到结构体中,设计标准属性提取格式及配套解析函数:
static ctrl_attrs_t _attr_[] = {
{'x', offset(ctrl_text_t, comm.x),"%d"},
{'y', offset(ctrl_text_t, comm.y),"%d"},
{'w', offset(ctrl_text_t, comm.xsize),"%d"},
{'h', offset(ctrl_text_t, comm.ysize),"%d"},
...
}
int32_t _get_attr(const char* cfg, void* data, ctrl_attrs_t* _attr_, int32_t counts){
int32_t items = 0;
const char* p = cfg;
while(1){
char* token = strchr(p,"{");
if(token == nullptr) break;//循环退出条件
token += 1;
char schar = *token;//指向第一个字符
for(int i=0; i<counts; i++){
if(schar == _attr_[i].type){
char* pdata = (char*)data;// 转为char单字节指针,方便字节操作
pdata += _attr_[i].data_off;
token += 2;
sscanf(token, _attr_[i].fmt, pdata);//将对应数据写入结构体变量中
items++;
}
}
p = token;
}
return items;
}
3.视图-界面控件
获取控件信息后,即可着手实现控件的创建与显示
// 具体某控件属性 例如TEXT
typedef struct{
ctrl_common_t comm;
...
}ctrl_text_t;
ctrl_text_t* ctrl_text_load(const char* cfg){
ctrl_text_t* data = new ctrl_text_t;
memset(data, '\0', sizeof(*data));
int items;
items = _get_attr(cfg, data, _attr_, _countof(_attr_));
if(items < 4){
delete data;
return NULL;
}
return data;
}
int32_t ctrl_text_create(QWidget* parent, ctrl_text_t* data){
QLabel* pctrl;
pctrl = new QLabel(parent);
pctrl->setGeometry(data->comm.x, data->comm.y, data->comm.xsize, data->comm.ysize);
pctrl->setProperty("who", QVariant::fromValue((void*)data));
data->comm.h = pctrl;
return 0;
}
4. "模型-视图"关联模式
pctrl->setProperty("who", QVaniant::fromValue((void*)data));
作用:给按钮设置一个自定义属性,关联原始数据
// 1. (void *) data:将data指针转换为void*类型
// 这是一种通用指针类型,可以指向任何数据
// 2. QVariant::fromValue((void *) data)
// 将void*指针包装成QVariant对象
// QVariant是Qt的通用数据容器
// 3. setProperty("who", ...)
// 给按钮设置一个名为"who"的动态属性
// 这个属性值就是包含data指针的QVariant
// 用途:稍后可以通过以下方式获取数据
void* ptr = pctrl->property("who").value<void*>();
my_data_type* data = static_cast<my_data_type*>(ptr);
data->comm.h = pctrl;
作用:在原始数据结构中保存创建的按钮指针
双向关联:
data结构体 ────────────────→ QPushButton对象
│ │
│ data->comm.h = pctrl │ setProperty("who", data)
│ (保存按钮指针) │ (保存数据指针)
│ │
└─────────────────────────────┘
双向引用,互相可以找到对方
// 1. 通过data可以找到对应的按钮
QPushButton* button = static_cast<QPushButton*>(data->comm.h);
// 2. 通过按钮可以找到对应的data
my_data_type* data = getDataFromButton(button);
5. 关联用途
pctrl->setProperty("who", QVariant::fromValue((void*) data));
data->comm.h = pctrl;
当关联后,可通过数据找到对应的控件,也可通过控件获取数据
//通过数据找到对应的控件
QLabel* label = static_cast<QLabel*>(data->comm.h);
或者
QLabel* label = (QLabel*)(data->comm.h);
// 通过控件获取数据
void* ptr = pctrl->setProperty("who").value<void*>();
ctrl_text_t* data = static_cast<ctrl_text_t*>(ptr);
或者
ctrl_text_t* data = (ctrl_text_t*)pctrl->setProperty("who").value<void*>();
6. 总结
- 模型(Model):
ctrl_text_t结构体,存储数据 - 视图(View):
QLabel对象,显示数据 - 关联机制:通过
setProperty和data->comm.h双向关联
灵活性:可以动态修改数据或界面
典型应用:动态界面生成系统

732

被折叠的 条评论
为什么被折叠?



