QtProperty Browser Framework

Property Browser Framework


框架介绍网址

1. 概述

  • 框架中包含:浏览部件、属性管理器、属性工厂以及属性
  • 浏览部件是允许客户根据一系列具有层次结构的属性编辑的界面
  • QtTreePropertyBrowser 以树状结构显示
  • QtGroupBoxPropertyBrowser 以分组框显示
  • 除此之外,可以通过继承QtAbstractPropertyBrowser自定义属性浏览部件
  • 通过继承QAbstractEditorFactory 可以自定义属性编辑控件
  • 属性只要封装到QtProperty实例中,就能应用于属性浏览器控件
  • 这些实例由继承自QtAbstructPropertyManager的属性管理器来创建和管理

在这里插入图片描述

2. 填充属性浏览器

  • 如果希望能够在属性浏览器中编辑属性,必须要为每个必要的属性创建管理器
  • 架构内为常用类型都提供了管理器
// 创建整型的属性
QtIntPropertyManager *intManager = new QtIntPropertyManager;
QtPorperty *priority = intManager->addProperty("Priority");

priority->setToolTip("Task Priority");
intManager->setRange(priority, 1, 5);
intManager->setValue(priority, 3);
  • QtProperty提供一系列接口,可用来设置属性名,提示信息,状态信息等
  • 不同类型的管理器提供独有的接口来设置或更改属性值
QtEnumPropertyManager *enumManager;
QtProperty *reportType;
QStringList types;   
...
types << "Bug" << "Suggestion" << "To Do";
enumManager->setEnumNames(reportType, types);
enumManager->setValue(reportType, 1); // "Suggestion"
  • 可以运用QtGroupPropertyManager对属性进行分组操作
QtGroupPropertyManager *groupManager = new QtGroupPropertyManager;
QtProperty *task1 = groupManager->addProperty("Task 1");

task1->addSubProperty(priority);
task1->addSubProperty(reportType);
  • 除此之外,每个属性也可以有零个或多个属性,统一的使用addSubProperty()添加
  • 通过属性组管理器创建的属性,没有任何值;它们的值将由层次结构中对应的元素提供;和其他属性一致,都能添加到属性浏览器中
  • 一旦有属性值,将每个属性与其类型首选编辑器的创建工厂向关联
  • 类似于管理器:架构也提供用于创建常用控件的工厂类
QtSpinBoxFactory *spinBoxFactory = new QtSpinBoxFactory;
QtEnumEditorFactory *enumFactory = new QtEnumEditorFactory;

QtTreePropertyBrowser *browser = new QtTreePropertyBrowser;
browser->setFactoryForManager(intManager, spinBoxFactory);	// 建立关联
browser->setFactoryForManager(enumManager, enumFactory);	

browser->addProperty(task1);
browser->show();
  • 通过将管理器与工厂关联,可以确保在浏览器中创建属性后,对应的工厂将创建编辑器
  • 只需将属性添加到浏览器中,并确保浏览器可见
  • 【NOTE】为同一个属性类型,提供不同的编辑器,只需再次调用setFactoryForManager()
  • 通过connect属性管理器的信号,来监控值的改变:例如 QtIntPropertyManager::ValueChanged()

3. QVariant类型方法

  • 在上一节,使用为不同的属性类型使用对应的处理类来填充属性浏览器
  • 框架中也提供另一种更便捷的方法:使用QVariant来保存所有的属性和值
  • 在这种方法中,开发者只需要一种属性管理器和一种编辑器工厂类
  • QtVariantProperty inherits QtProperty but has an enriched API, allowing properties values and attributes to be directly queried and altered.
  • QtVariantPropertyManager can be used for all property types, and its additional API can be used to query for supported variant types and their attribute lists.
  • QtVariantEditorFactory is capable of creating various editor widgets for the types supported by QtVariantPropertyManager.
  • 使用该种方法,上节中管理整型属性的方式变为
QtVariantPropertyManager *variantManager = new QtVariantPropertyManager;

// 首个参数 QVariant::Int 表示我们想要添加的属性类型;
// 它可以是 QVariant::Type 枚举和用户通过 qMetaTypeId() 自定义类型中的任一种
QtVariantProperty *priority = variantManager->addProperty(QVariant::Int, "Priority");	

// 将setRange更改为两次setAttribute
priority->setAttribute("minimum", 1);
priority->setAttribute("maximum", 5);

