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);
}
至此,基本功能大概完成。很草率,寫的代碼自己都看不下去,只是提供個思路而已,拋轉引玉…哎…