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);
}

在这里插入图片描述
在这里插入图片描述

至此,基本功能大概完成。很草率,写的代码自己都看不下去,只是提供个思路而已,抛转引玉…哎…

posted @ 2020-06-29 15:58  猫影  阅读(2132)  评论(0编辑  收藏  举报