Qt编写控件属性设计器-用户属性

2023-11-07

一、前言

用户属性是后面新增加的一个功能,自定义控件如果采用的Q_PROPERTY修饰的属性,会自动识别到属性栏中,这个一般称为控件属性,在组态设计软件中,光有控件本身的控件属性还是不够的,毕竟这些属性仅仅是以外观为主,并不能表示某个设备的属性,所以需要除了这个控件属性以外增加用户属性来存储该控件关联的设备属性,比如设备编号、设备名称、地理位置等信息,而这些信息也要和控件属性一样,都能导入导出到xml文件,同时能支持多个用户属性,用户自己填写名字和值,名字和值都支持中文描述,在xml文件中为了区分用户属性和控件属性,特意在用户属性前面加上user-前缀来表示,这样在读取xml文件加载控件的时候,识别到user-开头的都存储到该控件的用户属性列表中。

自从有了用户属性的机制,大大拓展了控件的现有功能,相当于可以绑定N个自定义的数据,而这些用户属性直接采用setProperty来设置即可,然后通过property来读取就行,为了支持中文的属性名称,需要设置属性的时候转换一下:widget->setProperty(name.toStdString().c_str(), value);

体验地址:https://gitee.com/feiyangqingyun/QUCSDK
https://github.com/feiyangqingyun/qucsdk

二、实现的功能

  1. 自动加载插件文件中的所有控件生成列表,默认自带的控件超过120个。
  2. 拖曳到画布自动生成对应的控件,所见即所得。
  3. 右侧中文属性栏,改变对应的属性立即应用到对应选中控件,直观简洁,非常适合小白使用。
  4. 独创属性栏文字翻译映射机制,效率极高,可以非常方便拓展其他语言的属性栏。
  5. 所有控件的属性自动提取并显示在右侧属性栏,包括枚举值下拉框等。
  6. 支持手动选择插件文件,外部导入插件文件。
  7. 可以将当前画布的所有控件配置信息导出到xml文件。
  8. 可以手动选择xml文件打开控件布局,自动根据xml文件加载控件。
  9. 可拉动滑动条、勾选模拟数据复选框、文本框输入,三种方式来生成数据应用所有控件。
  10. 控件支持八个方位拉动调整大小,自适应任意分辨率,可键盘上下左右微调位置。
  11. 打通了串口采集、网络采集、数据库采集三种方式设置数据。
  12. 代码极其精简,注释非常详细,可以作为组态的雏形,自行拓展更多的功能。
  13. 纯Qt编写,支持任意Qt版本+任意编译器+任意系统。

三、效果图

四、核心代码

