qt更改类名_Qt编写自定义控件属性设计器

1a4ee9051a82701221d9025a940c5c64.png

以前做.NET开发中,.NET直接就集成了属性设计器,VS不愧是宇宙第一IDE,你能够想到的都给你封装好了,用起来不要太爽!因为项目需要自从全面转Qt开发已经6年有余,在工业控制领域,有一些应用场景需要自定义绘制一些控件满足特定的需求,比如仪器仪表、组态等,而且需要直接用户通过属性设计的形式生成导出控件及界面数据,下次导入使用,要想从内置控件或者自定义控件拿到对应的属性方法等,首先联想到的就是反射,Qt反射对应的类叫QMetaObject,着实强大,其实整个Qt开发框架也是超级强大的,本人自从转为Qt开发为主后,就深深的爱上了她,在其他跨平台的GUI开发框架平台面前,都会被Qt秒成渣,Qt的跨平台性是毋庸置疑的,几十兆的内存存储空间即可运行,尤其是嵌入式linux这种资源相当紧张的情况下,Qt的性能发挥到极致。
接下来我们就一步步利用QMetaObject类和QtPropertyBrower(第三方开源属性设计器)来实现自己的控件属性设计器,其中包含了所见即所得的控件属性控制,以及xml数据的导入导出。

第一步:获取控件的属性名称集合。

所有继承自QObject类的类,都有元对象,都可以通过这个QObject类的元对象metaObject()获取属性+事件+方法等。

代码如下:

QPushButton *btn = new QPushButton;
const QMetaObject *metaobject = btn->metaObject();
int count = metaobject->propertyCount();
for (int i = 0; i < count; ++i) {QMetaProperty metaproperty = metaobject->property(i);const char *name = metaproperty.name();QVariant value = btn->property(name);qDebug() << name << value;
}

打印输出如下:

objectName QVariant(QString, "")
modal QVariant(bool, false)
windowModality QVariant(int, 0)
enabled QVariant(bool, true)
geometry QVariant(QRect, QRect(0,0 640x480))
frameGeometry QVariant(QRect, QRect(0,0 639x479))
normalGeometry QVariant(QRect, QRect(0,0 0x0))
省略后面很多…

可以看到打印了很多父类的属性,这些基本上我们不需要的,那怎么办呢,放心,Qt肯定帮我们考虑好了,该propertyOffset上场了。metaObject->propertyOffset()表示出了父类外,自己类本身属性的偏移位置即索引开始的位置,这下就好办了。

代码改为:

QPushButton *btn = new QPushButton;
const QMetaObject *metaobject = btn->metaObject();
int count = metaobject->propertyCount();
int index = metaobject->propertyOffset();
for (int i = index; i < count; ++i) {QMetaProperty metaproperty = metaobject->property(i);const char *name = metaproperty.name();QVariant value = btn->property(name);qDebug() << name << value;
}

就是将i的起始位置改为偏移位置即可。

打印输出如下:

autoDefault QVariant(bool, false)
default QVariant(bool, false)
flat QVariant(bool, false)

这个过滤非常有用,因为真实用到的大部分应用场景都是控件类本身的属性,而不是父类的。

第二步:将控件类绑定到属性设计器。
拿到了控件的属性是第一步,接下来就是需要拿到属性所关联的方法等,这里省略,因为QtPropertyBrower这个屌爆了的第三方开源的属性设计器,全部给我们写好了,可以查看Qt帮助文档或者QMetaObject的头文件看到,QMetaObject提供了哪些接口去获取或使用这些元信息。比如classInfo获取类的信息、enumerator获取枚举值信息、method获取方法,property获取属性、superClass获取父类的名称等。
QtPropertyBrower中提供了ObjectController类,该类继承自QWidget,这样的话我们在界面上拖一个QWidget控件,鼠标右键提升为ObjectController即可。
这个轮子造的不要太好,我们只需要一行代码就可以让所有属性自动罗列到属性设计器中,代码是ui->objectController->setObject(btn);

看下效果如图:

1d26a51082b666eca3c4c257ab0f952f.png

到这里是不是很兴奋呢,任意控件都可以这样来展示自己的属性。在右侧动态更改属性会立即应用生效。