// 设定初始值
priority->setValue(3);
  • 枚举类型未包含在QVariant中,可以使用静态函数QtVariantPropertyManager::enumTypeId()表示
QtVariantProperty *reportType = variantManager->addProperty(QtVariantPropertyManager::enumTypeId(), "Report Type");
QStringList types;
types << "Bug" << "Suggestion" << "To Do";
reportType->setAttribute("enumNames", types);
reportType->setValue(1); // "Suggestion"

4. 扩展框架

  • 已经介绍的两种向属性浏览器添加内容的方式都支持自定义
  • 采用特定类型的方式进行扩展:必须提供一个能创建自定义属性的管理器,并且管理器能保存所创建属性的状态
class FilePathManager : public QtAbstractPropertyManager	// 继承自QtAbstractPropertyManager
{
public:
    ...
        
    // 提供getter 和 setter 的属性管理接口
    QString value(const QtProperty *property) const;
    QString filter(const QtProperty *property) const;
    
public slots:
    void setValue(QtProperty *, const QString &);
    void setFilter(QtProperty *, const QString &);
    
signals:
    // 与对应的编辑器进行通讯,可用来监控属性值
    void valueChanged(QtProperty *, const QString &);
    void filterChanged(QtProperty *, const QString &);
    
protected:
    QString valueText(const QtProperty *property) const
    { return value(property); }
    
    void initializeProperty(QtProperty *property)
    { theValues[property] = Data(); }
    
    void uninitializeProperty(QtProperty *property)
    { theValues.remove(property); }
    
private:
    // 属性值
    struct Data {
        QString value;
        QString filter;
    };
    QMap<const QtProperty *, Data> theValues;
};
  • FilePathManager也需要实现来自基类的一些protected函数
  • valueText()返回代表属性值的字符串
  • initializeProperty()初始化新的属性
  • uninitializeProperty()当属性被删除后,请除相关内容
 QString FilePathManager::value(const QtProperty *property) const
 {
     // 如果是无效属性,返回空值
     if (!theValues.contains(property)) return "";
     return theValues[property].value;
 }


void FilePathManager::setValue(QtProperty *property, const QString &val)
{
    if (!theValues.contains(property)) return;	// 无效的属性
    Data data = theValues[property];
    if (data.value == val) return;
    data.value = val;
    theValues[property] = data;
    emit propertyChanged(property);				// 写入完成,通知其他部件
    emit valueChanged(property, data.value);
}
  • 在将自定义属性添加到属性浏览器之前,必须提供一个创建属性编辑控件的工厂
class FileEditFactory
    : public QtAbstractEditorFactory<FilePathManager> 
    // 继承自模板类,将FilePathManager作为模板参数,
	//意味着该工厂只能为FilePathManager创建编辑器
{
    ...
private slots:
    void slotPropertyChanged(QtProperty *property, const QString &value);
    void slotFilterChanged(QtProperty *property, const QString &filter);
    void slotSetValue(const QString &value);
    void slotEditorDestroyed(QObject *object);
private:
    QMap<QtProperty *, QList<FileEdit *> > createdEditors;	// 属性与编辑控件相关联
    QMap<FileEdit *, QtProperty *> editorToProperty;		// 与要编辑的属性进行了绑定
};
  • 与自定义管理器类型,自定义工厂类也要实现一些接口函数
  • connectPropertyManager():当工厂与管理器关联时,调用该函数
  • 管理器valueChanged() 和 filterChanged()的信号与slotPropertyChanged()和slotFilterChanged()对应
  • disconnectPropertyManager()用来移除信号上述联接关系
  • createEditor()函数创建并初始化FileEdit控件,将控件添加到内在联系
FileEdit *editor = new FileEdit(parent);
editor->setFilePath(manager->value(property));
editor->setFilter(manager->filter(property));
createdEditors[property].append(editor);
editorToProperty[editor] = property;

connect(editor, SIGNAL(filePathChanged(const QString &)),	// 内容修改
        this, SLOT(slotSetValue(const QString &)));
connect(editor, SIGNAL(destroyed(QObject *)),				// 被销毁
        this, SLOT(slotEditorDestroyed(QObject *)));
return editor;
  • 工厂内部将编辑控件的状态和属性的状态进行同步
  • 当控件的值变化时,需要通知框架;反之亦然
