PDA

View Full Version : Removing row from QAbstractItemModel



volcano
28th June 2016, 12:08
Hi all,

I'm using QML treeview with model sub-classed from QAbstractItemModel.
I'm facing this issue that model indexes aren't updated when user removes a row.

Here's the code


bool TreeModel::removeRows(int row, int count, const QModelIndex &parent)
{
TreeItem *parentItem = getItem(parent);

if(parentItem == null) return false;

bool success = false;

if (count > 0)
{
beginRemoveRows(parent, row, row + count - 1);
success = parentItem->removeChildren(row, count);
endRemoveRows();
}

return success;
}

bool TreeItem::removeChildren(int position, int count)
{
if (position < 0 || position + count > children_.size())
return false;

for (int row = 0; row < count; ++row)
delete children_.takeAt(position);

return true;
}


Could you point what am I missing?

anda_skoa
28th June 2016, 14:14
Looks ok.

Does it work with a QTreeView (widget based view)?

Have you tried the model test framework? https://wiki.qt.io/Model_Test

Cheers,
_

volcano
30th June 2016, 11:37
Thanks anda_skoa for the suggestion

I tried to use the test however I can't find the .pri file to add it to the project

anda_skoa
30th June 2016, 11:41
You could write your own one or just add the two files to your .pro

Cheers,
_

volcano
1st July 2016, 09:21
I added the two files in my project

Here's my pro file


QT += qml quick widgets testlib
CONFIG += c++11

SOURCES += main.cpp \
treeviewmodel.cpp \
treeviewitem.cpp \
app.cpp \
modeltest.cpp

RESOURCES += qml.qrc

HEADERS += \
treeviewmodel.h \
treeviewitem.h \
app.h \
modeltest.h



However I get the following error
main.obj : error LNK2019: unresolved external symbol "int __cdecl qInitResources_files(void)" (?qInitResources_files@@YAHXZ) referenced in function main

Here's my main


int main(int argc, char *argv[])
{
QApplication qapp(argc, argv);

QQuickView* view = new QQuickView(QUrl(QStringLiteral("qrc:/main.qml")));
view->show();

App* app = App::instance();
app->setView(view);

return qapp.exec();
}


Could you point me what am I missing?

volcano
3rd July 2016, 16:55
Could you point what am I missing?

ChrisW67
3rd July 2016, 22:04
Look carefully at what happens to the internal list in removeChildren() when count is greater than 1. Line 24, 25 of first listing.

You do not instantiate an instance of ModelTest as in the example on the page provided.

Regarding the linker error: Run make clean, rerun qmake, and rebuild. Does the problem persist?

volcano
6th July 2016, 10:02
Thanks ChrisW67 for your feedback.

On performing the following operation " Run make clean, rerun qmake, and rebuild", the project build successfully.

However I'm unable to trace the issue of indexes when a row is removed.
I refered the Qt example of "editabletreemodel", and I noticed that the model is casted to a QAbstractItemModel and then removeRow is called with the row and the parent


void MainWindow::removeRow()
{
QModelIndex index = view->selectionModel()->currentIndex();
QAbstractItemModel *model = view->model();
if (model->removeRow(index.row(), index.parent()))
updateActions();
}


Here's the removeRow method in that example


bool TreeModel::removeRows(int position, int rows, const QModelIndex &parent)
{
TreeItem *parentItem = getItem(parent);
bool success = true;

beginRemoveRows(parent, position, position + rows - 1);
success = parentItem->removeChildren(position, rows);
endRemoveRows();

return success;
}


Which is similar to the approach I'm trying to follow.

Kindly advice what am I missing

ChrisW67
6th July 2016, 10:21
I have reread your removeChildren() function and it does not suffer from the common mistake I initially thought. My mistake.

volcano
6th July 2016, 11:03
I tried to follow the same approach used in the editabletreemodel example i.e, call model->removeRow(index.row(), index.parent())

However, this is the problem I face..
If I have 5 items - [0]A, [1]B, [2]C, [3]D, [4]E, where the brackets specify the index and alphabets are the items
and I remove item C, I am expecting the row index should be [0]A, [1]B, [2]D, [3]E
However it remains as [0]A, [1]B, [3]D, [4]E

