背景
在项目开发过程中需要使 QComboBox 下拉一个树形列表,直接通过 setModel 和 setView 设置 combox 控件可以实现,但是在单击节点箭头按钮时也会隐藏下拉框的显示。因此需要重新实现 QComboBox 的方法来实现自己控制。
①.通过 QCheckTreeComboBoxView子类化 QTreeWidget 类,以重写 mousePressEvent 事件自己实现节点的展开和收缩,否则会有事件冲突。
②.通过 QCheckTreeComboBox子类化 QComboBox 类,以重写 hidePopup 方法,判断条件是否满足。
③.通过响应 QComboBox 类的 activated 信号,遍历获取当前的选择结果。
QCheckTreeComBox.h
#include <QComboBox>
#include "ui_QCheckTreeComboBox.h"
#include "QTreeWidget"
#include <functional>
class QCheckTreeComboBox : public QComboBox
{
Q_OBJECT
public:
QCheckTreeComboBox(QWidget *parent = Q_NULLPTR);
~QCheckTreeComboBox();
public:
typedef std::function<QString()> getTreeSelectedFunc;//用于指定自定义方法
class QCheckTreeComboBoxView : public QTreeWidget
{
public:
QCheckTreeComboBoxView(QCheckTreeComboBox * cbx ,QWidget *parent = Q_NULLPTR) :QTreeWidget(parent),treeComboBox(cbx) {}
~QCheckTreeComboBoxView() {};
protected:
void mousePressEvent(QMouseEvent *event) override;
private:
void treeMousePressed(bool inItem);
void setChildSelected(QTreeWidgetItem* root);
private:
//Qt 嵌套类不允许有信号槽,变为友元
QCheckTreeComboBox * treeComboBox = nullptr;
public :
void setItemChecked();
};
private:
Ui::QCheckTreeComboBox ui;
protected:
void hidePopup() override;//下拉列表收缩
bool eventFilter(QObject *watched, QEvent *event) override;
private:
bool canHidePopup = true;//允许收缩
bool clickItem = false;//是否单击了节点
public:
QCheckTreeComboBoxView * displayTreeWidget = nullptr;
getTreeSelectedFunc getTreeSelectedText;//自定义的数据获取方法
getTreeSelectedFunc getTreeSelectedData;
private:
QString getCurrentSelectedText();//默认获取读取方法
QString getCurrentSelectedData();
void updateTreeWidgetSelected();//更新节点选择状态
void updateEditText();//更新文本显示
public:
void iniSelectText();//初始化文本显示
signals:
void currentSelectedDataChange(QString & m_text);
void currentSelectedTextChange(QString & m_text);
};
QCheckTreeComBox.cpp
#include "QCheckTreeComboBox.h"
#include "qevent.h"
#include "QLineEdit"
using namespace std::placeholders;
QCheckTreeComboBox::QCheckTreeComboBox(QWidget *parent)
: QComboBox(parent)
{
ui.setupUi(this);
getTreeSelectedText = std::bind(&QCheckTreeComboBox::getCurrentSelectedText, this);
getTreeSelectedData = std::bind(&QCheckTreeComboBox::getCurrentSelectedData, this);
displayTreeWidget = new QCheckTreeComboBoxView(this);
displayTreeWidget->setSelectionMode(QAbstractItemView::SingleSelection);
displayTreeWidget->setHeaderHidden(true);
QLineEdit * displayEdit = new QLineEdit(this);
displayEdit->installEventFilter(this);
displayEdit->setEnabled(false);
this->setLineEdit(displayEdit);
this->setModel(displayTreeWidget->model());
this->setView(displayTreeWidget);
connect(this, static_cast<void(QComboBox::*)(int)>(&QComboBox::activated), this, [&](int index) {
if (clickItem) updateTreeWidgetSelected();
updateEditText();
canHidePopup = true;
});
}
QCheckTreeComboBox::~QCheckTreeComboBox()
{
}
void QCheckTreeComboBox::hidePopup()
{
if (canHidePopup) QComboBox::hidePopup();
}
bool QCheckTreeComboBox::eventFilter(QObject *watched, QEvent *event)
{
if (watched == this->lineEdit() && event->type() == QEvent::MouseButtonPress)
{
if (((QMouseEvent*)event)->button() == Qt::LeftButton)
{
showPopup();
}
}
return QComboBox::eventFilter(watched, event);
}
QString QCheckTreeComboBox::getCurrentSelectedText()
{
QString result = "";
QTreeWidgetItemIterator it(displayTreeWidget);
while (*it)
{
if ((*it)->checkState(0) == Qt::Checked)
{
result += (*it)->text(0)+",";
}
++it;
}
return result;
}
QString QCheckTreeComboBox::getCurrentSelectedData()
{
QString result = "";
QTreeWidgetItemIterator it(displayTreeWidget);
while ( (*it) && (*it)->data(0, Qt::UserRole).isValid() )
{
if ( (*it)->checkState(0) == Qt::Checked )
{
result += (*it)->data(0, Qt::UserRole).toString() + ",";
}
++it;
}
return result;
}
void QCheckTreeComboBox::updateTreeWidgetSelected()
{
displayTreeWidget->setItemChecked();
}
void QCheckTreeComboBox::updateEditText()
{
QString selText = getTreeSelectedText();
QString selData = getTreeSelectedData();
lineEdit()->setText(selText);
lineEdit()->setCursorPosition(0);
emit currentSelectedTextChange(selText);
emit currentSelectedDataChange(selData);
}
void QCheckTreeComboBox::iniSelectText()
{
updateEditText();
}
void QCheckTreeComboBox::QCheckTreeComboBoxView::mousePressEvent(QMouseEvent *event)
{
treeComboBox->canHidePopup = false;
auto curIndex = currentIndex();
auto rect = this->visualRect(curIndex);
auto buttonRect = QRect(rect.left() - 20, rect.top(), 20, rect.height());
if (buttonRect.contains(event->pos()))
{
if (isExpanded(curIndex)) setExpanded(curIndex, false);
else setExpanded(curIndex, true);
treeMousePressed(false);
}
else
{
treeMousePressed(true);
}
}
void QCheckTreeComboBox::QCheckTreeComboBoxView::treeMousePressed(bool inItem)
{
treeComboBox->clickItem = inItem;
}
void QCheckTreeComboBox::QCheckTreeComboBoxView::setChildSelected(QTreeWidgetItem* root)
{
int count = root->childCount();
for (int i = 0; i < count; ++i)
{
root->child(i)->setCheckState(0,root->checkState(0));
}
}
void QCheckTreeComboBox::QCheckTreeComboBoxView::setItemChecked()
{
currentItem()->setCheckState(0, currentItem()->checkState(0) == Qt::Checked ? Qt::Unchecked : Qt::Checked);
setChildSelected(currentItem());
}
代码示例
auto tree = ui.comboBox->displayTreeWidget;
for (int i = 1;i < 4;i++ )
{
QTreeWidgetItem* item = new QTreeWidgetItem(tree, { QString::number(i) });
item->setCheckState(0, Qt::Unchecked);
for (int j = 1; j < 5; j++)
{
QTreeWidgetItem* sub = new QTreeWidgetItem(item, { QString("%1.%2").arg(i).arg(j) });
sub->setCheckState(0, Qt::Unchecked);
}
}