void frmMain::openFile(const QString &fileName)
{
    //如果控件列表没有则不用继续
    if (ui->listWidget->count() == 0) {
        return;
    }

    //打开文件
    QFile file(fileName);
    if (!file.open(QFile::ReadOnly | QFile::Text)) {
        return;
    }

    //将文件填充到dom容器
    QDomDocument doc;
    if (!doc.setContent(&file)) {
        file.close();
        return;
    }

    file.close();

    listSelect.clear();
    listUserProperty.clear();
    xmlName = fileName;

    //先清空原有控件
    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 index = listNames.indexOf(name);
            if (index < 0) {
                continue;
            }

            //存储控件的坐标位置和宽度高度
            int x, y, width, height;
            //存储自定义控件属性
            QList<QPair<QString, QVariant> > propertys;
            //存储控件自定义属性
            QStringList userProperty;

            //节点名称不为空才继续
            if (!name.isEmpty()) {
                //遍历节点的属性名称和属性值
                QDomNamedNodeMap attrs = element.attributes();
                for (int i = 0; i < attrs.count(); i++) {
                    QDomNode node = attrs.item(i);
                    QString nodeName = node.nodeName();
                    QString nodeValue = node.nodeValue();
                    //qDebug() << name << nodeName << nodeValue;

                    //优先取出坐标+宽高属性,这几个属性不能通过设置弱属性实现
                    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 if (nodeName.startsWith("user-")) {
                        //取出user-开头的自定义属性
                        nodeName = nodeName.split("-").last();
                        userProperty << QString("%1|%2").arg(nodeName).arg(nodeValue);
                    } else {
                        QVariant value = QVariant(nodeValue);
                        //为了兼容Qt4,需要将颜色值的rgba分别取出来,因为Qt4不支持16进制字符串带透明度
                        //#6422a3a9 这种格式依次为 argb 带了透明度的才需要特殊处理
                        if (nodeValue.startsWith("#") && nodeValue.length() == 9) {
                            bool ok;
                            int alpha = nodeValue.mid(1, 2).toInt(&ok, 16);
                            int red = nodeValue.mid(3, 2).toInt(&ok, 16);
                            int green = nodeValue.mid(5, 2).toInt(&ok, 16);
                            int blue = nodeValue.mid(7, 2).toInt(&ok, 16);
                            value = QColor(red, green, blue, alpha);
                        }

                        propertys.append(qMakePair(nodeName, value));
                    }
                }
            }

            //qDebug() << name << x << y << width << height;

            //根据不同的控件类型实例化控件
            int countWidget = listWidgets.count();
            int countProperty = propertys.count();
            for (int i = 0; i < countWidget; i++) {
                QString className = listWidgets.at(i)->name();
                if (name == className) {
                    //生成对应的控件
                    QWidget *widget = createWidget(i);
                    //逐个设置自定义控件的属性
                    for (int j = 0; j < countProperty; j++) {
                        QPair<QString, QVariant> property = propertys.at(j);
                        QString name = property.first;
                        QVariant value = property.second;
                        widget->setProperty(name.toStdString().c_str(), value);
                    }

                    //设置控件坐标及宽高
                    widget->setGeometry(x, y, width, height);
                    //实例化选中窗体跟随控件一起
                    newSelect(widget, userProperty);
                    break;
                }
            }

            //移动到下一个节点
            node = node.nextSibling();
            element = node.toElement();
        }
    }
}

