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. Extra row in QTableView
Qt 6.11 is out! See what's new in the release blog

Extra row in QTableView

Scheduled Pinned Locked Moved Solved General and Desktop
8 Posts 3 Posters 1.6k 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.
  • PerdrixP Offline
    PerdrixP Offline
    Perdrix
    wrote on last edited by
    #1

    If I add three rows to my empty table model, the table view display the three rows I inserted, plus an extra blank row.

    I'd much prefer not to have that!

    If this isn't normal behaviour, what might cause this to happen?

    Thanks
    D

    JonBJ 1 Reply Last reply
    0
    • PerdrixP Offline
      PerdrixP Offline
      Perdrix
      wrote on last edited by
      #7

      For my convenience, the FrameList class had this:

      inline FrameList& beginInsertRows(int count) noexcept
      {
      	auto first{ imageGroups[index].pictures.rowCount() };	// Insert after end
      	auto last{ first + count };
      	imageGroups[index].pictures.beginInsertRows(QModelIndex(), first, last);
      	return (*this);
      }
      

      which I now realise had an "off by one" error!

      I changed the code to read:

      auto last{ first + count -1};

      and it now works as I would hope!!

      I also changed addImage() to read:

      void ImageListModel::addImage(ListBitMap image)
      {
          Q_ASSERT(std::find(mydata.begin(), mydata.end(), image) == mydata.end());
                  
          mydata.emplace_back(std::move(image));
      }
      

      as it should never actually attempt to add the same file twice (there are other checks).

      Thanks for the help in tracking this down.

      D.

      1 Reply Last reply
      1
      • PerdrixP Perdrix

        If I add three rows to my empty table model, the table view display the three rows I inserted, plus an extra blank row.

        I'd much prefer not to have that!

        If this isn't normal behaviour, what might cause this to happen?

        Thanks
        D

        JonBJ Offline
        JonBJ Offline
        JonB
        wrote on last edited by JonB
        #2

        @Perdrix
        "Nothing" would make that happen. And what about when you have 0 rows in the table without inserting anything? Are you sure your "extra blank row" is not just the empty area at the bottom/right of a QTreeView depending on its size? Otherwise show screenshot and code.

        1 Reply Last reply
        0
        • PerdrixP Offline
          PerdrixP Offline
          Perdrix
          wrote on last edited by
          #3

          Here's what the view looks like b4 loading a file:

          d26e1573-964b-4218-9b24-9fefcf6e709c-image.png

          and now after loading one file:

          9c40537c-d38b-4259-8c1e-ec66ca2c6c7c-image.png

          Here's the code that did it:

          		if (QDialog::Accepted == fileDialog.exec())
          		{
          			QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
          			QStringList files = fileDialog.selectedFiles();
          
          			//
          			// Before attempting to add the files prune out those that have already been loaded
          			// and issue an error message
          			//
          			auto it = std::remove_if(files.begin(), files.end(), 
          				[&](const QString& s) { return fileAlreadyLoaded(s.toStdU16String()); });
          			files.erase(it, files.end());
          
          			//
          			// Now add the images to the end of the current group in the frame list remembering
          			// that we need to bracket the code  that adds the with beginInsertRows(rowcount) and
          			// endInsertRows(), so the table model is informed of the new rows
          			//
          			frameList.beginInsertRows(files.size());
          			for (int i = 0; i < files.size(); i++)
          			{
          				fs::path file(files.at(i).toStdU16String());		// as UTF-16
          
          				frameList.addFile(file);
          
          				if (file.has_parent_path())
          					directory = QString::fromStdU16String(file.parent_path().generic_u16string());
          				else
          					directory = QString::fromStdU16String(file.root_path().generic_u16string());
          
          				extension = QString::fromStdU16String(file.extension().generic_u16string());
          			}
          			frameList.endInsertRows();
          

          the addFile mf of FrameList class reads like:

          		virtual bool addFile(fs::path file, PICTURETYPE PictureType = PICTURETYPE_LIGHTFRAME, bool bCheck = false, int nItem = -1)
          		{
          			imageGroups[index].addFile(file, PictureType, bCheck);
          			return true;
          		}
          

          and the mf that calls looks like:

          	void Group::addFile(fs::path file, PICTURETYPE PictureType, bool bCheck, [[maybe_unused]] int32_t nItem)
          	{
          		ZFUNCTRACE_RUNTIME();
          
          		ListBitMap			lb;
          
          		lb.m_groupId = Index;
          
          		if (lb.InitFromFile(file, PictureType))
          		{
                                : stuff deleted
          		};
          
          		pathToGroup.emplace(file, Index);
          
          		pictures.addImage(lb);
          

          pictures is an instance of a derived class of QAbstractTableModel which stores a std::vector<ListBitmap> as its backing data.

          the addImage() mf looks like:

              //
              // Add an image (row) to the table, Before calling addImage, Qt requires that 
              // beginInsertRows() be called, and once batch of images is added, then
              // endInsertRows() must be called.
              //
              void ImageListModel::addImage(ListBitMap image)
              {
                  if (std::find(mydata.begin(), mydata.end(), image) != mydata.end())
                      return;
                  mydata.emplace_back(std::move(image));
              }
          

          the rowCount mf() is simply:

                  int rowCount(const QModelIndex& parent = QModelIndex()) const override
                  {
                      // Return number of images we know about
                      return static_cast<int>(mydata.size());
                  };
          

          I checked using the debugger and rowCount is returning 1 after adding one file as I would expect.

          JonBJ 1 Reply Last reply
          0
          • PerdrixP Perdrix

            Here's what the view looks like b4 loading a file:

            d26e1573-964b-4218-9b24-9fefcf6e709c-image.png

            and now after loading one file:

            9c40537c-d38b-4259-8c1e-ec66ca2c6c7c-image.png

            Here's the code that did it:

            		if (QDialog::Accepted == fileDialog.exec())
            		{
            			QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
            			QStringList files = fileDialog.selectedFiles();
            
            			//
            			// Before attempting to add the files prune out those that have already been loaded
            			// and issue an error message
            			//
            			auto it = std::remove_if(files.begin(), files.end(), 
            				[&](const QString& s) { return fileAlreadyLoaded(s.toStdU16String()); });
            			files.erase(it, files.end());
            
            			//
            			// Now add the images to the end of the current group in the frame list remembering
            			// that we need to bracket the code  that adds the with beginInsertRows(rowcount) and
            			// endInsertRows(), so the table model is informed of the new rows
            			//
            			frameList.beginInsertRows(files.size());
            			for (int i = 0; i < files.size(); i++)
            			{
            				fs::path file(files.at(i).toStdU16String());		// as UTF-16
            
            				frameList.addFile(file);
            
            				if (file.has_parent_path())
            					directory = QString::fromStdU16String(file.parent_path().generic_u16string());
            				else
            					directory = QString::fromStdU16String(file.root_path().generic_u16string());
            
            				extension = QString::fromStdU16String(file.extension().generic_u16string());
            			}
            			frameList.endInsertRows();
            

            the addFile mf of FrameList class reads like:

            		virtual bool addFile(fs::path file, PICTURETYPE PictureType = PICTURETYPE_LIGHTFRAME, bool bCheck = false, int nItem = -1)
            		{
            			imageGroups[index].addFile(file, PictureType, bCheck);
            			return true;
            		}
            

            and the mf that calls looks like:

            	void Group::addFile(fs::path file, PICTURETYPE PictureType, bool bCheck, [[maybe_unused]] int32_t nItem)
            	{
            		ZFUNCTRACE_RUNTIME();
            
            		ListBitMap			lb;
            
            		lb.m_groupId = Index;
            
            		if (lb.InitFromFile(file, PictureType))
            		{
                                  : stuff deleted
            		};
            
            		pathToGroup.emplace(file, Index);
            
            		pictures.addImage(lb);
            

            pictures is an instance of a derived class of QAbstractTableModel which stores a std::vector<ListBitmap> as its backing data.

            the addImage() mf looks like:

                //
                // Add an image (row) to the table, Before calling addImage, Qt requires that 
                // beginInsertRows() be called, and once batch of images is added, then
                // endInsertRows() must be called.
                //
                void ImageListModel::addImage(ListBitMap image)
                {
                    if (std::find(mydata.begin(), mydata.end(), image) != mydata.end())
                        return;
                    mydata.emplace_back(std::move(image));
                }
            

            the rowCount mf() is simply:

                    int rowCount(const QModelIndex& parent = QModelIndex()) const override
                    {
                        // Return number of images we know about
                        return static_cast<int>(mydata.size());
                    };
            

            I checked using the debugger and rowCount is returning 1 after adding one file as I would expect.

            JonBJ Offline
            JonBJ Offline
            JonB
            wrote on last edited by JonB
            #4

            @Perdrix
            I admit I cannot spot what the issue is. [* Now see UPDATE below.]

            Remove all your code (for reading files, inserting into list/table, images, beginInsertRows(), etc.)! Just have rowCount() unconditionally return 1;. What does the table look like now? :)

            You return mydata.size(). But mageListModel::addImage() does not append an item to mydata if the image is already "found" in the list. Is that relevant?

            You have not said anything about your model, but I assume you are implementing your own QAbstractItemModel. Strange things happen if the implementation is incorrect. There is a QAbstractItemModelTester Class to test implementations, of ten it uncovers something.

            UPDATE STOP!!
            What is your

            frameList.beginInsertRows(files.size());
            

            ?? How does this get through Python? https://doc.qt.io/qtforpython/PySide6/QtCore/QAbstractItemModel.html#PySide6.QtCore.PySide6.QtCore.QAbstractItemModel.beginInsertRows

            PySide6.QtCore.QAbstractItemModel.beginInsertRows(parent, first, last)

            PARAMETERS

            parent – PySide6.QtCore.QModelIndex

            first – int

            last – int

            So apart from wrong number of parameters, note that last would need to be mydata.size() - 1.... Looks like you are telling it you're inserting 1 more than you are, hence an extra blank row at end? :)

            1 Reply Last reply
            3
            • M Offline
              M Offline
              mchinand
              wrote on last edited by
              #5

              The implementation of beginInsertRows() for frameList model sub-class must allow just the number of rows as a single parameter but apparently isn't calling the base class implementation correctly as @JonB points out.

              1 Reply Last reply
              0
              • PerdrixP Offline
                PerdrixP Offline
                Perdrix
                wrote on last edited by
                #6

                @JonB said in Extra row in QTableView:

                What is your
                frameList.beginInsertRows(files.size())

                it was 1 - I checked in the debugger.

                What Python got to do with it?

                David

                JonBJ 1 Reply Last reply
                0
                • PerdrixP Offline
                  PerdrixP Offline
                  Perdrix
                  wrote on last edited by
                  #7

                  For my convenience, the FrameList class had this:

                  inline FrameList& beginInsertRows(int count) noexcept
                  {
                  	auto first{ imageGroups[index].pictures.rowCount() };	// Insert after end
                  	auto last{ first + count };
                  	imageGroups[index].pictures.beginInsertRows(QModelIndex(), first, last);
                  	return (*this);
                  }
                  

                  which I now realise had an "off by one" error!

                  I changed the code to read:

                  auto last{ first + count -1};

                  and it now works as I would hope!!

                  I also changed addImage() to read:

                  void ImageListModel::addImage(ListBitMap image)
                  {
                      Q_ASSERT(std::find(mydata.begin(), mydata.end(), image) == mydata.end());
                              
                      mydata.emplace_back(std::move(image));
                  }
                  

                  as it should never actually attempt to add the same file twice (there are other checks).

                  Thanks for the help in tracking this down.

                  D.

                  1 Reply Last reply
                  1
                  • PerdrixP Perdrix

                    @JonB said in Extra row in QTableView:

                    What is your
                    frameList.beginInsertRows(files.size())

                    it was 1 - I checked in the debugger.

                    What Python got to do with it?

                    David

                    JonBJ Offline
                    JonBJ Offline
                    JonB
                    wrote on last edited by
                    #8

                    @Perdrix said in Extra row in QTableView:

                    What Python got to do with it?

                    Absolutely nothing :) I suddenly thought you had been writing in Python, and were somehow getting away with passing too few parameters, upon looking now I have no idea why!

                    1 Reply Last reply
                    0

                    • Login

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