工作中不乏这样一个需求:给ComboBox加上复选框,并且可以实时显示勾选内容。
扩展到Model-View框架中就形成了一个代理,对单元格中的内容进行自定义。
效果如图:
#ifndef WCOMBOBOXDELEGATE_H
#define WCOMBOBOXDELEGATE_H
#include <QDebug>
#include <QComboBox>
#include <QListView>
#include <QLineEdit>
#include <QItemDelegate>
#include <QAbstractItemView>
#include <QStandardItemModel>
/**
* @brief The WComboBox class
* @author wanghp
* 提供一个代理类,可以将表格某列编辑态变为有复选框的选择控件comboBox
* 还有部分问题被绕过,可以正常使用,未来修复
*/
class WComboBox : public QComboBox
{
Q_OBJECT
public:
WComboBox(QWidget *parent = nullptr)
: QComboBox (parent)
{
m_lineEdit = new QLineEdit();
m_lineEdit->setReadOnly(true);
m_listView = new QListView();
m_model = new QStandardItemModel();
this->setLineEdit(m_lineEdit);
this->setView(m_listView);
this->setModel(m_model);
connect(this, SIGNAL(activated(int)), this, SLOT(slot_activated(int)));
}
void addItems(const QStringList &labels)
{
for(auto i: labels)
{
QStandardItem *item = new QStandardItem(i);
item->setCheckState(Qt::Unchecked);
m_model->appendRow(item);
}
}
void setChecked(const QStringList &checkedList)
{
for(int i = 0; i < m_model->rowCount(); i ++)
{
QString curText = m_model->item(i)->text();
if(checkedList.contains(curText))
{
m_model->item(i)->setCheckState(Qt::Checked);
}
}
}
protected:
void hidePopup()
{
int width = this->view()->width();
int height = this->view()->height();
QRect rect(0, this->height(), width, height);
int x = QCursor::pos().x() - mapToGlobal(this->geometry().topLeft()).x() + this->geometry().x();
int y = QCursor::pos().y() - mapToGlobal(this->geometry().topLeft()).y() + this->geometry().y();
if(!rect.contains(x, y))
{
QComboBox::hidePopup();
}
}
private:
void updateText()
{
QStringList checkedLabels;
for(int i = 0; i < m_model->rowCount(); i ++)
{
if(m_model->item(i)->checkState() == Qt::Checked)
{
checkedLabels.append(m_model->item(i)->text());
}
}
m_lineEdit->setText(checkedLabels.join(","));
m_lineEdit->setToolTip(checkedLabels.join(","));
}
private slots:
void slot_activated(int index)
{
QStandardItem *cur_item = m_model->item(index);
if(!cur_item) return;
Qt::CheckState state = cur_item->checkState() == Qt::Unchecked ? Qt::Checked : Qt::Unchecked;
cur_item->setCheckState(state);
updateText();
}
private:
QLineEdit *m_lineEdit;
QListView *m_listView;
QStandardItemModel *m_model;
};
class WComboBoxDelegate : public QItemDelegate
{
Q_OBJECT
public:
WComboBoxDelegate(QObject *parent = nullptr)
: QItemDelegate(parent) { }
void setItems(const QStringList &texts) { _texts = texts; }
public:
QWidget *createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const override
{
WComboBox *editor = new WComboBox(parent);
editor->addItems(_texts);
QStringList text = index.model()->data(index, Qt::DisplayRole).toString().split(",");
if(text.count() > 1)
{
editor->setChecked(text);
}
else if(text.count() == 1 && text.at(0) == editor->itemText(0))
{
editor->setChecked(text);
}
return editor;
}
void setEditorData(QWidget *editor, const QModelIndex &index) const override
{
QStringList text = index.model()->data(index, Qt::EditRole).toStringList();
WComboBox *comboBox = static_cast<WComboBox*>(editor);
if(text.count() > 1)
{
comboBox->setCurrentText(text.join(","));
comboBox->setChecked(text);
}
else if(text.count() == 1)
{
if(text.at(0) == comboBox->itemText(0))
{
}
else
{
comboBox->setCurrentText(text.at(0));
}
}
}
void setModelData(QWidget *editor,
QAbstractItemModel *model,
const QModelIndex &index) const override
{
WComboBox *comboBox = static_cast<WComboBox*>(editor);
QString text = comboBox->currentText();
model->setData(index, text, Qt::EditRole);
qDebug() << "setModelData: " << text << endl;
}
void updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option,
const QModelIndex &index) const override
{
editor->setGeometry(option.rect);
}
private:
QStringList _texts;
};
#endif // WCOMBOBOXDELEGATE_H