目录

目录

QtTreePropertyBrowser的拓展,包含反射

2024-10-04 12:10:04

前言

本人使用qt5.15.2,让QtTreePropertyBrowser可以识别Q_PROPERTY宏引用的自定义类型,效果如下

GIF Image

主要是自定义了一个可以自己选择图片的控件

自定义类注册

#ifndef PIXMAPNODE_H_
#define PIXMAPNODE_H_
#include<qmetatype.h>
struct PixmapNode
{
	QString _name;
};
Q_DECLARE_METATYPE(PixmapNode)
#endif // !PIXMAPNODE_H_

在QVariant::Type枚举中自定义类通过QVariant::type返回的值全部是QVariant::UserType(1024),

自定义类的枚举值可以通过qMetaTypeId<T>()和QVariant::userType()获取,比较两个值可以判断是哪一个自定义类。

自定义PropertyManager(直接写在文件源码)

这里定义一个PixmapPropertyManager,然后复制前面内置的类,略微改一下

关于几个函数的说明:

/*只在QtVariantProperty::value中调用过,如果创建的QtVariantProperty
 *不使用value函数可以不写这个
 */
value(const QtProperty*);
/*在对应的自定义工厂slotSetValue槽函数中调用,slotSetValue会与自定义窗口的信号绑定
 */
setValue(QtProperty* property, PixmapNode& val);
/*自己的setValue被调用时触发,并触发工厂类的slotPropertyChanged(会更新自定义窗口相应值)
 */
valueChanged(QtProperty* property, const PixmapNode& val);
/*QtTreePropertyBrowser相应控件显示的值
 */
valueText(const QtProperty* property);                

后面几行复制就行了

//qtpropertymanager.h
class PixmapPropertyManagerPrivate;
 
class PixmapPropertyManager : public QtAbstractPropertyManager
{
    Q_OBJECT
public:
    PixmapPropertyManager(QObject* parent = 0);
    ~PixmapPropertyManager();
    PixmapNode value(const QtProperty* property) const;
 
public Q_SLOTS:
    void setValue(QtProperty* property, PixmapNode& val);
Q_SIGNALS:
    void valueChanged(QtProperty* property, const PixmapNode& val);
protected:
    QString valueText(const QtProperty* property) const;
    virtual void initializeProperty(QtProperty* property)override;
    virtual void uninitializeProperty(QtProperty* property)override;
private:
    QScopedPointer<PixmapPropertyManagerPrivate> d_ptr;
    Q_DECLARE_PRIVATE(PixmapPropertyManager)
    Q_DISABLE_COPY_MOVE(PixmapPropertyManager)
};
//qtpropertymanager.cpp
 
class PixmapPropertyManagerPrivate
{
    PixmapPropertyManager* q_ptr;
    Q_DECLARE_PUBLIC(PixmapPropertyManager)
public:
    struct Data
    {
        Data(){}
        PixmapNode val;
    };
    typedef QMap<const QtProperty*, Data> PropertyValueMap;
    QMap<const QtProperty*, Data> m_values;
};
PixmapPropertyManager::PixmapPropertyManager(QObject* parent)
    : QtAbstractPropertyManager(parent), d_ptr(new PixmapPropertyManagerPrivate)
{
    d_ptr->q_ptr = this;
}
PixmapPropertyManager::~PixmapPropertyManager()
{
    clear();
}
PixmapNode PixmapPropertyManager::value(const QtProperty* property) const
{
    return getValue<PixmapNode>(d_ptr->m_values, property);
}
QString PixmapPropertyManager::valueText(const QtProperty* property) const
{
    const PixmapPropertyManagerPrivate::PropertyValueMap::const_iterator it 
        = d_ptr->m_values.constFind(property);
    if (it == d_ptr->m_values.constEnd())
        return QString();
    return it.value().val._name+".png";
}
void PixmapPropertyManager::setValue(QtProperty* property, PixmapNode& val)
{
    const PixmapPropertyManagerPrivate::PropertyValueMap::iterator it 
        = d_ptr->m_values.find(property);
    if (it == d_ptr->m_values.end())
        return;
    PixmapPropertyManagerPrivate::Data data = it.value();
    if (data.val._name == val._name)
        return;
    data.val._name = val._name;
    it.value() = data;
    emit propertyChanged(property);
    emit valueChanged(property, data.val);
}
void PixmapPropertyManager::initializeProperty(QtProperty* property)
{
    d_ptr->m_values[property] = PixmapPropertyManagerPrivate::Data();
}
void PixmapPropertyManager::uninitializeProperty(QtProperty* property)
{
    d_ptr->m_values.remove(property);
}