Here's my treeitem and treemodel


TreeItem::TreeItem()
{
}

TreeItem::TreeItem(QVariant name, TreeItem *parent)
{
parentItem_ = parent;
itemName_ = name;
}

TreeItem::~TreeItem()
{
qDeleteAll(children_);
}

void TreeItem::insertChild(int row, TreeItem *item)
{
item->setParent(this);
children_.insert(row, item);
}

bool TreeItem::removeChildren(int position, int count)
{
if (position < 0 || position + count > children_.size())
return false;

for (int row = 0; row < count; ++row)
delete children_.takeAt(position);

return true;
}

TreeItem *TreeItem::child(int row) const
{
return children_.at(row);
}

TreeItem *TreeItem::parent()
{
return parentItem_;
}

void TreeItem::setParent(TreeItem* parent)
{
parentItem_ = parent;
}

int TreeItem::childrenCount() const
{
return children_.count();
}

int TreeItem::row() const
{
if (parentItem_)
return parentItem_->children_.indexOf(const_cast<TreeItem*>(this));

return 0;
}

void TreeItem::setItemName(QVariant name)
{
itemName_ = name;
}
QVariant TreeItem::itemName() const
{
return itemName_;
}

//////////////////////////////////////////////////////////////////////////
// TreeModel
TreeModel::TreeModel()
: QAbstractItemModel()
{
treeRoot_ = makeShared<TreeItem>();
}

TreeModel::~TreeModel()
{
}

QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent))
return QModelIndex();

TreeItem *parentItem = getItem(parent);

TreeItem *childItem = parentItem->child(row);
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}

QModelIndex TreeModel::parent(const QModelIndex &child) const
{
if (!child.isValid())
return QModelIndex();

TreeItem *childItem = static_cast<TreeItem*>(child.internalPointer());
TreeItem *parentItem = childItem->parent();

if (parentItem == treeRoot_.get())
return QModelIndex();

return createIndex(parentItem->row(), 0, parentItem);
}

int TreeModel::columnCount(const QModelIndex &parent) const
{
TreeItem* parentItem = getItem(parent);
return parentItem->propertyCount();
}

QVariant TreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();

TreeItem* item = getItem(index);

switch (role)
{
case NameRole:
return item->itemName();
}
return QVariant();
}

bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
bool result = true;

TreeItem* item = getItem(index);
SP_CNRF(item);

switch (role)
{
case NameRole:
item->setItemName(value);
break;

default:
result = false;
break;
}

if (result)
{
emit dataChanged(index, index);
}

return result;
}

int TreeModel::rowCount(const QModelIndex &parent) const
{
if (parent.column() > 0)
return 0;

TreeItem *parentItem = getItem(parent);
return parentItem->childrenCount();
}

bool TreeModel::removeRows(int row, int count, const QModelIndex &parent)
{
TreeItem *parentItem = getItem(parent);

if (count > 0)
{
beginRemoveRows(parent, row, row + count - 1);
success = parentItem->removeChildren(row, count);
endRemoveRows();
}

return success;
}

void TreeModel::insertItem(int position, TreeItem *item, const QModelIndex &parent)
{
TreeItem *parentItem = getItem(parent);

beginInsertRows(parent, position, position);
parentItem->insertChild(position, item);
endInsertRows();
}

TreeItem* TreeModel::getItem(const QModelIndex &index) const
{
if (index.isValid()) {
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
if (item)
return item;
}
return treeRoot_.get();
}

bool TreeModel::removeItem(const QModelIndex &index)
{
auto treeItem = getItem(index);
if (treeItem)
return removeRows(treeItem->row(), 1, parent(index));
else
return false;
}

void TreeModel::init()
{
if (treeRoot_)
{
beginResetModel();
removeRows(0, treeRoot_->childrenCount(), QModelIndex());
endResetModel();
}
}

QHash<int, QByteArray> TreeModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[NameRole] = "name";
return roles;
}