Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QListWidget with checkboxes - checking more than one row at a time
Forum Updated to NodeBB v4.3 + New Features

QListWidget with checkboxes - checking more than one row at a time

Scheduled Pinned Locked Moved Unsolved General and Desktop
14 Posts 4 Posters 236 Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • J Offline
    J Offline
    Jackmill
    wrote last edited by
    #1

    I have a QListWidget with a model with checkboxes on each item. I've enabled ExtendedSelection as the selection mode, but I still can only toggle one checkbox at a time. Is there a simple way to check/uncheck the checkboxes on all rows selected?

    I've tried getting the selected rows from the view's selection model in a connection to QListView::clicked, but that doesn't really work and will get out of step with the rows that are actually selected.

    1 Reply Last reply
    0
    • Kent-DorfmanK Offline
      Kent-DorfmanK Offline
      Kent-Dorfman
      wrote last edited by
      #2

      You don't mention the mouse/keyboard sequence you use to attempt multi-select. so, as remedial as it sounds, that is my first question.

      1 Reply Last reply
      0
      • J Offline
        J Offline
        Jackmill
        wrote last edited by
        #3

        In terms of user input, either by clicking and dragging on rows, or using the shift or ctrl key as defined by the selection mode enum, and then clicking on the checkbox on any of the selected rows. I haven't tried programmatically selecting rows.

        1 Reply Last reply
        0
        • Christian EhrlicherC Offline
          Christian EhrlicherC Offline
          Christian Ehrlicher
          Lifetime Qt Champion
          wrote last edited by
          #4

          You have to implement such a behavior by yourself. Simply retrieve the selected rows, iterate over them and set the check state you want.

          Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
          Visit the Qt Academy at https://academy.qt.io/catalog

          1 Reply Last reply
          0
          • J Offline
            J Offline
            Jackmill
            wrote last edited by Jackmill
            #5

            I think the challenge I'm having with that is differentiating between when the user clicks on the checkbox and when the user clicks anywhere in the QListView.

            I have tried subclassing QListView and reimplementing mouseReleaseEvent, but it never seems to be called:

            class MassCheckView : public QListView {
            	Q_OBJECT
            	public:
            		explicit MassCheckView(QWidget* parent = nullptr) : QListView(parent) {}
            
            		void mouseReleaseEvent(QMouseEvent *event) override;
            };
            

            --

            void MassCheckView::mouseReleaseEvent(QMouseEvent *event) {
            	auto under_mouse = childAt(event->position());
            	if (under_mouse) {
            		const auto indices = selectionModel()->selectedIndexes();
            		const auto first_checked = model()->data(indices.at(0), Qt::CheckStateRole).value<Qt::CheckState>();
            
            		for (const auto& idx : indices) {
            			model()->setData(model()->index(idx.row(), idx.column(), QModelIndex()),
            				first_checked ? Qt::Unchecked : Qt::Checked,
            				Qt::CheckStateRole);
            		}
            
            		return;
            	}
            
            	QListView::mouseReleaseEvent(event);
            }
            
            1 Reply Last reply
            0
            • Christian EhrlicherC Offline
              Christian EhrlicherC Offline
              Christian Ehrlicher
              Lifetime Qt Champion
              wrote last edited by
              #6

              Now you're using a QListView instead a QListWidget... you have to monitor your model to see when the checkstate changed then.

              Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
              Visit the Qt Academy at https://academy.qt.io/catalog

              1 Reply Last reply
              0
              • J Offline
                J Offline
                Jackmill
                wrote last edited by
                #7

                Oh shoot; that's my mistake -- I've meant QListView for this whole post.

                How might I go about that? I can connect something to dataChanged, but as far as I'm aware it will still only change one index at a time regardless of how many rows are selected.

                Christian EhrlicherC 1 Reply Last reply
                0
                • J Jackmill

                  Oh shoot; that's my mistake -- I've meant QListView for this whole post.

                  How might I go about that? I can connect something to dataChanged, but as far as I'm aware it will still only change one index at a time regardless of how many rows are selected.

                  Christian EhrlicherC Offline
                  Christian EhrlicherC Offline
                  Christian Ehrlicher
                  Lifetime Qt Champion
                  wrote last edited by
                  #8

                  @Jackmill said in QListWidget with checkboxes - checking more than one row at a time:

                  far as I'm aware it will still only change one index at a time regardless of how many rows are selected.

                  Yes, you get the dataChanged() signal, then lookup the selection and set the rest.

                  Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                  Visit the Qt Academy at https://academy.qt.io/catalog

                  1 Reply Last reply
                  0
                  • J Offline
                    J Offline
                    Jackmill
                    wrote last edited by
                    #9

                    I see. This seems to work:

                    connect(model, &QAbstractListModel::dataChanged,
                    		this, [table, model](const QModelIndex& topLeft, const QModelIndex& bottomRight) {
                    			const auto selection = table->selectionModel()->selectedIndexes();
                    
                    			if (selection.isEmpty()) { return; }
                    
                    			const auto check = model->data(topLeft, Qt::CheckStateRole).value<Qt::CheckState>();
                    
                    			model->setDataChangedEnabled(false);
                    			for (const auto& idx : selection) {
                    				if (!idx.isValid() || idx == topLeft) { continue; }
                    
                    				model->setData(idx, check ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole);
                    			}
                    			model->setDataChangedEnabled(true);
                    		});
                    

                    Enabling and disabling the emission of the dataChanged like this seems a little clunky but without it there's an endless loop and segfault.

                    jeremy_kJ Christian EhrlicherC 2 Replies Last reply
                    0
                    • J Jackmill

                      I see. This seems to work:

                      connect(model, &QAbstractListModel::dataChanged,
                      		this, [table, model](const QModelIndex& topLeft, const QModelIndex& bottomRight) {
                      			const auto selection = table->selectionModel()->selectedIndexes();
                      
                      			if (selection.isEmpty()) { return; }
                      
                      			const auto check = model->data(topLeft, Qt::CheckStateRole).value<Qt::CheckState>();
                      
                      			model->setDataChangedEnabled(false);
                      			for (const auto& idx : selection) {
                      				if (!idx.isValid() || idx == topLeft) { continue; }
                      
                      				model->setData(idx, check ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole);
                      			}
                      			model->setDataChangedEnabled(true);
                      		});
                      

                      Enabling and disabling the emission of the dataChanged like this seems a little clunky but without it there's an endless loop and segfault.

                      jeremy_kJ Offline
                      jeremy_kJ Offline
                      jeremy_k
                      wrote last edited by
                      #10

                      @Jackmill said in QListWidget with checkboxes - checking more than one row at a time:

                      Enabling and disabling the emission of the dataChanged like this seems a little clunky but without it there's an endless loop and segfault.

                      Blocking the dataChanged signal means that the view won't know that a particular cell needs to be redrawn. A flag or list of indexes to be modified could accomplish the same goal while leaving view management intact.

                      Asking a question about code? http://eel.is/iso-c++/testcase/

                      J 1 Reply Last reply
                      0
                      • J Jackmill

                        I see. This seems to work:

                        connect(model, &QAbstractListModel::dataChanged,
                        		this, [table, model](const QModelIndex& topLeft, const QModelIndex& bottomRight) {
                        			const auto selection = table->selectionModel()->selectedIndexes();
                        
                        			if (selection.isEmpty()) { return; }
                        
                        			const auto check = model->data(topLeft, Qt::CheckStateRole).value<Qt::CheckState>();
                        
                        			model->setDataChangedEnabled(false);
                        			for (const auto& idx : selection) {
                        				if (!idx.isValid() || idx == topLeft) { continue; }
                        
                        				model->setData(idx, check ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole);
                        			}
                        			model->setDataChangedEnabled(true);
                        		});
                        

                        Enabling and disabling the emission of the dataChanged like this seems a little clunky but without it there's an endless loop and segfault.

                        Christian EhrlicherC Offline
                        Christian EhrlicherC Offline
                        Christian Ehrlicher
                        Lifetime Qt Champion
                        wrote last edited by
                        #11

                        @Jackmill said in QListWidget with checkboxes - checking more than one row at a time:

                        check ? Qt::Checked

                        And this is wrong - see https://doc.qt.io/qt-6/qt.html#CheckState-enum
                        simply pass check

                        Also notice that you have to map the indexes as soon as you have a proxy model in between.

                        Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                        Visit the Qt Academy at https://academy.qt.io/catalog

                        1 Reply Last reply
                        0
                        • jeremy_kJ jeremy_k

                          @Jackmill said in QListWidget with checkboxes - checking more than one row at a time:

                          Enabling and disabling the emission of the dataChanged like this seems a little clunky but without it there's an endless loop and segfault.

                          Blocking the dataChanged signal means that the view won't know that a particular cell needs to be redrawn. A flag or list of indexes to be modified could accomplish the same goal while leaving view management intact.

                          J Offline
                          J Offline
                          Jackmill
                          wrote last edited by
                          #12

                          @jeremy_k said in QListWidget with checkboxes - checking more than one row at a time:

                          A flag or list of indexes to be modified could accomplish the same goal while leaving view management intact.

                          How might I do this? I'm having trouble thinking of a way to call setData without causing an endless loop.

                          1 Reply Last reply
                          0
                          • Christian EhrlicherC Offline
                            Christian EhrlicherC Offline
                            Christian Ehrlicher
                            Lifetime Qt Champion
                            wrote last edited by
                            #13

                            Just remeber which you already have set and that you're currently setting the other checkboxes.

                            Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                            Visit the Qt Academy at https://academy.qt.io/catalog

                            1 Reply Last reply
                            0
                            • jeremy_kJ Offline
                              jeremy_kJ Offline
                              jeremy_k
                              wrote last edited by
                              #14

                              @Jackmill said in QListWidget with checkboxes - checking more than one row at a time:

                              @jeremy_k said in QListWidget with checkboxes - checking more than one row at a time:

                              A flag or list of indexes to be modified could accomplish the same goal while leaving view management intact.

                              How might I do this? I'm having trouble thinking of a way to call setData without causing an endless loop.

                              void onDataChanged(const QModelIndex topLeft, const QModelIndex bottomRight, const QList<int> &roles)
                              {
                                  static bool updating = false;
                                  if (roles.contains(ItemDataRole::CheckStateRole) && !updating) {
                                      updating = true;
                                      auto value = topLeft.data(ItemDataRole::CheckStateRole);
                                      for (auto index : selectionModel.selectedIndexes())
                                          model->setData(index, ItemDataRole::CheckStateRole, value);
                                      updating = false;
                                  }
                              }
                              

                              The code could also disconnect this (and only this) slot from the signal prior to the loop, and reconnect it at the end.

                              Asking a question about code? http://eel.is/iso-c++/testcase/

                              1 Reply Last reply
                              1

                              • Login

                              • Login or register to search.
                              • First post
                                Last post
                              0
                              • Categories
                              • Recent
                              • Tags
                              • Popular
                              • Users
                              • Groups
                              • Search
                              • Get Qt Extensions
                              • Unsolved