2

Qt 5.8,Windows 10。

快速控制 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) 返回指向此类实例的指针:

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;
};
4

2 回答 2

1

该模型在非 GUI 线程中。我收到了这些调试消息(感谢 AlexanderVX 指出我):

QObject::connect: Cannot queue arguments of type 'QQmlChangeSet' 
(Make sure 'QQmlChangeSet' is registered using qRegisterMetaType().)

将模型对象移动到 GUI 线程解决了这个问题。

于 2017-02-27T22:50:11.443 回答
0

您提供的代码很好。在 QML 方面,只要你的模型是绑定的,而不是在 JS 中动态重新创建,你也应该很好。

ListView {
    model: mediaPlaylistModel
}

如果您超载beginResetModelendResetModel意外,可能会出现问题。出于测试目的,您可以尝试发出QAbstractItemModel::modelReset()信号,看看它是否改变了任何东西。

使用 QAbstractItemModel 很容易遗漏一些东西,导致不再工作!

于 2017-02-26T18:40:49.533 回答