第三步:获取自定义控件的插件的所有控件。
接下来这一步才是最关键的一步,以上举例是Qt自带控件的,如果是自定义控件插件比如就一个DLL文件呢,怎么办?放心,办法肯定是有的。
该插件类QPluginLoader上场了。通过QPluginLoader载入后的实例,通过QDesignerCustomWidgetCollectionInterface类获取插件容器,然后逐个遍历容器找出单个插件,包括获得类名+图标。

代码如下:

void frmMain::openPlugin(const QString &fileName)
{qDeleteAll(listWidgets);listWidgets.clear();listNames.clear();ui->listWidget->clear();//加载自定义控件插件集合信息,包括获得类名+图标QPluginLoader loader(fileName);if (loader.load()) {QObject *plugin = loader.instance();//获取插件容器,然后逐个遍历容器找出单个插件QDesignerCustomWidgetCollectionInterface *interfaces = qobject_cast<QDesignerCustomWidgetCollectionInterface *>(plugin);if (interfaces)  {listWidgets = interfaces->customWidgets();int count = listWidgets.count();for (int i = 0; i < count; i++) {QIcon icon = listWidgets.at(i)->icon();QString className = listWidgets.at(i)->name();QListWidgetItem *item = new QListWidgetItem(ui->listWidget);item->setText(className);item->setIcon(icon);listNames << className;}}//获取所有插件的类名const QObjectList objList = plugin->children();foreach (QObject *obj, objList) {QString className = obj->metaObject()->className();//qDebug() << className;}}
}

效果图如下:

8db104578d81e1c6488aa9a9cbc3216e.png

第四步:实例化new出控件并放到窗体。
拿到了所有的控件,前面还有个对应控件的小图标,是不是又有点小激动呢,接下来就是怎么双击或者拖动该控件到界面上立马实例化一个控件出来。上一步我们将所有控件放到了一个链表变量listWidgets中,该变量在头文件中定义如下:
QList<QDesignerCustomWidgetInterface *> listWidgets;

这里写了个函数,传入列表中控件的索引,即该类的索引位置,和控件默认要放置的坐标,即可在主界面生成该控件。

代码如下:

void frmMain::newWidget(int row, const QPoint &point)
{//列表按照同样的索引生成的,所以这里直接对该行的索引就行QWidget *widget = listWidgets.at(row)->createWidget(ui->centralwidget);widget->move(point);widget->resize(widget->sizeHint());//实例化选中窗体跟随控件一起newSelect(widget);//立即执行获取焦点以及设置属性widgetPressed(widget);
}

第五步:动态绑定控件到设计器。
这一步就比较轻松了,上面提到过,直接获取当前界面上选中的是哪个控件,遍历可以得到,然后设置object到属性设计器控件即可。

代码如下:

void frmMain::clearFocus()
{//将原有焦点窗体全部设置成无焦点foreach (SelectWidget *widget, selectWidgets) {widget->setDrawPoint(false);}
}
void frmMain::widgetPressed(QWidget *widget)
{//清空所有控件的焦点clearFocus();//设置当前按下的控件有焦点foreach (SelectWidget *w, selectWidgets) {if (w->getWidget() == widget) {w->setDrawPoint(true);break;}}//设置自动加载该控件的所有属性ui->objectController->setObject(widget);
}

第六步:导入导出控件属性到xml文件。
这一步比较难,本人也是花了好几个小时才搞定,前后折腾了好多次,因为遇到好几个棘手的问题,比如有些自定义控件中其实里边封装了Qt自带的控件例如QPushButton等,如果遍历控件设计窗体的所有控件,也会把该控件也遍历进去,所以要做过滤处理。

导入xml数据自动生成控件代码如下:

