Delete QTableWidget selected rows
-
@hbatalha said in Delete QTableWidget selected rows:
How would I connect the checkbox when clicked?
QObject::connect(tableWidget->model(),&QAbstractItemModel::dataChanged,[](const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles){ if(roles.isEmpty() || roles.contains(Qt::CheckStateRole){ for(int i=topLeft.row();i<bottomRight.row();++i){ for(int j=topLeft.column();j<bottomRight.column();++j){ qDebug() << "Checkbox changed in row " << i << " column " << j; } } } });
how to check if it is checked?
tableWidgetItem->data(Qt::CheckStateRole).value<Qt::CheckState>() == Qt::Checked
@VRonin said in Delete QTableWidget selected rows:
@hbatalha said in Delete QTableWidget selected rows:
How would I connect the checkbox when clicked?
QObject::connect(tableWidget->model(),&QAbstractItemModel::dataChanged,[](const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles){ if(roles.isEmpty() || roles.contains(Qt::CheckStateRole){ for(int i=topLeft.row();i<bottomRight.row();++i){ for(int j=topLeft.column();j<bottomRight.column();++j){ qDebug() << "Checkbox changed in row " << i << " column " << j; } } } });
I am trying to change the implementation however it is proving to be quite difficult for me to connect it. I need to connect it in a way that I will know whether I am checking or unchecking the checkbox. I tried this every time I add a row:
QTableWidgetItem* item = ui->table->item(dest_row, 0); item->setData(Qt::CheckStateRole,Qt::Checked); // makes the checkbox appear item->setFlags(item->flags() | Qt::ItemIsUserCheckable); QObject::connect(ui->table->model(),&QAbstractItemModel::dataChanged,[item](const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles) { if(item->data(Qt::CheckStateRole).value<Qt::CheckState>() == Qt::Checked) { //do something } });
But it is not practical since it becomes too slow when adding a lot of rows in a loop for example.
-
Are you calling
QObject::connect
every time you add a row? No, that should only be done once when you create the model (in the constructor) -
I see I confused you a bit. See this minimal example on how this works:
#include <QApplication> #include <QTableWidget> #include <QPushButton> #include <QVBoxLayout> #include <QDebug> class ExampleWidget : public QWidget{ Q_DISABLE_COPY(ExampleWidget) public: explicit ExampleWidget(QWidget* parent = nullptr) :QWidget(parent) { tableWidget= new QTableWidget(0,1,this); connect(tableWidget->model(),&QAbstractItemModel::dataChanged,this,&ExampleWidget::checkboxChanged); addRowButton= new QPushButton(tr("Add Row"),this); connect(addRowButton,&QPushButton::clicked,this,&ExampleWidget::addRow); QVBoxLayout* mainLay = new QVBoxLayout(this); mainLay->addWidget(tableWidget); mainLay->addWidget(addRowButton); } private slots: void addRow(){ QTableWidgetItem* item = new QTableWidgetItem; const int numRows = tableWidget->rowCount(); item->setData(Qt::EditRole, numRows+1); item->setData(Qt::CheckStateRole,Qt::Checked); // makes the checkbox appear item->setFlags(item->flags() | Qt::ItemIsUserCheckable); tableWidget->insertRow(numRows); tableWidget->setItem(numRows,0,item); } void checkboxChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles){ if(roles.contains(Qt::CheckStateRole)|| roles.isEmpty()){ for(int i=topLeft.row();i<=bottomRight.row();++i){ for(int j=topLeft.column();j<=bottomRight.column();++j){ qDebug() << "checkbox changed in row " << i << " column " << j; QTableWidgetItem* item = tableWidget->item(i,j); if(item->data(Qt::CheckStateRole).value<Qt::CheckState>() == Qt::Checked) qDebug() << "checkbox is checked"; else qDebug() << "checkbox is uncecked"; } } } } private: QTableWidget* tableWidget; QPushButton* addRowButton; }; int main(int argc, char *argv[]) { QApplication app(argc,argv); ExampleWidget wid; wid.show(); return app.exec(); }
As you can see the connect is done only once and it's overall really fast. If you don't want your slot to be triggered when a new item is inserted then you just need to remove
|| roles.isEmpty()
Forgot to mention this requires Qt >= 5.12 -
I see I confused you a bit. See this minimal example on how this works:
#include <QApplication> #include <QTableWidget> #include <QPushButton> #include <QVBoxLayout> #include <QDebug> class ExampleWidget : public QWidget{ Q_DISABLE_COPY(ExampleWidget) public: explicit ExampleWidget(QWidget* parent = nullptr) :QWidget(parent) { tableWidget= new QTableWidget(0,1,this); connect(tableWidget->model(),&QAbstractItemModel::dataChanged,this,&ExampleWidget::checkboxChanged); addRowButton= new QPushButton(tr("Add Row"),this); connect(addRowButton,&QPushButton::clicked,this,&ExampleWidget::addRow); QVBoxLayout* mainLay = new QVBoxLayout(this); mainLay->addWidget(tableWidget); mainLay->addWidget(addRowButton); } private slots: void addRow(){ QTableWidgetItem* item = new QTableWidgetItem; const int numRows = tableWidget->rowCount(); item->setData(Qt::EditRole, numRows+1); item->setData(Qt::CheckStateRole,Qt::Checked); // makes the checkbox appear item->setFlags(item->flags() | Qt::ItemIsUserCheckable); tableWidget->insertRow(numRows); tableWidget->setItem(numRows,0,item); } void checkboxChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles){ if(roles.contains(Qt::CheckStateRole)|| roles.isEmpty()){ for(int i=topLeft.row();i<=bottomRight.row();++i){ for(int j=topLeft.column();j<=bottomRight.column();++j){ qDebug() << "checkbox changed in row " << i << " column " << j; QTableWidgetItem* item = tableWidget->item(i,j); if(item->data(Qt::CheckStateRole).value<Qt::CheckState>() == Qt::Checked) qDebug() << "checkbox is checked"; else qDebug() << "checkbox is uncecked"; } } } } private: QTableWidget* tableWidget; QPushButton* addRowButton; }; int main(int argc, char *argv[]) { QApplication app(argc,argv); ExampleWidget wid; wid.show(); return app.exec(); }
As you can see the connect is done only once and it's overall really fast. If you don't want your slot to be triggered when a new item is inserted then you just need to remove
|| roles.isEmpty()
Forgot to mention this requires Qt >= 5.12@VRonin A good solution but I do have a question:
void checkboxChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles){ if(roles.contains(Qt::CheckStateRole)|| roles.isEmpty()){ for(int i=topLeft.row();i<=bottomRight.row();++i){ for(int j=topLeft.column();j<=bottomRight.column();++j){ qDebug() << "checkbox changed in row " << i << " column " << j; QTableWidgetItem* item = tableWidget->item(i,j); if(item->data(Qt::CheckStateRole).value<Qt::CheckState>() == Qt::Checked) qDebug() << "checkbox is checked"; else qDebug() << "checkbox is uncecked"; } } } }
From what I could see, isn't that a little expensive to check all the checkboxes every time one is changed, suppose the user has a table with 2 hundreds rows?
Edit: the
topLeft.row()
is the exact row where the checkbox is modified so it can be used instead of iterating through all the columns and rows. -
@VRonin A good solution but I do have a question:
void checkboxChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles){ if(roles.contains(Qt::CheckStateRole)|| roles.isEmpty()){ for(int i=topLeft.row();i<=bottomRight.row();++i){ for(int j=topLeft.column();j<=bottomRight.column();++j){ qDebug() << "checkbox changed in row " << i << " column " << j; QTableWidgetItem* item = tableWidget->item(i,j); if(item->data(Qt::CheckStateRole).value<Qt::CheckState>() == Qt::Checked) qDebug() << "checkbox is checked"; else qDebug() << "checkbox is uncecked"; } } } }
From what I could see, isn't that a little expensive to check all the checkboxes every time one is changed, suppose the user has a table with 2 hundreds rows?
Edit: the
topLeft.row()
is the exact row where the checkbox is modified so it can be used instead of iterating through all the columns and rows.@hbatalha said in Delete QTableWidget selected rows:
to check all the checkboxes
I'm not checking all the checkboxes. The signal tells you that something has changed in the rectangle with corners
topLeft
bottomRight
so i check only that rectangle. 99% of the cases that rectangle is just a single item (topLeft==bottomRight
) so you end up checking only 1 item -
@hbatalha said in Delete QTableWidget selected rows:
to check all the checkboxes
I'm not checking all the checkboxes. The signal tells you that something has changed in the rectangle with corners
topLeft
bottomRight
so i check only that rectangle. 99% of the cases that rectangle is just a single item (topLeft==bottomRight
) so you end up checking only 1 item -
@hbatalha said in Delete QTableWidget selected rows:
to check all the checkboxes
I'm not checking all the checkboxes. The signal tells you that something has changed in the rectangle with corners
topLeft
bottomRight
so i check only that rectangle. 99% of the cases that rectangle is just a single item (topLeft==bottomRight
) so you end up checking only 1 item -
See https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtableview
The
QTableView
's checkbox indicator can also be customized. In the following snippet the indicatorbackground-color
in unchecked state is customized:QTableView::indicator:unchecked { background-color: #d7d6d5 }
-
See https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtableview
The
QTableView
's checkbox indicator can also be customized. In the following snippet the indicatorbackground-color
in unchecked state is customized:QTableView::indicator:unchecked { background-color: #d7d6d5 }
@VRonin It looked kinda weird. So I tried to change the background-color for both checked and unchecked but it only works for one.
This is the result:
I want a similar result I achieved with a QCheckBox by setting the palette:
The code for QCheckBox:
QCheckBox *title_ckbox = new QCheckBox(title); title_ckbox->setToolTip(title); QPalette p = title_ckbox->palette( ); QColor blue( 0, 0, 255 ); p.setColor( QPalette::Active, QPalette::Base, blue ); title_ckbox->setPalette(p);
Is it possible to have something like that?
-
@VRonin It looked kinda weird. So I tried to change the background-color for both checked and unchecked but it only works for one.
This is the result:
I want a similar result I achieved with a QCheckBox by setting the palette:
The code for QCheckBox:
QCheckBox *title_ckbox = new QCheckBox(title); title_ckbox->setToolTip(title); QPalette p = title_ckbox->palette( ); QColor blue( 0, 0, 255 ); p.setColor( QPalette::Active, QPalette::Base, blue ); title_ckbox->setPalette(p);
Is it possible to have something like that?
-
@hbatalha said in Delete QTableWidget selected rows:
Is it possible to have something like that?
class CheckBoxDelegate : public QStyledItemDelegate{ Q_DISABLE_COPY(CheckBoxDelegate) public: using QStyledItemDelegate::QStyledItemDelegate; protected: void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const override{ QStyledItemDelegate::initStyleOption(option,index); option->palette.setColor( QPalette::Active, QPalette::Base, Qt::blue ); } };
tableWidget->setItemDelegate(new CheckBoxDelegate(this));
-
@hbatalha said in Delete QTableWidget selected rows:
Is it possible to have something like that?
class CheckBoxDelegate : public QStyledItemDelegate{ Q_DISABLE_COPY(CheckBoxDelegate) public: using QStyledItemDelegate::QStyledItemDelegate; protected: void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const override{ QStyledItemDelegate::initStyleOption(option,index); option->palette.setColor( QPalette::Active, QPalette::Base, Qt::blue ); } };
tableWidget->setItemDelegate(new CheckBoxDelegate(this));