从 QAbstractListModel 中删除行
Posted
技术标签:
【中文标题】从 QAbstractListModel 中删除行【英文标题】:Remove rows from QAbstractListModel 【发布时间】:2013-10-10 12:57:10 【问题描述】:我有一个从 QAbstractListModel 派生的自定义模型,它暴露给 QML。我需要支持添加新项目和删除现有项目的操作。虽然插入操作没有任何问题,但删除操作会导致应用程序在调用 endRemoveRows() 函数时崩溃。
void GPageModel::addNewPage()
if(m_pageList.count()<9)
beginInsertRows(QModelIndex(),rowCount(),rowCount());
GPage * page = new GPage();
QQmlEngine::setObjectOwnership(page,QQmlEngine::CppOwnership);
page->setParent(this);
page->setNumber(m_pageList.count());
page->setName("Page " + QString::number(m_pageList.count()+1));
m_pageList.append(page);
endInsertRows();
void GPageModel::removePage(const int index)
if(index>=0 && index<m_pageList.count())
beginRemoveRows(QModelIndex(),index,index);
qDebug()<<QString("beginRemoveRows(QModelIndex(),%1,%1)").arg(index);
GPage * page = m_pageList.at(index);
m_pageList.removeAt(index);
delete page;
endRemoveRows();
GPage 类派生自 QObject。在尝试调用 endRemoveRows() 时,试图找出导致应用程序崩溃的原因让我感到震惊。当调用 endRemoveRows() 时,我得到“QList::at:“索引超出范围”中的 ASSERT 失败”。如何从 QAbsracListModel 中删除行?有没有其他办法?
我在 Windows 7 64 位机器上使用 Qt 5.1.0。
【问题讨论】:
gpage 是否继承自 QObject?如果是这样使用page.deleteLater();
而不是直接删除
对不起...我应该提供更多信息。我已经编辑了我的问题。我尝试使用 page->deleteLater()。但应用程序仍然崩溃。
【参考方案1】:
下面的代码对我来说很好用。您的问题可能在其他地方。由于使用了 Qt Quick Controls,这是针对 Qt 5 的。
有两个视图访问同一个模型,这在视觉上确认模型发出正确的信号以通知视图更改。页面添加和删除通过标准insertRows
和removeRows
方法完成,通过Q_INVOKABLE
导出。到目前为止,此模型上不需要任何自定义方法。 Q_INVOKABLE
是 QML 和 QAbstractItemModel
之间的接口缺少的一些功能的解决方法。
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickWindow>
#include <QAbstractListModel>
#include <QQmlContext>
#include <QtQml>
class GPage : public QObject
Q_OBJECT
Q_PROPERTY(QString name NOTIFY nameChanged MEMBER m_name)
Q_PROPERTY(int number NOTIFY numberChanged MEMBER m_number)
QString m_name;
int m_number;
public:
GPage(QObject * parent = 0) : QObject(parent), m_number(0)
GPage(QString name, int number, QObject * parent = 0) :
QObject(parent), m_name(name), m_number(number)
Q_SIGNAL void nameChanged(const QString &);
Q_SIGNAL void numberChanged(int);
;
class PageModel : public QAbstractListModel
Q_OBJECT
QList<GPage*> m_pageList;
public:
PageModel(QObject * parent = 0) : QAbstractListModel(parent)
~PageModel() qDeleteAll(m_pageList);
int rowCount(const QModelIndex &) const Q_DECL_OVERRIDE
return m_pageList.count();
QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE
if (role == Qt::DisplayRole || role == Qt::EditRole)
return QVariant::fromValue<QObject*>(m_pageList.at(index.row()));
return QVariant();
bool setData(const QModelIndex &index, const QVariant &value, int role) Q_DECL_OVERRIDE
Q_UNUSED(role);
GPage* page = value.value<GPage*>();
if (!page) return false;
if (page == m_pageList.at(index.row())) return true;
delete m_pageList.at(index.row());
m_pageList[index.row()] = page;
QVector<int> roles;
roles << role;
emit dataChanged(index, index, roles);
return true;
Q_INVOKABLE bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) Q_DECL_OVERRIDE
Q_UNUSED(parent);
beginInsertRows(QModelIndex(), row, row + count - 1);
for (int i = row; i < row + count; ++ i)
QString const name = QString("Page %1").arg(i + 1);
GPage * page = new GPage(name, i + 1, this);
m_pageList.insert(i, page);
QQmlEngine::setObjectOwnership(page, QQmlEngine::CppOwnership);
endInsertRows();
return true;
Q_INVOKABLE bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) Q_DECL_OVERRIDE
Q_UNUSED(parent);
beginRemoveRows(QModelIndex(), row, row + count - 1);
while (count--) delete m_pageList.takeAt(row);
endRemoveRows();
return true;
;
int main(int argc, char *argv[])
PageModel model1;
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
model1.insertRows(0, 1);
engine.rootContext()->setContextProperty("model1", &model1);
qmlRegisterType<GPage>();
engine.load(QUrl("qrc:/main.qml"));
QObject *topLevel = engine.rootObjects().value(0);
QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);
window->show();
return app.exec();
#include "main.moc"
main.qml
import QtQuick 2.0
import QtQml.Models 2.1
import QtQuick.Controls 1.0
ApplicationWindow
width: 300; height: 300
Row
width: parent.width
anchors.top: parent.top
anchors.bottom: column.top
Component
id: commonDelegate
Rectangle
width: view.width
implicitHeight: editor.implicitHeight + 10
color: "transparent"
border.color: "red"
border.width: 2
radius: 5
TextInput
id: editor
anchors.margins: 1.5 * parent.border.width
anchors.fill: parent
text: edit.name // "edit" role of the model, to break the binding loop
onTextChanged:
display.name = text;
model.display = display
ListView
id: view
width: parent.width / 2
height: parent.height
model: DelegateModel
id: delegateModel1
model: model1
delegate: commonDelegate
spacing: 2
ListView
width: parent.width / 2
height: parent.height
model: DelegateModel
model: model1
delegate: commonDelegate
spacing: 2
Column
id: column;
anchors.bottom: parent.bottom
Row
Button
text: "Add Page";
onClicked: model1.insertRows(delegateModel1.count, 1)
Button
text: "Remove Page";
onClicked: model1.removeRows(pageNo.value - 1, 1)
SpinBox
id: pageNo
minimumValue: 1
maximumValue: delegateModel1.count;
main.qrc
<RCC>
<qresource prefix="/">
<file>main.qml</file>
</qresource>
</RCC>
【讨论】:
感谢您试用。您怀疑问题可能出在其他地方,这让我想到了其他事情,您是对的。我的子类中有一个方法,它是一个 Q_INVOKABLE 函数,并在给定索引处将 GPage * 实例作为 QObject * 返回。我在我的委托中使用这个函数来绑定属性。例如:text:pageModel.at(index).name
QObject *GPageModel::at(int index) return m_pageList.at(index);
每当 endRemoveRows 被调用时,下一个被调用的函数就是上面的函数,索引为 -1,导致崩溃
@Wonderkid:OTOH,当data()
工作正常时,为什么还要使用自定义at()
方法?我的意思是,如果您只是随机实现自己的功能,为什么还要使用模型呢?甚至我的示例也不完全符合犹太教规定,因为我不应该添加 size
属性,我只是懒得正确地做;我会解决的。将GPage*
作为QObject*
传递给QVariant
就可以了,尽管将data()
方法传递出去,您仍可以正常使用GPage
的属性。
@Wonderkid:看看我如何在代理中使用GPage
的属性。检索的神奇语法是role.property
。对于设置,您可以通过role
或model.role
访问项目的用户属性——这将调用setData
。或者,您可以直接通过role.property
访问该项目,但这不会调用setData
,只是修改之前由data
返回的GPage
的实例。要设置模型,您需要: 1. 修改对象并使用 role
/model.role
设置它或 2. 将属性映射到角色,使用 roleNames
机制轻松完成。
@Wonderkid:这一切都可以通过使用Q_INVOKABLE
的一些方法以及DelegateModel
(不是VisualModel
)的库存模型来完成。不需要自定义方法:)
感谢您的解决方案。 +1 详细说明。以上是关于从 QAbstractListModel 中删除行的主要内容,如果未能解决你的问题,请参考以下文章
从 QAbstractListModel 中删除项目后 QML 崩溃
什么是最适合 QAbstractListModel 和 QListView 的 Qt 容器
从 QAbstractListModel 继承的列表模型,列表项属性不会从 QML 更新
使用 QAbstractListModel 从 python 访问 QML 中的列表元素