TableView и QAbstracTableModel при вызове QQmlApplicationEngine из другого класса

Я пытаюсь сделать модель QAbstractTableModel в cpp и подключиться к qml.

Этот код работает хорошо.

MyModel.h

#ifndef MYMODEL_H
#define MYMODEL_H

#include <QAbstractTableModel>

class MyModel : public QAbstractTableModel
{
Q_OBJECT

public:
enum AnimalRoles {
TypeRole = Qt::UserRole + 1,
SizeRole
};

explicit MyModel(QObject *parent = nullptr);

// Basic functionality:
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;

QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;

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

private:
QList<Animal> m_animals;
};

#endif // MYMODEL_H

MyModel.cpp

#include "MyModel.h"#include <QDebug>

MyModel::MyModel(QObject *parent)
: QAbstractTableModel(parent)
{
qDebug() << __FUNCTION__;
addAnimal(Animal("Wolf", "Medium"));
addAnimal(Animal("Polar bear", "Large"));
addAnimal(Animal("Quoll", "Small"));

}

int MyModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_animals.size();
}

int MyModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return 2;
}

QVariant MyModel::data(const QModelIndex &index, int role) const
{
qDebug() << __FUNCTION__ << index.row() << index.column() << role;
if (!index.isValid())
return QVariant();
const Animal &animal = m_animals[index.row()];
switch (role) {
case TypeRole:
return animal.type();
case SizeRole:
return animal.size();
default:
break;
}

return QVariant();
}

void MyModel::addAnimal(const Animal &animal)
{
qDebug() << __FUNCTION__;
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_animals << animal;
endInsertRows();
}

QHash<int, QByteArray> MyModel::roleNames() const
{
qDebug() << __FUNCTION__;
QHash<int, QByteArray> roles;
roles[TypeRole] = "type";
roles[SizeRole] = "size";
return roles;
}

main.cpp

int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);

MyModel model;

QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("myModel", &model);
engine.load(QUrl(QStringLiteral("qrc:/resources/qmls/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;

return app.exec();
}

main_test.qml

import QtQuick 2.0
import QtQuick 2.6
import QtQuick.Window 2.2
import QtQuick.Controls 1.4

Window {
id: main_view
width: 250
height: 600
visible: true

ListView {
id: list_view
width: 200; height: 250

model: myModel
delegate: Text { text: "Animal Test: " + type + ", " + size }
}TableView {
id: table_view
objectName: "tableView"width: 250; height: 250
anchors.top:  list_view.bottom
model: myModel

TableViewColumn {
id: type_col
role: "type"title: "Type"width: 100
}
TableViewColumn {
id: size_col
role: "size"title: "Size"width: 100
}
}

}

Это выглядит так

введите описание изображения здесь

Но, если я немного изменю main.cpp, представление списка будет отображаться как обычно, но не представление таблицы.

main.cpp

#include "MainView.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);

MainView mainView;
return app.exec();
}

MainView.h

#ifndef MAINVIEW_H
#define MAINVIEW_H

#include <QObject>
#include <QQmlApplicationEngine>

class MainView: public QObject
{
Q_OBJECT
public:
explicit  MainView(QObject *parent=nullptr);
void initializeView();

private:
QQmlApplicationEngine m_engine;
};

#endif // MAINVIEW_H

MainView.cpp

#include "MainView.h"#include "MyModel.h"#include <QQmlContext>

MainView::MainView(QObject *parent)
: QObject(parent)
{
initializeView();
}

void MainView::initializeView()
{
MyModel model;
m_engine.rootContext()->setContextProperty("myModel", &model);
m_engine.load(QUrl(QStringLiteral("qrc:/resources/qmls/main_test.qml")));

}

Похоже на это.

введите описание изображения здесь

Я действительно не понимаю, почему это происходит. Какая разница между ListView и TableView во втором случае? И как это исправить, т.е. сделать отображение данных во втором случае? Заранее спасибо.

0

Решение

Проблема в initializeViewМодель является локальной переменной в этой области, и каждая локальная переменная удаляется из памяти, когда функция завершает выполнение. В первом случае модель будет исключена при закрытии приложения, во втором случае она будет исключена при initializeView заканчивается, то есть когда отображается окно, есть 2 возможных решения:

  • Создать указатель и управлять памятью, которую мы передаем MainView как родитель, так что он удаляет его из памяти (это последнее свойство QObjects):

void MainView::initializeView()
{
MyModel *model = new MyModel(this);
m_engine.rootContext()->setContextProperty("myModel", model);
m_engine.load(QUrl(QStringLiteral("qrc:/resources/qmls/main_test.qml")));

}
  • Сделайте модель членом класса.

*.час

#ifndef MAINVIEW_H
#define MAINVIEW_H

#include <QObject>
#include <QQmlApplicationEngine>

class MainView: public QObject
{
Q_OBJECT
public:
explicit  MainView(QObject *parent=nullptr);
void initializeView();

private:
QQmlApplicationEngine m_engine;
MyModel model{this};
};

#endif // MAINVIEW_H

* .cpp

...

void MainView::initializeView()
{
m_engine.rootContext()->setContextProperty("myModel", &model);
m_engine.load(QUrl(QStringLiteral("qrc:/resources/qmls/main_test.qml")));

}

Чтобы понять поведение, я добавил еще несколько впечатлений:

MyModel::~MyModel()
{
qDebug()<<"destructor";
}

TableView {
...
Component.onCompleted: {
console.log("completed table")
if(!timer.running)
timer.running = true
}
}

ListView {
...
Component.onCompleted: {
console.log("completed list")
if(!timer.running)
timer.running = true
}
}Timer {
id: timer
interval: 0;
onTriggered:  console.log(myModel, table_view.model, list_view.model)
}

И я получаю следующее:

MyModel
addAnimal
addAnimal
addAnimal
roleNames
data 0 0 257
data 0 0 258
data 1 0 257
data 1 0 258
data 2 0 257
data 2 0 258
roleNames
qml: completed list
data 0 0 257
data 0 0 258
qml: completed table
destructor
qml: null null null

Мы отмечаем, что ListView удается загрузить данные, тогда как TableView в середине нагрузки называется деструктор.

Возможное объяснение: я думаю, что ListView хранит данные, создавая их копию и обновляя их только при уведомлении модели; также следует уведомлять об удалении модели для очистки данных; похоже, это ошибка. С другой стороны, TableViewНаходясь в момент загрузки и удаления модели, имеет значение null, поэтому он уведомляется и очищает данные.

Делать еще один тест:

void MainView::initializeView()
{
MyModel *model = new MyModel;
m_engine.rootContext()->setContextProperty("myModel", model);
m_engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

QTimer::singleShot(1000, model, &MyModel::deleteLater);
}

Замечено, что оба загружают данные правильно, и через секунду модель уничтожается, но только тот, который уведомлен, является TableView так как это единственный, который очищает показанные данные, и ListView не.

мой вывод, что это ListView ошибка.

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]