Qt 5.11 - QML - show XML file in treeView as QAbstractItemModel - c++

I'm trying to show a simple XML file in a QML treeView through the QAbstractItemModel class, but I can't find any decent tutorial on how to do so online. The best I managed to find is a tutorial about how to do it in QWidget, but I want to do it in QML. I tried anyway with all the bits I could find here and there but couldn't even manage to get a tree to show up.
Code compiles, but my model is null. I'm not even sure if I'm actually reading my XML file.
There's very likely a lot of things I am missing or doing wrong. Can anyone help ?
DomItem.h
#ifndef DOMITEM_H
#define DOMITEM_H
#include <QDomNode>
#include <QHash>
class DomItem
{
public:
DomItem(const QDomNode &node, int row, DomItem *parent = nullptr);
~DomItem();
DomItem *child(int i);
DomItem *parent();
QDomNode node() const;
int row() const;
private:
QDomNode domNode;
QHash<int, DomItem *> childItems;
DomItem *parentItem;
int rowNumber;
};
#endif // DOMITEM_H
DomItem.cpp
#include "DomItem.h"
DomItem::DomItem(const QDomNode &node, int row, DomItem *parent)
: domNode(node),
parentItem(parent),
rowNumber(row)
{
}
DomItem::~DomItem()
{
qDeleteAll(childItems);
}
DomItem *DomItem::parent()
{
return parentItem;
}
int DomItem::row() const
{
return rowNumber;
}
QDomNode DomItem::node() const
{
return domNode;
}
DomItem *DomItem::child(int i)
{
DomItem *childItem = childItems.value(i);
if (childItem)
return childItem;
// if child does not yet exist, create it
if (i >= 0 && i < domNode.childNodes().count()) {
QDomNode childNode = domNode.childNodes().item(i);
childItem = new DomItem(childNode, i, this);
childItems[i] = childItem;
}
return childItem;
}
DomModel.h
#ifndef DOMMODEL_H
#define DOMMODEL_H
#include <QObject>
#include <QModelIndex>
#include <QAbstractItemModel>
#include <QDomDocument>
#include <QFile>
#include "DomItem.h"
class DomModel : public QAbstractItemModel
{
Q_OBJECT
public:
enum DomModelRoles
{
DomModelRoleName = Qt::UserRole + 1,
DomModelRoleAttributes,
DomModelRoleValue
};
explicit DomModel(const QDomDocument &document, QObject *parent = nullptr);
~DomModel();
QVariant data(const QModelIndex &index, int role) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &child) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
void loadXml(QString xmlFilePath);
void reload();
QHash<int, QByteArray> roleNames() const override;
private:
QDomDocument domDocument;
DomItem *rootItem;
QString xmlSource;
};
#endif // DOMMODEL_H
DomModel.cpp
#include "DomModel.h"
DomModel::DomModel(const QDomDocument &document, QObject *parent)
: QAbstractItemModel(parent),
domDocument(document)
{
rootItem = new DomItem(domDocument, 0);
}
DomModel::~DomModel()
{
delete rootItem;
}
int DomModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return 3;
}
Qt::ItemFlags DomModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return Qt::NoItemFlags;
return QAbstractItemModel::flags(index);
}
QVariant DomModel::headerData(int section, Qt::Orientation orientation,
int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch (section) {
case 0:
return tr("Name");
case 1:
return tr("Attributes");
case 2:
return tr("Value");
default:
break;
}
}
return QVariant();
}
QModelIndex DomModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent))
return QModelIndex();
DomItem *parentItem;
if (!parent.isValid())
parentItem = rootItem;
else
parentItem = static_cast<DomItem*>(parent.internalPointer());
DomItem *childItem = parentItem->child(row);
if (childItem)
return createIndex(row, column, childItem);
return QModelIndex();
}
int DomModel::rowCount(const QModelIndex &parent) const
{
if (parent.column() > 0)
return 0;
DomItem *parentItem;
if (!parent.isValid())
parentItem = rootItem;
else
parentItem = static_cast<DomItem*>(parent.internalPointer());
return parentItem->node().childNodes().count();
}
QModelIndex DomModel::parent(const QModelIndex &child) const
{
if (!child.isValid())
return QModelIndex();
DomItem *childItem = static_cast<DomItem*>(child.internalPointer());
DomItem *parentItem = childItem->parent();
if (!parentItem || parentItem == rootItem)
return QModelIndex();
return createIndex(parentItem->row(), 0, parentItem);
}
QVariant DomModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (role != Qt::DisplayRole)
return QVariant();
const DomItem *item = static_cast<DomItem*>(index.internalPointer());
const QDomNode node = item->node();
switch (role)
{
case DomModelRoleName:
return node.nodeName();
case DomModelRoleAttributes:
{
const QDomNamedNodeMap attributeMap = node.attributes();
QStringList attributes;
for (int i = 0; i < attributeMap.count(); ++i) {
QDomNode attribute = attributeMap.item(i);
attributes << attribute.nodeName() + "=\""
+ attribute.nodeValue() + '"';
}
return attributes.join(' ');
}
case DomModelRoleValue:
return node.nodeValue().split('\n').join(' ');
default:
break;
}
return QVariant();
}
void DomModel::loadXml(QString xmlFilePath)
{
xmlFilePath.replace("file:///","");
// on parse le xml de l'environnement
QFile file(xmlFilePath);
if (!file.open(QIODevice::ReadOnly| QFile::Text)){
//QMessageBox::critical(this, "ParserFichierXML", "Error");
}
else{
domDocument.clear();
if (!domDocument.setContent(&file)) {
//QMessageBox::critical(this, "ParserFichierXML", "Error");
}
file.close();
}
reload();
}
void DomModel::reload()
{
beginResetModel();
delete rootItem;
rootItem = new DomItem(domDocument, 0,false);
endResetModel();
}
QHash<int, QByteArray> DomModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[DomModelRoleName] = "Name";
roles[DomModelRoleAttributes] = "Attributes";
roles[DomModelRoleValue] = "Value";
return roles;
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QDomDocument>
#include <QQmlContext>
#include "DomModel.h"
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);
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);
QQmlContext *ctx = engine.rootContext();
QFile file("./test.xml");
if (file.open(QIODevice::ReadOnly))
{
QDomDocument document;
if (document.setContent(&file))
{
DomModel *model = new DomModel(document);
ctx->setContextProperty("theModel", model);
}
}
file.close();
engine.load(url);
return app.exec();
}
main.qml
import QtQuick 2.7
import QtQuick.Window 2.2
import QtQuick.Layouts 1.3
import QtQuick.Dialogs 1.2
import QtLocation 5.3
import QtQuick.Controls 1.4
import QtQuick.Controls 2.0
import QtQuick.Controls.Styles 1.4
import QtQml 2.0
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
TreeView
{
id:treeView
anchors.fill: parent
model: theModel
itemDelegate: TreeDelegate
{
id: treeDeleg
}
style: TreeViewStyle
{
id: styleTree
}
TableViewColumn
{
id:column1
role: "Name"
title: "Name"
width: 450
}
TableViewColumn
{
id:column3
role: "Value"
title: "Value"
width: 400
}
Component.onCompleted:
{
treeView.model = Qt.binding(function()
{
if (typeof theModel !== "undefined")
{
console.log("Model loaded");
return theModel;
}
else
{
console.log("Couldn't load model");
return null;
}
});
}
}
}
TreeDelegate.qml
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.3
import QtQuick 2.7
import QtQml 2.0
Item
{
id: item
width : parent.width
height: 25
TextEdit
{
id: text1
font.pixelSize: 14
readOnly: true
focus: false
width : parent.width
height: 25 * lineCount
anchors.left: parent.left
wrapMode: TextEdit.Wrap
text: styleData.value
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
}
}

