一、概述

QTreeView行列的插入与删除,若使用QTreeView+QStandardItemModel方式,或者QTreeWidget方式实现的话,直接调用相应的方法就行了。而采用QTreeView+自定义model方式的话,需要我们自己实现数据的插入与删除,故在本节中,我们主要对此方式进行说明。

前面已经对如何实现自定义model,进行了说明,所以对这部分不再赘述。

行与列的插入或删除,比较类似,所以我们只对行操作进行说明,列操作可参考行操作实现。

二、实现插入行

我们重写QAbstractItemModel类insertRows()函数,使用beginInsertRows(),endInsertRows()对实际数据插入操作进行包围,类似于告知View插入开始与结束。

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

    beginInsertRows(parent, row, row + count - 1);

    for (int i = 0; i < count; i++)
    {
        TreeItem* item = new TreeItem(parentItem);
        parentItem->insertChild(row + i, item);

        // 填充item数据
        if (parentItem->getType() == TreeItem::PROVINCE)
        {
            TreeItem::fillPersonData(item,
                                     QString("new name%1").arg(i),
                                     "man",
                                     25,
                                     "123456789");
        }
        else if (parentItem->getType() == TreeItem::UNKNOWN) // root
        {
            TreeItem::fillProvinceData(item, QString("new Province%1").arg(i));
        }
    }

    endInsertRows();
    return true;
}

beginInsertRows的参数,分别是父节点索引,插入点的行号,结束插入的行号。具体含义,下面的qt帮助截图,可以查看。

void beginInsertRows(const QModelIndex &parent, int first, int last);

Qabstractitemmodel删除行_自定义

另外,需要调用到的几个函数,我们在TreeItem中进行定义

void TreeItem::insertChild(int row, TreeItem* item)
{
    _children.insert(row, item);
}

void TreeItem::fillPersonData(TreeItem *item, const QString& name, const QString& sex, int age, const QString& phone)
{
    Person* per = new Person();
    per->name = name;
    per->sex = sex;
    per->age = age;
    per->phone = phone;

    item->setPtr(per);    // 保存数据指针
    item->setType(TreeItem::PERSON);  // 设置节点类型为PERSON
}

void TreeItem::fillProvinceData(TreeItem *item, const QString &name)
{
    Province* pro = new Province();
    pro->name = name;

    item->setPtr(pro);    // 保存数据指针
    item->setType(TreeItem::PROVINCE);  // 设置节点类型为PROVINCE
}

然后,我们在View的右键菜单槽函数中,调用model插入行

void MainWindow::onActionInsert()
{
    QModelIndex index = treeView->currentIndex();
    if(index.isValid())
    {
        treeView->model()->insertRow(index.row(), index.parent());
    }
}

小贴士:

此处调用的insertRow(),与TreeModel中重写的insertRows(),有何关联?请看下面实现:

inline bool QAbstractItemModel::insertRow(int arow, const >QModelIndex &aparent) { return insertRows(arow, 1, aparent); }

调用insertRow()会自动调用我们重写的insertRows()。

到此,我们实现了插入行。

三、实现删除行

接下来,实现删除行,就更简单了。

类比于插入行,我们也重写QAbstractItemModel类removeRows()函数,使用beginRemoveRows(),endRemoveRows()对实际数据删除操作进行包围,类似于告知View删除开始与结束。

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

    beginRemoveRows(parent, row, row + count - 1);

    for (int i = 0; i < count; i++)
    {
        parentItem->removeChild(row);
    }

    endRemoveRows();
    return true;
}

然后,我们在TreeItem中定义删除行函数

void TreeItem::removeChild(int row)
{
    TreeItem *item = _children.at(row);
    delete item;
    _children.removeAt(row);
}

然后,我们在View的右键菜单槽函数中,调用model删除行

void MainWindow::onActionRemove()
{
    QModelIndex index = treeView->currentIndex();
    if(index.isValid())
    {
        treeView->model()->removeRow(index.row(), index.parent());
    }
}

removeRow()与removeRows()的关系,与上文中小贴士提到的insertRow()与insertRows()关系雷同,不再赘述。

到此,我们实现了删除行。

四、结束

运行效果:

Qabstractitemmodel删除行_QTreeView 插入删除行_02