void frmMain::openFile(const QString &fileName)
{//打开文件QFile file(fileName);if (!file.open(QFile::ReadOnly | QFile::Text)) {return;}//将文件填充到dom容器QDomDocument doc;if (!doc.setContent(&file)) {file.close();return;}file.close();//先清空原有控件QList<QWidget *> widgets = ui->centralwidget->findChildren<QWidget *>();qDeleteAll(widgets);widgets.clear();//先判断根元素是否正确QDomElement docElem = doc.documentElement();if (docElem.tagName() == "canvas") {QDomNode node = docElem.firstChild();QDomElement element = node.toElement();while(!node.isNull()) {QString name = element.tagName();//存储坐标+宽高int x, y, width, height;//存储其他自定义控件属性QList<QPair<QString, QVariant> > propertys;//节点名称不为空才继续if (!name.isEmpty()) {//遍历节点的属性名称和属性值QDomNamedNodeMap attrs = element.attributes();for (int i = 0; i < attrs.count(); i++) {QDomNode n = attrs.item(i);QString nodeName = n.nodeName();QString nodeValue = n.nodeValue();//qDebug() << nodeName << nodeValue;//优先取出坐标+宽高属性,这几个属性不能通过setProperty实现if (nodeName == "x") {x = nodeValue.toInt();} else if (nodeName == "y") {y = nodeValue.toInt();} else if (nodeName == "width") {width = nodeValue.toInt();} else if (nodeName == "height") {height = nodeValue.toInt();} else {propertys.append(qMakePair(nodeName, QVariant(nodeValue)));}}}//qDebug() << name << x << y << width << height;//根据不同的控件类型实例化控件int count = listWidgets.count();for (int i = 0; i < count; i++) {QString className = listWidgets.at(i)->name();if (name == className) {QWidget *widget = listWidgets.at(i)->createWidget(ui->centralwidget);//逐个设置自定义控件的属性int count = propertys.count();for (int i = 0; i < count; i++) {QPair<QString, QVariant> property = propertys.at(i);widget->setProperty(property.first.toLatin1().constData(), property.second);}//设置坐标+宽高widget->setGeometry(x, y, width, height);//实例化选中窗体跟随控件一起newSelect(widget);break;}}//移动到下一个节点node = node.nextSibling();element = node.toElement();}}
}

导出所有控件到xml文件代码如下:

void frmMain::saveFile(const QString &fileName)
{QFile file(fileName);if (!file.open(QFile::WriteOnly | QFile::Text | QFile::Truncate)) {return;}//以流的形式输出文件QTextStream stream(&file);//构建xml数据QStringList list;//添加固定头部数据list << "<?xml version="1.0" encoding="UTF-8"?>";list << QString("<canvas width="%1" height="%2">").arg(ui->centralwidget->width()).arg(ui->centralwidget->height());//从容器中找到所有控件,根据控件的类名保存该类的所有属性QList<QWidget *> widgets = ui->centralwidget->findChildren<QWidget *>();foreach (QWidget *w, widgets) {const QMetaObject *metaObject = w->metaObject();QString className = metaObject->className();QStringList values;//如果当前控件的父类不是主窗体则无需导出,有些控件有子控件无需导出if (w->parent() != ui->centralwidget || className == "SelectWidget") {continue;}//metaObject->propertyOffset()表示当前控件的属性开始索引,0开始的是父类的属性int index = metaObject->propertyOffset();for (int i = index; i < metaObject->propertyCount(); i++) {QMetaProperty p = metaObject->property(i);QString nodeName = p.name();QVariant nodeValue = p.read(w);//枚举值要特殊处理,需要以字符串形式写入,不然存储到配置文件数据为intif (p.isEnumType()) {QMetaEnum enumValue = p.enumerator();nodeValue = enumValue.valueToKey(nodeValue.toInt());}QString temp = nodeValue.toString().toLocal8Bit().constData();values << QString("%1="%2"").arg(nodeName).arg(temp);//qDebug() << nodeName << nodeValue;}//逐个添加界面上的控件的属性QString str = QString("t<%1 x="%2" y="%3" width="%4" height="%5" %6/>").arg(className).arg(w->x()).arg(w->y()).arg(w->width()).arg(w->height()).arg(values.join(" "));list << str;
}
//添加固定尾部数据list << "</canvas>";//写入文件QString data = list.join("n");stream << data;file.close();
}

xml数据格式效果图:

53900e2c03f58a7d315f6f46f1b2a76f.png

完整效果图:

ec6d2fbae8a191cd38db4f83491eb247.png

6bfa3280e3674cca97094d78a30fdb49.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/554341.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sudo mysql压缩备份解压操作_高效管理文件之压缩及解压缩 .bz2 文件

