Add a QCheckBox as a header to a QTableWidget
-
Hi
Well this might help
https://www.qt.io/blog/2014/04/11/qt-weekly-5-widgets-on-a-qheaderview
there is a good wrapper here
https://stackoverflow.com/questions/27000484/add-custom-widgets-as-qtablewidget-horizontalheader -
Here's also an implementation for that specific issue: https://wiki.qt.io/Technical_FAQ#How_can_I_insert_a_checkbox_into_the_header_of_my_view.3F
-
Hi
Well this might help
https://www.qt.io/blog/2014/04/11/qt-weekly-5-widgets-on-a-qheaderview
there is a good wrapper here
https://stackoverflow.com/questions/27000484/add-custom-widgets-as-qtablewidget-horizontalheader@mrjj I honestly was expecting something a lot simpler.
@Chris-Kawa the example the mousepress event doesn't work very well when I use it.
I have a dialog which its class is a member of MainWindow, I have an action that when triggered will open that dialog that contains the tablewidget, when I have it open and I click on the box I have to close the dialog and open it again to see that it's been checked and vice-versa. -
@mrjj I honestly was expecting something a lot simpler.
@Chris-Kawa the example the mousepress event doesn't work very well when I use it.
I have a dialog which its class is a member of MainWindow, I have an action that when triggered will open that dialog that contains the tablewidget, when I have it open and I click on the box I have to close the dialog and open it again to see that it's been checked and vice-versa.@hbatalha said in Add a QCheckBox as a header to a QTableWidget:
when I have it open and I click on the box I have to close the dialog and open it again to see that it's been checked and vice-versa.
Then you must've done something differently. The example I linked to certainly does not behave like that and I see nothing in it that would make it so. Can you share your custom header implementation and the part of code where you create the dialog and set the header?
-
@hbatalha said in Add a QCheckBox as a header to a QTableWidget:
when I have it open and I click on the box I have to close the dialog and open it again to see that it's been checked and vice-versa.
Then you must've done something differently. The example I linked to certainly does not behave like that and I see nothing in it that would make it so. Can you share your custom header implementation and the part of code where you create the dialog and set the header?
Then you must've done something differently.
I just copied and pasted.
Can you share your custom header implementation and the part of code where you create the dialog and set the header?
class MyHeader : public QHeaderView { public: MyHeader(Qt::Orientation orientation, QWidget * parent = nullptr) : QHeaderView(orientation, parent) {} protected: void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const { painter->save(); QHeaderView::paintSection(painter, rect, logicalIndex); painter->restore(); if (logicalIndex == 0) { QStyleOptionButton option; option.rect = QRect(5,5,15,15); if (isOn) option.state = QStyle::State_On; else option.state = QStyle::State_Off; this->style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &option, painter); } } void mousePressEvent(QMouseEvent *event) { qDebug() << "here"; if (isOn) isOn = false; else isOn = true; this->update(); QHeaderView::mousePressEvent(event); } private: bool isOn; }; HistoryWindow::HistoryWindow(QWidget *parent) : QDialog(parent), ui(new Ui::historyWindow) { ui->setupUi(this); this->setWindowTitle(tr("History")); ui->search_lineEdit->setPlaceholderText(tr("Search")); setStyleSheet("QLineEdit{" " color: white;" //TEXT COLOR "}" "QLineEdit[text=\"\"]{" " color: gray;" //TEXTHOLDER COLOR "}"); connect(ui->search_lineEdit, &QLineEdit::textChanged, [=] { style()->polish(ui->search_lineEdit); }); ui->search_lineEdit->setFocusPolicy(Qt::ClickFocus); MyHeader* h = new MyHeader(Qt::Horizontal, ui->table); ui->table->setHorizontalHeader(h); ui->table->setColumnWidth(0, 318); ui->table->setColumnWidth(1, 70); ui->table->setShowGrid(false); ui->table->horizontalScrollBar()->hide(); ui->table->setSelectionMode(QAbstractItemView::NoSelection); ui->table->setFocusPolicy(Qt::NoFocus); ui->search_lineEdit->clearFocus(); } HistoryWindow::~HistoryWindow() { delete ui; }
-
Sigh... right, don't you just love when internet examples are buggy? :P
Here's a fixed version:
class MyHeader : public QHeaderView { public: using QHeaderView::QHeaderView; protected: void paintSection(QPainter* painter, const QRect &rect, int logicalIndex) const override { painter->save(); QHeaderView::paintSection(painter, rect, logicalIndex); painter->restore(); if (model() && logicalIndex >= 0) { QStyleOptionButton option; option.init(this); QRect checkbox_rect = style()->subElementRect(QStyle::SubElement::SE_CheckBoxIndicator, &option, this); checkbox_rect.moveCenter(rect.center()); bool checked = model()->headerData(logicalIndex, orientation(), Qt::CheckStateRole).toBool(); option.rect = checkbox_rect; option.state = checked ? QStyle::State_On : QStyle::State_Off; style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &option, painter); } } void mouseReleaseEvent(QMouseEvent* event) override { QHeaderView::mouseReleaseEvent(event); if(model()) { int section = logicalIndexAt(event->pos()); if (section >= 0) { bool checked = model()->headerData(section, orientation(), Qt::CheckStateRole).toBool(); model()->setHeaderData(section, orientation(), !checked, Qt::CheckStateRole); viewport()->update(); } } } };
Just a note - for simplicity this version toggles the checkbox whenever you click anywhere in the header section. You should probably shrink this area to only the actual checkbox. You have all the info in the example - just get the checkbox rectangle and see if the click was inside it.
-
Sigh... right, don't you just love when internet examples are buggy? :P
Here's a fixed version:
class MyHeader : public QHeaderView { public: using QHeaderView::QHeaderView; protected: void paintSection(QPainter* painter, const QRect &rect, int logicalIndex) const override { painter->save(); QHeaderView::paintSection(painter, rect, logicalIndex); painter->restore(); if (model() && logicalIndex >= 0) { QStyleOptionButton option; option.init(this); QRect checkbox_rect = style()->subElementRect(QStyle::SubElement::SE_CheckBoxIndicator, &option, this); checkbox_rect.moveCenter(rect.center()); bool checked = model()->headerData(logicalIndex, orientation(), Qt::CheckStateRole).toBool(); option.rect = checkbox_rect; option.state = checked ? QStyle::State_On : QStyle::State_Off; style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &option, painter); } } void mouseReleaseEvent(QMouseEvent* event) override { QHeaderView::mouseReleaseEvent(event); if(model()) { int section = logicalIndexAt(event->pos()); if (section >= 0) { bool checked = model()->headerData(section, orientation(), Qt::CheckStateRole).toBool(); model()->setHeaderData(section, orientation(), !checked, Qt::CheckStateRole); viewport()->update(); } } } };
Just a note - for simplicity this version toggles the checkbox whenever you click anywhere in the header section. You should probably shrink this area to only the actual checkbox. You have all the info in the example - just get the checkbox rectangle and see if the click was inside it.
@Chris-Kawa thanks for the code.
I believe that instead of this line
option.init(this);
you meant
option.initFrom(this);
becasue the compiler complains about the non-existence ofinit
A question:
How can I add MyHeader only to the first colum like in the buggy example, instead of adding to all the columns in the header?Edit: To add MyHeader only to the first colum I just edited this line:
if (model() && logicalIndex >= 0)
to
if (model() && logicalIndex == 0)
-
Sigh... right, don't you just love when internet examples are buggy? :P
Here's a fixed version:
class MyHeader : public QHeaderView { public: using QHeaderView::QHeaderView; protected: void paintSection(QPainter* painter, const QRect &rect, int logicalIndex) const override { painter->save(); QHeaderView::paintSection(painter, rect, logicalIndex); painter->restore(); if (model() && logicalIndex >= 0) { QStyleOptionButton option; option.init(this); QRect checkbox_rect = style()->subElementRect(QStyle::SubElement::SE_CheckBoxIndicator, &option, this); checkbox_rect.moveCenter(rect.center()); bool checked = model()->headerData(logicalIndex, orientation(), Qt::CheckStateRole).toBool(); option.rect = checkbox_rect; option.state = checked ? QStyle::State_On : QStyle::State_Off; style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &option, painter); } } void mouseReleaseEvent(QMouseEvent* event) override { QHeaderView::mouseReleaseEvent(event); if(model()) { int section = logicalIndexAt(event->pos()); if (section >= 0) { bool checked = model()->headerData(section, orientation(), Qt::CheckStateRole).toBool(); model()->setHeaderData(section, orientation(), !checked, Qt::CheckStateRole); viewport()->update(); } } } };
Just a note - for simplicity this version toggles the checkbox whenever you click anywhere in the header section. You should probably shrink this area to only the actual checkbox. You have all the info in the example - just get the checkbox rectangle and see if the click was inside it.
@Chris-Kawa How would I implement a clicked() signal mechanism?
-
@Chris-Kawa How would I implement a clicked() signal mechanism?
Hi
you would define a signal in .h ( in the class)..
signals:
void Clicked();then use it with the keyword emit
in void mouseReleaseEvent(QMouseEvent* event)
in the spot where you check/uncheck ( maybe after viewport()->update(); )
likeemit clicked();
Do not i didn't put a parameter here to the clicked signal but in real world you might want to consider adding an int/id to say which section was clicked.
-
Just to add to what @mrjj said - a click is when you press and release mouse button on the same widget.
Having said that you shouldn't just always emit it in release event. You should record in press event which section was pressed, then in release check if it's the same one and only then emit a clicked signal.
If you release on different section than the one you press on nothing should happen. This is for the purpose of user realizing mid-click that they pressed on the wrong area. They can then move the mouse away and release and this is basically cancelling the click.
-
Just to add to what @mrjj said - a click is when you press and release mouse button on the same widget.
Having said that you shouldn't just always emit it in release event. You should record in press event which section was pressed, then in release check if it's the same one and only then emit a clicked signal.
If you release on different section than the one you press on nothing should happen. This is for the purpose of user realizing mid-click that they pressed on the wrong area. They can then move the mouse away and release and this is basically cancelling the click.
@Chris-Kawa @mrjj Done.
Is there a way to control the checkbox programatically, sending the signal and get the checkbox checked. I need the checkbox default state to be checked and I need to uncheck it when I uncheck any checkbox in the rows in the qtablewidget. Like in the OP image.
-
@Chris-Kawa @mrjj Done.
Is there a way to control the checkbox programatically, sending the signal and get the checkbox checked. I need the checkbox default state to be checked and I need to uncheck it when I uncheck any checkbox in the rows in the qtablewidget. Like in the OP image.
-
@hbatalha
hi
it uses the model so you can just set it in modelmodel->setHeaderData(section, orientation(), true/false, Qt::CheckStateRole);
-
@mrjj said in Add a QCheckBox as a header to a QTableWidget:
model->setHeaderData(section, orientation(), true/false, Qt::CheckStateRole);
what would be the value of
section
?@hbatalha
just 0,1,2,3,4 for the sections.
unless you allow drag to reorder then you have to use
https://doc.qt.io/qt-5/qheaderview.html#visualIndex
to map to the right index/ID -
@hbatalha
just 0,1,2,3,4 for the sections.
unless you allow drag to reorder then you have to use
https://doc.qt.io/qt-5/qheaderview.html#visualIndex
to map to the right index/ID -
@hbatalha
just 0,1,2,3,4 for the sections.
unless you allow drag to reorder then you have to use
https://doc.qt.io/qt-5/qheaderview.html#visualIndex
to map to the right index/ID@mrjj just 0,1,2,3,4 for the sections.
unless you allow drag to reorder then you have to use
https://doc.qt.io/qt-5/qheaderview.html#visualIndex https://www.worktime.com/employee-time-tracking-software
to map to the right index/IDHello,
the question is too simple, but I'm a beginner, please tell me what parameters of the indexer should be?Thank you
-
@mrjj just 0,1,2,3,4 for the sections.
unless you allow drag to reorder then you have to use
https://doc.qt.io/qt-5/qheaderview.html#visualIndex https://www.worktime.com/employee-time-tracking-software
to map to the right index/IDHello,
the question is too simple, but I'm a beginner, please tell me what parameters of the indexer should be?Thank you
@lisukovigor492
Hi
you mean for the
int QHeaderView::visualIndex(int logicalIndex) ?well its for when its reordered. then say index 5 is in front and then comes 2 and so on.
The issues is they are no longer in order.so you use
int realIndex = HeaderView->visualIndex(0)
and IF the header is reorderd it will then tell you what index is a section 0.
like 7. or what ever what dragged there.there is also
int QHeaderView::logicalIndex(int visualIndex)I might recall it reverse. :) but they allow to map between the sections you see on screen and their real Id/indexs.
But this is only important if you allow user to move the sections around.
To get to know them, you could enable it and then
drag some around and the see what therse functions returns for the sections.its all just integers values so nothing fancy at all.
-
Sigh... right, don't you just love when internet examples are buggy? :P
Here's a fixed version:
class MyHeader : public QHeaderView { public: using QHeaderView::QHeaderView; protected: void paintSection(QPainter* painter, const QRect &rect, int logicalIndex) const override { painter->save(); QHeaderView::paintSection(painter, rect, logicalIndex); painter->restore(); if (model() && logicalIndex >= 0) { QStyleOptionButton option; option.init(this); QRect checkbox_rect = style()->subElementRect(QStyle::SubElement::SE_CheckBoxIndicator, &option, this); checkbox_rect.moveCenter(rect.center()); bool checked = model()->headerData(logicalIndex, orientation(), Qt::CheckStateRole).toBool(); option.rect = checkbox_rect; option.state = checked ? QStyle::State_On : QStyle::State_Off; style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &option, painter); } } void mouseReleaseEvent(QMouseEvent* event) override { QHeaderView::mouseReleaseEvent(event); if(model()) { int section = logicalIndexAt(event->pos()); if (section >= 0) { bool checked = model()->headerData(section, orientation(), Qt::CheckStateRole).toBool(); model()->setHeaderData(section, orientation(), !checked, Qt::CheckStateRole); viewport()->update(); } } } };
Just a note - for simplicity this version toggles the checkbox whenever you click anywhere in the header section. You should probably shrink this area to only the actual checkbox. You have all the info in the example - just get the checkbox rectangle and see if the click was inside it.
@Chris-Kawa is there a way to change the checkbox border and mark color so it doesn't look grayed out
I have tried
QPalette p(Qt::black); style()->polish(p);
painter->setPen(Qt::black); painter->setBrush(Qt::back);
option.palette = QPalette(Qt::black);
But none of the above worked.
Is there a way to change its color? -
@Chris-Kawa is there a way to change the checkbox border and mark color so it doesn't look grayed out
I have tried
QPalette p(Qt::black); style()->polish(p);
painter->setPen(Qt::black); painter->setBrush(Qt::back);
option.palette = QPalette(Qt::black);
But none of the above worked.
Is there a way to change its color?@hbatalha It's an image so changing pen, brush or palette doesn't affect it. It might appear disabled if you give it the wrong state flags when painting, so check that the state member is set correctly in the style option.
The checkbox also appears crooked in your screenshot, which suggests the rect member is set incorrectly, so again make sure you set all the members of your style options object correctly before painting with it.