Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Qt QTableView - Alignment of checkbox when using IsUserCheckable

I am using QTableView's checkbox flag of Qt::ItemIsUserCheckable to display a checkbox in a table cell.

After reading some things on alignment in an attempt to center the checkbox within the cell, I am returning the Qt::AlignCenter as the TextAlignmentRole from the models data() function.

QVariant ExampleModel::data(const QModelIndex &index, int role) const 
{
  if(!index.isValid())
     return QVariant();

  if (role == Qt::TextAlignmentRole) 
       return Qt::AlignCenter | Qt::AlignVCenter;
}

This however is not aligning my checkbox.

Does anyone know how to align checkboxes is this mode?

like image 939
cweston Avatar asked Dec 09 '10 22:12

cweston


2 Answers

Solution for Python (PySide, PyQt) to center the checkbox and with editable allowed:

class BooleanDelegate(QItemDelegate):

    def __init__(self, *args, **kwargs):
        super(BooleanDelegate, self).__init__(*args, **kwargs)

    def paint(self, painter, option, index):
        # Depends on how the data function of your table model is implemented
        # 'value' should recive a bool indicate if the checked value.
        value = index.data(Qt.CheckStateRole)  
        self.drawCheck(painter, option, option.rect, value)
        self.drawFocus(painter, option, option.rect)

    def editorEvent(self, event, model, option, index):
        if event.type() == QEvent.MouseButtonRelease:
            value = bool(model.data(index, Qt.CheckStateRole))
            model.setData(index, not value)
            event.accept()
        return super(BooleanDelegate, self).editorEvent(event, model, option, index)

In your table model, make sure that the flags allow the user to check/uncheck the cell.

class MyTableModel(QAbstractTableModel):

    ...

    def flags(self, index):
        if not index.isValid():
            return Qt.ItemIsEnabled
        if index.column() in self.columns_boolean:
            return Qt.ItemIsEnabled | Qt.ItemIsUserCheckable
        return Qt.ItemFlags(QAbstractTableModel.flags(self, index) | Qt.ItemIsEditable)

Finally, set the BooleanDelagate in your table

self.boolean_delegate = BooleanDelegate()
self.input_gui.setItemDelegateForColumn(5, self.boolean_delegate)
like image 53
E.G. Cortes Avatar answered Sep 17 '22 17:09

E.G. Cortes


This is the solution I came up with. This is assuming that you want the checkbox to be the only thing in the cell.

class CenteredCheckboxDelegate final : public QStyledItemDelegate
{
public:
    using QStyledItemDelegate::QStyledItemDelegate;

    void paint(QPainter * painter, const QStyleOptionViewItem & o, const QModelIndex & index ) const override
    {
        auto option2 = o;
        initStyleOption(&option2, index);

        auto * widget = option2.widget;
        auto * style = widget ? widget->style() : QApplication::style();

        // Turn off all features and just draw the background
        option2.state.setFlag(QStyle::State_HasFocus, false);
        option2.features.setFlag(QStyleOptionViewItem::HasDisplay, false);
        option2.features.setFlag(QStyleOptionViewItem::HasDecoration, false);
        option2.features.setFlag(QStyleOptionViewItem::HasCheckIndicator, false);
        style->drawControl(QStyle::CE_ItemViewItem, &option2, painter, widget);

        // Then just draw the a checkbox centred in the cell
        option2.rect = getCheckboxRect(option2);
        auto stateFlag = option2.checkState == Qt::Checked ? QStyle::State_On : QStyle::State_Off;
        option2.state.setFlag(stateFlag, true);
        style->drawPrimitive(QStyle::PE_IndicatorViewItemCheck, &option2, painter, widget);
    }

    bool editorEvent(QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index) override
    {
        auto flags = index.flags();
        if (!flags.testFlag(Qt::ItemIsUserCheckable) || !flags.testFlag(Qt::ItemIsEnabled))
        {
            return false;
        }

        if(event->type() == QEvent::MouseButtonRelease)
        {
            auto * mouseEvent = static_cast<QMouseEvent*>(event);
            bool mouseOverCheckbox = getCheckboxRect(option).contains(mouseEvent->pos());
            if(!mouseOverCheckbox) return false;
        }
        else if(event->type() == QEvent::KeyPress)
        {
            auto * keyEvent = static_cast<QKeyEvent*>(event);
            if(keyEvent->key() != Qt::Key_Space) return false;
        }
        else
        {
            return false;
        }

        auto checkState = index.data(Qt::CheckStateRole).value<Qt::CheckState>();
        auto toggledCheckState = checkState == Qt::Checked ? Qt::Unchecked : Qt::Checked;
        return model->setData(index, toggledCheckState, Qt::CheckStateRole);
    }

private:
    QRect getCheckboxRect(const QStyleOptionViewItem & option) const
    {
        auto * widget = option.widget;
        auto * style = widget ? widget->style() : QApplication::style();
        auto checkboxSize = style->subElementRect(QStyle::SE_CheckBoxIndicator, &option, widget).size();
        return QStyle::alignedRect(option.direction, Qt::AlignCenter, checkboxSize, option.rect);
    }
};
like image 43
Parker Coates Avatar answered Sep 17 '22 17:09

Parker Coates