对文件进行压缩&#xff0c;可以通过使用较少的字节对文件中的数据进行编码来显著地减小文件的大小&#xff0c;并且在跨网络的文件的备份和传送时很有用。 另一方面&#xff0c;解压文件意味着将文件中的数据恢复到初始状态。Linux 中有几个文件压缩和解压缩更具&#xff0c;比…

zygoteinit.java_源码跟踪之启动流程:从ZygoteInit到onCreate

InstrumentationSDK版本名称: PieAPI Level: 28一、源码调用时序图1. Activity的启动流程说明&#xff1a;其中ActivityThread中执行的scheduleTransaction方法在其父类ClientTransactionHandler中&#xff0c;发送了ActivityThread.H.EXECUTE_TRANSACTION&#xff0c;Activity…

小城交通大转型!苏州金龙助力杭州建德公交开新格局

新安江畔&#xff0c;密林丛生&#xff0c;一辆辆绿色巴士穿梭而行&#xff0c;杭州市首款纯电动无站立位公交车正在试运行中。 12月19日&#xff0c;杭州建德&#xff0c;23辆苏州金龙海格牌6米无站立位新能源纯电动公交车正式交付建德市公共交通运输有限公司。自此&#xff…

java虚拟机性能优化_死磕Java虚拟机-性能调优实战篇

Java命令分为如下三种1. 以java - 开头&#xff1a;标准参数2. 以java -X 开头&#xff1a;非标参数3. 以java -XX 开头&#xff1a;性能调优主要用这个开头的参数&#xff0c;但是无法找到相关参数的帮助文档&#xff0c;下面我教大家几个常用的命令-XX:UseSerialGC Seria…

java volatile 原子性_Java中volatile不能保证原子性的证明

Java并发编程之验证volatile不能保证原子性通过系列文章的学习&#xff0c;凯哥已经介绍了volatile的三大特性。1&#xff1a;保证可见性 2&#xff1a;不保证原子性 3&#xff1a;保证顺序。那么怎么来验证可见性呢&#xff1f;本文凯哥(凯哥Java:kaigejava)将通过代码演示来证…

mysql 漏洞如何修复_Mysql漏洞修复方法思路及注意事项

【系统环境】系统环境&#xff1a;Red Hat Enterprise Linux Server release 5.4 (Tikanga) 5.7.16 MySQL Community Server (GPL)【漏洞信息】漏洞信息报告&#xff0c;根据集团第三方软件扫描出对应数据库版本的漏洞信息&#xff0c;可以从DVE号跟当前数据库发布版本时间来判…

Java飞机大战敌机消失_Shoot 飞机大战,功能是子弹打在敌机上, 消失 且在内存中 , 小蜜蜂上同理 Games 游戏 247万源代码下载- www.pudn.com...

文件名称: Shoot下载 收藏√ [5 4 3 2 1 ]所属分类: Games开发工具: Java文件大小: 371 KB上传时间: 2015-12-01下载次数: 0提 供 者: 刘星详细说明&#xff1a;飞机大战&#xff0c;功能是子弹打在敌机上&#xff0c;子弹消失敌机消失且在内存中消失&#xff0c;打在小蜜…

日志分析告警实现java_关于Aborted connection告警日志的分析

前言&#xff1a;有时候&#xff0c;连接MySQL的会话经常会异常退出&#xff0c;错误日志里会看到"Got an error reading communication packets"类型的告警。本篇文章我们一起来讨论下该错误可能的原因以及如何来规避。1.状态变量Aborted_clients和Aborted_connects…

网页java在div输出内容_JS实现读取xml内容并输出到div中的方法示例

本文实例讲述了JS实现读取xml内容并输出到div中的方法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;note.xml文件结构:GeorgeJohnReminderaJohnReminderGeorgeJohnReminder利用js将xml输出到div中&#xff1a;www.jb51.net js读取xml.aaaa{width: 30%;height: 50px;…

java枚举加载顺序_java 中类的加载顺序(转)

1、虚拟机在首次加载Java类时&#xff0c;会对静态初始化块、静态成员变量、静态方法进行一次初始化2、只有在调用new方法时才会创建类的实例3、类实例创建过程&#xff1a;按照父子继承关系进行初始化&#xff0c;首先执行父类的初始化块部分&#xff0c;然后是父类的构造方法…

java相遇问题_两车追及或相遇问题(hdu1275)数学题

