Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
[Qt/C++] QComboBox with support of checkboxes
#ifndef QCHECKLIST
#define QCHECKLIST
#include <QWidget>
#include <QComboBox>
#include <QStandardItemModel>
#include <QLineEdit>
#include <QEvent>
#include <QStyledItemDelegate>
#include <QListView>
/**
* @brief QComboBox with support of checkboxes
* http://stackoverflow.com/questions/8422760/combobox-of-checkboxes
*/
class QCheckList : public QComboBox
{
Q_OBJECT
public:
/**
* @brief Additional value to Qt::CheckState when some checkboxes are Qt::PartiallyChecked
*/
static const int StateUnknown = 3;
private:
QStandardItemModel* m_model;
/**
* @brief Text displayed when no item is checked
*/
QString m_noneCheckedText;
/**
* @brief Text displayed when all items are checked
*/
QString m_allCheckedText;
/**
* @brief Text displayed when some items are partially checked
*/
QString m_unknownlyCheckedText;
signals:
void globalCheckStateChanged(int);
public:
QCheckList(QWidget* _parent = 0) : QComboBox(_parent)
{
m_model = new QStandardItemModel();
setModel(m_model);
setEditable(true);
lineEdit()->setReadOnly(true);
lineEdit()->installEventFilter(this);
setItemDelegate(new QCheckListStyledItemDelegate(this));
connect(lineEdit(), &QLineEdit::selectionChanged, lineEdit(), &QLineEdit::deselect);
connect((QListView*) view(), SIGNAL(pressed(QModelIndex)), this, SLOT(on_itemPressed(QModelIndex)));
connect(m_model, SIGNAL(dataChanged(QModelIndex, QModelIndex, QVector<int>)), this, SLOT(on_modelDataChanged()));
}
~QCheckList()
{
delete m_model;
}
void setAllCheckedText(const QString &text)
{
m_allCheckedText = text;
updateText();
}
void setNoneCheckedText(const QString &text)
{
m_noneCheckedText = text;
updateText();
}
void setUnknownlyCheckedText(const QString &text)
{
m_unknownlyCheckedText = text;
updateText();
}
/**
* @brief Adds a item to the checklist (setChecklist must have been called)
* @return the new QStandardItem
*/
QStandardItem* addCheckItem(const QString &label, const QVariant &data, const Qt::CheckState checkState)
{
QStandardItem* item = new QStandardItem(label);
item->setCheckState(checkState);
item->setData(data);
item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
m_model->appendRow(item);
updateText();
return item;
}
/**
* @brief Computes the global state of the checklist :
* - if there is no item: StateUnknown
* - if there is at least one item partially checked: StateUnknown
* - if all items are checked: Qt::Checked
* - if no item is checked: Qt::Unchecked
* - else: Qt::PartiallyChecked
*/
int globalCheckState()
{
int nbRows = m_model->rowCount(), nbChecked = 0, nbUnchecked = 0;
if (nbRows == 0)
{
return StateUnknown;
}
for (int i = 0; i < nbRows; i++)
{
if (m_model->item(i)->checkState() == Qt::Checked)
{
nbChecked++;
}
else if (m_model->item(i)->checkState() == Qt::Unchecked)
{
nbUnchecked++;
}
else
{
return StateUnknown;
}
}
return nbChecked == nbRows ? Qt::Checked : nbUnchecked == nbRows ? Qt::Unchecked : Qt::PartiallyChecked;
}
protected:
bool eventFilter(QObject* _object, QEvent* _event)
{
if (_object == lineEdit() && _event->type() == QEvent::MouseButtonPress)
{
showPopup();
return true;
}
return false;
}
private:
void updateText()
{
QString text;
switch (globalCheckState())
{
case Qt::Checked:
text = m_allCheckedText;
break;
case Qt::Unchecked:
text = m_noneCheckedText;
break;
case Qt::PartiallyChecked:
for (int i = 0; i < m_model->rowCount(); i++)
{
if (m_model->item(i)->checkState() == Qt::Checked)
{
if (!text.isEmpty())
{
text+= ", ";
}
text+= m_model->item(i)->text();
}
}
break;
default:
text = m_unknownlyCheckedText;
}
lineEdit()->setText(text);
}
private slots:
void on_modelDataChanged()
{
updateText();
emit globalCheckStateChanged(globalCheckState());
}
void on_itemPressed(const QModelIndex &index)
{
QStandardItem* item = m_model->itemFromIndex(index);
if (item->checkState() == Qt::Checked)
{
item->setCheckState(Qt::Unchecked);
}
else
{
item->setCheckState(Qt::Checked);
}
}
public:
class QCheckListStyledItemDelegate : public QStyledItemDelegate
{
public:
QCheckListStyledItemDelegate(QObject* parent = 0) : QStyledItemDelegate(parent) {}
void paint(QPainter * painter_, const QStyleOptionViewItem & option_, const QModelIndex & index_) const
{
QStyleOptionViewItem & refToNonConstOption = const_cast<QStyleOptionViewItem &>(option_);
refToNonConstOption.showDecorationSelected = false;
QStyledItemDelegate::paint(painter_, refToNonConstOption, index_);
}
};
};
#endif // QCHECKLIST
@eugesh
Copy link

eugesh commented Oct 25, 2021

Hi! I only don't understand importance of QCheckListStyledItemDelegate class. Everything works fine even with default QStyledItemDelegate implementation.
I have one question: I applied your class for one of Qt's examples. I would like to upload that patch on Qt's git. How do you feel about if I upload it adding you as the author?

@mistic100
Copy link
Author

mistic100 commented Oct 25, 2021

  1. I have no idea, but it is mentionned here https://stackoverflow.com/a/20118475
  2. sure go ahead

@eugesh
Copy link

eugesh commented Oct 28, 2021

Hi, thank you for agreeing to upload your code. But if you would like to finish the work, we need your formal consent. Could you register on gerrit?
Please look at discussion.

@mistic100
Copy link
Author

mistic100 commented Oct 28, 2021

that's crazy, do you also have to get the consent from Silas Parker https://stackoverflow.com/a/8423904 ? the solution entirely come from this post

if it is here publicly it's to be used, I don't care

@eugesh
Copy link

eugesh commented Oct 28, 2021

I think your agreement would be enough ;) The idea is clear and comes from documentation. Otherwise I'd simplify the code to smth similar to Silas Parker's solution with just me as an author. May be I'll mention link to his solution in a comment.

@eugesh
Copy link

eugesh commented Oct 28, 2021

I'm not Qt programmer, just contributer. I said that this is not my code and they asked me for your response. If it is not difficult for you, please.
But if they agreed to include the class to QWidgets, would you like to became Qt contributer?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment