qt擴展屬性框qtpropertybrowser 中添加按鈕

simple

QtPropertyBrowser提供了豐富的示例來展示該擴展是如何使用的。
示例simple展示如下:

示例
其中包括int、bool、string等普通數據類型,file文件類型,group族類型等。如果要重繪某種類型的控件如何辦呢?

特殊需求

比如在group中來增加兩個按鈕,實現組內成員的增減,效果如下:
示例
點擊“+”,組內增加指定類型的成員,點擊“-”,組內刪除選中的成員。

問題

需要解決以下問題:

  1. 把按鈕加進去
  2. 給多組“+”/"-"按鈕綁定信號槽
  3. 改變group組的顯示風格,例如:按鈕正常狀態下也要顯示/不設置焦點捕獲等
  4. 兼容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);
}

在這裏插入圖片描述
在這裏插入圖片描述

至此,基本功能大概完成。很草率,寫的代碼自己都看不下去,只是提供個思路而已,拋轉引玉…哎…

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章