How can you implement the data function of QAbstractListModel such that it returns something with properties that are visible from a QML ListView's delegate?
For the item type, I tried implementing a QObject subclass that has Q_PROPERTYs, but QAbstractListModel::data returns QVariant, and a QObject* can't be converted to QVariant?
How do I create a QVariant that has named properties visible to QML?
class MyListItem : public QObject {
Q_OBJECT
Q_PROPERTY(type name READ name WRITE set_name NOTIFY nameChanged)
/*...*/
public:
MyListItem(QObject* parent, const QString& name) : QObject(parent) {
set_name(name);
}
};
class MyList : public QAbstractListModel
{
public:
MyList(QObject* parent);
Q_INVOKABLE int rowCount(const QModelIndex &parent = QModelIndex()) const override;
Q_INVOKABLE QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QVector<MyListItem*> items;
};
Q_INVOKABLE int MyList::rowCount(const QModelIndex &) const {
return items.size();
}
Q_INVOKABLE QVariant MyList::data(const QModelIndex &index, int) const {
MyListItem* item = items[index.row()];
return item; // <--- ERROR
}
Getting:
In member function ‘virtual QVariant MyList::data(const QModelIndex&, int) const’:
error: use of deleted function ‘QVariant::QVariant(void*)’
18 | return item;
| ^~~~
If you want to encapsulate a QObject in a QVariant then you must use QVariant::fromValue:
return QVariant::fromValue(item);
MWE:
main.cpp
#include <QAbstractListModel>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
class MyListItem : public QObject {
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE set_name NOTIFY nameChanged)
public:
MyListItem(const QString& name, QObject* parent=nullptr) : QObject(parent), m_name(name) {
}
QString name() const{
return m_name;
}
public slots:
void set_name(QString name){
if (m_name == name)
return;
m_name = name;
emit nameChanged(m_name);
}
signals:
void nameChanged(QString name);
private:
QString m_name;
};
class MyList : public QAbstractListModel
{
public:
enum MyListRoles {
ItemRole = Qt::UserRole + 1
};
MyList(QObject* parent=nullptr): QAbstractListModel(parent){}
~MyList(){
qDeleteAll(items);
items.clear();
}
void append(MyListItem *item){
beginInsertRows(QModelIndex(), rowCount(), rowCount());
items.append(item);
endInsertRows();
}
int rowCount(const QModelIndex &parent=QModelIndex()) const override{
if(parent.isValid())
return 0;
return items.count();
}
QVariant data(const QModelIndex &index, int role) const override{
if(!index.isValid())
return {};
if(index.row() <0 || index.row() >= rowCount())
return {};
if(role == ItemRole)
return QVariant::fromValue(items[index.row()]);
return {};
}
QHash<int, QByteArray> roleNames() const override{
return {{ItemRole, "item"}};
}
private:
QVector<MyListItem*> items;
};
#include "main.moc"
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
MyList model;
model.append(new MyListItem("foo1"));
model.append(new MyListItem("foo2"));
model.append(new MyListItem("foo3"));
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("mylist", &model);
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
ListView{
anchors.fill: parent
model: mylist
delegate: Text {
text: model.item.name
}
}
}
Related
I am trying to update a model in a QML ListView from a cpp file..
Here is my implemenation. I copied evrything to this therfore it will be a little bit bigger from code size.
this is my model implementation albummodel.h
#include <QAbstractListModel>
#include <QStringList>
#include <QQmlListProperty>
class Album
{
public:
Album(const QString &type, const QString &size);
QString type() const;
QString size() const;
private:
QString m_type;
QString m_size;
};
class AlbumModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(QQmlListProperty<QObject> myModel NOTIFY listChanged)
public:
enum AlbumRoles {
TypeRole = Qt::UserRole + 1,
SizeRole
};
AlbumModel(QObject *parent = 0);
//QQmlListProperty<QObject> getList();
void addAlbum(const Album &album);
int rowCount(const QModelIndex & parent = QModelIndex()) const;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
protected:
QHash<int, QByteArray> roleNames() const;
private:
QList<Album> m_albums;
signals:
void listChanged();
#include <QAbstractListModel>
#include <QStringList>
#include <QQmlListProperty>
class Album
{
public:
Album(const QString &type, const QString &size);
QString type() const;
QString size() const;
private:
QString m_type;
QString m_size;
};
class AlbumModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(QQmlListProperty<QObject> myModel NOTIFY listChanged)
public:
enum AlbumRoles {
TypeRole = Qt::UserRole + 1,
SizeRole
};
AlbumModel(QObject *parent = 0);
//QQmlListProperty<QObject> getList();
void addAlbum(const Album &album);
int rowCount(const QModelIndex & parent = QModelIndex()) const;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
protected:
QHash<int, QByteArray> roleNames() const;
private:
QList<Album> m_albums;
signals:
void listChanged();
};
This is my albummodel.cpp
#include "albummodel_mediaplayer.h"
Album::Album(const QString &type, const QString &size)
: m_type(type), m_size(size)
{
}
QString Album::type() const
{
return m_type;
}
QString Album::size() const
{
return m_size;
}
AlbumModel::AlbumModel(QObject *parent)
: QAbstractListModel(parent)
{
}
void AlbumModel::addAlbum(const Album &album)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_albums << album;
endInsertRows();
emit listChanged();
}
int AlbumModel::rowCount(const QModelIndex & parent) const {
Q_UNUSED(parent);
return m_albums.count();
}
QVariant AlbumModel::data(const QModelIndex & index, int role) const {
if (index.row() < 0 || index.row() >= m_albums.count())
return QVariant();
const Album &Album = m_albums[index.row()];
if (role == TypeRole)
return Album.type();
else if (role == SizeRole)
return Album.size();
return QVariant();
}
QHash<int, QByteArray> AlbumModel::roleNames() const {
QHash<int, QByteArray> roles;
roles[TypeRole] = "type";
roles[SizeRole] = "size";
return roles;
}
/*QQmlListProperty<QObject> AlbumModel::getList(){
return QQmlListProperty<QObject>(this, *list);
}*/
and my qml file
import AlbumModel 1.0
ListView {
model: AlbumModel.myModel
delegate: playListThumbnail
Component {
id: playListThumbnail
Item {
Text {
id: albumTitleText
text: type//item_title
}}
....
}
I added the main Programm Code. Hope this is enough for a mvp
Main.cpp
void main() {
qmlRegisterType<AlbumModel>("AlbumModel",1,0,"AlbumModel");
Album g("test", "test");
AlbumModel h();
h.addAlbum(g);
}
But the model isn't updated. Which signal did i miss?
I am trying to view a list of c++ models in qml by subclassing QAbstractListModel following this tutorial Models and Views: AbstractItemModel Example
Here is my implementation:
Item model class
class User
{
private:
QString m_macAddr;
QString m_firstName;
QString m_lastName;
public:
User();
User(const QString &mac);
QString macAddr() const;
void setMacAddr(QString &mac);
};
User::User()
{}
User::User(const QString &mac){
m_macAddr = mac;
}
QString User::macAddr() const{
return m_macAddr;
}
void User::setMacAddr(QString &mac){
if(mac == m_macAddr)
return;
m_macAddr = mac;
// emit macAddrChanged();
}
QAbstractListModel subclass
class UserListModel : public QAbstractListModel
{
Q_OBJECT
public:
// User model roles
enum roles{
macRole = Qt::UserRole + 1
};
UserListModel(QObject *parent = nullptr);
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;
QModelIndex getIndex(int r, int c, void *p);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
void addUser(const User &user);
private:
QList<User> m_users;
protected:
QHash<int, QByteArray> rolesNames() const;
};
// Class inplementation.
UserListModel::UserListModel(QObject *parent)
: QAbstractListModel(parent)
{
}
void UserListModel::addUser(const User &user){
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_users << user;
endInsertRows();
}
int UserListModel::rowCount(const QModelIndex &parent) const
{
return m_users.count();
}
QVariant UserListModel::data(const QModelIndex &index, int role) const
{
if (index.row() < 0 || index.row() >= rowCount())
return QVariant();
const User &user = m_users[index.row()];
if(role == macRole){
return user.macAddr();
}
return QVariant();
}
QModelIndex UserListModel::index(int row, int column, const QModelIndex &parent) const
{
return parent;
}
QModelIndex UserListModel::parent(const QModelIndex &index) const
{
return index;
}
QModelIndex UserListModel::getIndex(int r, int c, void *p) {
return this->createIndex(r, c, p);
}
QHash<int, QByteArray> UserListModel::rolesNames() const {
QHash<int, QByteArray> roles;
roles[macRole] = "mac";
return roles;
}
main.cpp
QQmlApplicationEngine engine;
QString mac = "12:3e:3w:4r:33";
userlistModel->addUser(User(mac));
userlistModel->addUser(User(mac));
userlistModel->addUser(User(mac));
userlistModel->addUser(User(mac));
engine.rootContext()->setContextProperty("usersModel", userlistModel);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QModelIndex id = userlistModel->getIndex(1, 0, 0);
QVariant v1 = userlistModel->data(id, Qt::UserRole + 1);
qDebug() << "===========" << v1.toString();
controller::DatabaseService<SqliteDB> *sqliteDb = new controller::DatabaseService<SqliteDB>();
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
QML
ListView{
id: listView
anchors.fill: parent
model:usersModel
delegate: Component { HorizontalListItem{mac: mac}}
}
The Problem is :
In main function I am adding 4 models to the list model but, They are shown in qml without data. althought, I am trying to view mac role.
what I tried
I tried to add a breakpoint inside methodes rolesNames() and data()
but, It seems that the compiler is not getting inside anyone of them.
I tried to invoke data() method in main function and it returns the desired data.
It is not necessary that you override the index, or parent methods, nor do you create the getIndex method. In your case you need to implement the roleNames method:
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QAbstractListModel>
#include <QDebug>
class User
{
QString m_macAddr;
public:
User(){}
User(const QString &mac): m_macAddr(mac){}
QString macAddr() const{return m_macAddr;}
void setMacAddr(QString &mac){m_macAddr = mac;}
};
class UserListModel : public QAbstractListModel
{
public:
enum roles{
macRole = Qt::UserRole + 1
};
explicit UserListModel(QObject *parent = nullptr)
: QAbstractListModel(parent){}
int rowCount(const QModelIndex &parent = QModelIndex()) const override {
if (parent.isValid())
return 0;
return m_users.count();
}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override {
if (!index.isValid())
return QVariant();
if (index.row() < 0 || index.row() >= rowCount())
return QVariant();
const User &user = m_users[index.row()];
if(role == macRole)
return user.macAddr();
return QVariant();
}
QHash<int, QByteArray> roleNames() const override{
QHash<int, QByteArray> roles;
roles[macRole] = "mac";
return roles;
}
void addUser(const User &user){
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_users << user;
endInsertRows();
}
private:
QList<User> m_users;
};
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
UserListModel userlistModel;
QString mac = "12:3e:3w:4r:33";
userlistModel.addUser(User(mac));
userlistModel.addUser(User(mac));
userlistModel.addUser(User(mac));
userlistModel.addUser(User(mac));
QModelIndex ix = userlistModel.index(1, 0);
QVariant v = userlistModel.data(ix, UserListModel::macRole);
qDebug() << "===========" << v.toString();
engine.rootContext()->setContextProperty("usersModel", &userlistModel);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
ListView{
anchors.fill: parent
model:usersModel
delegate: Component
{
Text{
text: model.mac
}
}
}
}
Is it possible to have a model within a model accessible via qml and each model are different classes
eg modelclass 1 has a vector of a subclass of model
i.e parent model can have many sub model
I tried to return a QAbstractItemModel as a role of QAbstractItemModel parent but it seems thats not possible any ideas of how to do these stuff??
[Edit 1]
The code :
message.h
#include <QAbstractListModel>
#include <QStringList>
//![0]
class Animal
{
public:
Animal(const QString &type, const QString &size);
//![0]
QString type() const;
QString size() const;
private:
QString m_type;
QString m_size;
//![1]
};
class AnimalModel : public QAbstractListModel
{
Q_OBJECT
public:
enum AnimalRoles {
TypeRole = Qt::UserRole + 1,
SizeRole
};
AnimalModel(QObject *parent = 0);
//![1]
void addAnimal(const Animal &animal);
int rowCount(const QModelIndex & parent = QModelIndex()) const;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole)
const;
protected:
QHash<int, QByteArray> roleNames() const;
private:
QList<Animal> m_animals;
//![2]
};
//![2]
model.h
#include <QAbstractListModel>
#include <QStringList>
#include <QDebug>
#include <message.h>
//![0]
//!
//!
class lister{
public:
int id;
AnimalModel *list=nullptr;
void addlist(){
list->addAnimal(Animal("Wolf", "Medium"));
list->addAnimal(Animal("Polar bear", "Large"));
list->addAnimal(Animal("Quoll", "Small"));
}
};
class TestModel : public QAbstractListModel
{
public:
enum EventRoles {
ModelRole = Qt::UserRole + 1,
IdRole = Qt::UserRole + 2
};
TestModel()
{
m_roles[ ModelRole] = "mm";
m_roles[ IdRole] = "id";
// setRoleNames(m_roles);
}
int rowCount(const QModelIndex & = QModelIndex()) const
{
return mylist.count();
}
QVariant data(const QModelIndex &index, int role) const
{
if(role == IdRole)
{
return mylist.at(index.row()).id;
}
else if(role == ModelRole)
{
return QVariant::fromValue(mylist.at(index.row()).list);
}
return 0;
}
void addData(){
static int a=0;
qDebug()<<a<<"value";
beginInsertRows(QModelIndex(), rowCount(), rowCount());
lister temp;
temp.id=a;
temp.addlist();
a++;
mylist<<temp;
temp.id=a;
temp.addlist();
a++;
mylist<<temp;
endInsertRows();
}
QHash<int, QByteArray> roleNames() const {
QHash<int, QByteArray> roles;
roles[ModelRole] = "mm";
roles[IdRole] = "id";
return roles;
}
QList<lister> mylist;
QHash<int, QByteArray> m_roles;
};
message.cpp
#include "message.h"
Animal::Animal(const QString &type, const QString &size)
: m_type(type), m_size(size)
{
}
QString Animal::type() const
{
return m_type;
}
QString Animal::size() const
{
return m_size;
}
AnimalModel::AnimalModel(QObject *parent)
: QAbstractListModel(parent)
{
}
void AnimalModel::addAnimal(const Animal &animal)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_animals << animal;
endInsertRows();
}
int AnimalModel::rowCount(const QModelIndex & parent) const {
Q_UNUSED(parent);
return m_animals.count();
}
QVariant AnimalModel::data(const QModelIndex & index, int role) const {
if (index.row() < 0 || index.row() >= m_animals.count())
return QVariant();
const Animal &animal = m_animals[index.row()];
if (role == TypeRole)
return animal.type();
else if (role == SizeRole)
return animal.size();
return QVariant();
}
//![0]
QHash<int, QByteArray> AnimalModel::roleNames() const {
QHash<int, QByteArray> roles;
roles[TypeRole] = "type";
roles[SizeRole] = "size";
return roles;
}
//![0]
main.cpp
#include "model.h"
#include <QGuiApplication>
#include <qqmlengine.h>
#include <qqmlcontext.h>
#include <qqml.h>
#include <QtQuick/qquickitem.h>
#include <QtQuick/qquickview.h>
//![0]
int main(int argc, char ** argv)
{
QGuiApplication app(argc, argv);
TestModel model;
// model.addAnimal(Animal("Wolf", "Medium"));
// model.addAnimal(Animal("Polar bear", "Large"));
// model.addAnimal(Animal("Quoll", "Small"));
model.addData();
model.addData();
model.addData();
qRegisterMetaType<AnimalModel*>("AnimalModel*" );
QQuickView view;
view.setResizeMode(QQuickView::SizeRootObjectToView);
QQmlContext *ctxt = view.rootContext();
ctxt->setContextProperty("myModel", &model);
//![0]
view.setSource(QUrl("qrc:view.qml"));
view.show();
return app.exec();
}
view.qml
import QtQuick 2.0
import QtQml 2.0
ListView {
anchors.fill: parent
model: myModel //this is your main model
delegate:
Rectangle {
height: 100
width: 100
color: "red"
Text {
id: cc
text: id
}
ListView {
anchors.fill: parent
model: mm //the internal QVariantList
delegate: Rectangle {
anchors.right: parent.right
width: 50
height: 50
color: "green"
border.color: "black"
Text {
text: type //role to get data from internal model
}
}
}
}
}
Yes its fine.
You need to return a pointer to your sub model object wrapped in a variant,
QVariant::fromValue(&subModel)
You probably also need to register your model pointer with Metatype system using
qRegisterMetaType<MySubModelClass*>("MySubModelClass*" );
Here is an implementation with DelegateModel introducet in Qt 5.13
import QtQuick 2.12
import QtQuick.Window 2.12
import com.example 1.0
import QtQml.Models 2.12
import QtQuick.Layouts 1.3
Window {
visible: true
width: 800
height: 480
title: qsTr("Hello World")
DelegateModel
{
id: delegateModel
model : MyModel{}
delegate:ColumnLayout
{
width: parent.width
Row
{
Text
{
text: qsTr("Word:")
color: "red"
}
Text
{
text: word
}
}
Text
{
color: "red"
text: qsTr("In Other Languages:")
}
ListView {
model: translations
height: 50
delegate:
Rectangle
{
height: 20
width: 100
Text {
anchors.right: parent.right
text: translations[index]
}
}
}
}
}
ListView
{
model : delegateModel
anchors.centerIn: parent
width: parent.width/2
height: parent.height
}
}
MyModel.h
#include <QAbstractListModel>
class MyModel : public QAbstractListModel
{
Q_OBJECT
public:
enum WordRoles
{
WordRole = Qt::UserRole+1,
TranslationsModelRole
};
explicit MyModel(QObject* parent=nullptr);
int rowCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
QHash<int, QByteArray> roleNames() const override;
private:
struct Word
{
QString word;
QStringList translations;
};
QList<Word> m_words;
};
MyModel.cpp
#include<MyModel.h>
#include <QList>
MyModel::MyModel(QObject *parent)
:QAbstractListModel(parent)
{
m_words={{"apple",{"elma","Apfel"}},
{"ball",{"top","ball"}},
{"friend",{"arkadas","freund"}}
};
}
int MyModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_words.size();
}
QVariant MyModel::data(const QModelIndex &index, int role) const
{
if(index.row()>=m_words.size() || index.row()<0)
return QVariant();
if ( role == WordRole)
{
return m_words[index.row()].word;
}
else if ( role == TranslationsModelRole)
{
return m_words[index.row()].translations;
}
return QVariant();
}
QHash<int, QByteArray> MyModel::roleNames() const
{
QHash<int,QByteArray> roles;
roles[WordRoles::WordRole] = "word";
roles[WordRoles::TranslationsModelRole]="translations";
return roles;
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <MyModel.h>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
qmlRegisterType<MyModel>("com.example",1,0,"MyModel");
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
Result is ListView inside ListViev:
I have a classes.
dataitem
datamodel
interface
networkconnector
In dataitem is specified item properties.
class MDataItem : public QObject
{
Q_OBJECT
Q_PROPERTY(QString host READ host WRITE setHost NOTIFY hostChanged)
Q_PROPERTY(QString service READ service WRITE setService NOTIFY serviceChanged)
Q_PROPERTY(QString status READ status WRITE setStatus NOTIFY statusChanged)
Q_PROPERTY(QString lastchck READ lastchck WRITE setLastChck NOTIFY lastchckChanged)
Q_PROPERTY(QString duration READ duration WRITE setDuration NOTIFY durationChanged)
Q_PROPERTY(QString info READ info WRITE setInfo NOTIFY infoChanged)
public:
explicit MDataItem(QObject *parent = 0);
QString host() const;
void setHost(const QString &newhost);
QString service() const;
void setService(const QString &newservice);
QString status() const;
void setStatus(const QString &newstatus);
QString lastchck() const;
void setLastChck(const QString &newlastchck);
QString duration() const;
void setDuration(const QString &newduration);
QString info() const;
void setInfo(const QString &newinfo);
signals:
void hostChanged();
void serviceChanged();
void statusChanged();
void lastchckChanged();
void durationChanged();
void infoChanged();
private:
QString m_host;
QString m_service;
QString m_status;
QString m_lastchck;
QString m_duration;
QString m_info;
};
In model is model specification
class MDataModel : public QAbstractTableModel
{
public:
enum Columns{
Host = Qt::UserRole,
Service = Qt::UserRole + 1,
Status = Qt::UserRole + 2,
Duration = Qt::UserRole + 3,
Info = Qt::UserRole + 4
};
MDataModel(QObject *parent = 0);
QHash<int, QByteArray> roleNames() const;
void fillData(QNetworkReply *r);
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
private:
QString dataAt(int offset) const;
QList<MDataItem*> items_;
public:
void prepareDataFinished(QNetworkReply *r);
};
The network get some data from the web.
class MNetworkConnector : public QObject
{
Q_OBJECT
public:
MNetworkConnector(QObject *parent=0);
private:
QNetworkAccessManager *manager;
void getData();
private slots:
void replyFinished(QNetworkReply *r);
void requireAuth(QNetworkReply *r, QAuthenticator *a);
signals:
void dataWasChanged(QNetworkReply *r);
};
and in the interface the network fill data in the model:
class MInterface : public QObject
{
Q_OBJECT
public:
MInterface();
MDataModel *mainModel;
MNetworkConnector *newConnection;
MDataModel* getModel();
public slots:
void dataWasPrepared(QNetworkReply *r);
};
At this point everything is Ok. In the debuger I see that data are in the model, in the correct MDataItem format.
But when I add model to the QML listView - application show nothing.
Where is the problem? Can you push me on the right way?
Main.cpp looks this:
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
MInterface *myIface = new MInterface();
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("MainModel", myIface->getModel());
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
Main qml file:
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQml 2.0
Window {
visible: true
width: Screen.width / 2
height: Screen.height / 2
title: qsTr("webinfo")
ListView
{
id: myList
visible: true
header: Header{}
model: MainModel
delegate: Item
{
Rectangle
{
Text { text: host }
}
}
}
}
When I set debuger breakpoint into my modedata class and in the function which should return the data, debuger not stop there. It looks that program never go inside this function.
Maybe problem is how I set model? Function getmodel() return pointer only.
main.cpp:
engine.rootContext()->setContextProperty("MainModel", myIface->getModel());
minterface.cpp:
MDataModel* MInterface::getModel()
{
return this->mainModel;
}
There is MDataModel.cpp
MDataModel::MDataModel(QObject *parent) : QAbstractTableModel(parent)
{
}
int MDataModel::rowCount(const QModelIndex & /* parent */) const
{
return items_.count();
}
int MDataModel::columnCount(const QModelIndex & /* parent */) const
{
return 5;
}
QVariant MDataModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
if (role == Qt::DisplayRole)
{
switch(index.column()){
case Columns::Host:
return items_.value(index.row())->host();
case Columns::Service:
return items_.value(index.row())->service();
case Columns::Status:
return items_.value(index.row())->status();
case Columns::Duration:
return items_.value(index.row())->duration();
case Columns::Info:
return items_.value(index.row())->info();
}
}
return QVariant();
}
QVariant MDataModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
{
switch(section){
case Columns::Host:
return tr("Host");
case Columns::Service:
return tr("Service");
case Columns::Status:
return tr("Status");
case Columns::Duration:
return tr("Duration");
case Columns::Info:
return tr("Info");
}
}
return QVariant();
}
void MDataModel::prepareDataFinished(QNetworkReply *r)
{
QList<MDataItem*> newItems;
MDataItem *item = new MDataItem();
QString data;
while(r->canReadLine())
{
data = r->readLine();
item = new MDataItem();
if(data.contains("warning", Qt::CaseInsensitive))
{
item->setStatus("WARNING");
}
else if(data.contains("critical", Qt::CaseInsensitive))
{
item->setStatus("CRITICAL");
}
else if(data.contains("unknown", Qt::CaseInsensitive))
{
item->setStatus("UNKNOWN");
}
item->setHost(reg.cap(0).replace("host=","").toUpper());
...
...
}
}
items_ = newItems;
qDebug() << items_;
}
QHash<int, QByteArray> MDataModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[Columns::Host] = "host";
roles[Columns::Service] = "service";
roles[Columns::Status] = "status";
roles[Columns::Duration] = "duration";
roles[Columns::Info] = "info";
return roles;
}
Your main problems are:
MDataModel inherits QAbstractTableModel instead of QAbstractListModel. This is not an absolute problem since you could use a QAbstractTableModel, but since you are using a ListView in QML you should prefer using QAbstractListModel.
You are mixing roles and columns. This is a consequence of the first point since QAbstractListModel hides the concept of column.
You do not signal the model (and therefore the view/QML) you changed the data. Whenever changes happen to the underlying data you must tell the model by calling functions by pair, depending on what the change is: beginResetModel()/endResetModel(), beginInsertRows()/endInsertRows(), beginRemoveRows()/endRemoveRows(), etc. There are also 2 unpaired signals for that purpose: dataChanged() and modelReset(). Note that you can get away by always calling modelReset(), but this is not a good practice and you should favor using the most sepcific pair of function.
You should also read Qt documentation:
Using C++ Models with Qt Quick Views > QAbstractItemModel subclass
QAbstractItemModel Class > Protected Functions
And here is you fixed model. I removed columnCount() and headerData() as you do not need them to make it work.
MDataModel.h
class MDataModel : public QAbstractListModel
{
public:
enum Role{
Host = Qt::UserRole,
Service,
Status,
Duration,
Info
};
MDataModel(QObject *parent = 0);
QHash<int, QByteArray> roleNames() const;
void fillData(QNetworkReply *r);
int rowCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
private:
QString dataAt(int offset) const;
QList<MDataItem*> items_;
public:
void prepareDataFinished(QNetworkReply *r);
};
MDataModel.cpp
MDataModel::MDataModel(QObject *parent) : QAbstractListModel(parent)
{
}
int MDataModel::rowCount(const QModelIndex & /* parent */) const
{
return items_.count();
}
QVariant MDataModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
switch(role){
// For C++ views
case Qt::DisplayRole:
return items_.value(index.row())->host();
// For QML views
case Host:
return items_.value(index.row())->host();
case Service:
return items_.value(index.row())->service();
case Status:
return items_.value(index.row())->status();
case Duration:
return items_.value(index.row())->duration();
case Info:
return items_.value(index.row())->info();
}
return QVariant();
}
void MDataModel::prepareDataFinished(QNetworkReply *r)
{
QList<MDataItem*> newItems;
MDataItem *item = new MDataItem();
QString data;
while(r->canReadLine())
{
data = r->readLine();
item = new MDataItem();
if(data.contains("warning", Qt::CaseInsensitive))
{
item->setStatus("WARNING");
}
else if(data.contains("critical", Qt::CaseInsensitive))
{
item->setStatus("CRITICAL");
}
else if(data.contains("unknown", Qt::CaseInsensitive))
{
item->setStatus("UNKNOWN");
}
item->setHost(reg.cap(0).replace("host=","").toUpper());
...
...
}
}
// Call beginResetModel() and endResetModel() to tell the model
// and the view the data changed.
beginResetModel();
items_ = newItems;
endResetModel();
qDebug() << items_;
}
QHash<int, QByteArray> MDataModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[Host] = "host";
roles[Service] = "service";
roles[Status] = "status";
roles[Duration] = "duration";
roles[Info] = "info";
// Add displayRole so that you can get what C++ views displays in QML
// e.g Text { text: host + ' ' + status + ' (' + displayRole +')' }
roles[Qt::DisplayRole] = "displayRole";
return roles;
}
QML fix
delegate: Component
{
Rectangle
{
width: 250
height: textItem.height
Text {
id: textItem
text: host + ' ' + status + ' (' + displayRole +')'
}
}
}
At this moment, my MDataModel.h and MDataModel.cpp looks like this:
MDataModel.h
class MDataModel : public QAbstractListModel
{
public:
enum icingaRoles{
host,
service,
status,
duration,
info
};
MDataModel(QObject *parent = 0);
QHash<int, QByteArray> roleNames() const;
void fillData(QNetworkReply *r);
int rowCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
private:
QString dataAt(int offset) const;
QList<MDataItem*> items_;
public:
void prepareDataFinished(QNetworkReply *r);
};
MDataModel.cpp
MDataModel::MDataModel(QObject *parent) : QAbstractListModel(parent)
{
}
int MDataModel::rowCount(const QModelIndex & /* parent */) const
{
return items_.count();
}
QVariant MDataModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
if (role == Qt::DisplayRole)
{
switch(index.column()){
case host:
return items_.value(index.row())->host();
case service:
return items_.value(index.row())->service();
case status:
return items_.value(index.row())->status();
case duration:
return items_.value(index.row())->duration();
case info:
return items_.value(index.row())->info();
}
}
return QVariant();
}
void MDataModel::prepareDataFinished(QNetworkReply *r)
{
QList<MDataItem*> newItems;
MDataItem *item = new MDataItem();
QString data;
while(r->canReadLine())
{
data = r->readLine();
QRegExp reg;
if(data.contains("statusbgwarning'><a href", Qt::CaseInsensitive) || data.contains("statusbgcritical'><a href", Qt::CaseInsensitive) || data.contains("statusbgunknown'><a href", Qt::CaseInsensitive))
{
item = new MDataItem();
newItems.append(item);
if(data.contains("warning", Qt::CaseInsensitive))
{
item->setStatus("WARNING");
}
else if(data.contains("critical", Qt::CaseInsensitive))
{
item->setStatus("CRITICAL");
}
else if(data.contains("unknown", Qt::CaseInsensitive))
{
item->setStatus("UNKNOWN");
}
...
...
}
// Call beginResetModel() and endResetModel() to tell the model
// and the view the data changed.
beginResetModel();
items_ = newItems;
endResetModel();
}
QHash<int, QByteArray> MDataModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[host] = "host";
roles[service] = "service";
roles[status] = "status";
roles[duration] = "duration";
roles[info] = "info";
return roles;
}
I've created Proxy model by subclassing QAbstractProxyModel and connected it as a model to my view. I also set up source model for this proxy model. Unfortunately something is wrong because I'm not getting anything displayed on my listView (it works perfectly when I have my model supplied as a model to view but when I supply this proxy model it just doesn't work). Here are some snippets from my code:
#ifndef FILES_PROXY_MODEL_H
#define FILES_PROXY_MODEL_H
#include <QAbstractProxyModel>
#include "File_List_Model.h"
class File_Proxy_Model: public QAbstractProxyModel
{
public:
explicit File_Proxy_Model(File_List_Model* source_model)
{
setSourceModel(source_model);
}
virtual QModelIndex mapFromSource(const QModelIndex & sourceIndex) const
{
return index(sourceIndex.row(),sourceIndex.column());
}
virtual QModelIndex mapToSource(const QModelIndex & proxyIndex) const
{
return index(proxyIndex.row(),proxyIndex.column());
}
virtual int columnCount(const QModelIndex & parent = QModelIndex()) const
{
return sourceModel()->columnCount();
}
virtual int rowCount(const QModelIndex & parent = QModelIndex()) const
{
return sourceModel()->rowCount();
}
virtual QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const
{
return createIndex(row,column);
}
virtual QModelIndex parent(const QModelIndex & index) const
{
return QModelIndex();
}
};
#endif // FILES_PROXY_MODEL_H
//and this is a dialog class:
Line_Counter::Line_Counter(QWidget *parent) :
QDialog(parent), model_(new File_List_Model(this)),
proxy_model_(new File_Proxy_Model(model_)),
sel_model_(new QItemSelectionModel(proxy_model_,this))
{
setupUi(this);
setup_mvc_();
}
void Line_Counter::setup_mvc_()
{
listView->setModel(proxy_model_);
listView->setSelectionModel(sel_model_);
}
mapToSource should return an index from the source model:
virtual QModelIndex mapToSource(const QModelIndex & proxyIndex) const
{
return sourceModel()->index(proxyIndex.row(),proxyIndex.column());
}
I tested with that code:
#include <QtGui>
#include "file_proxy_model.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QDir dir(QDesktopServices::storageLocation(QDesktopServices::HomeLocation));
File_List_Model model(dir.entryList());
File_Proxy_Model proxy(&model);
QListView listView;
listView.setModel(&proxy);
listView.show();
return a.exec();
}
// In "File_List_Model.h"
class File_List_Model : public QStringListModel
{
public:
explicit File_List_Model(const QStringList & list, QObject *parent = 0)
: QStringListModel(list, parent)
{
}
};