// 添加自定义属性的流程与添加其他属性一致
FilePathManager *filePathManager = new FilePathManager;
QtProperty *example = filePathManager->addProperty("Example");

filePathManager->setValue(example, "main.cpp");
filePathManager->setFilter(example, "Source files (*.cpp *.c)");

FileEditFactory *fileEditFactory = new FileEditFactory 
browser->setFactoryForManager(filePathManager, fileEditFactory);
task1->addSubProperty(example);

5. 扩展QVariant

  • 使用这种方法,管理器继承自QtVariantPropertyManager;工厂继承自QtVariantPropertyFactory
// 自定义类型
class FilePathPropertyType
{
    ...
};
Q_DECLARE_METATYPE(FilePathPropertyType)	// 将自定义注册到QVariant
  • 自定义管理器需要实现一些继承的接口,并提供一个static函数用于返回属性的类型
// 该函数返回专属的类型标识符,并且返回结果能用于 QtVariantPropertyManager::addProperty()  创建新的自定义属性
int VariantManager::filePathTypeId()
{
    return qMetaTypeId<FilePathPropertyType>();
}
  • 与FilePathManager不同之处在于:继承自QtVariantPropertyManager的接口都有实现;需要增加对自定义类型的处理,其他的调用基类成员即可
bool VariantManager::isPropertyTypeSupported(
         int propertyType) const
{
    if (propertyType == filePathTypeId())	// 对于自定义类型,返回true
        return true;
    return QtVariantPropertyManager::		// 对于其他类型,调用基类的成员处理
        isPropertyTypeSupported(propertyType);
}
  • 自定义工厂类和其他工厂类一样,无需新增接口;实现内容与上节的FileEditFactory类似
  • 唯一不同的是:connectPropertyManager()函数中,将管理器的attributeChanged()信号与通用的slotPropertyAttributeChanged()槽绑定
// 必须检测 属性的类型 是否与编辑器的编辑对象类型一致
if (manager->propertyType(property) == VariantManager::filePathTypeId()) {
    ...
   	connect(editor, SIGNAL(filePathChanged(const QString&)),	// 监控自定义的filePathChanged()信号
    		this, SLOT(slotSetValue(const QString &)));
    connect(editor, SIGNAL(destroyed(QObject *)),
            this, SLOT(slotEditorDestroyed(QObject *)));
    return editor;
}
  • 使用自定义变体管理器的方式与框架提供的基本类型并无不同
QtVariantPropertyManager *variantManager = new VariantManager;
QtVariantEditorFactory *variantFactory = new VariantFactory;

QtVariantProperty *example = variantManager->addProperty(VariantManager::filePathTypeId(), "Example");
example->setValue("main.cpp");
example->setAttribute("filter", "Source files (*.cpp *.c)");

QtVariantProperty *task1 = variantManager->addProperty(QtVariantPropertyManager::groupTypeId(), "Task 1");
task1->addSubProperty(example);

6. Demo演示

demo地址
在这里插入图片描述