void frmMain::saveFile(const QString &fileName)
{
    //如果控件列表没有则不用继续
    if (ui->listWidget->count() == 0) {
        return;
    }

    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\"?>";
    //添加canvas主标签,保存宽高和背景图片,还可以自行添加其他属性
    list << QString("<canvas width=\"%1\" height=\"%2\" image=\"%3\">")
         .arg(ui->centralwidget->width()).arg(ui->centralwidget->height()).arg("bg.jpg");

    //从容器中找到所有控件,根据控件的类名保存该类的所有属性
    QList<QWidget *> widgets = ui->centralwidget->findChildren<QWidget *>();
    foreach (QWidget *widget, widgets) {
        const QMetaObject *metaObject = widget->metaObject();
        QString className = metaObject->className();

        //如果当前控件的父类不是主窗体则无需导出,有些控件有子控件无需导出
        if (widget->parent() != ui->centralwidget || className == "SelectWidget") {
            continue;
        }

        //逐个存储自定义控件属性
        //metaObject->propertyOffset()表示当前控件的属性开始索引,0开始的是父类的属性
        QStringList values;
        int index = metaObject->propertyOffset();
        int count = metaObject->propertyCount();
        for (int i = index; i < count; i++) {
            QMetaProperty property = metaObject->property(i);
            QString nodeName = property.name();
            QVariant variant = property.read(widget);
            QString typeName = variant.typeName();
            QString nodeValue = variant.toString();

            //如果是颜色值则取出透明度一起,颜色值toString在Qt4中默认不转透明度
            if (typeName == "QColor") {
                QColor color = variant.value<QColor>();
                if (color.alpha() < 255) {
                    //Qt4不支持HexArgb格式的字符串,需要挨个取出来拼接
                    //nodeValue = color.name(QColor::HexArgb);
                    QString alpha = QString("%1").arg(color.alpha(), 2, 16, QChar('0'));
                    QString red = QString("%1").arg(color.red(), 2, 16, QChar('0'));
                    QString green = QString("%1").arg(color.green(), 2, 16, QChar('0'));
                    QString blue = QString("%1").arg(color.blue(), 2, 16, QChar('0'));
                    nodeValue = QString("#%1%2%3%4").arg(alpha).arg(red).arg(green).arg(blue);
                }
            }

            //枚举值要特殊处理,需要以字符串形式写入,不然存储到配置文件数据为int
            if (property.isEnumType()) {
                QMetaEnum enumValue = property.enumerator();
                nodeValue = enumValue.valueToKey(nodeValue.toInt());
            }

            values << QString("%1=\"%2\"").arg(nodeName).arg(nodeValue);
            //qDebug() << nodeName << nodeValue << variant;
        }

        //找到当前控件对应的索引
        index = -1;
        count = listSelect.count();
        for (int i = 0; i < count; i++) {
            if (listSelect.at(i)->getWidget() == widget) {
                index = i;
                break;
            }
        }

        //可以用下面方法列出所有的用户属性,然后取值,本程序已经用 listUserProperty 存储了
        //qDebug() << widget->dynamicPropertyNames();

        //逐个存储控件的用户属性
        QStringList userProperty = listUserProperty.at(index);
        count = userProperty.count();
        for (int i = 0; i < count; i++) {
            QStringList list = userProperty.at(i).split("|");
            values << QString("user-%1=\"%2\"").arg(list.at(0)).arg(list.at(1));
        }

        //逐个添加界面上的控件的属性
        QString geometry = QString("x=\"%1\" y=\"%2\" width=\"%3\" height=\"%4\"").arg(widget->x()).arg(widget->y()).arg(widget->width()).arg(widget->height());
        QString str = QString("\t<%1 %2 %3/>").arg(className).arg(geometry).arg(values.join(" "));
        list << str;
    }

    //添加固定尾部数据
    list << "</canvas>";

    //写入文件
    QString data = list.join("\n");
    stream << data;
    file.close();
}

本文福利,费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块等等)↓↓↓↓↓↓见下面↓↓文章底部点击费领取↓↓

五、控件介绍

  1. 超过160个精美控件,涵盖了各种仪表盘、进度条、进度球、指南针、曲线图、标尺、温度计、导航条、导航栏,flatui、高亮按钮、滑动选择器、农历等。远超qwt集成的控件数量。
  2. 每个类都可以独立成一个单独的控件,零耦合,每个控件一个头文件和一个实现文件,不依赖其他文件,方便单个控件以源码形式集成到项目中,较少代码量。qwt的控件类环环相扣,高度耦合,想要使用其中一个控件,必须包含所有的代码。
  3. 全部纯Qt编写,QWidget+QPainter绘制,支持Qt4.6到Qt5.12的任何Qt版本,支持mingw、msvc、gcc等编译器,支持任意操作系统比如windows+linux+mac+嵌入式linux等,不乱码,可直接集成到Qt Creator中,和自带的控件一样使用,大部分效果只要设置几个属性即可,极为方便。
  4. 每个控件都有一个对应的单独的包含该控件源码的DEMO,方便参考使用。同时还提供一个所有控件使用的集成的DEMO。
  5. 每个控件的源代码都有详细中文注释,都按照统一设计规范编写,方便学习自定义控件的编写。
  6. 每个控件默认配色和demo对应的配色都非常精美。
  7. 超过130个可见控件,6个不可见控件。
  8. 部分控件提供多种样式风格选择,多种指示器样式选择。
  9. 所有控件自适应窗体拉伸变化。
  10. 集成自定义控件属性设计器,支持拖曳设计,所见即所得,支持导入导出xml格式。
  11. 自带activex控件demo,所有控件可以直接运行在ie浏览器中。
  12. 集成fontawesome图形字体+阿里巴巴iconfont收藏的几百个图形字体,享受图形字体带来的乐趣。
  13. 所有控件最后生成一个动态库文件(dll或者so等),可以直接集成到qtcreator中拖曳设计使用。
  14. 目前已经有qml版本,后期会考虑出pyqt版本,如果用户需求量很大的话。
  15. 自定义控件插件开放动态库使用(永久免费),无任何后门和限制,请放心使用。
  16. 目前已提供26个版本的dll,其中包括了qt5.12.3 msvc2017 32+64 mingw 32+64 的。
  17. 不定期增加控件和完善控件,不定期更新SDK,欢迎各位提出建议,谢谢!
  18. Qt入门书籍推荐霍亚飞的《Qt Creator快速入门》《Qt5编程入门》,Qt进阶书籍推荐官方的《C++ GUI Qt4编程》。
  19. 强烈推荐程序员自我修养和规划系列书《大话程序员》《程序员的成长课》《解忧程序员》,受益匪浅,受益终生!
  20. SDK下载链接:https://pan.baidu.com/s/1A5Gd77kExm8Co5ckT51vvQ 提取码:877p