两车追及或相遇问题Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 902 Accepted Submission(s):259Problem Description外号叫“猪头三”的小学生在数学课上&#xff0c;经常遇到两车相遇或追及的方程题&#xff0c;…

java常用class类_java常用类

java常用类内部类1.成员内部类&#xff1a;在一个类的内部定义一个完整的类例如&#xff1a;外部类public class Body{内部类class Header{}}内部类可以直接访问外部类的私有成员&#xff0c;而且不破坏封装内部类可以为外部类提供必要的功能组件&#xff0c;成员内部类 在类的…

mysql 分类计数器_PHP MySQL映像计数器

我是PHP新手,一直在研究计数器.计数器很好用,但是现在我想将数字转换成图像.我创建了12张图片0-9,一个空格和一个逗号图片.我在上下搜索,以获取将数字格式转换为图像所需的提示,但没有成功.到目前为止,我所发现的就是如何仅使用文件PHP / MySQL来建立基本的计数器,以及如何使用…

java gc时会暂停运行吗,java gc 项目终止运行

当前位置:我的异常网 编程 java gc 项目终止运行java gc 项目终止运行www.myexceptions.net 网友分享于&#xff1a;2013-09-03 浏览&#xff1a;6次java gc 项目停止运行首先感谢阿宝同学的帮助&#xff0c;我才对这个gc算法的调整有了一定的认识&#xff0c;而不是停留在过…

php 取消页面一些样式,jquery如何去除样式

jquery去除样式的方法&#xff1a;1、使用【removeClass()】方法&#xff0c;代码为【removeClass(function(index,class))】&#xff1b;2、使用toggleClass方法&#xff0c;代码为【toggleClass(class)】。本教程操作环境&#xff1a;windows7系统、jquery3.2.1版&#xff0c…

matlab对经济指标分析,经济背景下的数据预测分析--基于matlab建模(郑铿城)

1、经济预测概述经济预测有三个要素&#xff0c;实际资料是预测的依据&#xff1b;经济理论是预测的基础&#xff1b;数学模型是预测的手段&#xff1b;经济预测的内容包括生产和资源预测&#xff0c;市场预测&#xff0c;国民收入分配预测&#xff0c;居民生活质量预测等。预测…

php nginx 静态资源,Nginx实践篇(1)- Nginx作为静态资源web服务 - 静态资源压缩

一、静态资源web服务1. 静态资源类型类型文件类型浏览器端渲染HTML、CSS、JS图片JEPG、GIF、PNG视频FLV、MPEG文件TXT等其他下载文件2. 静态资源服务场景-CDN二、静态资源核心配置1. 文件读取 sendfilesendfile 是一种高效传输文件的模式.sendfile设置为on表示启动高效传输文件…

php将get传参解析成数组,php解析url (parse_url) 参数成数组 (parse_str)

今天首先让我们为天津8.12大爆炸和陕西山阳山体滑坡遇难的同胞默哀3分钟&#xff0c;祝愿受伤的群众早日康复&#xff0c;脱离危险&#xff0c;希望国家严惩事故责任人&#xff0c;安抚受损群众。希望杯具以后不再发生……最近天灾人祸&#xff0c;大家出行一定要注意安全&…

猛将赵云java,这五位三国猛将临危救主,赵云只能排第二位,第一位大家都服...

曹洪&#xff0c;曹子廉。是曹操的从弟&#xff0c;在《三国演义》小说中&#xff0c;最早跟随曹操起兵&#xff0c;虽然战斗力没有一流战将那么强悍&#xff0c;但是此人在曹操起兵讨伐董卓时&#xff0c;曹操在荥阳被徐荣打败&#xff0c;坐骑丢失。正要被徐荣士兵解决的时刻…

用python画三维图、某区域的高程,python - 在PyQt中绘制具有高程和降低效果的3D矩形/多边形 - SO中文参考 - www.soinside.com...

据我所知&#xff0c;没有内置的PyQt 3D绘制小部件/功能&#xff0c;因为您只能绘制2D多边形。但我们可以创建一个自定义类来模拟3D绘画。从您的Java链接reference&#xff1a;Java支持3D矩形&#xff0c;但第三维的效果不是很明显。由于海拔较低&#xff0c;效果可以忽略不计。…