如何在QComboBox上实现多选
1. QComboBox的底层实现
QComboBox底层是由QLineEdit和QComboBoxListView组成的。其中对QLineEdit和QComboBoxListView数据的管理则是通过QComboBoxPrivate类来处理。在QComboBoxPrivate中有个QComboBoxPrivateContainer* viewContainer();函数⽤于初始化QComboBox的view,其实现如下:
QComboBoxPrivateContainer* QComboBoxPrivate::viewContainer()
{
if (container)
return container;
Q_Q(QComboBox);
container = new QComboBoxPrivateContainer(new QComboBoxListView(q), q);
container->itemView()->setModel(model);
container->itemView()->setTextElideMode(Qt::ElideMiddle);
updateDelegate(true);
updateLayoutDirection();
updateViewContainerPaletteAndOpacity();
QObject::connect(container, SIGNAL(itemSelected(QModelIndex)),
q, SLOT(_q_itemSelected(QModelIndex)));
QObject::connect(container->itemView()->selectionModel(),
SIGNAL(currentChanged(QModelIndex,QModelIndex)),
q, SLOT(_q_emitHighlighted(QModelIndex)));
QObject::connect(container, SIGNAL(resetButton()), q, SLOT(_q_resetButton()));
return container;
}
其中的QComboBoxListView继承与QListView。其model设置则是在QComboBoxPrivate的init⽅法中完成的。init的实现如下:
void QComboBoxPrivate::init()
{
Q_Q(QComboBox);
#ifdef Q_OS_OSX
// On OS X, only line edits and list views always get tab focus. It's only
// when we enable full keyboard access that other controls can get tab focus.
// When it's not editable, a combobox looks like a button, and it behaves as
// such in this respect.
if (!q->isEditable())
q->setFocusPolicy(Qt::TabFocus);
else
qt listview#endif
q->setFocusPolicy(Qt::WheelFocus);
q->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed,
QSizePolicy::ComboBox));
setLayoutItemMargins(QStyle::SE_ComboBoxLayoutItem);
q->setModel(new QStandardItemModel(0, 1, q));
if (!q->isEditable())
q->setAttribute(Qt::WA_InputMethodEnabled, false);
else
q->setAttribute(Qt::WA_InputMethodEnabled);
}
在QComboBox的构造函数中,会调⽤这个init⽅法。
2. 实现多选
从QComboBox的底层实现可以看出,QComboBox分别依赖于QLineEdit、QListView和QStandardItemModel来⼯作,则要实现多选可以有以下两种选择:
通过QStandardItem的setCheckable(true)来显⽰多选框。如:
QComboBox *defaultBox = new QComboBox(mainWindow);
QStandardItemModel *dModel = qobject_cast<QStandardItemModel *>( defaultBox->model());
QStandardItem *item1 = new QStandardItem("default----aaaa");
item1->setCheckable(true);
dModel->appendRow(item1);
运⾏效果如下:
由于QComboBox的默认操作是在点击了⼀个元素后,收起下拉列表并将点击的元素的text更新到QLineEdit中。所以这种⽅式存在以下问题:
选择框的选中状态需要另外编写代码来控制;
在点击⼀个元素时,需要控制QComboBox不收起下拉列表;
在解决第2个问题的同时,需要控制QComboBox正常收起下拉列表。
由于QComboBox底层对于view的控制都是在QComboBoxPrivateContainer这个私有类中实现的,所以要解决上⾯这⼏个问题⽐较复杂,所以推荐使⽤下⾯的实现⽅式。
通过QAbstractItemView的setIndexWidget()结合QCheckBox来实现。这样做,⼀⽅⾯可以更好地控制各个元素的多选框的状态;
另⼀⽅⾯在点击的时候,由于点击的是checkbox控件,所以不会触发QComboBox⾃⾝的item点击处理,所以不会收起下拉列表。
(前提是checkbox控件铺满view的整个⾏)如:
QComboBox *defaultBox = new QComboBox(mainWindow);
QStandardItemModel *dModel = qobject_cast<QStandardItemModel *>( defaultBox->model());
dModel->insertRow(0);
defaultBox->view()->setIndexWidget(dModel->index(0,0), new QCheckBox("default-bbbbbbbb"));
运⾏效果如下:
从这个运⾏效果中可以看到,我们可以很容易的选中某个元素。但是也可以看到,上⾯的QLineEdit中并没有显⽰出对应的item内容,这还需要修改QComboBox的QLineEdit控件以及添加⼀个_q_itemSelected函数,并重写QComboBox的hidePoup()和
resizeEvent()来实现。
3. 总结 — 在QComboBox上实现多选的简单⽅法
创建⼀个类MultiCombo继承于QComboBox;
调⽤QComboBox的setLineEdit()函数替换原有的LineEdit,如:
m_lineEdit = new QLineEdit(this);
m_lineEdit->setReadOnly(true);
m_lineEdit->setStyleSheet("border:None;");
this->setLineEdit(m_lineEdit);
替换lineEdit的⽬的是为了显⽰多个item的⽂本。因为QComboBox默认只能显⽰与下拉列表中的⼀个item对应的⽂本,否则会发⽣异常。
创建⼀个addItem函数⽤于向view中添加元素,如:
void MultiCombo::addItem(const QString &text)
{
QStandardItemModel *viewModel = qobject_cast<QStandardItemModel *>(m_listView->model());
viewModel->insertRow(viewModel->rowCount());
QCheckBox *newItemCheck = new QCheckBox(this);
newItemCheck->setText(text);
m_listView->setIndexWidget(viewModel->index(viewModel->rowCount() - 1, 0), newItemCheck);
}
注册_q_itemSelected函数⽤于更新lineEdit, 如:
void _q_itemSelected(const QModelIndex &item = QModelIndex());
Q_PRIVATE_SLOT(self(), void _q_itemSelected(const QModelIndex &));
也可以在重写的hidePopup函数中去更新lineEdit,但是这样做有⼀个问题。就是如果列表中含有可直接触发QComboBox的item点击处理的元素的话(⽐如,就是⼀个字符串),那么点击了这个元素,则lineEdit中的内容就可能只是被点击的这个元素的text。
重写QComboBox的hidePoup函数,如:
void MultiCombo::hidePopup()
{
QComboBox::hidePopup();
_q_itemSelected();
}
重写QComboBox的resizeEvent事件处理。重写这个事件的⽬的是为了实时更新下拉列表中元素的长度,使其能够铺满view的这个⾏,确保点击元素时,不触发QComboBox的默认item点击处理。
这是⼀个实现⽰例,有需要的朋友可以去下载了看⼀看。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。