qt 实现C# propertygrid

qt 实现C# propertygrid

qt 中的 QtTreePropertyBrowser

很多使用C# 的小伙伴都知道propertygrid控件,这个控件可以直接将我们自定义的属性和页面关联起来,在一些需要在UI修改属性值时的需求里使用起来非常方便。但是在Qt常用的控件中,却找不到类似的控件。实际上在qt源码中存在这个控件,但并未应用到常用控件中。

导入项目

路径:D:\qt6.2.4\6.2.4\Src\qttools\src\shared\qtpropertybrowser

该控件qt的安装路径下,使用时只需要将整个文件夹复制在你的项目路径下,并在该文件夹中添加 .pri文件
PropertyDemo为我的项目
然后.pri文件中需要将这些 .h,.cpp文件加载

HEADERS += \
    $$PWD/qtbuttonpropertybrowser.h \
    $$PWD/qteditorfactory.h \
    $$PWD/qtgroupboxpropertybrowser.h \
    $$PWD/qtpropertybrowser.h \
    $$PWD/qtpropertybrowserutils_p.h \
    $$PWD/qtpropertymanager.h \
    $$PWD/qttreepropertybrowser.h \
    $$PWD/qtvariantproperty.h


SOURCES += \
    $$PWD/qtbuttonpropertybrowser.cpp\
    $$PWD/qteditorfactory.cpp\
    $$PWD/qtgroupboxpropertybrowser.cpp\
    $$PWD/qtpropertybrowser.cpp\
    $$PWD/qtpropertybrowserutils.cpp\
    $$PWD/qtpropertymanager.cpp\
    $$PWD/qttreepropertybrowser.cpp\
    $$PWD/qtvariantproperty.cpp

之后只需要在项目的.pro文件中添加 这个.pri,等待一会,就会将这些源码加载在你的项目中

// .pro文件增加这一句
include(qtpropertybrowser/qtpropertybrowser.pri)
使用

将该控件加入在你的项目之后,只需要在你的UI中新建一个widget,然后将这个widget提升为 QtTreePropertyBrowser 即可。
在这里插入图片描述

控件提升

控件创建好之后,我们则需要开始在这个表格中添加我们需要可视化的属性,但是在添加属性之前,我们还需要知道两种类,一种是管理者类,一个是工厂类。

管理者类

控件的管理者类,只有通过这个类才可以在空间中添加我们需要的属性

// 注意 Variant ,这个类对应的则是我们常用的类型,int,bool,doublt,flot等等
class QtVariantPropertyManager : public QtAbstractPropertyManager

// 对应的是枚举类,也就是C# 中对应的 combox 的使用 
class QtEnumPropertyManager : public QtAbstractPropertyManager

// 可以看到以上两个类都继承QtAbstractPropertyManager,还有其他的manager类,则各位自己查阅源码
工厂类

管理者类,可以给控件添加属性,而工厂类需要和管理者类绑定,才可以让可视化属性可以修改,如果不绑定工厂类,则无法修改。
因此如果我们有些属性不需要修改,有些属性需要修改,则可以创建两个管理类,一个绑定工厂类,一个不绑定工厂类。

// 针对常用类型管理者类的工厂
class QtVariantEditorFactory : public QtAbstractEditorFactory<QtVariantPropertyManager>
// 针对枚举类型管理者对应的工厂
class QtEnumEditorFactory : public QtAbstractEditorFactory<QtEnumPropertyManager>

// 同理,这两个类都是继承 QtAbstractEditorFactory
初始化

在明确需要上述类之后,则可以在初始化中定义

private:
    Ui::MainWindow *ui;

    // 表格的管理对象
    // 在表格中添加信息都需要通过这个普通类型管理对象
    QtVariantPropertyManager* m_pVarManager;
    // 枚举类型管理对象
    QtEnumPropertyManager *enumManager;

    // 和普通管理对象绑定的工厂,只有绑定了对象才可以在UI界面的表格修改值,否则无法修改
    QtVariantEditorFactory* m_pVarFactory;

    // 枚举管理对象关联的工厂
    QtEnumEditorFactory* enumFactory;

    // 用于将表格中的子节点和自定义参数的属性名一一对应,方便根据属性名修改参数对象中的值,后续会说到
    QMap<QtProperty*,QString> paramMap = QMap<QtProperty*,QString>();

    // 自定义属性对象,用于验证功能
    Students* stu;

init()

// 创建表格的管理对象
    m_pVarManager = new QtVariantPropertyManager(ui->widget);
    // 创建和管理对象的绑定工厂
    m_pVarFactory = new QtVariantEditorFactory(ui->widget);

    enumManager = new QtEnumPropertyManager(ui->widget);
    enumFactory = new QtEnumEditorFactory(ui->widget);

    stu = new Students();

    // 设置表格可以拉伸
    // QtTreePropertyBrowser::Interactive            //交互
    // QtTreePropertyBrowser::Fixed                  //固定
    // QtTreePropertyBrowser::ResizeToContents       //自动调整内容
    // QtTreePropertyBrowser::Stretch                //拉伸
    ui->widget->setResizeMode(QtTreePropertyBrowser::Interactive);

我的自定义结构体 Students 如下

#ifndef STUDENTS_H
#define STUDENTS_H
#include <QObject>


enum Hobby
{
    footBall,
    basketBall,
    pingpang
};