Demo显示
int main( int argc, char **argv ) {
    QApplication app( argc, argv );

    QWidget *w = new QWidget();

    //===== ===== 创建各类型的属性管理器 ===== =====//
    // Qt[Type]PropertyManager
    QtBoolPropertyManager *  boolManager   = new QtBoolPropertyManager( w );
    QtIntPropertyManager *   intManager    = new QtIntPropertyManager( w );
    QtStringPropertyManager *stringManager = new QtStringPropertyManager( w );
    QtSizePropertyManager *  sizeManager   = new QtSizePropertyManager( w );
    QtRectPropertyManager *  rectManager   = new QtRectPropertyManager( w );
    QtSizePolicyPropertyManager *sizePolicyManager =
        new QtSizePolicyPropertyManager( w );
    QtEnumPropertyManager * enumManager  = new QtEnumPropertyManager( w );
    QtGroupPropertyManager *groupManager = new QtGroupPropertyManager( w );

    //===== ===== 通过各类型的管理器创建属性对象 ===== =====//
    QtProperty *item0 = groupManager->addProperty( "QObject" ); // 属性标识
    QtProperty *item1 = stringManager->addProperty( "objectName" );
    item0->addSubProperty( item1 ); // 添加为 item0的子属性

    QtProperty *item2 = boolManager->addProperty( "enabled" );
    item0->addSubProperty( item2 );

    QtProperty *item3 = rectManager->addProperty( "geometry" );
    item0->addSubProperty( item3 );

    QtProperty *item4 = sizePolicyManager->addProperty( "sizePolicy" );
    item0->addSubProperty( item4 );

    QtProperty *item5 = sizeManager->addProperty( "sizeIncrement" );
    item0->addSubProperty( item5 );

    QtProperty *item7 = boolManager->addProperty( "mouseTracking" );
    item0->addSubProperty( item7 );

    QtProperty *item8 = enumManager->addProperty( "direction" );
    QStringList enumNames;
    enumNames << "Up"
              << "Right"
              << "Down"
              << "Left";
    enumManager->setEnumNames( item8, enumNames );
    QMap<int, QIcon> enumIcons;
    enumIcons[ 0 ] = QIcon( ":/demo/images/up.png" );
    enumIcons[ 1 ] = QIcon( ":/demo/images/right.png" );
    enumIcons[ 2 ] = QIcon( ":/demo/images/down.png" );
    enumIcons[ 3 ] = QIcon( ":/demo/images/left.png" );
    enumManager->setEnumIcons( item8, enumIcons );
    item0->addSubProperty( item8 );

    QtProperty *item9 = intManager->addProperty( "value" );
    intManager->setRange( item9, -100, 100 );
    item0->addSubProperty( item9 );

    //===== ===== 各类型的编辑控件工厂 ===== =====//
    QtCheckBoxFactory *  checkBoxFactory  = new QtCheckBoxFactory( w );
    QtSpinBoxFactory *   spinBoxFactory   = new QtSpinBoxFactory( w );
    QtSliderFactory *    sliderFactory    = new QtSliderFactory( w );
    QtScrollBarFactory * scrollBarFactory = new QtScrollBarFactory( w );
    QtLineEditFactory *  lineEditFactory  = new QtLineEditFactory( w );
    QtEnumEditorFactory *comboBoxFactory  = new QtEnumEditorFactory( w );

    //===== ===== Part 1 ===== =====//
    /// 以树形结构显示属性
    QtAbstractPropertyBrowser *editor1 = new QtTreePropertyBrowser();
    editor1->setFactoryForManager( boolManager, checkBoxFactory );
    editor1->setFactoryForManager( intManager, spinBoxFactory );
    editor1->setFactoryForManager( stringManager, lineEditFactory );
    editor1->setFactoryForManager( sizeManager->subIntPropertyManager(),
                                   spinBoxFactory );
    editor1->setFactoryForManager( rectManager->subIntPropertyManager(),
                                   spinBoxFactory );
    editor1->setFactoryForManager( sizePolicyManager->subIntPropertyManager(),
                                   spinBoxFactory );
    editor1->setFactoryForManager( sizePolicyManager->subEnumPropertyManager(),
                                   comboBoxFactory );
    editor1->setFactoryForManager( enumManager, comboBoxFactory );
    editor1->addProperty( item0 );

    //===== ===== Part 2 ===== =====//
    /// 以树形结构显示属性 —— 不配对工厂,属性不可编辑,只读
    QtAbstractPropertyBrowser *editor2 = new QtTreePropertyBrowser();
    editor2->addProperty( item0 );

    //===== ===== Part 3 ===== =====//
    /// 以GroupBox形式显示属性
    QtAbstractPropertyBrowser *editor3 = new QtGroupBoxPropertyBrowser();
    editor3->setFactoryForManager( boolManager, checkBoxFactory );
    editor3->setFactoryForManager( intManager, spinBoxFactory );
    editor3->setFactoryForManager( stringManager, lineEditFactory );
    editor3->setFactoryForManager( sizeManager->subIntPropertyManager(),
                                   spinBoxFactory );
    editor3->setFactoryForManager( rectManager->subIntPropertyManager(),
                                   spinBoxFactory );
    editor3->setFactoryForManager( sizePolicyManager->subIntPropertyManager(),
                                   spinBoxFactory );
    editor3->setFactoryForManager( sizePolicyManager->subEnumPropertyManager(),
                                   comboBoxFactory );
    editor3->setFactoryForManager( enumManager, comboBoxFactory );

    editor3->addProperty( item0 );

    QScrollArea *scroll3 = new QScrollArea();
    scroll3->setWidgetResizable( true );
    scroll3->setWidget( editor3 );

    //===== ===== Part 4 ===== =====//
    /// 以GroupBox形式显示属性
    QtAbstractPropertyBrowser *editor4 = new QtGroupBoxPropertyBrowser();
    editor4->setFactoryForManager( boolManager, checkBoxFactory );
    editor4->setFactoryForManager( intManager, scrollBarFactory );
    editor4->setFactoryForManager( stringManager, lineEditFactory );
    editor4->setFactoryForManager( sizeManager->subIntPropertyManager(),
                                   spinBoxFactory );
    editor4->setFactoryForManager( rectManager->subIntPropertyManager(),
                                   spinBoxFactory );
    editor4->setFactoryForManager( sizePolicyManager->subIntPropertyManager(),
                                   sliderFactory );
    editor4->setFactoryForManager( sizePolicyManager->subEnumPropertyManager(),
                                   comboBoxFactory );
    editor4->setFactoryForManager( enumManager, comboBoxFactory );

    editor4->addProperty( item0 );

    QScrollArea *scroll4 = new QScrollArea();
    scroll4->setWidgetResizable( true );
    scroll4->setWidget( editor4 );

    //===== ===== Part 5===== =====//
    /// 基于QToolButton的下拉式显示
    QtAbstractPropertyBrowser *editor5 = new QtButtonPropertyBrowser();
    editor5->setFactoryForManager( boolManager, checkBoxFactory );
    editor5->setFactoryForManager( intManager, scrollBarFactory );
    editor5->setFactoryForManager( stringManager, lineEditFactory );
    editor5->setFactoryForManager( sizeManager->subIntPropertyManager(),
                                   spinBoxFactory );
    editor5->setFactoryForManager( rectManager->subIntPropertyManager(),
                                   spinBoxFactory );
    editor5->setFactoryForManager( sizePolicyManager->subIntPropertyManager(),
                                   sliderFactory );
    editor5->setFactoryForManager( sizePolicyManager->subEnumPropertyManager(),
                                   comboBoxFactory );
    editor5->setFactoryForManager( enumManager, comboBoxFactory );

    editor5->addProperty( item0 );

    QScrollArea *scroll5 = new QScrollArea();
    scroll5->setWidgetResizable( true );
    scroll5->setWidget( editor5 );

    QGridLayout *layout = new QGridLayout( w );
    QLabel *     label1 = new QLabel( "Editable Tree Property Browser" );
    QLabel *     label2 = new QLabel(
        "Read Only Tree Property Browser, editor factories are not set" );
    QLabel *label3 = new QLabel( "Group Box Property Browser" );
    QLabel *label4 = new QLabel(
        "Group Box Property Browser with different editor factories" );
    QLabel *label5 = new QLabel( "Button Property Browser" );
    label1->setWordWrap( true );
    label2->setWordWrap( true );
    label3->setWordWrap( true );
    label4->setWordWrap( true );
    label5->setWordWrap( true );
    label1->setFrameShadow( QFrame::Sunken );
    label2->setFrameShadow( QFrame::Sunken );
    label3->setFrameShadow( QFrame::Sunken );
    label4->setFrameShadow( QFrame::Sunken );
    label5->setFrameShadow( QFrame::Sunken );
    label1->setFrameShape( QFrame::Panel );
    label2->setFrameShape( QFrame::Panel );
    label3->setFrameShape( QFrame::Panel );
    label4->setFrameShape( QFrame::Panel );
    label5->setFrameShape( QFrame::Panel );
    label1->setAlignment( Qt::AlignCenter );
    label2->setAlignment( Qt::AlignCenter );
    label3->setAlignment( Qt::AlignCenter );
    label4->setAlignment( Qt::AlignCenter );
    label5->setAlignment( Qt::AlignCenter );

    layout->addWidget( label1, 0, 0 );
    layout->addWidget( label2, 0, 1 );
    layout->addWidget( label3, 0, 2 );
    layout->addWidget( label4, 0, 3 );
    layout->addWidget( label5, 0, 4 );
    layout->addWidget( editor1, 1, 0 );
    layout->addWidget( editor2, 1, 1 );
    layout->addWidget( scroll3, 1, 2 );
    layout->addWidget( scroll4, 1, 3 );
    layout->addWidget( scroll5, 1, 4 );
    w->show();

    int ret = app.exec();
    delete w;
    return ret;
}

根据JSON文件动态加载

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值