原文链接:cnblogs.com/feiyangqingyun/p/11922938.html

本文福利,费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块等等)↓↓↓↓↓↓见下面↓↓文章底部点击费领取↓↓

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Qt编写控件属性设计器-用户属性 的相关文章

随机推荐

  • 机器学习第八课--决策树

    举个例子 明天如果下雨我就不出门了 在这里我们用了一个决策条件 是否下雨 然后基于这个条件会有不同的结果 出门和不出门 这就是一个经典的决策树 决策树的核心组成部分 节点 边 最后的结论就是第一个决策树要优于第二个决策树 因为它的准确率更高
  • openGL之API学习(七十)windows的opengl扩展wgl

    WGL扩展说白了是Windows操作系统和OpenGL做交互的一系列扩展 glut也好 其他框架也好 都是对这些接口进行了封装 之前的文章也提到了 Windows在对OpenGL的原生支持非常不友好 甚至差点就夭折了 在wingdi h这个
  • CSRF攻击原理及防护

    CSRF攻击原理及防护 0x01 CSRF是什么 ssrf 服务端请求伪造 CSRF全称为跨站请求伪造 Cross site request forgery 是一种网络攻击方式 也被称为 one click attack 或者 sessio
  • Robot Framework 基于图像识别的 C/S 自动化测试 --- 从入门到实战

    目录 引子 雏形 库的选择 实施过程 1 导入并改造ImageHorizonLibrary 使其支持中文路径和中文名称 2 基于目录结构的Page Object封装 3 通过传统手段选获取页面文字内容的方法 1 通过鼠标三击获取字符串内容
  • Qt实现多选文件夹对话框

    简述 Qt使用QFileDialog类可以实现文件选择对话框 多文件选择对话框 以及文件夹选择对话框 就是没有多文件夹选择对话框 做的并不是很完美无法直接调用win下的原生对话框 下面将介绍一下Qt实现多选文件夹对话框的两种方式 目录 使用
  • Web项目之网络爬虫

    一 爬虫基础篇 Python网络爬虫 认识爬虫 Python网络爬虫 http和https协议 Python网络爬虫 基于urllib库的get请求页面 Python网络爬虫 requests模块应用1 Python网络爬虫 验证码处理 P
  • 水下图像——不平衡衰减相关文献阅读

    目录 不平衡衰减 相关文献 1 Underwater Single Image Color Restoration Using Haze Lines and a New Quantitative Dataset 1 2 Underwater
  • 电感怎么掌握?读懂本文就够了

    一提到电感 不少做设计的同事就发憷 因为不知道电感要怎么用选 很多时候 就像薛定谔的猫一样 只有打开了盒子 才知道猫是不是死的 只有电感实际在电路中焊上去 用起来了 才知道用的对不对 用的好不好 为什么电感这么难搞 因为电感涉及到电磁场 而
  • Web项目中获取SpringBean——在非Spring组件中获取SpringBean

    自定义一个工具类 实现自ApplicationContextAware接口 接口的方法是setApplicationContext 我们实现它 并让其为我们服务 因为Spring在load自己的时候会将上下文环境填充进来 我们所要做的就是将
  • 微服务体系下如何快速构建一个服务

    近两三年的时间 微服务是热度陡增 作为旧有SOA体系的一下特殊展现 在企业级应用市场上面应用越来越广泛 越来越多的团队 开始采用微服务架构来改造现有的架构体系 不管实施的情况如何 至少已经有成形的案例在线上跑 哪我们这些远未达到微服务架构的
  • 谷粒商城详细笔记

    前言 mysql安装在腾讯云 redis安装在本地虚拟机master上 运行时 renren fast这个项目要到单独开个idea窗口打开 一 项目简介 1 项目微服务架构图 微服务 拒绝大型单体应用 基于业务边界进行服务微化拆分 各个服务
  • 浏览器如何使用断点调试?

    在浏览器中按下F12进入开发者模式 点击Sources gt 选中相应的html文件 gt 在对应的代码行前点击 出现小红点 断点 再次运行 在执行到断点时就会停下 等待按下F9 F12才会向下执行 F11执行下一步 F9 返回上一步 将鼠
  • python3安装Pillow(PIL)

    本方法亲测可用 我的是win7 32位 Python3 4 官网上还没有支持Python3的PIL 使用Pillow代替PIL 首先 下载对应的whl文件 来源http www lfd uci edu gohlke pythonlibs 4
  • n个数分为两组,两组数的个数尽可能相等,差值最小

    题目描述 对于有n个数的数组 分为两组 这两组的数的个数尽可能相等 不超过1 同时两组的数之和的差值最小 这个题目使用类似0 1背包问题 思路 从k个数中选i个数 求所有可能的和 并把这些和放在flag中用true表示 k i flag见代
  • ubuntu忘记root密码怎么办?

    普通用户 无论你是否申请了root帐号 或是普通账号密码忘记了都没有问题的 首先 重启ubuntu 随即长按shift进入grub菜单 其次 选择第二个高级模式recovery mode进入Recovery Menu界面 选择root Dr
  • jsp代码中EL表达式无法显示(已解决)

    jstl最近是不是有问题 还是我的代码有问题 每次遍历都不会出来 都是显示这样 有的说是必须要有jstl jar和 standard jar 但是我也有了 但是也是没有说打刀口上 其实是忽略了一个知识点 EL表达式 在这里加上 isELIg
  • CUnit 单元测试 方法总结

    CUnit是一个用C语言编写 管理和运行单元测试的轻量级系统 它为C程序员提供了基本的测试功能和灵活的各种用户接口 CUnit被构建为一个与用户的测试代码链接的静态库 它使用一个简单的框架来构建测试结构 并为测试常见数据类型提供了一套丰富的
  • Java 多线程 --- 终止线程 Terminate Threads

    Java 多线程 终止线程 Terminate Threads 为什么要终止线程 终止线程的方法 return stop interrupt InterruptedException 为什么要终止线程 线程消耗资源 包括内存 内核 CPU等
  • 非常详细的51单片机引脚介绍

    引用cy pp 的 非常详细的51单片机引脚介绍 T89C2051是精简版的51单片机 精简掉了P0口和P2口 只有20引脚 但其内部集成了一个很实用的模拟比较器 特别适合开发精简的51应用系统 毕竟很多时候我们开发简单的产品时用不了全部3
  • Qt编写控件属性设计器-用户属性

    一 前言 用户属性是后面新增加的一个功能 自定义控件如果采用的Q PROPERTY修饰的属性 会自动识别到属性栏中 这个一般称为控件属性 在组态设计软件中 光有控件本身的控件属性还是不够的 毕竟这些属性仅仅是以外观为主 并不能表示某个设备的