Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

QComboBox set title text regardless of items

Tags:

c++

qt

qt5

I have a QComboBox with a QStandardModel, to which I add items the following way:

QStandardItem * item = new QStandardItem();
item->setText("someText");
item->setCheckable(true);

item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
item->setData(Qt::Checked, Qt::CheckStateRole);

m_model->appendRow(item);
m_ComboBox->setModel(m_model);

Which gives me a combobox with checkboxes, which is exactly what I want. Now, when the user deselects all of the items, "None" should be the text of the dropdown menu. If the user selects some of the items, "Multiple" would be the text.

I haven't found a way on how to set the header text of a QComboBox. Is there no convenient way apart from subclassing and doing it on my own?

like image 568
Hafnernuss Avatar asked Nov 30 '17 14:11

Hafnernuss


2 Answers

There is no convenient way to do it without subclassing. In fact, subclassing is the most convenient way.

Even if you could run through your QComboBox model and check which items are checked or not, you will have no way to tell your combo box to update itself once the items are all (or partially all) checked. There is no signal or particular function allowing you to do it.

But subclassing your QComboBox to have this behaviour is not very complex: you need to reimplement the paintEvent().

//return a list with all the checked indexes
QModelIndexList MyComboBox::checkedIndexes()const
{
    return model()->match(model()->index(0, 0), Qt::CheckStateRole, Qt::Checked, -1, Qt::MatchRecursive);
}

// returns a list with all the unchecked indexes
QModelIndexList MyComboBox::uncheckedIndexes()const
{
    return model()->match(model()->index(0, 0), Qt::CheckStateRole, Qt::Unchecked, -1, Qt::MatchRecursive);
}

//return true if all the items are checked
bool MyComboBox::allChecked()const
{
    return (uncheckedIndexes().count() == 0);
}

//return true if all the items are unchecked
bool MyComboBox::noneChecked()const
{
    return (checkedIndexes().count() == 0);
}

void MyComboBox::paintEvent(QPaintEvent *)
{
    QStylePainter painter(this);

    // draw the combobox frame, focusrect and selected etc.
    QStyleOptionComboBox opt;
    this->initStyleOption(&opt);

    // all items are checked
    if (allChecked())
        opt.currentText = "All";
    // none are checked
    else if (noneChecked())
        opt.currentText = "None";
    //some are checked, some are not
    else
        opt.currentText = "Multiple";

    painter.drawComplexControl(QStyle::CC_ComboBox, opt);

    // draw the icon and text
    painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
}
like image 83
IAmInPLS Avatar answered Oct 20 '22 23:10

IAmInPLS


In case if someone what the implementation in python, use following code,

class CheckableComboBox(QtGui.QComboBox):
    def __init__(self, parent=None):
        super(CheckableComboBox, self).__init__(parent)
        self.view().pressed.connect(self.handleItemPressed)
        self._changed = False

    def handleItemPressed(self, index):
        item = self.model().itemFromIndex(index)
        if item.checkState() == QtCore.Qt.Checked:
            item.setCheckState(QtCore.Qt.Unchecked)
        else:
            item.setCheckState(QtCore.Qt.Checked)
        self._changed = True

    def hidePopup(self):
        if not self._changed:
            super(CheckableComboBox, self).hidePopup()
        self._changed = False

    def itemChecked(self, index):
        item = self.model().item(index, self.modelColumn())
        return item.checkState() == QtCore.Qt.Checked

    def setItemChecked(self, index, checked=True):
        item = self.model().item(index, self.modelColumn())
        if checked:
            item.setCheckState(QtCore.Qt.Checked)
        else:
            item.setCheckState(QtCore.Qt.Unchecked)

    def checkedIndexes(self):
        return self.model().match(self.model().index(0,0), QtCore.Qt.CheckStateRole, QtCore.Qt.Checked, -1, QtCore.Qt.MatchRecursive)

    def uncheckedIndexes(self):
        return self.model().match(self.model().index(0,0), QtCore.Qt.CheckStateRole, QtCore.Qt.Unchecked, -1, QtCore.Qt.MatchRecursive)

    def allChecked(self):
        return len(self.uncheckedIndexes()) == 0

    def noneChecked(self):
        return len(self.checkedIndexes()) == 0

    def paintEvent(self, event):

        painter = QtGui.QStylePainter(self)
        opt = QtGui.QStyleOptionComboBox()
        self.initStyleOption(opt)

        if self.allChecked():
            opt.currentText = "All"
        elif self.noneChecked():
            opt.currentText = "None"
        else:
            opt.currentText = "Multiple"

        painter.drawComplexControl(QtGui.QStyle.CC_ComboBox, opt)
        painter.drawControl(QtGui.QStyle.CE_ComboBoxLabel, opt)
like image 27
Oli Avatar answered Oct 20 '22 23:10

Oli