I'm trying to get TableView (the new one from pure qml introdudced in qt5.12) with column which resize them dynamically to fill extra available space but It's not working
here is my minimal reproducible example:-
main.qml:-
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.15
Window {
id: window
width: 640
height: 480
visible: true
title: qsTr("Hello World")
TableView {
anchors.fill: parent
model: KoolModel
// columnWidthProvider: function (_) { return parent.width/3} // Method 1, Does not work
delegate: Frame {
// implicitWidth: parent.width/3 //Method 2, Does not work
implicitWidth: 640/3 // works but not the solution for my problem
Label {
text: display
anchors.centerIn: parent
}
}
}
}
If I execute this code, I get following:-
it is the desired output but when I resize the window the columns should expand which does not happen due to hardcoding the implicitWidth
if I use columnWidthProvider: function (_) { return parent.width/3} then I get nothing, just a white window:-
and If I try to use binding like this implicitWidth: parent.width/3 I get following:-
main.cpp:-
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "models/exams.hpp"
int main(int argc, char* argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
Models::Exams exams;
engine.rootContext()->setContextProperty("KoolModel", &exams);
const QUrl url(QStringLiteral("qrc:/main.qml"));
engine.load(url);
return app.exec();
}
models/exam.cpp
#pragma once
#include <QAbstractTableModel>
#include <QString>
#include <map>
namespace Models {
class Exams : public QAbstractTableModel {
private:
struct DS {
QString title;
unsigned int marks;
int state;
std::vector<int>* questions = nullptr;
};
//id and exams
std::map<int, DS> exams;
public:
Exams();
// QAbstractItemModel interface
int rowCount(const QModelIndex& parent) const override;
int columnCount(const QModelIndex& parent) const override;
QVariant data(const QModelIndex& index, int role) const override;
// QAbstractItemModel interface
public:
QHash<int, QByteArray> roleNames() const override;
};
} //end namespace Models
models/exam.cpp:-
#include "exams.hpp"
namespace Models {
Exams::Exams()
{
for (int i = 0; i < 200; i++) { //fill garbage data for now
DS exam {
"Exam" + QString::number(i),
0,
(i * 3) / 2,
nullptr
};
exams[i] = exam;
}
exams[2] = {
"Exam" + QString::number(10000000324),
0,
10,
nullptr
};
}
int Exams::rowCount(const QModelIndex& parent) const
{
return exams.size();
}
int Exams::columnCount(const QModelIndex& parent) const
{
return 3;
}
QVariant Exams::data(const QModelIndex& index, int role) const
{
if (role == Qt::DisplayRole) {
if (index.column() == 0)
return exams.at(index.row()).title;
else if (index.column() == 1)
return exams.at(index.row()).marks;
else if (index.column() == 2)
return exams.at(index.row()).state;
}
return QVariant();
}
QHash<int, QByteArray> Exams::roleNames() const
{
return { { Qt::DisplayRole, "display" } };
}
} // end namepsace Models
I'm using Qt 5.15 from Archlinux repos
Restating my comment as an answer: For performance reasons, TableView does not re-calculate its row height or column width unless absolutely necessary. But you can force it by calling forceLayout() whenever the width changes.
onWidthChanged: forceLayout()
Related
I need to present all the data in the QLinkedList container (this was given by the task). I created two classes, DataObject for my delegates in ListView, Glav for container with DataObject objects. I have a button on which I add data to the container (the addItem function in the Glav class). The data is added but not displayed in the ListView. How to display them? I tried it through signals, it didn't work.
Here is the complete code of the project.
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QStringListModel>
#include <QQmlContext>
#include <QLinkedList>
#include <QQuickView>
#include <container.h>
#include <dataobject.h>
#include "glav.h"
//#include <container.cpp>
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;
qmlRegisterUncreatableType<tile::Glav>( "Tile", 1, 0, "DataItem", "interface" );
qmlRegisterType<tile::Glav>( "Tile", 1, 0, "Glav");
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.Controls 2.5
//import App 1.0
import Tile 1.0
ApplicationWindow {
width: 640
height: 480
visible: true
title: qsTr("Scroll")
// Container {
// id: container
// }
Glav {
id: glav
}
Row {
id: buttons
spacing: 20
padding: 10
anchors.horizontalCenter: parent.horizontalCenter
RoundButton {
padding: 20
text: "add item"
onClicked: {
glav.addItem()
listView.currentIndex = -1
}
}
Connections{
target: myModelObjectWhichWasSetAsContextProperty
onRowsInserted: console.log("rows were inserted")
}
ScrollView {
anchors.fill: parent
anchors.topMargin: buttons.implicitHeight + 10
ListView {
id: listView
width: parent.width
model: glav.list
//required model
delegate: Text {
property var d
d: model.modelData.id
text: model.modelData.name
}
removeDisplaced: Transition {
NumberAnimation { properties: "x,y"; duration: 100; easing.type: Easing.InOutQuad }
}
}
}
}
dataobject.h
#ifndef DATAOBJECT_H
#define DATAOBJECT_H
#include <QObject>
namespace tile {
class DataObject : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY changed)
Q_PROPERTY(QString color READ color WRITE setColor NOTIFY changed)
Q_PROPERTY(int id READ id WRITE setId NOTIFY changed)
public:
explicit DataObject(QString name = "Wana", QString color = "red", int id = 1, QObject *parent = nullptr);
QString name();
void setName(const QString &name);
QString color();
void setColor(const QString &color);
int id() const;
void setId(int id);
signals:
void changed();
private:
QString m_name;
QString m_color;
int m_id;
};
}
#endif // DATAOBJECT_H
dataobject.cpp
#include "dataobject.h"
#include <QDebug>
namespace tile {
DataObject::DataObject(QString name, QString color, int id, QObject* parent) :
QObject(parent)
, m_name (name)
, m_color (color)
, m_id (id)
{
//new DataObject();
qDebug() << m_name;
//emit changed();
}
QString DataObject::name()
{
return m_name;
}
void DataObject::setName(const QString &name)
{
m_name = name;
qDebug() << m_name;
emit changed();
}
QString DataObject::color()
{
return m_color;
}
void DataObject::setColor(const QString &color)
{
m_color = color;
emit changed();
}
int DataObject::id() const
{
return m_id;
}
void DataObject::setId(int id)
{
m_id = id;
emit changed();
}
}
glav.h
#ifndef GLAV_H
#define GLAV_H
#include <QObject>
#include <QLinkedList>
namespace tile {
class Glav : public QObject
{
Q_OBJECT
Q_PROPERTY( QLinkedList<QObject *> list READ list CONSTANT )
public:
explicit Glav(QObject *parent = nullptr);
//QLinkedList<QObject *> list();
//void setList(const QLinkedList<QObject *> &list);
Q_INVOKABLE QLinkedList<QObject *> pollist();
Q_INVOKABLE void addItem();
// Q_INVOKABLE QVariant pol();
signals:
void changed();
private:
QLinkedList<QObject *> m_list;
QLinkedList<QObject *> list();
};
}
#endif // GLAV_H
glav.cpp
#include "glav.h"
#include "dataobject.h"
#include <QDebug>
#include <QAbstractListModel>
namespace tile {
Glav::Glav(QObject *parent) : QObject(parent)
{
QLinkedList<QObject *> dataList = {
new DataObject("Item 1", "red"),
new DataObject("Item 2", "green"),
new DataObject("Item 3", "blue"),
new DataObject("Item 9", "yellow")
};
m_list << dataList;
QVariant::fromValue(m_list);
}
QLinkedList<QObject *> Glav::list()
{
return m_list;
}
//void Glav::setList(const QLinkedList<QObject *> &list)
//{
// m_list = list;
//}
//QVariant Glav::pol(){
// QVariant re = QVariant::fromValue(m_list);
// return re;
//}
QLinkedList<QObject *> Glav::pollist()
{
//qDebug() << QVariant::fromValue(m_list);
//QLinkedList<QObject *> f = m_list;
//QVariant::fromValue(m_list);
//qDebug() <<m_list;
return m_list;
}
void Glav::addItem()
{
qDebug() << m_list.count();
m_list << new DataObject();
qDebug() << m_list.count();
emit changed();
}
}
Your Glav class has a changed() signal but it doesn't do anything because your list property is constant (Q_PROPERTY( QLinkedList<QObject *> list READ list CONSTANT ))
You should change it to this:
Q_PROPERTY(QLinkedList<QObject*> list READ list NOTIFY changed)
That way you can let the ListView know that the list has just been changed by emitting the changed() signal.
Also, it's better to name the signal in accordance with the property it corresponds to: listChanged.
More info on how Q_PROPERTY works in the official documentation: https://doc.qt.io/qt-5/properties.html
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
}
}
According to HorizontalHeaderView's doc, If the model is a QAbstractTableModel, then the header will display the model's horizontal headerData(); otherwise, the model's data(). but It's not even calling it in my case. but QTableView from widgets module seems to work just fine.
here is my main.qml:-
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.15
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
id: root
HorizontalHeaderView {
id: header
syncView: tb
anchors.top: parent.top
// model: KoolModel // specify model explicitly also does not work
delegate: Button {
text: display
}
}
TableView {
id: tb
width: parent.width
height: parent.height - header.height
anchors.top: header.bottom
onWidthChanged: forceLayout()
model: KoolModel
columnWidthProvider: function (_) {
return root.width / 3
}
delegate: Frame {
Label {
text: display
anchors.centerIn: parent
}
}
}
}
here is my model:-
#include <QApplication>
#include <QGuiApplication>
#include <QTableView>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QAbstractTableModel>
#include <QString>
#include <map>
class Exams : public QAbstractTableModel {
private:
struct DS {
QString title;
unsigned int marks;
int state;
std::vector<int>* questions = nullptr;
};
//id and exams
std::map<int, DS> exams;
public:
Exams()
{
for (int i = 0; i < 10; i++) {
DS exam { "Exam" + QString::number(i), 0, (i * 3) / 2, nullptr }; // fill with garbage data for now
exams[i] = exam;
}
}
int rowCount(const QModelIndex& parent) const override
{
return exams.size();
}
int columnCount(const QModelIndex& parent) const override
{
return 3;
}
QVariant data(const QModelIndex& index, int role) const override
{
if (role == Qt::DisplayRole) {
if (index.column() == 0)
return exams.at(index.row()).title;
else if (index.column() == 1)
return exams.at(index.row()).marks;
else if (index.column() == 2)
return exams.at(index.row()).state;
}
qDebug() << "oops";
return QVariant();
}
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
{
qDebug() << "headerData is at least called";
if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
switch (section) {
case 0:
return QString("Title");
case 1:
return QString("Marks");
case 2:
return QString("state");
}
}
return QVariant();
}
QHash<int, QByteArray> roleNames() const override
{
return { { Qt::DisplayRole, "display" } };
}
};
int main(int argc, char* argv[])
{
Exams exams;
// widgets seems to work
// QApplication app(argc, argv);
// QTableView widget;
// widget.setModel(&exams);
// widget.show();
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("KoolModel", &exams);
const QUrl url(QStringLiteral("qrc:/main.qml"));
engine.load(url);
return app.exec();
}
The problem is caused by a naming conflict between the Button's display property and the role. The solution is to access the role through the model explicitly:
// ...
delegate: Button {
text: model.display
}
// ...
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 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().