QAbstractListModel:更新角色名称

QAbstractListModel: update role names

我正在尝试创建一个基于异步数据库的列表模型 api。这是我希望如何使用它的 qml 示例:

ListView {
    id: view;

    anchors.fill: parent;

    model: DatabaseModel {
        id: dmodel

        query: "SELECT id FROM test"
        database: "toto.sqlite"
    }

    delegate: Label {
        anchors.horizontalCenter: parent.horizontalCenter;
        width: view.width / 2;
        height: 30;
        text: id;

        color: "teal";
    }
}

显然在某些时候我需要的不仅仅是数据库中的 ID 和标签来显示该项目。

为了能够在我的标签定义中使用 "id",我使用这样的角色名称:

QHash<int, QByteArray> DatabaseListModel::roleNames() const
{
    QHash<int, QByteArray> b = this->QAbstractItemModel::roleNames();

    if (m_query != "" && m_database) {
        QStringList l = m_database->currentRequestFields();
        for (int i = 0; i < l.count(); ++i) {
            b.insert(Qt::UserRole + i + 1, l.at(i).toLocal8Bit());
        }
    }
    return b;
}
在这种情况下,

m_database 是到 "toto.sqlite" 的数据库会话,而 m_query 是 "SELECT id FROM test"。

问题是我的数据库会话是异步的,m_database->currentRequestFields() 不能立即可用,但是我收到一个信号告诉我什么时候可用,所以我想现在而不是之前更新 roleNames 列表.

即使 m_database 可能看起来像一个黑盒子,以下是我更新模型的方法:

void DatabaseListModel::updateModel()
{
    if (m_query != "" && m_database) {
        m_mutex.lock();
        beginResetModel();
        m_cache.clear();

        QObject::connect(m_database, &CollaoDatabase::databaseReady, this, [this] (CollaoDatabase* database) {
            database->setQueryStringi(m_query);
            database->executei(); //currentRequestFields() becomes available 
            database->fetchAlli();
            database->sendNotifierEventi(0); //when everything written before this line has been executed, ask the database to emit CollaoDatabase::notifierEventProcessed. It's not instant and might take a while depending on the query  
        });
        QObject::connect(m_database, &CollaoDatabase::resultReady, this, [this] (QVariantMap result) {
            if (m_cache.size() <= 0)
                m_cache.reserve(m_database->currentPendingFetches() + 1);
            m_cache.append(result.values());
        });

        QObject::connect(m_database, (void (CollaoDatabase::*)())&CollaoDatabase::notifierEventProcessed, this, [this](){
            endResetModel();
            //TODO: update roleNames here

            m_mutex.unlock();
            m_database = NULL; //as soon as stop() is called, we cannot assume the existance of this object anymore
            //it is therefore safer to make it null now
        });
        QObject::connect(m_database, SIGNAL(notifierEventProcessed()), m_database, SLOT(stop()));

        m_database->start();
    }
}

一个可能符合您需求的想法(假设您想要一个很好的 api 供此模型的用户使用,并且只有 SELECT 查询)是按以下方式构建您的查询:

  • 字符串table
  • 字符串[]列
  • 字符串选择
  • 字符串[] selectionArgs
  • 字符串分组
  • 字符串有
  • 字符串顺序
  • 字符串限制

(创意来自 http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html#query%28java.lang.String,%20java.lang.String[],%20java.lang.String,%20java.lang.String[],%20java.lang.String,%20java.lang.String,%20java.lang.String,%20java.lang.String%29)

因此您的简单示例可能如下所示

DatabaseModel {
    id: dmodel

    table: "test"
    colums: ["id"]
    database: "toto.sqlite"
}

这样您就可以尽早获得可用的列名称,以便将它们用于角色名称。

好吧,我终于得到了我想要的行为,它能够延迟我的 itemModel 角色名称的第一次初始化。代码与一些重新排序基本相同。特别是角色名称必须在调用 beginResetModel 之前可用。您可以将此片段与我的问题中的片段进行比较

void DatabaseListModel::updateModel()
{
    if (m_query != "" && m_database) {
        m_mutex.lock();

        QObject::connect(m_database, &CollaoDatabase::databaseReady, this, [this] (CollaoDatabase* database) {
            database->setQueryStringi(m_query);
            database->executei();
            database->sendNotifierEventi(1);
            database->fetchAlli();
            database->sendNotifierEventi(0);
        });
        QObject::connect(m_database, &CollaoDatabase::resultReady, this, [this] (QVariantMap result) {
            if (m_cache.size() <= 0) {
                m_fields = result.keys();
                beginResetModel();
                m_cache.reserve(m_database->currentPendingFetches() + 1);
                m_numRows = m_database->currentPendingFetches() + 1;
                emit numRowsChanged();
                m_progress = 0;
            }

            m_cache.append(result.values());
            ++m_progress;
            if (m_progress % (m_numRows / 100 + 1) == 0)
                emit progressChanged();
        });

        QObject::connect(m_database, (void (CollaoDatabase::*)(int))&CollaoDatabase::notifierEventProcessed, this, [this](int eventIndex){
            switch (eventIndex) {
            case 0: /*terminate*/
                emit progressChanged();
                endResetModel();
                m_mutex.unlock();
                m_database->stop();
                m_database = NULL; //as soon as stop() is called, we cannot assume the existance of this object anymore
                //it is therefore safer to make it null now
                break;

            case 1: /*now able to reset the model*/
                m_cache.clear();
                break;
            }
        });

        m_database->start();
    }
}