2016-07-28 21 views
3

Ich versuche, eine QML Gui für eine große dynamische C/Fortran-Simulation zu schreiben. Die Daten, die ich anzeigen möchte, werden in Fortran-Common-Blöcken gespeichert und in festen Zeitschritten aktualisiert. Mein Problem ist, dass QML ListView nicht aktualisiert wird, wenn das dataChanged-Signal nach jedem Zeitschritt ausgegeben wird, obwohl das Signal vom Gui empfangen wird (Test ist in dem Code unten).C++/QML: ListView ist nicht auf DataChanged-Signal von QAbstractListModel aktualisiert

Ich verpasse wahrscheinlich etwas wirklich Offensichtliches, weil, wenn ich meine ListView nach unten und oben streiche, die angezeigten Daten aktualisiert und korrekt sind (ich denke, weil die QML-Engine Elemente erneut rendert, wenn sie "außer Sicht" und wieder rein). Das einzige, was nicht funktioniert, ist, dass das ListView jedes Mal aktualisiert wird, wenn das dataChanged-Signal empfangen wird, und nicht nur, wenn es erneut gerendert wird. Im Folgenden finden Sie eine detailliertere Beschreibung meines Ansatzes und der relevanten Code-Teile.

Jede Simulationseinheit hat mehrere Attribute (lebendig, Position ...), also habe ich beschlossen, ein ListModel zu erstellen, das für jede Entität ein DataObject enthält. Dies ist die entsprechende Header-Datei (die eigentlichen Simulationsdatum als extern structs in "interface.h" erklärt, so kann ich es über Zeiger zugreifen):

"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); 
}; 

Wie die Kopfzeile wird auch die .cpp-Datei von dem übernommen, was Sie in Qt5 Cadaques Book here finden, außer dass mein Konstruktor über alle Simulationsentitäten iteriert, um die Zeiger zu setzen. Außerdem gibt es die Funktion do_update, die das Signal dataChanged für die gesamte Liste ausgibt.

"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); 
} 

Wenn die Simulation läuft, do_update() wird einmal pro Sekunde aufgerufen. Ich habe einen Test Gui mit einem Listview erstellt und ausgesetzt mein Modell, um es mit:

Auszug aus „threadcontrol.cpp“

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

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

(Dieser Code Teil einer größeren Datei, die die verschiedenen steuert Ich bin mir ziemlich sicher, der Rest ist nicht relevant für das eigentliche Problem und diese Frage wird wirklich lang ...)

So endlich gibt es main.qml. Es enthält eine Liste mit MAX_ENTITIES-Elementen und jedes Element enthält Textfelder zum Anzeigen meiner Daten. Ich habe auch ein Connections-Element hinzugefügt, um zu überprüfen, ob das dataChanged-Signal vom Gui empfangen wird.

"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") 
     } 
    } 
} 

Wenn die Simulation läuft, wird die "Datachanged empfangen" -Meldung wird jede Sekunde gedruckt.

Edit: Ich wurde in die Listmodel verbinden und nicht auf die Listview hier, obwohl das Listview das datachanged-Signal zu empfangen hat. Da das Konsolenprotokoll bei der Verbindung zu listviewer nicht funktioniert, fehlt mir wahrscheinlich die Verbindung zwischen listView und dataChanged Signal. Aber ich denke, das sollte bei der Implementierung des DataChanged-Signals automatisch funktionieren.

Zusätzliche Informationen: Ich habe eine similar problem here mit Qt Map gefunden und es schien tatsächlich ein Fehler zu sein, der in Qt 5.6 behoben wurde. Das Ausführen von qmake mit Qt 5.7 hat mein Problem jedoch nicht behoben.

+0

'dataChanged (start_index, end_index)' ist keine Funktion, es ist ein Signal. Versuchen Sie 'emit dataChanged (start_index, end_index);' – ramtheconqueror

+0

Vielen Dank für Ihre Eingabe! Leider hat sich daran nichts geändert. Soweit ich weiß, ist das Schlüsselwort empty optional, da dataChanged bereits als Signal definiert ist. Allerdings habe ich einen Fehler in meinem QML-Verbindungsblock aufgrund deines Kommentars festgestellt und werde meine Frage bearbeiten. – whittey

Antwort

3

Sie dürfen das Signal dataChanged() in Ihrer Klasse nicht deklarieren, da Sie das Signal AbstractItemModel::dataChanged() ausgeben möchten. Wenn Sie es erneut deklarieren, fügen Sie ein völlig neues und unterschiedliches Signal hinzu, das nirgends verbunden ist. Wenn Sie die Deklaration in acdata.h entfernen, sollte alles funktionieren.