QtnProperty:一个基于 Qt 框架的第三方高级属性库

目录

1.简介

2.核心架构与主要类

2.1.核心类层次

2.2.关键类详解

3.核心功能详解

3.1.属性层次结构管理

3.2.属性状态管理

3.3.序列化与反序列化

3.4.自定义属性编辑器

[3.5.QtnPEG 代码生成工具](#3.5.QtnPEG 代码生成工具)

4.快速集成与使用步骤

4.1.环境准备与编译

4.2.项目集成

4.3.基础使用示例

4.4.多对象编辑示例

5.高级应用场景

[5.1.与 QObject 集成](#5.1.与 QObject 集成)

5.2.属性依赖与联动

5.3.自定义属性类型

6.自定义属性的完整示例

7.总结


1.简介

QtnProperty 是一个基于 Qt 框架的第三方高级属性库 (开源项目,GitHub: qtinuum/QtnProperty),旨在解决标准 Qt 属性系统的局限性,提供更强大、更灵活的属性管理与编辑能力。它特别适合构建需要复杂属性配置界面的 GUI 应用,如编辑器、IDE、配置工具等。

与 Qt 原生属性系统的对比:

特性 Qt 原生属性系统 (Q_PROPERTY) QtnProperty
层次结构 仅支持扁平结构 支持任意深度的属性层次结构 (树状)
编辑界面 需自行实现或使用 QtPropertyBrowser 内置 QtnPropertyWidget/QtnPropertyView,统一编辑体验
变更通知 仅提供值变更后信号 提供变更前变更后双重信号
状态管理 有限支持 完整支持禁用 (disabled)、隐藏 (hidden)、只读等状态
数据类型 基础类型 + 自定义类型 (需注册) 内置丰富类型 (int, float, bool, color, rect 等),支持自定义扩展
多对象编辑 不支持 支持多属性 (QtnMultipleProperty),可同时编辑多个对象的属性
代码生成 提供 QtnPEG 工具,从 QML-like 文件生成 C++ 属性代码
序列化 需自行实现 内置 QDataStream 序列化支持

https://gitee.com/koala999/QtnProperty
https://github.com/qtinuum/QtnProperty/

2.核心架构与主要类

2.1.核心类层次

cpp 复制代码
QtnProperty (基类)
├── QtnPropertyInt, QtnPropertyFloat, QtnPropertyBool, QtnPropertyQColor... (基础类型属性)
├── QtnPropertySet (属性集合,用于组织层次结构)
└── QtnMultipleProperty (多属性,用于同时编辑多个对象)

2.2.关键类详解

1.QtnProperty (属性基类)

  • 核心功能:定义属性的基本行为,包括名称、显示名、描述、状态、值访问等
  • 关键方法
    • setName()/name():设置 / 获取属性名称
    • setDisplayName()/displayName():设置 / 获取显示名称(UI 中显示)
    • setDescription()/description():设置 / 获取属性描述(帮助信息)
    • setValue()/value():设置 / 获取属性值
    • setState()/state():设置 / 获取属性状态(如QtnPropertyStateDisabledQtnPropertyStateHidden
    • connect():连接属性变更信号到槽函数
  • 信号
    • beforeValueChanged():属性值变更前发出
    • valueChanged():属性值变更后发出

2.QtnPropertySet (属性集合)

  • 核心功能:管理一组属性,支持层次化组织(属性可嵌套属性集)
  • 关键特性
    • 采用类似字典的数据结构存储属性,支持快速查找
    • 提供深度优先和广度优先两种遍历策略
    • 支持序列化与反序列化
    • 可与 QtnPropertyWidget 绑定,自动生成 UI 界面

3.QtnPropertyWidget/QtnPropertyView (属性编辑控件)

  • 核心功能:提供可视化界面,用于观察和编辑属性集合
  • 关键特性
    • 自动根据属性类型选择合适的编辑器控件
    • 支持树形结构展示属性层次
    • 提供自定义委托 (delegate) 机制,可定制属性显示和编辑方式
    • 支持多属性编辑,当多个对象属性值不同时显示为 "Multiple properties"

4.QtnMultipleProperty (多属性)

  • 核心功能:将多个相同类型的属性组合成一个虚拟属性,用于同时编辑多个对象
  • 关键特性
    • 当所有对象属性值相同时,显示该值;不同时显示为灰色
    • 设置新值时,自动更新所有关联对象的属性值
    • 可通过qtnCreateQObjectMultiPropertySet快速创建 QObject 的多属性集合

3.核心功能详解

3.1.属性层次结构管理

QtnProperty 支持任意深度的属性嵌套,例如:

cpp 复制代码
// 创建根属性集
auto rootSet = new QtnPropertySet(this);

// 创建子属性集
auto appearanceSet = qtnCreatePropertySet<QtnPropertySet>(rootSet);
appearanceSet->setName("Appearance");
appearanceSet->setDisplayName(tr("Appearance"));

// 向子属性集添加属性
auto textColor = qtnCreateProperty<QtnPropertyQColor>(appearanceSet);
textColor->setName("TextColor");
textColor->setDisplayName(tr("Text Color"));
textColor->setValue(QColor(0, 0, 0));

3.2.属性状态管理

属性可处于以下状态(可组合):

  • QtnPropertyStateNormal:正常状态(默认)
  • QtnPropertyStateDisabled:禁用状态(用户无法编辑)
  • QtnPropertyStateHidden:隐藏状态(不在 UI 中显示)
  • QtnPropertyStateImmutable:不可变状态(无法通过 UI 或代码修改)

示例:

cpp 复制代码
// 当replaceTabsWithSpaces为false时,tabSize属性不可编辑
connect(replaceTabsWithSpaces, &QtnPropertyBool::valueChanged, [=](bool value) {
    tabSize->switchState(QtnPropertyStateImmutable, !value);
});

3.3.序列化与反序列化

QtnProperty 内置支持通过 QDataStream 序列化属性值:

cpp 复制代码
// 序列化
QDataStream out(&file);
out << *propertySet;

// 反序列化
QDataStream in(&file);
in >> *propertySet;

同时支持与 QVariant 和 QString 的转换:

cpp 复制代码
QVariant var = property->toVariant();
property->fromVariant(var);

QString str = property->toString();
property->fromString(str);

3.4.自定义属性编辑器

通过委托 (delegate) 机制定制属性显示和编辑方式:

  1. 继承QtnPropertyDelegate
  2. 重写createEditor()方法创建自定义编辑器控件
  3. 重写propertyValueToStr()方法自定义值的显示文本

示例:

cpp 复制代码
class MyCustomDelegate : public QtnPropertyDelegate
{
public:
    QWidget* createEditor(QWidget* parent, const QtnPropertyDelegateInfo& info) override
    {
        auto editor = new MyCustomEditor(parent);
        // 绑定属性值与编辑器
        connect(editor, &MyCustomEditor::valueChanged, [=](const QVariant& value) {
            info.property()->fromVariant(value);
        });
        return editor;
    }

    QString propertyValueToStr(const QtnProperty* property) const override
    {
        return property->toVariant().toString();
    }
};

// 注册委托
QtnPropertyDelegateFactory::staticInstance()->registerDelegateDefault(
    &QtnPropertyMyCustom::staticMetaObject,
    new MyCustomDelegate());

3.5.QtnPEG 代码生成工具

QtnPEG 是一个可选工具,类似 Qt 的 moc,可从 QML-like 的.pef文件生成 C++ 属性代码。

示例.pef文件:

cpp 复制代码
#include "QtnProperty/PropertyCore.h"

property_set TextEditor
{
    Bool enableWrapping
    {
        description = "Enable/disable text wrapping";
        value = true;
    }

    Bool replaceTabsWithSpaces
    {
        description = "Automatically replace tabs with spaces";
        value = false;
        slot propertyDidChange
        {
            tabSize.switchState(QtnPropertyStateImmutable, !replaceTabsWithSpaces);
        }
    }

    UInt tabSize
    {
        description = "Number of spaces to be placed.";
        state = QtnPropertyStateImmutable;
        value = 4;
    }
}

生成的代码可直接在项目中使用:

cpp 复制代码
QtnPropertySetTextEditor params;
params.enableWrapping = false;
if (params.replaceTabsWithSpaces)
    document.replaceTabsWithSpaces(params.tabSize);

4.快速集成与使用步骤

4.1.环境准备与编译

1.依赖:Qt 5.9+,可选 Flex 和 Bison(用于编译 QtnPEG)

2.编译命令

cpp 复制代码
mkdir build && cd build
qmake path/to/QtnProperty/QtnProperty.pro -r
make

3.动态库编译(可选):

cpp 复制代码
qmake path/to/QtnProperty/QtnProperty.pro -r CONFIG+=qtnproperty_dynamic

4.2.项目集成

.pro文件中添加:

cpp 复制代码
include(path/to/QtnProperty/QtnPropertyDepend.pri)

4.3.基础使用示例

cpp 复制代码
#include <QApplication>
#include <QtnProperty/PropertyWidget.h>
#include <QtnProperty/PropertySet.h>
#include <QtnProperty/PropertyFloat.h>
#include <QtnProperty/PropertyQColor.h>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    // 1. 创建属性集
    auto propertySet = new QtnPropertySet();

    // 2. 添加浮点属性
    auto floatProp = qtnCreateProperty<QtnPropertyFloat>(propertySet);
    floatProp->setName("value");
    floatProp->setDisplayName(tr("Value"));
    floatProp->setDescription(tr("Float value"));
    floatProp->setMaxValue(1.0f);
    floatProp->setMinValue(0.0f);
    floatProp->setStepValue(0.1f);
    floatProp->setValue(0.3f);

    // 3. 添加颜色属性
    auto colorProp = qtnCreateProperty<QtnPropertyQColor>(propertySet);
    colorProp->setName("textColor");
    colorProp->setDisplayName(tr("Text Color"));
    colorProp->setDescription(tr("Foreground text color"));
    colorProp->setValue(QColor(0, 0, 0));

    // 4. 创建属性编辑窗口
    QtnPropertyWidget widget;
    widget.setPropertySet(propertySet);
    widget.show();

    return app.exec();
}

4.4.多对象编辑示例

cpp 复制代码
// 创建多个对象
auto obj1 = new MyObject();
auto obj2 = new MyObject();
auto obj3 = new MyObject();

// 创建多属性集
auto multiSet = qtnCreateQObjectMultiPropertySet({obj1, obj2, obj3});

// 显示多属性编辑界面
QtnPropertyWidget widget;
widget.setPropertySet(multiSet);
widget.show();

5.高级应用场景

5.1.与 QObject 集成

QtnProperty 可与 QObject 无缝集成,通过QtnPropertyQObject类包装 QObject 的属性:

cpp 复制代码
#include <QtnProperty/QObjectPropertySet.h>

// 包装QObject的属性
auto qobjectSet = qtnCreateQObjectPropertySet(qobject);

// 显示QObject的属性编辑界面
QtnPropertyWidget widget;
widget.setPropertySet(qobjectSet);
widget.show();

5.2.属性依赖与联动

通过信号与槽机制实现属性间的联动:

cpp 复制代码
// 当enableLogging为true时,logLevel属性可用
connect(enableLogging, &QtnPropertyBool::valueChanged, [=](bool enabled) {
    logLevel->setState(enabled ? QtnPropertyStateNormal : QtnPropertyStateDisabled);
});

// 当logLevel变化时,更新日志系统配置
connect(logLevel, &QtnPropertyInt::valueChanged, [=](int level) {
    LogSystem::instance()->setLogLevel((LogLevel)level);
});

5.3.自定义属性类型

创建自定义属性类型的步骤:

  1. 继承QtnProperty类,实现特定类型的值存储和访问
  2. 实现toVariant()/fromVariant()toString()/fromString()方法
  3. 创建对应的属性委托 (delegate),实现自定义编辑器
  4. 注册属性类型和委托

示例:

cpp 复制代码
// 自定义Point属性
class QtnPropertyPoint : public QtnProperty
{
    Q_OBJECT
    Q_PROPERTY(QPoint value READ value WRITE setValue NOTIFY valueChanged)
public:
    QPoint value() const { return m_value; }
    void setValue(const QPoint& value) {
        if (m_value != value) {
            emit beforeValueChanged();
            m_value = value;
            emit valueChanged();
        }
    }

    QVariant toVariant() const override { return QVariant::fromValue(m_value); }
    bool fromVariant(const QVariant& var) override {
        if (var.canConvert<QPoint>()) {
            setValue(var.value<QPoint>());
            return true;
        }
        return false;
    }

private:
    QPoint m_value;
};

6.自定义属性的完整示例

项目结构:

cpp 复制代码
QtnPropertyDemo/
├── QtnPropertyDemo.pro   # 项目配置文件
├── main.cpp              # 主程序入口
├── CustomIPProperty.h    # 自定义IP属性
├── CustomIPProperty.cpp
├── IPDelegate.h          # 自定义IP编辑器
└── IPDelegate.cpp

项目配置文件:QtnPropertyDemo.pro

修改路径为你本地的 QtnProperty 路径即可

cpp 复制代码
QT       += core gui widgets

TARGET = QtnPropertyDemo
TEMPLATE = app

# ===================== 配置QtnProperty路径 =====================
# 替换为你本地的QtnProperty源码路径
QTN_PROPERTY_PATH = D:/QtProjects/QtnProperty

# 头文件路径
INCLUDEPATH += $$QTN_PROPERTY_PATH \
               $$QTN_PROPERTY_PATH/PropertyCore \
               $$QTN_PROPERTY_PATH/PropertyWidget

# 库文件路径(根据你的编译版本修改:debug/release)
LIBS += -L$$QTN_PROPERTY_PATH/build/Desktop_Qt_5_15_2_MinGW_64_bit-Debug/lib \
        -lQtnPropertyCore \
        -lQtnPropertyWidget

# 源文件
SOURCES += \
    main.cpp \
    CustomIPProperty.cpp \
    IPDelegate.cpp

HEADERS += \
    CustomIPProperty.h \
    IPDelegate.h

主程序:main.cpp

包含基础属性、嵌套属性、属性联动、自定义属性

cpp 复制代码
#include <QApplication>
#include <QtnPropertyWidget.h>
#include <QtnPropertySet.h>
#include <QtnPropertyInt.h>
#include <QtnPropertyFloat.h>
#include <QtnPropertyBool.h>
#include <QtnPropertyQColor.h>
#include <QtnPropertyQString.h>
#include "CustomIPProperty.h"
#include "IPDelegate.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    // ============== 1. 创建根属性集 ==============
    QtnPropertySet* rootSet = new QtnPropertySet();
    rootSet->setDisplayName("项目配置");

    // ============== 2. 基础属性 ==============
    // 整数属性
    QtnPropertyInt* port = qtnCreateProperty<QtnPropertyInt>(rootSet);
    port->setName("Port");
    port->setDisplayName("端口号");
    port->setDescription("服务监听端口");
    port->setMinValue(1);
    port->setMaxValue(65535);
    port->setValue(8080);

    // 浮点数属性
    QtnPropertyFloat* speed = qtnCreateProperty<QtnPropertyFloat>(rootSet);
    speed->setName("Speed");
    speed->setDisplayName("运行速度");
    speed->setMinValue(0.1);
    speed->setMaxValue(10.0);
    speed->setStepValue(0.1);
    speed->setValue(1.0);

    // 布尔属性
    QtnPropertyBool* enable = qtnCreateProperty<QtnPropertyBool>(rootSet);
    enable->setName("EnableService");
    enable->setDisplayName("启用服务");
    enable->setValue(true);

    // 颜色属性
    QtnPropertyQColor* color = qtnCreateProperty<QtnPropertyQColor>(rootSet);
    color->setName("ThemeColor");
    color->setDisplayName("主题颜色");
    color->setValue(QColor(30, 144, 255));

    // ============== 3. 嵌套属性集(树形结构) ==============
    QtnPropertySet* networkSet = qtnCreatePropertySet<QtnPropertySet>(rootSet);
    networkSet->setName("Network");
    networkSet->setDisplayName("网络配置");

    // 自定义IP属性(放入嵌套集)
    CustomIPProperty* ip = qtnCreateProperty<CustomIPProperty>(networkSet);
    ip->setName("ServerIP");
    ip->setDisplayName("服务器IP");
    ip->setValue("192.168.1.1");

    // ============== 4. 属性联动(核心功能) ==============
    // 禁用服务时,端口号自动变灰不可编辑
    QObject::connect(enable, &QtnPropertyBool::valueChanged, [=](bool isEnable){
        port->switchState(QtnPropertyStateImmutable, !isEnable);
    });

    // ============== 5. 注册自定义编辑器 ==============
    QtnPropertyDelegateFactory::staticInstance()->registerDelegateDefault(
        &CustomIPProperty::staticMetaObject,
        new IPDelegate()
    );

    // ============== 6. 创建属性窗口 ==============
    QtnPropertyWidget w;
    w.setPropertySet(rootSet);
    w.setWindowTitle("QtnProperty 完整演示");
    w.resize(500, 700);
    w.show();

    return a.exec();
}

自定义 IP 属性:CustomIPProperty.h

cpp 复制代码
#ifndef CUSTOMIPPROPERTY_H
#define CUSTOMIPPROPERTY_H

#include <QtnProperty.h>

// 自定义IP地址属性
class CustomIPProperty : public QtnProperty
{
    Q_OBJECT
public:
    explicit CustomIPProperty(QObject *parent = nullptr);

    // 核心值操作
    QString value() const;
    void setValue(const QString& value);

    // 序列化接口
    QVariant toVariant() const override;
    bool fromVariant(const QVariant &var) override;
    QString toString() const override;
    bool fromString(const QString &str) override;

protected:
    QString m_value;
};

#endif // CUSTOMIPPROPERTY_H

CustomIPProperty.cpp

cpp 复制代码
#include "CustomIPProperty.h"
#include <QRegularExpression>

CustomIPProperty::CustomIPProperty(QObject *parent)
    : QtnProperty(parent),
      m_value("127.0.0.1")
{
}

QString CustomIPProperty::value() const
{
    return m_value;
}

void CustomIPProperty::setValue(const QString &value)
{
    // IP地址格式验证
    static QRegularExpression ipRegex(
        "^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$");

    if (m_value != value && ipRegex.match(value).hasMatch())
    {
        emit beforeValueChanged();
        m_value = value;
        emit valueChanged();
    }
}

QVariant CustomIPProperty::toVariant() const
{
    return m_value;
}

bool CustomIPProperty::fromVariant(const QVariant &var)
{
    setValue(var.toString());
    return true;
}

QString CustomIPProperty::toString() const
{
    return m_value;
}

bool CustomIPProperty::fromString(const QString &str)
{
    setValue(str);
    return true;
}

自定义 IP 编辑器:IPDelegate.h

cpp 复制代码
#ifndef IPDELEGATE_H
#define IPDELEGATE_H

#include <QtnPropertyDelegate.h>

// IP属性自定义编辑器
class IPDelegate : public QtnPropertyDelegate
{
public:
    explicit IPDelegate();

    // 创建编辑器控件
    QWidget *createEditor(QWidget *parent, const QtnPropertyDelegateInfo &info) override;
    // 设置编辑器值
    void setEditorValue(QWidget *editor, const QVariant &value) override;
    // 获取编辑器值
    QVariant editorValue(QWidget *editor) const override;
};

#endif // IPDELEGATE_H

IPDelegate.cpp

cpp 复制代码
#include "IPDelegate.h"
#include <QLineEdit>
#include <QRegularExpressionValidator>

IPDelegate::IPDelegate()
{
}

QWidget *IPDelegate::createEditor(QWidget *parent, const QtnPropertyDelegateInfo &)
{
    QLineEdit *editor = new QLineEdit(parent);
    // IP输入格式限制
    QRegularExpression ipRegex("^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$");
    editor->setValidator(new QRegularExpressionValidator(ipRegex, editor));
    return editor;
}

void IPDelegate::setEditorValue(QWidget *editor, const QVariant &value)
{
    QLineEdit *edit = qobject_cast<QLineEdit*>(editor);
    if (edit) edit->setText(value.toString());
}

QVariant IPDelegate::editorValue(QWidget *editor) const
{
    QLineEdit *edit = qobject_cast<QLineEdit*>(editor);
    return edit ? edit->text() : "";
}

编译运行步骤:

  • 打开 Qt Creator
  • 新建Empty Qt Widgets Project,替换所有文件
  • 修改pro文件中的QTN_PROPERTY_PATH你本地路径
  • 直接点击运行

7.总结

QtnProperty 通过提供层次化属性管理统一编辑界面完整状态控制多对象编辑等核心特性,显著提升了 Qt 应用中属性管理的灵活性和开发效率。它不仅适用于简单的配置界面,也能满足复杂的多对象编辑、动态属性生成等高级需求,是 Qt 开发中构建属性编辑器的理想选择。

如果你需要构建类似 Qt Designer 的属性编辑界面、复杂的应用配置面板或支持多对象批量编辑的工具,QtnProperty 将是一个非常有价值的工具库。

相关推荐
yujunl7 小时前
U9的OpenAPI接口的应用
开发语言
承渊政道7 小时前
【动态规划算法】(子数组系列问题建模与解题思路精讲)
数据结构·c++·学习·算法·leetcode·动态规划·哈希算法
沐知全栈开发7 小时前
JSP 表单处理
开发语言
AI进化营-智能译站7 小时前
ROS2 C++开发系列04:如何有效输出机器人状态
开发语言·c++·ai·机器人
AI进化营-智能译站7 小时前
ROS2 C++开发系列05:机器人启动如何传递命令行参数实战
开发语言·c++·ai·机器人
春蕾夏荷_7282977257 小时前
1、c++ acl udp服务器客户端简单实例-客户器端(2)
服务器·c++·udp
落羽的落羽7 小时前
【网络】计算机网络世界的基础概念
linux·服务器·网络·c++·人工智能·计算机网络·机器学习
AC赳赳老秦7 小时前
团队知识库搭建:用 OpenClaw 自动整理会议纪要、技术方案、故障复盘,同步到 Confluence / 语雀
开发语言·前端·python·github·visual studio·deepseek·openclaw
handler017 小时前
算法:图的基本概念
c语言·开发语言·c++·笔记·算法·图论