Related

How to use QAbstractItemModel for ComboBox

I have implemented QAbstractItemModel as a C++ class and I'm trying to use it as a model for QComboBox. The model is storing QList of QStrings, which are names of project apps INI files. I can print out the list into console, and I'am certainly getting the right list of files. However I'm getting an error Unable to assign [undefined] to QString.
Can you please help me, what am I doing wrong?
LcuModel.cpp
#include "LcuModel.h"
#include "logging/LoggingQtCategories.h"
#include <QCoreApplication>
#include <QSettings>
#include <QFileInfo>
#include <QDir>
LcuModel::LcuModel(QObject *parent)
: QAbstractItemModel(parent)
{
QSettings settings;
QFileInfo fileInfo(settings.fileName());
QDir directory(fileInfo.absolutePath());
iniFiles_ = directory.entryList({"*.ini"},QDir::Files);
if (iniFiles_.empty())
{
qCWarning(lcLcu) << "No ini files were found.";
}
qCInfo(lcLcu) << iniFiles_; //this prints out all the files to console, so the list is certainly not empty
}
QModelIndex LcuModel::index(int row, int column, const QModelIndex &parent) const
{
Q_UNUSED(row)
Q_UNUSED(column)
Q_UNUSED(parent)
return QModelIndex();
}
QModelIndex LcuModel::parent(const QModelIndex &index) const
{
Q_UNUSED(index)
return QModelIndex();
}
int LcuModel::rowCount(const QModelIndex & parent) const {
Q_UNUSED(parent)
return iniFiles_.count();
}
int LcuModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return 1;
}
QVariant LcuModel::data(const QModelIndex & index, int role) const
{
Q_UNUSED(role);
int row = index.row();
qCInfo(lcLcu) << QString::number(row) << " ";
if (row < 0 || row >= iniFiles_.count()) {
return QVariant();
}
return QVariant(iniFiles_.at( index.row() ));
}
QHash<int, QByteArray> LcuModel::roleNames() const
{
QHash<int, QByteArray> names;
names[textRole] = "fileName";
return names;
}
LcuModel.h
#ifndef LCUMODEL_H
#define LCUMODEL_H
#include <QAbstractItemModel>
class LcuModel : public QAbstractItemModel
{
Q_OBJECT
public:
explicit LcuModel(QObject *parent = nullptr);
enum {
textRole
};
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const override;
protected:
virtual QHash<int, QByteArray> roleNames() const override;
private:
QList<QString> iniFiles_;
};
#endif // LCUMODEL_H
LcuMain.qml
import QtQuick 2.12
import QtQuick.Controls 2.12
import project.lcu 1.0
Item {
id: lcuMain
LcuModel{
id:lcuModel
}
Component.onCompleted: {
console.info("Completed "+ comboBox.count + "items")
} //this gives me right number of items from the list
Column{
ComboBox{
id: comboBox
model: lcuModel
delegate: ItemDelegate
{
contentItem: Text {
text: fileName
}
}
onActivated: console.info(comboBox.currentText + comboBox.currentIndex) //it prints out an empty string
}
}
}
console output
18:02:25.543 [ info ] project.lcu: LcuModel::LcuModel - ("first.ini", "second.ini", "third.ini", "fourth.ini", "fifth.ini", "sixth.ini", "seventh.ini", "eight.ini", "nineth.ini")
18:02:25.616 [warning] unknown - ComboBox.qml:47:5: QML Connections: Cannot assign to non-existent property "onCountChanged"
18:02:25.675 [ info ] qml: onCompleted - 9
18:02:25.678 [warning] unknown - qrc:/LcuMain.qml:46:25: Unable to assign [undefined] to QString
It is unnecessary for the model to inherit from QAbstractItemModel, QAbstractListModel is enough so you don't have to implement multiple methods. On the other hand, you must establish the role that you want to show through textRole and in the delegate you must use modelData:
#ifndef LCUMODEL_H
#define LCUMODEL_H
#include <QAbstractListModel>
class LcuModel : public QAbstractListModel
{
Q_OBJECT
public:
enum {
textRole = Qt::UserRole
};
explicit LcuModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override;
private:
QStringList iniFiles_;
};
#endif // LCUMODEL_H
#include "lcumodel.h"
#include <QDir>
#include <QFileInfo>
#include <QSettings>
LcuModel::LcuModel(QObject *parent)
: QAbstractListModel(parent)
{
QSettings settings;
QFileInfo fileInfo(settings.fileName());
QDir directory(fileInfo.absolutePath());
iniFiles_ = directory.entryList({"*.ini"}, QDir::Files);
}
int LcuModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
return iniFiles_.length();
}
QVariant LcuModel::data(const QModelIndex &index, int role) const
{
if(!checkIndex(index)){
return QVariant();
}
if(role == textRole){
return iniFiles_.at(index.row());
}
return QVariant();
}
QHash<int, QByteArray> LcuModel::roleNames() const
{
QHash<int, QByteArray> names;
names[textRole] = "fileName";
return names;
}
import QtQuick 2.12
import QtQuick.Controls 2.12
import project.lcu 1.0
Item{
id: lcuMain
LcuModel{
id:lcuModel
}
Column{
ComboBox{
id: comboBox
model: lcuModel
textRole: "fileName"
delegate: ItemDelegate{
text: modelData
width: parent.width
}
}
}
}

