基于模型-视图关联架构的HMI控制器控件界面与底层数据交互机制开发

在大型嵌入式系统开发中,通常不宜将大量信息直接写入源代码。为便于后期维护和测试,需要实现低成本调整页面和控件等元素。因此,资源管理系统显得尤为重要,不仅要管理图片、脚本和参数等常规资源,还需要以文本形式加载每个页面的具体信息。

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 对象,显示数据
  • 关联机制:通过 setPropertydata->comm.h 双向关联

灵活性:可以动态修改数据或界面
典型应用:动态界面生成系统

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值