目录
[3.5.QtnPEG 代码生成工具](#3.5.QtnPEG 代码生成工具)
[5.1.与 QObject 集成](#5.1.与 QObject 集成)
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():设置 / 获取属性状态(如QtnPropertyStateDisabled、QtnPropertyStateHidden)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) 机制定制属性显示和编辑方式:
- 继承
QtnPropertyDelegate类 - 重写
createEditor()方法创建自定义编辑器控件 - 重写
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.自定义属性类型
创建自定义属性类型的步骤:
- 继承
QtnProperty类,实现特定类型的值存储和访问 - 实现
toVariant()/fromVariant()和toString()/fromString()方法 - 创建对应的属性委托 (delegate),实现自定义编辑器
- 注册属性类型和委托
示例:
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 将是一个非常有价值的工具库。