qt扩展属性框qtpropertybrowser 中添加按钮
simple
QtPropertyBrowser提供了丰富的示例来展示该扩展是如何使用的。
示例simple展示如下:
其中包括int、bool、string等普通数据类型,file文件类型,group族类型等。如果要重绘某种类型的控件如何办呢?
特殊需求
比如在group中来增加两个按钮,实现组内成员的增减,效果如下:
点击“+”,组内增加指定类型的成员,点击“-”,组内删除选中的成员。
问题
需要解决以下问题:
- 把按钮加进去
- 给多组“+”/"-"按钮绑定信号槽
- 改变group组的显示风格,例如:按钮正常状态下也要显示/不设置焦点捕获等
- 兼容int/double/bool/file等众多类型,模板化
方法
选用simple示例中展示的size类型来进行重写,当然你可以选择你感兴趣的任何类型。
QtVectorPropertyManager的实现
继承QtVariantPropertyManager,实现以下接口:
QString valueText(const QtProperty *property) const;
virtual void initializeProperty(QtProperty *property);
virtual void uninitializeProperty(QtProperty *property);
QtProperty *addProperty(const QString &name);
QtVectorEditoryFactory的实现
继承QtAbstractEditorFactory,实现以下接口:
void connectPropertyManager(QtArrayPropertyManager *manager);
QWidget *createEditor(QtArrayPropertyManager *manager, QtProperty *property,
QWidget *parent); //关于按钮的,主要在createEditor这个函数里实现。
void disconnectPropertyManager(QtArrayPropertyManager *manager);
void pushGroupItem(QtProperty *property);
void delGroupItem(QtProperty* property);
void btnSignalsSlots();
qtvectorypropertymanager.h
QtVectorPropertyManager.h
class QtVectorPropertyManager: public QtVariantPropertyManager
{
Q_OBJECT
public:
explicit QtVectorPropertyManager(QWidget *parent = 0);
virtual ~QtVectorPropertyManager();
public:
// must a name to identity different group
QtProperty *addProperty(const QString &name);
//
void setValue(const QString &value);
void setTreeBrowser(QtAbstractPropertyBrowser* browser);
QtAbstractPropertyBrowser* getTreeBrowser();
protected:
QString displayText(const QtProperty *property) const;
QString valueText(const QtProperty *property) const;
virtual void initializeProperty(QtProperty *property);
virtual void uninitializeProperty(QtProperty *property);
private:
QMap<const QtProperty *, QString> _propertyToGroup;
// 用于动态绑定
QtAbstractPropertyBrowser *_browser;
};
qtvectorypropertymanager.cpp
QtVectoryPropertyManager.cpp
QtVectoryPropertyManager::QtVectoryPropertyManager(QWidget *parent) :
QtVectoryPropertyManager(parent)
{
}
QtVectoryPropertyManager::~QtVectoryPropertyManager()
{
}
// must a name to identity different group
QtProperty* QtVectoryPropertyManager::addProperty(const QString &name)
{
// add a top item
QtProperty* topItem = QtVariantPropertyManager::addProperty(QVariant::Size, name);
_propertyToGroup[topItem] = name;
topItem->setGroupBox(true);
return topItem;
}
//
void QtVectoryPropertyManager::setValue(const QString &value)
{
return;
}
void QtVectoryPropertyManager::setTreeBrowser(QtAbstractPropertyBrowser* browser)
{
_browser = browser;
}
QtVectoryPropertyManager* QtArrayPropertyManager::getTreeBrowser()
{
return _browser;
}
QString QtVectoryPropertyManager::displayText(const QtProperty *property) const
{
return "";
}
QString QtVectoryPropertyManager::valueText(const QtProperty *property) const
{
// nothing return;
//QString text = QtVariantPropertyManager::valueText(property);
//if (!_propertyToGroup.contains(property))
// return text;
//QString data = _propertyToGroup[property];
return "";
}
void QtVectoryPropertyManager::initializeProperty(QtProperty *property)
{
return;
}
void QtVectoryPropertyManager::uninitializeProperty(QtProperty *property)
{
QtVariantPropertyManager::uninitializeProperty(property);
}
qtvectoreditorfactory.h
使用模板,确保多类型的兼容。由于用了模板,类的实现也必须在头文件里完成。
template <class PropertyManager, class EditorFactory>
class QtVectorEditorFactory : public QtAbstractEditorFactory<QtVectoryPropertyManager>
{
public:
explicit QtVectorEditorFactory(QObject *parent = 0);
virtual ~QtVectorEditorFactory();
PropertyManager* subManager();
EditorFactory* subFactory();
protected:
void connectPropertyManager(QtVectoryPropertyManager*manager);
QWidget *createEditor(QtVectoryPropertyManager*manager, QtProperty *property,
QWidget *parent);
void disconnectPropertyManager(QtVectoryPropertyManager *manager);
void pushGroupItem(QtProperty *property);
void delGroupItem(QtProperty* property);
void btnSignalsSlots();
private:
struct UserEditor {
QPushButton* btnAdd = nullptr;
QPushButton* btnRemove = nullptr;
bool isConnected = false;
};
QtVariantEditorFactory *_originalFactory = nullptr;
PropertyManager* _manager = nullptr;
EditorFactory *_factory = nullptr;
QMap<QtProperty*, UserEditor> _porpertyToEditor;
//different group,different index.
// auto add index.
QMap<QtProperty*, int> _propertyIndex;
};
template <class PropertyManager, class EditorFactory>
QtVectorEditorFactory< PropertyManager, EditorFactory >::QtArrayEditorFactory(QObject *parent)
: QtAbstractEditorFactory<QtVectoryPropertyManager>(parent)
{
_originalFactory = new QtVariantEditorFactory(this);
_manager = new PropertyManager(this);
_factory = new EditorFactory(this);
}
template <class PropertyManager, class EditorFactory>
QtVectorEditorFactory< PropertyManager, EditorFactory >::~QtArrayEditorFactory()
{
// not need to delete editor widgets, because they will be deleted by originalFactory in its destructor
}
template <class PropertyManager, class EditorFactory>
PropertyManager* QtVectorEditorFactory< PropertyManager, EditorFactory >::subManager()
{
return _manager;
}
template <class PropertyManager, class EditorFactory>
EditorFactory* QtVectorEditorFactory< PropertyManager, EditorFactory >::subFactory()
{
return _factory;
}
template <class PropertyManager, class EditorFactory>
void QtVectorEditorFactory< PropertyManager, EditorFactory >::connectPropertyManager(QtVectoryPropertyManager *manager)
{
_originalFactory->addPropertyManager(manager);
// set factory for tpl.
manager->getTreeBrowser()->setFactoryForManager(_manager, _factory);
}
template <class PropertyManager, class EditorFactory>
void QtVectorEditorFactory< PropertyManager, EditorFactory >::prepareSignalsSlots()
{
QMap<QtProperty*, UserEditor>::Iterator it = _porpertyToEditor.begin();
while (it != _porpertyToEditor.end())
{
QtProperty* prop = it.key();
UserEditor ueditor = it.value();
if (!ueditor.isConnected)
{
// add button click singal and slot;
connect(ueditor.btnAdd, &QPushButton::clicked, this, [=] {
_propertyIndex[prop]++;
this->pushBackGroupItem(prop);
});
// del button clicked singal and slot;
connect(ueditor.btnRemove, &QPushButton::clicked, this, [=] {
this->removeGroupItem(prop);
});
ueditor.isConnected = true;
_porpertyToEditor[prop] = ueditor;
}
it++;
}
}
template <class PropertyManager, class EditorFactory>
QWidget *QtVectorEditorFactory< PropertyManager, EditorFactory >::createEditor(QtVectoryPropertyManager*manager, QtProperty *property,
QWidget *parent)
{
// 这里指定了widget的parent,无需delete ... ????
QWidget* groupEditor = new QWidget();
QPushButton* btnAdd = new QPushButton(groupEditor);
QPushButton* btnRemove = new QPushButton(groupEditor);
UserEditor editor{ btnAdd,btnRemove,false };
_porpertyToEditor[property] = editor;
groupEditor->setParent(parent);
groupEditor->setToolTip("manager your properties here");
QSpacerItem* spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum);
QHBoxLayout *layout = new QHBoxLayout(groupEditor);
groupEditor->setAttribute(Qt::WA_StyledBackground, true);
groupEditor->setLayout(layout);
layout->addItem(spacer);
layout->setContentsMargins(0, 0, 0, 0);
btnAdd->setFixedWidth(24);
btnAdd->setToolTip("add");
btnRemove->setFixedWidth(24);
btnRemove->setToolTip("remove");
btnAdd->setIcon(QIcon(":/res/add.svg"));
btnRemove->setIcon(QIcon(":/res/remove.svg"));
layout->addWidget(btnAdd);
layout->addWidget(btnRemove);
// bind signals and slots.
// prepare signals and slots.
btnSignalsSlots();
return groupEditor;
}
template <class PropertyManager, class EditorFactory>
void QtVectorEditorFactory< PropertyManager, EditorFactory >::disconnectPropertyManager(QtVectoryPropertyManager*manager)
{
_originalFactory->removePropertyManager(manager);
_originalFactory->addPropertyManager(manager);
}
template <class PropertyManager, class EditorFactory>
void QtVectorEditorFactory< PropertyManager, EditorFactory >::pushGroupItem(QtProperty *property)
{
QtArrayPropertyManager *manager = propertyManager(property);
if (!manager)
return;
QtProperty* itemTop = property;
QList<QtProperty*> properies = itemTop->subProperties();
QtProperty* item = _manager->addProperty(QString::number(_propertyIndex[property]));
properies.append(item);
itemTop->addSubProperty(item);
}
template <class PropertyManager, class EditorFactory>
void QtVectorEditorFactory< PropertyManager, EditorFactory >::delGroupItem(QtProperty* property)
{
QtVectoryPropertyManager*manager = propertyManager(property);
if (!manager) return;
QtProperty* itemTop = property;
QList<QtProperty *> subProperties = itemTop->subProperties();
QtBrowserItem * curItem = manager->getTreeBrowser()->currentItem();
// select nothing.
if (curItem == nullptr) return;
QtProperty* curSelProperty = curItem->property();
// select top item.
if (curSelProperty == itemTop)
return;
// remove selected from subproperties;
for (int i = 0; i < subProperties.size(); i++)
{
if (subProperties.contains(curSelProperty))
{
int ret = QMessageBox::warning(NULL, "warning", "Are you sure to remove?", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if (ret == QMessageBox::Yes) {
//...
// remove from list.
itemTop->removeSubProperty(curSelProperty);
return;
}
return;
}
}
}
使用方法,以文件的类型为例:
// 创建一个array
_arrayManager = new QtVectorPropertyManager(this);
_arrayFactory = new QtVectorEditorFactory<QtFilePathPropertyManager, QtFilePathEditorFactory>(this);
_arrayManager->setTreeBrowser(_brower);
_brower->setFactoryForManager(_arrayManager, _arrayFactory);
connect(_arrayFactory->subManager(), &QtFilePathPropertyManager::valueChanged, _arrayFactory->subFactory(),
[=](QtProperty *subprop, const QString& val){
// do something ...
});
改变group组的显示风格
例如:按钮正常状态下也要显示/不设置焦点捕获等
这个group组,正常状态下是需要显示“+”,“-”的,并不是每次点击属性都需要运行createEditor这个函数。这里就需要改qtpropertybrowser的源码了。
修改文件qttreepropertybrowser.cpp 函数propertyInserted
void QtTreePropertyBrowserPrivate::propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex)
{
QTreeWidgetItem *afterItem = m_indexToItem.value(afterIndex);
QTreeWidgetItem *parentItem = m_indexToItem.value(index->parent());
QTreeWidgetItem *newItem = 0;
if (parentItem) {
newItem = new QTreeWidgetItem(parentItem, afterItem);
} else {
newItem = new QTreeWidgetItem(m_treeWidget, afterItem);
}
m_itemToIndex[newItem] = index;
m_indexToItem[index] = newItem;
newItem->setFlags(newItem->flags() | Qt::ItemIsEditable);
if ( index->property()->isGroupBox() ) // index->property()->isGroupBox() 是个属性控制,区分正常显示与组框显示的风格。自己添加。。。
{
QWidget* editor = createEditor(index->property(), m_treeWidget);
editor->setFocusPolicy(Qt::NoFocus);
m_treeWidget->setItemWidget(newItem, 1, editor);
}
m_treeWidget->setItemExpanded(newItem, true);
updateItem(newItem);
}
至此,基本功能大概完成。很草率,写的代码自己都看不下去,只是提供个思路而已,抛转引玉…哎…
假舆马者,非利足也,而致千里;假舟楫者,非能水也,而绝江河