How to center a column with a checkbox in QTableView?
-
wrote on 26 Aug 2018, 17:06 last edited by
I was able to get my QTableView widget to display a checkbox by returning Qt::CheckState in the model's data() function. However, it shows it left aligned in the column. I looked at the delegate stuff but can't figure out how to change the default alignment of a column without subclassing QAbstractItemDelegate, which seems like way too much work for this.
Thanks for any help!
-
You're correct - the only way to do so is to write your own delegate (derived from QStyledItemDelegate)
-
wrote on 15 Jun 2019, 15:29 last edited by gurpal2000
This is such a common use case that there are literally no trustworthy examples for beginners for this case.
It'd make QT more of a pleasure to get into if there were examples for these common patterns. I agree with the OP that this is just a large amount of code for such a simple thing.
Maybe even the components should just support this out of the box with minimal code. Why hasn't this been done though?
-
wrote on 16 Jun 2019, 16:08 last edited by
You can try a different use...
Add QWidget instead of QCheckBox in your QTableView.
for example:QWidget * checkWidget = new QWidget(this); QCheckBox * check = new QCheckBox(this); QHBoxLayout * checkLayout = new QHBoxLayout(checkWidget); checkLayout->addWidget(check); checkLayout->setAlignment(Qt::AlignCenter); checkLayout->setContentsMargins(0,0,0,0);
// add checkWidget to QTableView
-
And how to you set the value from the model? Using setIndexWidget is definitely no way to go.
@gurpal2000 : fell free to provide a patch to Qt so it gets integrated... you can add me as reviewer. -
This is such a common use case that there are literally no trustworthy examples for beginners for this case.
It'd make QT more of a pleasure to get into if there were examples for these common patterns. I agree with the OP that this is just a large amount of code for such a simple thing.
Maybe even the components should just support this out of the box with minimal code. Why hasn't this been done though?
wrote on 17 Jun 2019, 11:40 last edited by VRoninThis post is deleted! -
wrote on 17 Jun 2019, 13:05 last edited byThis post is deleted!
-
wrote on 17 Jun 2019, 13:50 last edited by
Try something like this:
#include <QApplication> #include <QTableWidget> #include <QStandardItemModel> #include <QProxyStyle> #include <QStyleOptionViewItem> enum { CheckAlignmentRole = Qt::UserRole + Qt::CheckStateRole + Qt::TextAlignmentRole }; class CenteredBoxProxy : public QProxyStyle{ public: QRect subElementRect(QStyle::SubElement element, const QStyleOption *option, const QWidget *widget) const override{ const QRect baseRes = QProxyStyle::subElementRect(element, option, widget); if(element==SE_ItemViewItemCheckIndicator){ const QStyleOptionViewItem* const itemOpt = qstyleoption_cast<const QStyleOptionViewItem*>(option) ; Q_ASSERT(itemOpt); const QVariant alignData = itemOpt->index.data(CheckAlignmentRole); if(alignData.isNull()) return baseRes; const QRect itemRect = option->rect; Q_ASSERT(itemRect.width()>baseRes.width() && itemRect.height()>baseRes.height()); const int alignFlag = alignData.toInt(); int x=0,y=0; if(alignFlag & Qt::AlignLeft){ x=baseRes.x(); } else if(alignFlag & Qt::AlignRight){ x=itemRect.x() + itemRect.width() - (baseRes.x()-itemRect.x())-baseRes.width(); } else if(alignFlag & Qt::AlignHCenter){ x=itemRect.x() + (itemRect.width()/2)-(baseRes.width()/2); } return QRect(QPoint(x,baseRes.y()), baseRes.size()); } return baseRes; } }; int main(int argc, char **argv) { QApplication app(argc,argv); app.setStyle(new CenteredBoxProxy); QTableView wid; QStandardItemModel* model = new QStandardItemModel(&wid); model->insertColumns(0,3); model->insertRows(0,10); for(int i=0;i<10;++i){ model->setData(model->index(i,0), i%2==0 ? Qt::Unchecked : Qt::Checked, Qt::CheckStateRole); model->setData(model->index(i,0), Qt::AlignCenter, CheckAlignmentRole); model->setData(model->index(i,1), i%2==0 ? Qt::Unchecked : Qt::Checked, Qt::CheckStateRole); model->setData(model->index(i,2), i); Q_ASSERT(model->item(i,0)); model->item(i,0)->setFlags(model->item(i,0)->flags() | Qt::ItemIsUserCheckable); } wid.setModel(model); wid.show(); return app.exec(); }
-
Try something like this:
#include <QApplication> #include <QTableWidget> #include <QStandardItemModel> #include <QProxyStyle> #include <QStyleOptionViewItem> enum { CheckAlignmentRole = Qt::UserRole + Qt::CheckStateRole + Qt::TextAlignmentRole }; class CenteredBoxProxy : public QProxyStyle{ public: QRect subElementRect(QStyle::SubElement element, const QStyleOption *option, const QWidget *widget) const override{ const QRect baseRes = QProxyStyle::subElementRect(element, option, widget); if(element==SE_ItemViewItemCheckIndicator){ const QStyleOptionViewItem* const itemOpt = qstyleoption_cast<const QStyleOptionViewItem*>(option) ; Q_ASSERT(itemOpt); const QVariant alignData = itemOpt->index.data(CheckAlignmentRole); if(alignData.isNull()) return baseRes; const QRect itemRect = option->rect; Q_ASSERT(itemRect.width()>baseRes.width() && itemRect.height()>baseRes.height()); const int alignFlag = alignData.toInt(); int x=0,y=0; if(alignFlag & Qt::AlignLeft){ x=baseRes.x(); } else if(alignFlag & Qt::AlignRight){ x=itemRect.x() + itemRect.width() - (baseRes.x()-itemRect.x())-baseRes.width(); } else if(alignFlag & Qt::AlignHCenter){ x=itemRect.x() + (itemRect.width()/2)-(baseRes.width()/2); } return QRect(QPoint(x,baseRes.y()), baseRes.size()); } return baseRes; } }; int main(int argc, char **argv) { QApplication app(argc,argv); app.setStyle(new CenteredBoxProxy); QTableView wid; QStandardItemModel* model = new QStandardItemModel(&wid); model->insertColumns(0,3); model->insertRows(0,10); for(int i=0;i<10;++i){ model->setData(model->index(i,0), i%2==0 ? Qt::Unchecked : Qt::Checked, Qt::CheckStateRole); model->setData(model->index(i,0), Qt::AlignCenter, CheckAlignmentRole); model->setData(model->index(i,1), i%2==0 ? Qt::Unchecked : Qt::Checked, Qt::CheckStateRole); model->setData(model->index(i,2), i); Q_ASSERT(model->item(i,0)); model->item(i,0)->setFlags(model->item(i,0)->flags() | Qt::ItemIsUserCheckable); } wid.setModel(model); wid.show(); return app.exec(); }
wrote on 20 Jun 2019, 12:28 last edited by@VRonin ... THANK YOU! This works very well.
-
wrote on 25 Jun 2019, 11:25 last edited by
I saw this in qstyle.h in the "enum SubElement":
SE_ViewItemCheckIndicator, // ### Qt 6: remove SE_ItemViewItemCheckIndicator = SE_ViewItemCheckIndicator,
...
I assume thatSE_ItemViewItemCheckIndicator
is safe to continue to use? -
Hi,
Yes it is.
-
wrote on 26 Jun 2019, 09:07 last edited by
I have adapted the code a little bit to take care of the focus rectangle which is also centered now. Of course, I will include a link to this forum topic in my code, which will be open source (thanks again, @VRonin :)
#ifndef CENTEREDBOXPROXY_HPP #define CENTEREDBOXPROXY_HPP #include <QProxyStyle> #include <QStyleOptionViewItem> enum { CheckAlignmentRole = Qt::UserRole + Qt::CheckStateRole + Qt::TextAlignmentRole }; class CenteredBoxProxy : public QProxyStyle { public: QRect subElementRect(QStyle::SubElement element, const QStyleOption *option, const QWidget *widget) const override { const QRect baseRes = QProxyStyle::subElementRect(element, option, widget); const QRect itemRect = option->rect; QRect retval = baseRes; QSize sz = baseRes.size(); const QStyleOptionViewItem* const itemOpt = qstyleoption_cast<const QStyleOptionViewItem*>(option); if (itemOpt) { const QVariant alignData = itemOpt->index.data(CheckAlignmentRole); if(!alignData.isNull()) { const uint alignFlag = alignData.toUInt(); if (alignFlag & Qt::AlignHCenter) { if (element == SE_ItemViewItemCheckIndicator) { int x = itemRect.x() + (itemRect.width()/2) - (baseRes.width()/2); retval = QRect( QPoint(x, baseRes.y()), sz); } else if (element == SE_ItemViewItemFocusRect) { sz.setWidth(baseRes.width()+baseRes.x()); retval = QRect( QPoint(0, baseRes.y()), sz); } } } } return retval; } }; #endif
-
Thanks for the update !
-
I have adapted the code a little bit to take care of the focus rectangle which is also centered now. Of course, I will include a link to this forum topic in my code, which will be open source (thanks again, @VRonin :)
#ifndef CENTEREDBOXPROXY_HPP #define CENTEREDBOXPROXY_HPP #include <QProxyStyle> #include <QStyleOptionViewItem> enum { CheckAlignmentRole = Qt::UserRole + Qt::CheckStateRole + Qt::TextAlignmentRole }; class CenteredBoxProxy : public QProxyStyle { public: QRect subElementRect(QStyle::SubElement element, const QStyleOption *option, const QWidget *widget) const override { const QRect baseRes = QProxyStyle::subElementRect(element, option, widget); const QRect itemRect = option->rect; QRect retval = baseRes; QSize sz = baseRes.size(); const QStyleOptionViewItem* const itemOpt = qstyleoption_cast<const QStyleOptionViewItem*>(option); if (itemOpt) { const QVariant alignData = itemOpt->index.data(CheckAlignmentRole); if(!alignData.isNull()) { const uint alignFlag = alignData.toUInt(); if (alignFlag & Qt::AlignHCenter) { if (element == SE_ItemViewItemCheckIndicator) { int x = itemRect.x() + (itemRect.width()/2) - (baseRes.width()/2); retval = QRect( QPoint(x, baseRes.y()), sz); } else if (element == SE_ItemViewItemFocusRect) { sz.setWidth(baseRes.width()+baseRes.x()); retval = QRect( QPoint(0, baseRes.y()), sz); } } } } return retval; } }; #endif
wrote on 1 Jul 2019, 07:27 last edited by@Robert-Hairgrove said in How to center a column with a checkbox in QTableView?:
retval = QRect( QPoint(0, baseRes.y()), sz);
This seams dodgy. Could you try making the second column the one with the centred checkbox and see if it still works as expected
-
@Robert-Hairgrove said in How to center a column with a checkbox in QTableView?:
retval = QRect( QPoint(0, baseRes.y()), sz);
This seams dodgy. Could you try making the second column the one with the centred checkbox and see if it still works as expected
wrote on 1 Jul 2019, 11:32 last edited by Robert Hairgrove 7 Jan 2019, 11:35@VRonin ... Looks OK to me. But thanks for the suggestion, because I should have tried this myself before posting the update (and I hadn't).
The normal behavior for item views seems to be this:
- When the widget has focus, the view item's focus indicator fills out the background of the entire item rectangle and is darker in color (e.g. when navigating the view with the arrow keys);
- When the widget goes out of focus (e.g. clicking outside of the entire widget), there is another focus indicator which is shown, lighter in color, but only over the text normally accompanying the checkbox -- not over the checkbox itself.
When the checkbox has no text (i.e. when it is centered), this seems a little odd. This update "fixes" the non-focussed focus rectangle. :)
-
wrote on 1 Jul 2019, 12:26 last edited by
Sorry I should have been more specific, my only concern is on having
0
as first argument toQPoint
there as I think it's in coordinates of the scrollarea not of the item itself so my concern is that if the checkbox is in any column except the first column,retval
would still point somewhere in the first column -
You can try a different use...
Add QWidget instead of QCheckBox in your QTableView.
for example:QWidget * checkWidget = new QWidget(this); QCheckBox * check = new QCheckBox(this); QHBoxLayout * checkLayout = new QHBoxLayout(checkWidget); checkLayout->addWidget(check); checkLayout->setAlignment(Qt::AlignCenter); checkLayout->setContentsMargins(0,0,0,0);
// add checkWidget to QTableView
wrote on 24 Dec 2021, 20:10 last edited by@A-A-SEZEN It does not work.
-
@A-A-SEZEN It does not work.
wrote on 25 Dec 2021, 15:57 last edited by@Alexey-Serebryakov .. I am actively using those codes. With only one difference. QTableWidget instead of QTableView.
else if(yapi->slAlan[i] == "ISLEM") { auto * checkWidget = new QWidget(this); auto * check = new QCheckBox(this); auto * checkLayout = new QHBoxLayout(checkWidget); checkLayout->addWidget(check); checkLayout->setAlignment(Qt::AlignCenter); checkLayout->setContentsMargins(0,0,0,0); check->setMaximumWidth(yapi->slColumnWidth[i].toInt()); check->setChecked(q.value(yapi->slAlan[i]).toString() == "2"); check->setCheckable(!bPasif); if(!renk.isEmpty()) { checkWidget->setStyleSheet(tr("background-color: %1").arg(renk)); } tablo->setCellWidget(iSatir, i, checkWidget); checkWidget->setContextMenuPolicy(Qt::CustomContextMenu); connect(checkWidget, &QMenu::customContextMenuRequested, this, &kucukbas::doganPopupMenu); connect(check, &QCheckBox::stateChanged, this, [=]() { doganKaydet("ISLEM", check->isChecked() ? "2" : "0", idDogan()); }); }