QML ListView 未在模型重置时更新
QML ListView is not updated on model reset
Qt 5.8,Windows10。
速控 2 应用程序。在 QML 中,我有一个带有从 QAbstractListModel 派生的模型的 ListView。
在模型中我有以下代码:
void MediaPlaylistModel::update()
{
beginResetModel();
{
std::unique_lock<std::mutex> lock(m_mutex);
m_ids = m_playlist->itemsIds();
}
endResetModel();
}
调用此方法后 QML 视图中没有任何反应:列表未更新。
如果我返回然后前进(到带有 ListView 的页面)- 它将包含更新的数据。模型对象实例始终相同。
我是不是做错了什么?
更新 #1:
我重写的唯一方法是:
QHash<int, QByteArray> roleNames() const override;
int rowCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
更新 #2:
C++模型代码:
MediaPlaylistModel::MediaPlaylistModel(
QSharedPointer<AbstractMediaPlaylist> playlist,
QObject *parent) :
base_t(parent),
m_playlist(playlist)
{
Q_ASSERT(m_playlist);
connect(playlist.data(), &AbstractMediaPlaylist::changed,
this, &MediaPlaylistModel::update);
update();
}
QHash<int, QByteArray> MediaPlaylistModel::roleNames() const
{
QHash<int, QByteArray> result;
result[IdRole] = "id";
result[TitleRole] = "title";
result[DurationRole] = "duration";
return result;
}
void MediaPlaylistModel::update()
{
Q_ASSERT_SAME_THREAD;
beginResetModel();
{
std::unique_lock<std::mutex> lock(m_mutex);
m_ids = m_playlist->itemsIds();
}
endResetModel();
}
int MediaPlaylistModel::rowCount(
const QModelIndex &parent) const
{
Q_UNUSED(parent);
std::unique_lock<std::mutex> lock(m_mutex);
return static_cast<int>(m_ids.size());
}
QVariant MediaPlaylistModel::data(
const QModelIndex &index,
int role) const
{
auto row = static_cast<size_t>(index.row());
int id = 0;
{
std::unique_lock<std::mutex> lock(m_mutex);
if (row >= m_ids.size())
return QVariant();
id = m_ids[row];
}
if (role == IdRole)
return id;
QVariant result;
auto item = m_playlist->item(id);
switch(role)
{
case Qt::DisplayRole:
case TitleRole:
result = item.title;
break;
case DurationRole:
result = item.duration;
break;
}
return result;
}
QML代码:
import QtQuick 2.7
import QtQuick.Controls 2.0
import com.company.application 1.0
Page
{
id : root
property int playlistId
property var playlistApi: App.playlists.playlist(playlistId)
ListView
{
id : playlist
anchors.fill: parent
model: playlistApi.model
delegate: ItemDelegate
{
text: model.title
width: parent.width
onClicked: App.player.play(playlistId, model.id)
}
ScrollIndicator.vertical: ScrollIndicator {}
}
}
更新 #3:
更新模型时(添加了一项),QML ListView 发生了一些奇怪的事情:除了它没有更新(并且它不调用
MediaPlaylistModel::data 以检索新项目), 现有项目已损坏。当我点击已存在的项目时,它的 model.id 属性 总是 0。例如在应用程序启动时,它的 model.id 是 24,在添加一项后它的 model.id 变成了 0.
更新 #4:
App.playlists.playlist(playlistId) returns 指向此 class 实例的指针:
class CppQmlPlaylistApi :
public QObject
{
Q_OBJECT
Q_PROPERTY(QObject* model READ model NOTIFY modelChanged)
Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged)
public:
explicit CppQmlPlaylistApi(
int playlistId,
QWeakPointer<CorePlaylistsManager> playlistsMgr,
QObject *parent = 0);
QObject* model() const;
QString title() const;
void setTitle(const QString &title);
signals:
void modelChanged();
void titleChanged();
void loadPlaylistRequested(int id);
protected slots:
void onPlaylistLoaded(int id);
void onPlaylistRemoved(int id);
protected:
int m_playlistId = 0;
QWeakPointer<CorePlaylistsManager> m_playlistsMgr;
QSharedPointer<QAbstractItemModel> m_model;
};
您提供的代码很好。在 QML 方面,只要你的模型是绑定的,而不是在 JS 中动态重新创建,你也应该很好。
ListView {
model: mediaPlaylistModel
}
如果不小心超载了 beginResetModel
或 endResetModel
,就会出现问题。出于测试目的,您可以尝试发出 QAbstractItemModel::modelReset()
信号,看看它是否改变了什么。
使用 QAbstractItemModel 很容易遗漏一些东西,导致不再工作!
模型处于非 GUI 线程中。
我收到了这些调试消息(感谢 AlexanderVX 指出我):
QObject::connect: Cannot queue arguments of type 'QQmlChangeSet'
(Make sure 'QQmlChangeSet' is registered using qRegisterMetaType().)
将模型对象移动到 GUI 线程解决了这个问题。
Qt 5.8,Windows10。
速控 2 应用程序。在 QML 中,我有一个带有从 QAbstractListModel 派生的模型的 ListView。
在模型中我有以下代码:
void MediaPlaylistModel::update()
{
beginResetModel();
{
std::unique_lock<std::mutex> lock(m_mutex);
m_ids = m_playlist->itemsIds();
}
endResetModel();
}
调用此方法后 QML 视图中没有任何反应:列表未更新。 如果我返回然后前进(到带有 ListView 的页面)- 它将包含更新的数据。模型对象实例始终相同。
我是不是做错了什么?
更新 #1:
我重写的唯一方法是:
QHash<int, QByteArray> roleNames() const override;
int rowCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
更新 #2:
C++模型代码:
MediaPlaylistModel::MediaPlaylistModel(
QSharedPointer<AbstractMediaPlaylist> playlist,
QObject *parent) :
base_t(parent),
m_playlist(playlist)
{
Q_ASSERT(m_playlist);
connect(playlist.data(), &AbstractMediaPlaylist::changed,
this, &MediaPlaylistModel::update);
update();
}
QHash<int, QByteArray> MediaPlaylistModel::roleNames() const
{
QHash<int, QByteArray> result;
result[IdRole] = "id";
result[TitleRole] = "title";
result[DurationRole] = "duration";
return result;
}
void MediaPlaylistModel::update()
{
Q_ASSERT_SAME_THREAD;
beginResetModel();
{
std::unique_lock<std::mutex> lock(m_mutex);
m_ids = m_playlist->itemsIds();
}
endResetModel();
}
int MediaPlaylistModel::rowCount(
const QModelIndex &parent) const
{
Q_UNUSED(parent);
std::unique_lock<std::mutex> lock(m_mutex);
return static_cast<int>(m_ids.size());
}
QVariant MediaPlaylistModel::data(
const QModelIndex &index,
int role) const
{
auto row = static_cast<size_t>(index.row());
int id = 0;
{
std::unique_lock<std::mutex> lock(m_mutex);
if (row >= m_ids.size())
return QVariant();
id = m_ids[row];
}
if (role == IdRole)
return id;
QVariant result;
auto item = m_playlist->item(id);
switch(role)
{
case Qt::DisplayRole:
case TitleRole:
result = item.title;
break;
case DurationRole:
result = item.duration;
break;
}
return result;
}
QML代码:
import QtQuick 2.7
import QtQuick.Controls 2.0
import com.company.application 1.0
Page
{
id : root
property int playlistId
property var playlistApi: App.playlists.playlist(playlistId)
ListView
{
id : playlist
anchors.fill: parent
model: playlistApi.model
delegate: ItemDelegate
{
text: model.title
width: parent.width
onClicked: App.player.play(playlistId, model.id)
}
ScrollIndicator.vertical: ScrollIndicator {}
}
}
更新 #3:
更新模型时(添加了一项),QML ListView 发生了一些奇怪的事情:除了它没有更新(并且它不调用 MediaPlaylistModel::data 以检索新项目), 现有项目已损坏。当我点击已存在的项目时,它的 model.id 属性 总是 0。例如在应用程序启动时,它的 model.id 是 24,在添加一项后它的 model.id 变成了 0.
更新 #4:
App.playlists.playlist(playlistId) returns 指向此 class 实例的指针:
class CppQmlPlaylistApi :
public QObject
{
Q_OBJECT
Q_PROPERTY(QObject* model READ model NOTIFY modelChanged)
Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged)
public:
explicit CppQmlPlaylistApi(
int playlistId,
QWeakPointer<CorePlaylistsManager> playlistsMgr,
QObject *parent = 0);
QObject* model() const;
QString title() const;
void setTitle(const QString &title);
signals:
void modelChanged();
void titleChanged();
void loadPlaylistRequested(int id);
protected slots:
void onPlaylistLoaded(int id);
void onPlaylistRemoved(int id);
protected:
int m_playlistId = 0;
QWeakPointer<CorePlaylistsManager> m_playlistsMgr;
QSharedPointer<QAbstractItemModel> m_model;
};
您提供的代码很好。在 QML 方面,只要你的模型是绑定的,而不是在 JS 中动态重新创建,你也应该很好。
ListView {
model: mediaPlaylistModel
}
如果不小心超载了 beginResetModel
或 endResetModel
,就会出现问题。出于测试目的,您可以尝试发出 QAbstractItemModel::modelReset()
信号,看看它是否改变了什么。
使用 QAbstractItemModel 很容易遗漏一些东西,导致不再工作!
模型处于非 GUI 线程中。 我收到了这些调试消息(感谢 AlexanderVX 指出我):
QObject::connect: Cannot queue arguments of type 'QQmlChangeSet'
(Make sure 'QQmlChangeSet' is registered using qRegisterMetaType().)
将模型对象移动到 GUI 线程解决了这个问题。