自定义窗口

//xxx.h
#ifndef PIXMAPSELECTEDWIDGET_H_
#define PIXMAPSELECTEDWIDGET_H_
#include<qwidget.h>
#include<qlabel.h>
#include<qpushbutton.h>
class PixmapSelectedWidget :public QWidget
{
	Q_OBJECT
public:
	PixmapSelectedWidget(QWidget* parent = nullptr);
	void setValue(const QString&);
public slots:
	void slot_PixmapChanged();
signals:
	void valueChanged(const QString&);
private:
	QLabel* m_label;
	QPushButton* m_button;
};
#endif // !PIXMAPSELECTEDWIDGET_H_
//xxx.cpp
#include"PixmapSelectedWidget.h"
#include<QHBoxLayout>
#include<qfiledialog.h>
PixmapSelectedWidget::PixmapSelectedWidget(QWidget* parent)
    :QWidget(parent),m_label(0),m_button(0)
{
	QHBoxLayout* layout = new QHBoxLayout(this);
	layout->setMargin(0);
	m_label = new QLabel(".png", this);
	m_button = new QPushButton("select", this);
	m_button->setFixedWidth(60);
	layout->addWidget(m_label);
	layout->addWidget(m_button);
	this->setLayout(layout);
	connect(m_button, 
            &QPushButton::clicked, 
            this, 
            &PixmapSelectedWidget::slot_PixmapChanged);
}
void PixmapSelectedWidget::setValue(const QString&name)
{
	m_label->setText(name + ".png");
}
void PixmapSelectedWidget::slot_PixmapChanged()
{
	QString path = QFileDialog::getOpenFileName(
        this, "selected picture", 
        QDir::currentPath(), 
        QString("Image File(*.png)"
    ));
	if (path.isEmpty())return;
	int last_splitter = path.lastIndexOf('/');
	QString name = path.mid(last_splitter + 1, path.length() - last_splitter - 5);
	this->setValue(name);
	emit valueChanged(name);
}

一个QLabel加一个按钮的控件

自定义EditorFactory(写在文件源码)

该类的特殊设计,槽函数都写在私有类里,通过Q_PRIVATE_SLOT归为公有类

这里也是对照内置类复制,略改一下就行

