簡體   English   中英

C ++ / QML:未對來自QAbstractListModel的dataChanged信號更新ListView

[英]C++/QML: ListView is not updated on dataChanged signal from QAbstractListModel

我正在嘗試為大型動態C / Fortran仿真編寫QML Gui。 我想要顯示的數據存儲在Fortran Common塊中,並按固定時間步驟更新。 我的問題是,當每個時間步之后發出dataChanged信號時,QML ListView不刷新,盡管Gui接收到信號(測試在下面的代碼中)。

我可能錯過了一些非常明顯的東西,因為當我向下輕彈我的ListView時,顯示的數據會更新並且正確(我猜是因為當QML引擎“看不見”並再次返回時重新呈現元素) 。 所以唯一不起作用的是每次收到dataChanged信號時ListView都會更新,而不僅僅是在重新渲染時。 以下是我的方法和相關代碼部分的更詳細說明。

每個模擬實體都有幾個屬性(alive,position ...),因此我決定為每個實體創建一個包含DataObject的ListModel。 這是相應的頭文件(實際模擬數據在“interface.h”中聲明為extern結構,因此我可以通過指針訪問它):

“acdata.h”

#include <QtCore>
#include <QObject>
#include <QtGui>

extern "C" {
     #include "interface.h"
}


class AcDataObject : public QObject
{
    Q_OBJECT

public:
    explicit AcDataObject(int id_, int *pac_live, double *pac_pos_x, QObject *parent = 0) :
        QObject(parent)
    {
        entity_id = id_;
        ac_live = pac_live;
        ac_pos_x = pac_pos_x;
    }

    int entity_id;
    int *ac_live;
    double *ac_pos_x;
};


class AcDataModel : public QAbstractListModel 
{
    Q_OBJECT

public:
    enum RoleNames {
        IdRole = Qt::UserRole,
        LiveRole = Qt::UserRole + 1,
        PosXRole = Qt::UserRole + 2
    };

    explicit AcDataModel(QObject *parent = 0);
    virtual int rowCount(const QModelIndex &parent) const;
    virtual QVariant data(const QModelIndex &index, int role) const;
    Q_INVOKABLE Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;
    void do_update();

protected:
    virtual QHash<int, QByteArray> roleNames() const;

private:
    QList<AcDataObject*> data_list;
    QHash<int, QByteArray> m_roleNames;
    QModelIndex start_index;
    QModelIndex end_index;

signals:
    void dataChanged(const QModelIndex &start_index, const QModelIndex &end_index);
};

與標題一樣,.cpp文件也可以在Qt5 Cadaques Book中找到 ,除了我的構造函數遍歷所有模擬實體以設置指針。 此外,還有do_update函數,它為整個列表發出dataChanged信號。

“acdata.cpp”

#include "acdata.h"

AcDataModel::AcDataModel(QObject *parent) :
    QAbstractListModel(parent)
{
    m_roleNames[IdRole] = "entity_id";
    m_roleNames[LiveRole] = "ac_live";
    m_roleNames[PosXRole] = "ac_pos_x";


    for (int i = 0; i < MAX_ENTITIES; i++)    // MAX_ENTITIES is defined in interface.h 
    {
         AcDataObject *data_object = new AcDataObject( i,
                                                      &fdata_ac_.ac_live[i],    // fdata_ac_ is the C struct/Fortran common block defined in interface.h
                                                      &fdata_ac_.ac_pos_x[i] );
         data_list.append(data_object);
    }
}

int AcDataModel::rowCount(const QModelIndex &parent) const {
    Q_UNUSED(parent);
    return data_list.count();
}

QVariant AcDataModel::data(const QModelIndex &index, int role) const
{
    int row = index.row();

    if(row < 0 || row >= data_list.count()) {
         return QVariant();
    }

    const AcDataObject *data_object = data_list.at(row);

    switch(role) {
         case IdRole: return data_object->entity_id;
         case LiveRole: return *(data_object->ac_live);
         case PosXRole: return *(data_object->ac_pos_x);
     }
     return QVariant();
 }

QHash<int, QByteArray> AcDataModel::roleNames() const
{
    return m_roleNames;
}

void AcDataModel::do_update() {
    start_index = createIndex(0, 0);
    end_index = createIndex((data_list.count() - 1), 0);
    dataChanged(start_index, end_index);
}

Qt::ItemFlags AcDataModel::flags(const QModelIndex &index) const
{
    if (!index.isValid()) {return 0;}

    return Qt::ItemIsEditable | QAbstractItemModel::flags(index);
}

模擬運行時,每秒調用do_update()。 我用ListView創建了一個測試Gui,並將我的模型暴露給它:

摘錄自“threadcontrol.cpp”

acdata = new AcDataModel();
viewer = new QtQuick2ApplicationViewer();

viewer->rootContext()->setContextProperty("acdata", acdata);
viewer->setMainQmlFile(QStringLiteral("../lib/qml_gui/main.qml"));
viewer->showExpanded();

(此代碼是控制不同線程的較大文件的一部分。我非常確定其余部分與實際問題無關,這個問題變得非常長......)

所以最后有main.qml。 它包含一個包含MAX_ENTITIES元素的列表,每個元素都包含文本字段以顯示我的數據。 我還添加了一個Connections元素來檢查Gui是否收到了dataChanged信號。

“main.qml”

ListView {
    id: listviewer
    model: acdata
    delegate: Rectangle {
        /* ... some formatting stuff like height etc ... */

        Row {
            anchors.fill: parent

            Text {
                /* ... formatting stuff ... */
                text: model.entity_id
            }

            Text {
                /* ... formatting stuff ... */
                text: model.ac_live
            }

            Text {
                /* ... formatting stuff ... */
                text: model.ac_pos_x
            }
        }
    }

    Connections {
        target: listviewer.model    // EDIT: I drew the wrong conclusions here, see text below!
        onDataChanged: {
            console.log("DataChanged received")
        }
    }
}

運行模擬時,每秒打印一次“DataChanged received”消息。

編輯:我連接到ListModel而不是這里的ListView,雖然ListView必須接收dataChanged信號。 由於連接到listviewer時控制台日志不起作用,我可能錯過了listView和dataChanged信號之間的連接。 但是,我認為這應該在實現dataChanged信號時自動工作?

附加信息:我 Qt Map中發現了類似的問題 ,它實際上似乎是在Qt 5.6中修復的錯誤。 但是,使用Qt 5.7運行qmake並沒有解決我的問題。

您不能在類中聲明dataChanged()信號,因為您要發出信號AbstractItemModel::dataChanged() 如果你重新聲明它,你添加一個新的和不同的信號,在任何地方都沒有連接。 如果刪除acdata.h中的聲明,一切都應該正常。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM