Я пытаюсь сделать модель 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 во втором случае? И как это исправить, т.е. сделать отображение данных во втором случае? Заранее спасибо.
Проблема в initializeView
Модель является локальной переменной в этой области, и каждая локальная переменная удаляется из памяти, когда функция завершает выполнение. В первом случае модель будет исключена при закрытии приложения, во втором случае она будет исключена при initializeView
заканчивается, то есть когда отображается окно, есть 2 возможных решения:
MainView
как родитель, так что он удаляет его из памяти (это последнее свойство QObject
s):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
ошибка.
Других решений пока нет …