class Students : public QObject
{
    Q_OBJECT

	// 后续添加到 paramMap 中的 value 字符串必须和 age,name 这些类型一样,方便后续序列化修改对象的值
    Q_PROPERTY(int age READ getAge WRITE setAge)
    Q_PROPERTY(QString name READ getName WRITE setName)
    Q_PROPERTY(bool isLeagueMember READ getLeagueMember WRITE setLeagueMember)
    Q_PROPERTY(Hobby hobby  READ getHobby WRITE setHobby )


public:
    explicit Students(QObject* parent = nullptr);


    const QString &getName() const;
    void setName(const QString &newName);

    bool getLeagueMember() const;
    void setLeagueMember(bool newIsLeagueMember);

    Hobby getHobby() const;
    void setHobby(Hobby newHobby);

    void setAge(int newAge);
    int getAge() const;

private:
    int m_age = 10;
    QString m_name = "十二号少年";
    bool m_isLeagueMember = true;
    Hobby m_hobby = Hobby::footBall;
};

#endif // STUDENTS_H

添加表格属性
// 新建组1
    QtProperty *groupItem1 = m_pVarManager->addProperty(QtVariantPropertyManager::groupTypeId(),"基本信息");

    // 新建字段 参数1:当前字段类型   参数2:当前字段显示名称
    QtVariantProperty *nameItem = m_pVarManager->addProperty(QVariant::String,"姓名");
    // map 用于存储 字段对象和自定义结构体中的属性参数名,方便后续修改UI页面的值之后修改 对象中的值
    // 因此map中的 value 必须和 属性名相同
    paramMap.insert(nameItem,"name");
    // 字段赋值,也可以赋值 stu对象中的默认值
    nameItem->setValue("十二号少年");

    QtVariantProperty *ageItem = m_pVarManager->addProperty(QVariant::Int,"年龄");
    paramMap.insert(ageItem,"age");
    ageItem->setValue(18);

    // 将字段添加进组中
    groupItem1->addSubProperty(nameItem);
    groupItem1->addSubProperty(ageItem);


    // 创建组2
    QtVariantProperty *groupItem2 = m_pVarManager->addProperty(QtVariantPropertyManager::groupTypeId(),"详细信息");

    // 创建字段
    QtVariantProperty *boolItem = m_pVarManager->addProperty(QVariant::Bool,"是否共青团员");
    boolItem->setValue(true);
    paramMap.insert(boolItem,"isLeagueMember");

    // 添加枚举类型,需要从枚举类型的管理者中创建
    QtProperty *hobbyItem = enumManager->addProperty("爱好");
    // 增加选择类型
    QStringList hobbyslist;
    hobbyslist << "足球" << "篮球" << "乒乓球";
    // 枚举类型加入管理者,字段和结果对应
    enumManager->setEnumNames(hobbyItem,hobbyslist);
    // 选择最开始选择位置
    enumManager->setValue(hobbyItem,0);
    paramMap.insert(hobbyItem,"hobby");

    // 将字段添加进组中
    groupItem2->addSubProperty(boolItem);
    groupItem2->addSubProperty(hobbyItem);


    // 将组添加进表格
    ui->widget->addProperty(groupItem1);
    ui->widget->addProperty(groupItem2);
    // 将管理者和工厂绑定
    ui->widget->setFactoryForManager(m_pVarManager,m_pVarFactory);
    ui->widget->setFactoryForManager(enumManager,enumFactory);


    // 关联对象,UI修改属性的关键,后续会说明
    connect(m_pVarManager,&QtVariantPropertyManager::valueChanged,this,&MainWindow::variantChangeSlot);
    connect(enumManager,&QtEnumPropertyManager::valueChanged,this,&MainWindow::variantChangeSlot);

创建了好之后,我们就成功了一半,效果如下:
在这里插入图片描述

数据同步

如上图,我们建立的表格和students类中的属性都是一一对应的,那接下来则需要实现UI中修改对应的值,在底层中的Students对象stu中的属性也需要修改成对应的值。

这里则需要用到之前说到的管理者类中的信号

// QtVariantPropertyManager 类中 signals
// QtProperty* property  反馈当前修改参数的对象,即 我们之前存放在paramMap中的key
// QVariant &val  当前修改参数的值,正是我们需要的值
virtual void setValue(QtProperty *property, const QVariant &val);

// QtEnumPropertyManager 中的信号
void valueChanged(QtProperty *property, int val);

有了这个自带的信号,我们则只需要创建槽函数来关联,并在槽函数中修改我们需要的值。

// 相应页面参数修改的槽函数,用于修改对应的参数
void MainWindow::variantChangeSlot(QtProperty *property, const QVariant &val)
{
    qDebug() << " age:" << stu->getAge() << " name:" << stu->getName()
             << " 团员:" << stu->getLeagueMember() << " 爱好:"
             << stu->getHobby();

    // map中获取到对应的属性值
    QString paramName = paramMap[property];
    // 通过序列化直接修改对应的结果
    stu->setProperty(paramName.toStdString().c_str(),val);

    qDebug() << " age:" << stu->getAge() << " name:" << stu->getName() << " 团员:" << stu->getLeagueMember() << " 爱好:" << stu->getHobby();
}

运行结果如下:
在这里插入图片描述

至此,我们需要的功能均已实现。

你知道的越多,你不知道的越多,我们下期见。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值