Я пытаюсь написать QML Gui для большой динамической симуляции C / Fortran. Данные, которые я хочу отобразить, хранятся в блоках Fortran Common и обновляются с фиксированными временными шагами. Моя проблема в том, что QML ListView не обновляется, когда сигнал dataChanged испускается после каждого временного шага, хотя сигнал принимается интерфейсом Gui (тест в коде ниже).
Я, вероятно, упускаю что-то действительно очевидное, потому что когда я перелистываю свой ListView вниз и вверх, отображаемые данные обновляются и корректируются (я полагаю, потому что механизм QML повторно отображает элементы, когда они «исчезают из виду» и снова возвращаются) , Таким образом, единственное, что не работает, — это то, что ListView обновляется каждый раз, когда принимается сигнал dataChanged, а не только при повторной визуализации. Ниже приведено более подробное описание моего подхода и соответствующих частей кода.
Каждый объект симуляции имеет несколько атрибутов (live, position …), поэтому я решил создать ListModel, содержащий DataObject для каждого объекта. Это соответствующий заголовочный файл (фактические данные моделирования объявлены как внешние структуры в «interface.h», поэтому я могу получить к ним доступ через указатель):
«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 и выставил ему свою модель:
Выдержка из «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, чтобы проверить, принят ли сигнал 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 полученный» печатается каждую секунду.
Редактировать: Я подключался к ListModel, а не к ListView здесь, хотя ListView должен получать сигнал dataChanged. Поскольку консольный журнал не работает при подключении к списку просмотра, я, вероятно, пропускаю соединение между listView и сигналом dataChanged. Тем не менее, я думаю, что это должно работать автоматически при реализации сигнала dataChanged?
Дополнительная информация: Я нашел похожая проблема здесь с Qt Map, и на самом деле это была ошибка, исправленная в Qt 5.6. Однако, запуск qmake с Qt 5.7 не решил мою проблему.
Вы не должны объявлять dataChanged()
сигнал в вашем классе, потому что вы хотите испустить сигнал AbstractItemModel::dataChanged()
, Если вы повторно объявите его, вы добавите совершенно новый и другой сигнал, который нигде не подключен. Если вы удалите объявление в acdata.h, все должно работать нормально.
Других решений пока нет …