Extra row in QTableView
-
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.
-
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@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 aQTreeViewdepending on its size? Otherwise show screenshot and code. -
Here's what the view looks like b4 loading a file:

and now after loading one file:

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.
-
Here's what the view looks like b4 loading a file:

and now after loading one file:

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.
@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 haverowCount()unconditionallyreturn 1;. What does the table look like now? :)You return
mydata.size(). ButmageListModel::addImage()does not append an item tomydataif theimageis 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 yourframeList.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
lastwould need to bemydata.size() - 1.... Looks like you are telling it you're inserting 1 more than you are, hence an extra blank row at end? :) -
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.
-
@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