QAbstractListModel::Data() method is never called

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

Unknown method return type even if declared

Qt5.11 / QML. I'm trying to create a two-level QAbstractListModel to be viewed in QML. Think as a folders (first-level) and files (second-level) model.
Here my code:
model.h
class File
{
public:
File(const QString name) { _name = name; }
QString name() const { return _name; }
void setName(const QString &name) { _name = name; }
QPixmap thumbnail() const { return _thumbnail; }
void setThumbnail(QPixmap thumbnail) { _thumbnail = thumbnail; }
private:
QString _name;
QPixmap _thumbnail;
};
class FilesModel : public QAbstractListModel
{
Q_OBJECT
public:
enum FileRoles
{
NameRole = Qt::UserRole + 1,
ThumbnailRole
};
FilesModel(QObject *parent = nullptr);
void addFile(const File &file);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const;
protected:
QHash<int, QByteArray> roleNames() const;
private:
QString _name;
QList<File> _listFiles;
};
class Folder
{
public:
Folder(const QString name);
QString name() const { return _name; }
void setName(const QString &name) { _name = name; }
FilesModel *model() { return _model; }
private:
QString _name;
FilesModel *_model;
};
class FolderModel : public QAbstractListModel
{
Q_OBJECT
public:
enum FolderRoles
{
NameRole = Qt::UserRole + 1,
ModelRole
};
FolderModel(QObject *parent = nullptr);
void addFolder(const Folder &folder);
void clear();
int rowCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const;
Folder *findFolder(QString name);
FilesModel *filesModel(QString name);
protected:
QHash<int, QByteArray> roleNames() const;
bool removeRows(int row, int count, const QModelIndex &parent);
private:
QString _name;
QList<Folder> _list;
};
Q_DECLARE_METATYPE(FilesModel*)
model.cpp
FilesModel::FilesModel(QObject *parent) : QAbstractListModel(parent) { }
void FilesModel::addFile(const File &file)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
_listFiles << file;
endInsertRows();
}
int FilesModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return _listFiles.count();
}
QVariant FilesModel::data(const QModelIndex &index, int role) const
{
if (index.row() < 0 || index.row() >= _listFiles.count()) return QVariant();
const File &item = _listFiles[index.row()];
switch (role) {
case NameRole: return item.name(); break;
case ThumbnailRole: return item.thumbnail(); break;
default: return QVariant();
}
}
QHash<int, QByteArray> FilesModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[NameRole] = "name";
roles[ThumbnailRole] = "thumbnail";
return roles;
}
FolderModel::FolderModel(QObject *parent) : QAbstractListModel(parent) { }
void FolderModel::addFolder(const Folder &folder)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
_list << folder;
endInsertRows();
}
bool FolderModel::removeRows(int row, int count, const QModelIndex &parent)
{
if (count == 0) return false;
beginRemoveRows(parent, row, row + count - 1);
for (int i = 0; i < count; i++) _list.removeAt(row);
endRemoveRows();
emit dataChanged(this->index(row, 0), this->index(row, 0));
return true;
}
void FolderModel::clear()
{
removeRows(0, rowCount(), QModelIndex());
}
int FolderModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return _list.count();
}
QVariant FolderModel::data(const QModelIndex &index, int role) const
{
if (index.row() < 0 || index.row() >= _list.count()) return QVariant();
const Folder &item = _list[index.row()];
switch (role) {
case NameRole: return item.name(); break;
default: return QVariant();
}
}
Folder *FolderModel::findFolder(QString name)
{
for (int i = 0; i < _list.size(); i++)
{
if (_list[i].name() == name) return &_list[i];
}
return NULL;
}
FilesModel *FolderModel::filesModel(QString name)
{
Folder *folder = findFolder(name);
if (folder)
{
FilesModel *model = folder->model();
QQmlEngine::setObjectOwnership(model, QQmlEngine::CppOwnership);
return model;
}
return NULL;
}
QHash<int, QByteArray> FolderModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[NameRole] = "name";
roles[ModelRole] = "model";
return roles;
}
Folder::Folder(const QString name) : _name(name)
{
_model = new FilesModel();
}
main.cpp
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
MyApp myapp;
QQmlApplicationEngine qmlEngine;
qmlEngine.rootContext()->setContextProperty("MyModel", myapp.model());
qmlEngine.rootContext()->setContextProperty("MyApp", &myapp);
qmlEngine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (qmlEngine.rootObjects().isEmpty()) return -1;
return app.exec();
}
in MyApp.cpp I populate some items:
FolderModel _model;
_model.addFolder(Folder("Folder1"));
_model.addFolder(Folder("Folder2"));
// ...
Folder *objFolder = _model.findFolder(folder);
objFolder->model()->addFile(File("File 1"));
objFolder->model()->addFile(File("File 2"));
The QML code is mainly based upon the Photo Viewer demo.
main.qml
ApplicationWindow {
id: mainWindow
visible: true
DelegateModel { id: albumVisualModel; model: MyModel; delegate: AlbumDelegate {} }
GridView {
id: albumView
width: parent.width
height: parent.height
cellWidth: 210
cellHeight: 220
model: albumVisualModel.parts.album
visible: albumsShade.opacity !== 1.0
}
Rectangle {
id: albumsShade; color: mainWindow.color
width: parent.width; height: parent.height; opacity: 0.0
}
ListView {
anchors.fill: parent
model: albumVisualModel.parts.browser
interactive: false
}
}
AlbumDelegate.qml (first part)
Component {
id: albumDelegate
Package {
Item {
Package.name: 'browser'
GridView {
id: photosGridView; model: visualModel.parts.grid; width: mainWindow.width; height: mainWindow.height - 21
x: 0; y: 21; cellWidth: 160; cellHeight: 153; interactive: false
onCurrentIndexChanged: photosListView.positionViewAtIndex(currentIndex, ListView.Contain)
}
}
Item {
Package.name: 'fullscreen'
ListView {
id: photosListView; model: visualModel.parts.list; orientation: Qt.Horizontal
width: mainWindow.width; height: mainWindow.height; interactive: false
onCurrentIndexChanged: photosGridView.positionViewAtIndex(currentIndex, GridView.Contain)
highlightRangeMode: ListView.StrictlyEnforceRange; snapMode: ListView.SnapOneItem
}
}
Item {
Package.name: 'album'
id: albumWrapper; width: 210; height: 220
DelegateModel {
property string tag
id: visualModel; delegate: PhotoDelegate { }
model: MyApp.filesModel(tag)
}
It correctly shows the first-level model (folders) but it fails when I try to show the files in one of them with the following error:
qrc:/AlbumDelegate.qml:36: Error: Unknown method return type: FilesModel*
But I've declared that type using Q_DECLARE_METATYPE.
Do I need to add something else?
As far as I know I don't need qRegisterMetaType() because I'm not using it in signals or slots.

QT qml model within a model? and accesible via qml

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:

SqlQueryModel generic model - Role Names not setting right

I have been trying to use the SqlQueryModel generic model from the qt documentation but I just can't seem to get it working properly with qml.
very simular to another problem I saw on Stack OverFlow but they haven't got an answer yet either
My QSqlQueryModel doesn't show data in the listview
I can query the database and I get the right amount of results in my listview but if I try to access a property via the RoleName then it says the property is undefined
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "wordmodel.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
WordModel *wordListModel = new WordModel( qApp);
engine.rootContext()->setContextProperty("WLModel", wordListModel);
engine.load(QUrl(QStringLiteral("qrc:///qml/main.qml")));
return app.exec();
}
SqlQueryModel.h
#ifndef SQLQUERYMODEL_H
#define SQLQUERYMODEL_H
#include <QSqlQueryModel>
#include <QHash>
#include <QByteArray>
class SqlQueryModel : public QSqlQueryModel
{
Q_OBJECT
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole ) const;
inline RoleNameHash roleNames() const { return *roles; }
public:
explicit SqlQueryModel(QObject *parent = 0);
Q_INVOKABLE void setQuery(const QString &query, const QSqlDatabase &db = QSqlDatabase());
Q_INVOKABLE void setQuery();
QHash<int, QByteArray> generateRoleNames();
//inline RoleNameHash roleNames() const { return *roles; }
QHash<int, QByteArray> roleNames() const{return roles;}
signals:
void countChanged();
public slots:
void connectToDb();
void closeDB();
private:
//QSqlQuery SQL_SELECT;
QHash<int, QByteArray> roles;
QSqlDatabase mydb;
};
#endif // SQLQUERYMODEL_H
SqlQueryModel.cpp
#include "sqlquerymodel.h"
#include <QSqlRecord>
#include <QSqlField>
#include <QDebug>
SqlQueryModel::SqlQueryModel(QObject *parent) :
QSqlQueryModel(parent)
{
mydb=QSqlDatabase::addDatabase("QSQLITE");
QString dbPath = "E://Qt//sqlite//dictionary.db";
mydb.setDatabaseName(dbPath);
mydb.setUserName("admin");
mydb.setPassword("admin");
}
void SqlQueryModel::connectToDb()
{
if(!mydb.open())
{
qDebug() << "Database didnt open";
}
else
{
qDebug() << "Your database is open";
}
}
void SqlQueryModel::closeDB()
{
mydb.close();
}
void SqlQueryModel::setQuery(const QString &query, const QSqlDatabase &db)
{
connectToDb();
QSqlQueryModel::setQuery("SELECT * FROM words_en WHERE word LIKE 'young%' LIMIT 10",mydb);
generateRoleNames();
closeDB();
}
void SqlQueryModel::setQuery()
{
connectToDb();
QSqlQueryModel::setQuery("SELECT * FROM words_en WHERE word LIKE 'young%' LIMIT 10");
generateRoleNames();
closeDB();
}
QHash<int, QByteArray> SqlQueryModel::generateRoleNames()
{
roles.clear();
for( int i = 0; i < record().count(); i++) {
roles[Qt::UserRole + i + 1] = record().fieldName(i).toLatin1();
qDebug() << roles[Qt::UserRole + i + 1];
}
return roles;
}
QVariant SqlQueryModel::data(const QModelIndex &index, int role) const
{
qDebug() << "ALALLALALALALALA";
QVariant value = QSqlQueryModel::data(index, role);
qDebug() << value;
if(role < Qt::UserRole)
{
value = QSqlQueryModel::data(index, role);
}
else
{
int columnIdx = role - Qt::UserRole - 1;
QModelIndex modelIndex = this->index(index.row(), columnIdx);
value = QSqlQueryModel::data(modelIndex, Qt::DisplayRole);
}
return value;
}
main.qml
import QtQuick 2.2
import QtQuick.Window 2.1
Window {
visible: true
width: 800
height: 800
// here i tried putting the class into a Qml type with the qmlRegisterType()
// but also to no avail
// SqlQueryModel{
// id:something
// Component.onCompleted: {
// something.setQuery()
// }
// }
ListView{
width:parent.width
height:parent.height
model:wordListModel
delegate: Item{
width:parent.width
height: width/10
Text {
id: name
text: word
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
anchors.fill:parent
Component.onCompleted: {
console.log(id)
console.log(word)
}
}
}
}
}
Which version of Qt are you using. Method generateRoleNames() has been deprecated, reimplement roleNames() instead of generateRoleNames().