Qt wiki will be updated on October 12th 2023 starting at 11:30 AM (EEST) and the maintenance will last around 2-3 hours. During the maintenance the site will be unavailable.
Center a QCheckBox or Decoration in an Itemview
En Ar Bg De El Es Fa Fi Fr Hi Hu It Ja Kn Ko Ms Nl Pl Pt Ru Sq Th Tr Uk Zh
Overview
Currently Qt does not support centering a QCheckBox or the itemview decoration out-of-the box. There are two possibilities to achieve this behaviour: a custom item delegate derived from QStyledItemDelegate or a custom QProxyStyle.
Derive from QStyledItemDelegate
The proposed solution works when the checkbox or decoration should be aligned. As alignment indicator the Qt::TextAlignmentRole is 'misused' since no text is drawn for the cell.
class MyDelegate : public QStyledItemDelegate { public: using QStyledItemDelegate::QStyledItemDelegate; void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override { QStyleOptionViewItem opt = option; const QWidget *widget = option.widget; initStyleOption(&opt, index); QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, widget); if (opt.features & QStyleOptionViewItem::HasCheckIndicator) { switch (opt.checkState) { case Qt::Unchecked: opt.state |= QStyle::State_Off; break; case Qt::PartiallyChecked: opt.state |= QStyle::State_NoChange; break; case Qt::Checked: opt.state |= QStyle::State_On; break; } auto rect = style->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &opt, widget); opt.rect = QStyle::alignedRect(opt.direction, index.data(Qt::TextAlignmentRole).value<Qt::Alignment>(), rect.size(), opt.rect); opt.state = opt.state & ~QStyle::State_HasFocus; style->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &opt, painter, widget); } else if (!opt.icon.isNull()) { // draw the icon QRect iconRect = style->subElementRect(QStyle::SE_ItemViewItemDecoration, &opt, widget); iconRect = QStyle::alignedRect(opt.direction, index.data(Qt::TextAlignmentRole).value<Qt::Alignment>(), iconRect.size(), opt.rect); QIcon::Mode mode = QIcon::Normal; if (!(opt.state & QStyle::State_Enabled)) mode = QIcon::Disabled; else if (opt.state & QStyle::State_Selected) mode = QIcon::Selected; QIcon::State state = opt.state & QStyle::State_Open ? QIcon::On : QIcon::Off; opt.icon.paint(painter, iconRect, opt.decorationAlignment, mode, state); } else { QStyledItemDelegate::paint(painter, option, index); } } protected: bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override { Q_ASSERT(event); Q_ASSERT(model); // make sure that the item is checkable Qt::ItemFlags flags = model->flags(index); if (!(flags & Qt::ItemIsUserCheckable) || !(option.state & QStyle::State_Enabled) || !(flags & Qt::ItemIsEnabled)) return false; // make sure that we have a check state QVariant value = index.data(Qt::CheckStateRole); if (!value.isValid()) return false; const QWidget *widget = option.widget; QStyle *style = option.widget ? widget->style() : QApplication::style(); // make sure that we have the right event type if ((event->type() == QEvent::MouseButtonRelease) || (event->type() == QEvent::MouseButtonDblClick) || (event->type() == QEvent::MouseButtonPress)) { QStyleOptionViewItem viewOpt(option); initStyleOption(&viewOpt, index); QRect checkRect = style->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &viewOpt, widget); checkRect = QStyle::alignedRect(viewOpt.direction, index.data(Qt::TextAlignmentRole).value<Qt::Alignment>(), checkRect.size(), viewOpt.rect); QMouseEvent *me = static_cast<QMouseEvent *>(event); if (me->button() != Qt::LeftButton || !checkRect.contains(me->pos())) return false; if ((event->type() == QEvent::MouseButtonPress) || (event->type() == QEvent::MouseButtonDblClick)) return true; } else if (event->type() == QEvent::KeyPress) { if (static_cast<QKeyEvent *>(event)->key() != Qt::Key_Space && static_cast<QKeyEvent *>(event)->key() != Qt::Key_Select) return false; } else { return false; } Qt::CheckState state = static_cast<Qt::CheckState>(value.toInt()); if (flags & Qt::ItemIsUserTristate) state = ((Qt::CheckState)((state + 1) % 3)); else state = (state == Qt::Checked) ? Qt::Unchecked : Qt::Checked; return model->setData(index, state, Qt::CheckStateRole); } };
The delegate can be added directly to the desired view:
int main(int argc, char *argv[]) { QApplication a(argc, argv); QStandardItemModel model; model.setColumnCount(1); model.setRowCount(2); QPixmap pix(20, 20); pix.fill(Qt::blue); auto checkableItem = new QStandardItem; checkableItem->setFlags(checkableItem->flags() | Qt::ItemIsUserCheckable); checkableItem->setTextAlignment(Qt::AlignCenter); checkableItem->setCheckState(Qt::Checked); model.setItem(0, 0, checkableItem); checkableItem = new QStandardItem; checkableItem->setFlags(checkableItem->flags() | Qt::ItemIsUserCheckable); checkableItem->setTextAlignment(Qt::AlignCenter); checkableItem->setIcon(pix); model.setItem(1, 0, checkableItem); QTableView tv; tv.setModel(&model); tv.setItemDelegate(new MyDelegate); // tv.setItemDelegateForColumn(0, new MyDelegate); // when only a single column should use the delegate tv.show(); return a.exec(); }
Use a custom style derived from QProxyStyle
Another alternative is to create a custom QProxyStyle. The alignment of the checkbox and decoration are handled by custom roles (CheckAlignmentRole, DecorationAlignmentRole) in this case to not get in conflict with the text alignment and to have an indicator which cell should have the special checkbox handling.
enum { CheckAlignmentRole = Qt::UserRole + Qt::CheckStateRole + Qt::TextAlignmentRole, DecorationAlignmentRole = Qt::UserRole + Qt::DecorationRole + Qt::TextAlignmentRole }; class CenteredBoxProxyStyle : public QProxyStyle { public: using QProxyStyle::QProxyStyle; QRect subElementRect(QStyle::SubElement element, const QStyleOption *option, const QWidget *widget) const override { const QRect baseRes = QProxyStyle::subElementRect(element, option, widget); switch (element) { case SE_ItemViewItemCheckIndicator: { const QStyleOptionViewItem *const itemOpt = qstyleoption_cast<const QStyleOptionViewItem *>(option); const QVariant alignData = itemOpt ? itemOpt->index.data(CheckAlignmentRole) : QVariant(); if (!alignData.isNull()) return QStyle::alignedRect(itemOpt->direction, alignData.value<Qt::Alignment>(), baseRes.size(), itemOpt->rect); break; } case SE_ItemViewItemDecoration: { const QStyleOptionViewItem *const itemOpt = qstyleoption_cast<const QStyleOptionViewItem *>(option); const QVariant alignData = itemOpt ? itemOpt->index.data(DecorationAlignmentRole) : QVariant(); if (!alignData.isNull()) return QStyle::alignedRect(itemOpt->direction, alignData.value<Qt::Alignment>(), baseRes.size(), itemOpt->rect); break; } case SE_ItemViewItemFocusRect: { const QStyleOptionViewItem *const itemOpt = qstyleoption_cast<const QStyleOptionViewItem *>(option); const QVariant checkAlignData = itemOpt ? itemOpt->index.data(CheckAlignmentRole) : QVariant(); const QVariant decorationAlignData = itemOpt ? itemOpt->index.data(DecorationAlignmentRole) : QVariant(); if (!checkAlignData.isNull() || !decorationAlignData.isNull()) // when it is not null, then the focus rect should be drawn over the complete cell return option->rect; break; } default: break; } return baseRes; } };
The proxy style can either set for the complete application or directly for the desired view:
int main(int argc, char *argv[]) { QApplication a(argc, argv); CenteredBoxProxyStyle style; // a.setStyle(&style); // when it should be set for the complete application QStandardItemModel model; model.setColumnCount(1); model.setRowCount(2); QPixmap pix(20, 20); pix.fill(Qt::blue); auto checkableItem = new QStandardItem; checkableItem->setFlags(checkableItem->flags() | Qt::ItemIsUserCheckable); checkableItem->setCheckState(Qt::Checked); checkableItem->setData(Qt::AlignCenter, CheckAlignmentRole); model.setItem(0, 0, checkableItem); checkableItem = new QStandardItem; checkableItem->setFlags(checkableItem->flags() | Qt::ItemIsUserCheckable); checkableItem->setData(Qt::AlignCenter, DecorationAlignmentRole); checkableItem->setIcon(pix); model.setItem(1, 0, checkableItem); QTableView tv; tv.setModel(&model); tv.setStyle(&style); tv.show(); return a.exec(); }