//qeditorfactory.h
class PixmapEditorFactoryPrivate;
class PixmapEditorFactory :public QtAbstractEditorFactory<PixmapPropertyManager>
{
    Q_OBJECT
public:
    PixmapEditorFactory(QObject* parent = nullptr);
    ~PixmapEditorFactory();
protected:
    virtual void connectPropertyManager(PixmapPropertyManager* manager)override;
    virtual QWidget* createEditor(PixmapPropertyManager* manager, 
                                  QtProperty* property, 
                                  QWidget* parent)override;
    virtual void disconnectPropertyManager(PixmapPropertyManager* manager)override;
private:
    PixmapNode m_receiver;
    QScopedPointer<PixmapEditorFactoryPrivate> d_ptr;
    Q_DECLARE_PRIVATE(PixmapEditorFactory)
    Q_DISABLE_COPY_MOVE(PixmapEditorFactory)
    Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty* property, 
                                                      const PixmapNode& value))
    Q_PRIVATE_SLOT(d_func(), void slotSetValue(const QString& value))
    Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject*))
};
//qeditorfactory.cpp
#include"../PixmapSelectedWidget.h"
class PixmapEditorFactoryPrivate :public EditorFactoryPrivate<PixmapSelectedWidget>
{
    PixmapEditorFactory* q_ptr;
    Q_DECLARE_PUBLIC(PixmapEditorFactory)
public:
    void slotPropertyChanged(QtProperty* property, const PixmapNode& value);
    void slotSetValue(const QString& value);
};
void PixmapEditorFactoryPrivate::slotPropertyChanged(QtProperty* property, const PixmapNode& value)
{
    const auto it = m_createdEditors.constFind(property);
    if (it == m_createdEditors.constEnd())
        return;
    for (PixmapSelectedWidget* editor : it.value()) {
        editor->setValue(value._name);
    }
} 
void PixmapEditorFactoryPrivate::slotSetValue(const QString& value)
{
    q_ptr->m_receiver._name = value;
    QObject* object = q_ptr->sender();
    const QMap<PixmapSelectedWidget*, QtProperty*>::ConstIterator ecend = m_editorToProperty.constEnd();
    for (QMap<PixmapSelectedWidget*, QtProperty*>::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor)
        if (itEditor.key() == object) {
            QtProperty* property = itEditor.value();
            PixmapPropertyManager* manager = q_ptr->propertyManager(property);
            if (!manager)
                return;
            manager->setValue(property, q_ptr->m_receiver);
            return;
        }
}
//###############
PixmapEditorFactory::PixmapEditorFactory(QObject* parent)
    : QtAbstractEditorFactory<PixmapPropertyManager>(parent), d_ptr(new PixmapEditorFactoryPrivate())
{
    d_ptr->q_ptr = this;
}
PixmapEditorFactory::~PixmapEditorFactory()
{
    qDeleteAll(d_ptr->m_editorToProperty.keys());
}
void PixmapEditorFactory::connectPropertyManager(PixmapPropertyManager* manager)
{
    connect(manager, 
            SIGNAL(valueChanged(QtProperty*, PixmapNode)),
        	this, 
            SLOT(slotPropertyChanged(QtProperty*, PixmapNode)));
}
QWidget* PixmapEditorFactory::createEditor(PixmapPropertyManager* manager,
    QtProperty* property, QWidget* parent)
{
    PixmapSelectedWidget* editor = d_ptr->createEditor(property, parent);
    editor->setValue(manager->value(property)._name);
    connect(editor, SIGNAL(valueChanged(QString)),
        this, SLOT(slotSetValue(QString)));
    connect(editor, SIGNAL(destroyed(QObject*)),
        this, SLOT(slotEditorDestroyed(QObject*)));
    return editor;
}
void PixmapEditorFactory::disconnectPropertyManager(PixmapPropertyManager* manager)
{
    disconnect(manager, 
               SIGNAL(valueChanged(QtProperty*, PixmapNode)),
        	   this, 
               SLOT(slotPropertyChanged(QtProperty*, PixmapNode)));
}

载入QVariantPropertyManager

公有类中声明设置值槽函数

在qtvariantproperty.h的QtVariantPropertyManager中写如下UserDefine和DefineEnd间的内容

PNG Image

私有类中实现槽

在qtvariantproperty.cpp的QtVariantPropertyManagerPrivate中声明槽

PNG Image

实现槽(相关信号触发为源码内容)

PNG Image

初始化(QtVariantPropertyManager构造函数中)

PNG Image

QtVariantPropertyManager::setValue

PNG Image

载入QtVariantEditorFactory中

声明自定义工厂变量

PNG Image

初始化(QtVariantEditorFactory构造函数中)

PNG Image

QtVariantEditorFactory::connectPropertyManager

PNG Image

QtVariantEditorFactory::disconnectPropertyManager

PNG Image

类中反射声明

PNG Image

完成以上,便可使QtVariantPropertyManager::addProperty(metaProperty.userType(),metaProperty.name())

创建相应属性栏

文章简介