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
inheritsQtProperty
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 byQtVariantPropertyManager
.
- 使用该种方法,上节中管理整型属性的方式变为
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演示
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;
}