I am trying to populate a TableView in QML with some information retrieved from a MySQL database.
I am able to connect to the database using QSqlQuery, but when trying to use QSqlQueryModel, it is not working (results obtained in the image at the end). I've been debugging the application, but the overrided function data and the overrided function roleNames of the model are never called.
This is how my model file looks like: tableModel.h
#ifndef TABLEMODEL_H
#define TABLEMODEL_H
#include <QObject>
#include <QtQml/qqml.h>
#include <QSqlQueryModel>
#include <source/database/mySQL/mySqlQueries.h>
class TableModel : public QSqlQueryModel {
Q_OBJECT
public:
// List all the roles that will be used in the TableView
enum Roles {
CHROM_ROLE = Qt::UserRole + 1,
POS_ROLE,
ID_ROLE,
REF_ROLE,
ALT_ROLE,
QUAL_ROLE
};
explicit TableModel(QObject *parent = 0);
// Override the method that will return the data
QVariant data( const QModelIndex & index, int role = Qt::DisplayRole ) const override;
protected:
/* hashed table of roles for speakers.
* The method used in the wilds of the base class QAbstractItemModel,
* from which inherits the class QSqlQueryModel
* */
QHash<int, QByteArray> roleNames() const override;
};
#endif // TABLEMODEL_H
Here, the implementation of the file: tableModel.cpp
#include "tableModel.h"
TableModel::TableModel(QObject *parent) : QSqlQueryModel(parent) {
}
// The method for obtaining data from the model
QVariant TableModel::data( const QModelIndex & index, int role) const {
switch (role) {
case CHROM_ROLE:
return QString("%1, %2").arg(index.column()).arg(index.row());
case POS_ROLE:
return QString("%1, %2").arg(index.column()).arg(index.row());
case ID_ROLE:
return QString("%1, %2").arg(index.column()).arg(index.row());
case REF_ROLE:
return QString("%1, %2").arg(index.column()).arg(index.row());
case ALT_ROLE:
return QString("%1, %2").arg(index.column()).arg(index.row());
case QUAL_ROLE:
return QString("%1, %2").arg(index.column()).arg(index.row());
default:
break;
}
return QVariant();
}
QHash<int, QByteArray> TableModel::roleNames() const {
QHash<int, QByteArray> roles;
roles[CHROM_ROLE] = "CHROM_ROLE";
roles[POS_ROLE] = "POS_ROLE";
roles[ID_ROLE] = "ID_ROLE";
roles[REF_ROLE] = "REF_ROLE";
roles[ALT_ROLE] = "ALT_ROLE";
roles[QUAL_ROLE] = "QUAL_ROLE";
return roles;
}
As you can see, I've overrided data and roleNames. None of those functions are called when executing it. (Right now, data should return the row and column numbers, not the real data).
The corresponding part of the qml file containing the TableView object is the following:
import QtQuick.Controls 2.4
import QtQuick.Controls 1.4 as Controls
import QtQuick.Window 2.11
import Qt.labs.qmlmodels 1.0
Window {
id: root
visible: true
width: 640
height: 480
title: qsTr("Title")
Controls.TableView {
id: tableview
width: root.width * 0.8
height: root.height * 0.8
anchors.centerIn: parent
clip: true
Controls.TableViewColumn {
role: "CHROM_ROLE" // These roles are roles names coincide with a C ++ model
title: "#Chrom"
}
Controls.TableViewColumn {
role: "POS_ROLE" // These roles are roles names coincide with a C ++ model
title: "Pos."
}
Controls.TableViewColumn {
role: "ID_ROLE" // These roles are roles names coincide with a C ++ model
title: "ID"
}
Controls.TableViewColumn {
role: "REF_ROLE" // These roles are roles names coincide with a C ++ model
title: "Ref."
}
Controls.TableViewColumn {
role: "ALT_ROLE" // These roles are roles names coincide with a C ++ model
title: "Alt."
}
Controls.TableViewColumn {
role: "QUAL_ROLE" // These roles are roles names coincide with a C ++ model
title: "Qual."
}
// We set the model in the TableView
model: TableModel
}
}
As you can see, the roles for each of the TableViewColumns, corresponds with the roles in the cpp file.
The pro file contains the following line (besides, SQL queries are working):
QT += sql
In the main.cpp file, I am instantiating everything like this.:
QGuiApplication app(argc, argv);
qmlRegisterType<TableModel>("TableModel", 1, 0, "TableModel");
TableModel tableModel;
MySqlConnector db;
db.connectToDB("localhost", "dbname", "user", "Password");
db.open();
// I've tested that the query is working using just QSqlQuery and retrieves info from database
tableModel.setQuery(MySQLQueries::queryBodyOfVCF.arg(1), db.db);
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("TableModel", &tableModel);
const QUrl url(QStringLiteral("qrc:/views/MasterView.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();
When I execute it, this is what I am getting:
The expected results should be something like this (In the real database there is only one row, and as mentioned in the comments, tableModel.rowCount() returns 1 as expected). This is an example of real data that should be displayed:
chrom pos ref alt qual id
ctg1 9 A C, G 100 rs001
ctg3 12 C T 100 rs002
Is there something wrong with my implementation? I think that I may be me misunderstanding some concept, or something missing in the .h file or the .cpp file.
The problem is caused because you are using the name of the item: TableModel, the solution is to change the name of the context-property:
engine.rootContext()->setContextProperty("tableModel", &tableModel);
model: tableModel
On the other hand, I recommend using the model that is implemented in another post where I generalize the logic.
Related
I have subclasses the QAbstractTableModel and provided the headerData override:
/**
* #brief Obtains the header (columns) names.
* #param section: column number.
* #param orientation: Accepts only horizontal.
* #param role: Accepts only display.
* #return The column header text in case all params are valid.
* Otherwise empty QVariant.
*/
QVariant CVarTableModel::headerData(int section,
Qt::Orientation orientation,
int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
if (orientation != Qt::Horizontal)
return QVariant();
if (section >= static_cast<int>(Columns::ZCOUNT))
return QVariant();
return QVariant::fromValue(static_cast<Columns>(section));
}
I am trying to figure out how to make my QML TableView component utilize this function. Is there a way to do this automatically?
Make your method invokbale from QML by using the macro Q_INVOKABLE. Then, use it in your QML as any other method:
class Model: public QStandardItemModel
{
public:
Model(QObject* parent=nullptr): QStandardItemModel(parent)
{
setColumnCount(2);
setRowCount(2);
}
Q_INVOKABLE virtual QVariant headerData(int section,
Qt::Orientation orientation,
int role=Qt::DisplayRole) const override
{
qDebug() << section << orientation << role;
if (role != Qt::DisplayRole)
return QVariant();
if (section == 0)
return "First Column";
return "Not first column";
}
};
// In main.cpp
Model* model = new Model();
QQuickView *view = new QQuickView;
view->rootContext()->setContextProperty("myModel", model);
view->setSource(QUrl("qrc:/main.qml"));
view->show();
TableView {
TableViewColumn {
role: "title"
title: myModel.headerData(0, Qt.Vertical);
width: 100
}
TableViewColumn {
role: "author"
title: myModel.headerData(1, Qt.Vertical);
width: 200
}
model: myModel
}
I found today a different solution to this I did not know: https://doc.qt.io/qt-6/qml-qtquick-controls2-horizontalheaderview.html
Column{
HorizontalHeaderView{
syncView: tableView
}
TableView {
id: tableView
model: myModel
}
}
The HorizontalHeaderView will use what you provide in your model with headerData.
I'm trying to use QML TreeView Model. The example from Qt doesn't include how to create the model. I read this post and tried to use the code from #Tarod but the result is not what I expected.
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "animalmodel.h"
#include <qqmlcontext.h>
#include <qqml.h>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
AnimalModel model;
model.addAnimal("wolf", "Medium");
model.addAnimal("Bear", "Large");
QQmlApplicationEngine engine;
QQmlContext *ctxt = engine.rootContext();
ctxt->setContextProperty("myModel", &model);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
animalmodel.h
#ifndef ANIMALMODEL_H
#define ANIMALMODEL_H
#include <QStandardItemModel>
class AnimalModel : public QStandardItemModel
{
Q_OBJECT //The Q_OBJECT macro must appear in the private section of a class definition that declares its own signals and slots or that uses other services provided by Qt's meta-object system.
public:
enum AnimalRoles {
TypeRole = Qt::UserRole + 1,
SizeRole
};
AnimalModel(QObject *parent = 0);
Q_INVOKABLE void addAnimal(const QString &type, const QString &size);
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
protected:
QHash<int, QByteArray> roleNames() const;
};
#endif // ANIMALMODEL_H
animalmodel.cpp
#include "animalmodel.h"
AnimalModel::AnimalModel(QObject *parent)
: QStandardItemModel(parent)
{
}
void AnimalModel::addAnimal(const QString &type, const QString &size)
{
QStandardItem* entry = new QStandardItem();
entry->setData(type, TypeRole);
auto childEntry = new QStandardItem();
childEntry->setData(size, SizeRole);
entry->appendRow(childEntry);
appendRow(entry);
}
QVariant AnimalModel::data(const QModelIndex & index, int role) const {
QStandardItem *myItem = itemFromIndex(index);
if (role == TypeRole)
return myItem->data(TypeRole);
else if (role == SizeRole) {
if (myItem->child(0) != 0)
{
return myItem->child(0)->data(SizeRole);
}
}
return QVariant();
}
QHash<int, QByteArray> AnimalModel::roleNames() const {
QHash<int, QByteArray> roles;
roles[TypeRole] = "type";
roles[SizeRole] = "size";
return roles;
}
main.qml
import QtQuick 2.6
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
menuBar: MenuBar {
Menu {
title: qsTr("&File")
MenuItem {
text: qsTr("&Open")
onTriggered: messageDialog.show(qsTr("Open Action Triggered"));
}
MenuItem {
text: qsTr("&Exit")
onTriggered: Qt.quit();
}
}
}
TreeView {
anchors.fill: parent
model: myModel
TableViewColumn {
title: "Name"
role: "type"
width: 300
}
TableViewColumn {
title: "Size"
role: "size"
width: 300
}
}
}
What I got is something like this:
Result
What I want to have is the animal size as a child of animal type.
Model sub-classing is one of the worst minefields in Qt. The advice is always to have it go through the model test (https://wiki.qt.io/Model_Test) to see if everything was implemented correctly.
On the other hand, in 90% of the cases you do not need to subclass a model at all as the default models provided by Qt work quite well. What I'd do is just use QStandardItemModel using, on the C++ side, only the QAbstractItemModel interface (i.e. force yourself to use QAbstractItemModel* model = new QStandardItemModel(/*parent*/);) this way, if in the future you feel like you really need to reimplement the model (for efficiency) you'll just need to change 1 line in your existing code.
In your case:
void AnimalModel::addAnimal(const QString &type, const QString &size)
{
if(columnCount()==0) insertColumn(0); // make sure there is at least 1 column
insertRow(rowCount()); // add a row to the root
const QModelIndex addedIdx = index(rowCount()-1,0);
setData(addedIdx, type, TypeRole); // set the root data
insertRow(rowCount(addedIdx),addedIdx ); // add 1 row ...
insertColumn(0,addedIdx ); // ... and 1 column to the added root row
setData(index(0,0,addedIdx), size, SizeRole); // set the data to the child
}
I have some model class that inherits QAbstractListModel:
VehiclesModel.h:
class VehiclesModel : public QAbstractListModel {
Q_OBJECT
public:
enum Roles {
ImagePathRole = Qt::UserRole + 1, // QString
NameRole // QString
};
virtual int rowCount(const QModelIndex & parent = QModelIndex()) const override { ... }
virtual QVariant data(const QModelIndex & index, int role) const override { ... }
virtual QHash<int, QByteArray> roleNames() const override {
QHash<int, QByteArray> roles = QAbstractListModel::roleNames();
roles[ImagePathRole] = "imagePath";
roles[NameRole] = "name";
return roles;
}
};
main.cpp:
#include "VehiclesModel.h"
int main(int argc, char * argv[]) {
QGuiApplication app(argc, argv);
VehiclesModel vehiclesModel;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("vehiclesModel", &vehiclesModel);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
And ComboBox that displays this model:
main.qml:
ComboBox {
id: control
model: vehiclesModel
delegate: ItemDelegate {
contentItem: RowLayout {
Image {
source: imagePath
}
Label {
text: name
}
}
highlighted: control.highlightedIndex == index
}
contentItem: RowLayout {
Image {
source: ??imagePath??
}
Label {
text: ??name??
}
}
}
I want to customize the ComboBox to show vehicle image and name. I can access to model data from ItemDelegate but how to access to model data outside the ItemDelegate? For example I want to access current index data (ImagePathRole and NameRole) to display vehicle image and name in contentItem.
Is it possible to do it without calling QAbstractListModel methods directly (i.e. index() and data() methods) and making them Q_INVOKABLE?
Not in any sort of a decent built-in way at the present time, unfortunately, this is something I've found to be lacking for quite a while, and I've considered implementing something for this in the QML models functionality, but I haven't yet had the time to do so.
For the time being, you can either do it yourself (like you're discussing), at the cost of type-safety and so on, or (the way I've typically tackled this before), you can create a QObject subclass to represent a single item in the model (ItemDataThing or whatever you choose to call it); provide it with a source model & index, properties, and let it represent a single instance of data from the model.
Something like:
class ImageDataThing : public QObject
{
Q_OBJECT
Q_PROPERTY(QString imagePath READ imagePath NOTIFY imagePathChanged)
Q_PROPERTY(QAbstractItemModel* model READ model WRITE setModel NOTIFY modelChanged)
Q_PROPERTY(int index READ index WRITE setIndex NOTIFY indexChanged)
public:
QString imagePath() const;
QAbstractItemModel *model() const;
void setModel(const QAbstractItemModel *newModel);
int index() const;
void setIndex(int newIndex);
signals:
void imagePathChanged(const QString &imagePath);
void modelChanged(QAbstractItemModel *model);
void indexChanged(int indexChanged);
};
... and in your implementation, whenever the model is set, hook the change signals (e.g. rowsInserted, rowsRemoved, ...) to alter the stored index (if provided) to keep it mapped to the correct place in the model.
In the model data getters (here, imagePath for instance), access the model instance (using the index) to grab the data out, and return it.
This has the obvious disadvantage of being a lot of boilerplate, but on the other hand, it's easy-to-write code if you are familiar with models, type-safe, and one could autogenerate it fairly easily.
You could create your own function to get data from the model, like the one I'm currently using,
VehiclesModel.h:
public slots:
int size() const; // to access from QML javascript
QVariant getData(int index, int role); // to access from QML javascript
VehiclesModel.cpp:
int VehiclesModel::size() const {
return m_list.size();
}
QVariant VehiclesModel::getData(int index, int role) {
if (index < 0 || index >= m_list.count())
return QVariant();
switch (role) {
case ImagePathRole:
return ...
break;
default:
break;
}
}
Shameless plug for my SortFilterProxyModel library.
The problem you are asking is actually a head-scratching scenario. I've found a way to do it somewhat correctly but it's kinda complicated and involves an external library. In my solution we filter the source model to only expose the element corresponding to the current index of the combo box and instantiate a delegate for this element and use it as the contentItem of the ComboBox.
This has the advantage of not having to modify your model and keeping in synch with your model changes.
import SortFilterProxyModel 0.2 // from https://github.com/oKcerG/SortFilterProxyModel
import QtQml 2.2
/*
...
*/
ComboBox {
id: control
model: vehiclesModel
delegate: ItemDelegate {
contentItem: RowLayout {
Image {
source: imagePath
}
Label {
text: name
}
}
highlighted: control.highlightedIndex == index
}
contentItem: { currentIndex; return selectedInstantiator.object; } // use currentIndex to force the binding reevaluation. When the model changes, the instantiator doesn't notify object has changed
Instantiator {
id: selectedInstantiator
model: SortFilterProxyModel {
sourceModel: control.model
filters: IndexFilter {
minimumIndex: control.currentIndex
maximumIndex: control.currentIndex
}
}
delegate: RowLayout {
Image {
source: imagePath
}
Label {
text: name
}
}
}
}
I strongly suggest to look at the Qt QML Tricks library made by Thomas Boutroue:
https://gitlab.com/qt-qml-libraries-4-me/qt-qml-tricks-ng
More specific the QQmlObjectListModel (from the Qt QML Models) could do the trick for you.
Expanding with using the Qt Super-Macros, it reduces overhead writing setters/getters!
These macros basically expand to a Q_PROPERTY, resulting in accessibility from QML, and add definition of a setter, getter and private variable.
Usage in your specific case this could look something like this, quickly written down, not validated (check using the correct index for referencing the model):
VehicleItem.h:
#include <QObject>
#include "QQmlVarPropertyHelpers.h" // Include library Qt Super-Macros
class VehicleItem : public QObject {
Q_OBJECT
QML_WRITABLE_VAR_PROPERTY(QString, imagePath)
QML_WRITABLE_VAR_PROPERTY(QString, name)
public:
explicit VehicleItem(QString imagePath, QString name, QObject* parent=0)
: QObject (parent)
, m_imagePath (imagePath)
, m_name (name)
{}
};
VehiclesModel.h:
#include <QObject>
#include "QQmlObjectListModel.h" // Include library Qt QML Models
#include "VehicleItem.h"
class VehiclesModel : public QObject {
Q_OBJECT
QML_OBJMODEL_PROPERTY(VehicleItem, modelList)
public:
explicit VehiclesModel(QObject *parent = 0);
};
VehiclesModel.c:
#include "VehiclesModel.h"
VehiclesModel::VehiclesModel(QObject *parent) :
QObject(parent), m_modelList(new QQmlObjectListModel<VehicleItem>())
{}
main.c (remains the same):
#include "VehiclesModel.h"
int main(int argc, char * argv[]) {
QGuiApplication app(argc, argv);
VehiclesModel vehiclesModel;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("vehiclesModel", &vehiclesModel);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
main.qml:
ComboBox {
id: control
model: vehiclesModel
delegate: ItemDelegate {
contentItem: RowLayout {
Image {
source: imagePath
}
Label {
text: name
}
}
highlighted: control.highlightedIndex == index
}
contentItem: RowLayout {
Image {
source: vehiclesModel.modelList.get(index).imagePath
}
Label {
text: vehiclesModel.modelList.get(index).name
}
}
}
As modelList (and also imagePath and name) is expanded by the macro to a Q_PROPERTY, it is accessible from QML side.
For the ins-and-outs of this library, be sure to check Thomas Boutroue's lightning talk at the QtWS2015: https://www.youtube.com/watch?v=96XAaH97XYo
Can I manage QSqlQueryModel's subclass at run time calling its methods from QML code and updating (changing) the current model? (The data is send to TableView ) How can I do it?
My QSqlQueryModel's subclass:
class SqlQueryModel : public QSqlQueryModel
{
Q_OBJECT
public:
explicit SqlQueryModel(QObject *parent = 0);
void setQuery(const QString &query, const QSqlDatabase &db = QSqlDatabase());
void setQuery(const QSqlQuery &query);
QVariant data(const QModelIndex &index, int role) const;
QHash<int, QByteArray> roleNames() const { return m_roleNames; }
private:
void generateRoleNames();
QHash<int, QByteArray> m_roleNames;
};
main.cpp:
// ...
SqlQueryModel sqlQueryModel;
QQuickView view;
QQmlContext *context = view.rootContext();
context->setContextProperty("sqlQueryModel", &sqlQueryModel);
// ...
For example, I need to call Q_INVOKABLE method changeModel() at run time that changes the current model and updates it with parameterized SELECT query:
void SqlQueryModel::changeModel(const int someValue)
{
QString statement;
QSqlQuery query;
statement = "SELECT * FROM 'tablename' WHERE some_field = ?;";
query.prepare(statement);
query.addBindValue(someValue);
query.exec();
setQuery(query);
}
And as result we get update the data into TableView:
TableView {
id: view
model: sqlQueryModel
TableViewColumn {
title: "1st field"
role: "someValue"
delegate: Text {
text: styleData.value
}
}
TableViewColumn {
title: "2nd field"
role: "oneMoreValue"
delegate: Text {
text: styleData.value
}
}
}
// ...
onSomeSignal: {
// query like this:
sqlQueryModel.changeModel(someValue);
}
Is it possible to do it using QSqlQueryModel? Please help me to solve this problem.
UPD: Perhaps, it's necessary to call function like qmlRegisterType() in order to allow using Q_INVOKABLE methods of SqlQueryModel class from the outside (from QML) and then initialize SqlQueryModel as a type within QML file. Thereafter we can connect our new SqlQueryModel type as TableView's data model.
UPD: I don't need to edit the data stored in the database. I want to be able to change SELECT query from one to similar.
It's very strange but the right answer is in the question.
The implementation described there works correctly. Perhaps, it went badly because of typo or defect that sneaked into project's source code.
I have been trying to use a QML TableView to display a QAbstractTableModel. The missing part of the equation seems to be that it is not possible to have a variable number of columns in the TableView, despite overriding QAbstractItemModel::roleNames which should tell Qt the number and name of my columns. I tried testing this out using only QML:
import QtQuick 2.0
import QtQuick.Controls 1.1
Rectangle {
anchors.fill: parent
property real showImage: 1.0
width: 500
TableView {
id: myTable
model: myModel
// TableViewColumn {
// role: "title"; title: "Name"; width: 200
// }
}
ListModel {
id: myModel
ListElement {
title: "item one"
}
ListElement {
title: "item two"
}
}
}
When run this doesn't show anything despite the TableView's mode containing ListElements with roles defined in them.
However if the above code is uncommented and a TableViewColumn is defined then the column will display data for that role as expected but the table will still not display any other roles. Obviously that will only work for a statically defined number of columns and not my case where the number of columns is not known until run time.
The example given is basically the same as my real life example except that my model is defined in C++.
It seems as if this may have already been asked here but it did not gain any response.
EDIT: I had tried calling a javascript function:
function addColumnToTable(roleName) {
var columnString = 'import QtQuick 2.3; import QtQuick.Controls 1.2; TableViewColumn {role: "'
+ roleName + '"; title: "' + roleName + '"; width: 40}';
var column = Qt.createQmlObject(
columnString
, myTable
, "dynamicSnippet1")
myTable.addColumn(column);
}
From C++:
QVariant roleName = "name";
QObject *root = view->rootObject();
QMetaObject::invokeMethod(root, "addColumnToTable", Q_ARG(QVariant, roleName));
This at least allowed me to dynamically add columns from C++ although not from within the model/view architecture. Yoann's solution is far and away better than this though.
You could create dynamically as many TableViewColumn as you need, using the resources property of your TableView.
You will have to add a method in your custom model class which will give you the roleNames you want to display.
QML:
Component
{
id: columnComponent
TableViewColumn{width: 100 }
}
TableView {
id: view
anchors.fill: parent
resources:
{
var roleList = myModel.customRoleNames
var temp = []
for(var i=0; i<roleList.length; i++)
{
var role = roleList[i]
temp.push(columnComponent.createObject(view, { "role": role, "title": role}))
}
return temp
}
model: myModel
MyModel.h:
class MyModel: public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(QStringList userRoleNames READ userRoleNames CONSTANT)
public:
explicit MyModel(QObject *parent = 0);
enum MyModelRoles {
UserRole1 = Qt::UserRole + 1,
UserRole2,
...
};
QStringList userRoleNames();
int rowCount(const QModelIndex & parent = QModelIndex()) const;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
...
private:
QHash<int, QByteArray> roleNames() const;
...
};
MyModel.cpp:
...
...
QHash<int, QByteArray> MyModel::roleNames() const {
QHash<int, QByteArray> roles = QAbstractListModel::roleNames ();
roles[UserRole1] = "whatever";
roles[UserRole2] = "youwant";
return roles;
}
QStringList MyModel::userRoleNames() // Return ordered List of user-defined roles
{
QMap<int, QString> res;
QHashIterator<int, QByteArray> i(roleNames());
while (i.hasNext()) {
i.next();
if(i.key() > Qt::UserRole)
res[i.key()] = i.value();
}
return res.values();
}
...
...
The solution with resources does not work for me (Qt 5.6). The TableView is not updated.
This works for me:
Component{
id: columnComponent
TableViewColumn{width: 30 }
}
TableView {
id: tableView
model: listModel
property var titles: somethingDynamic
property var curTitles: {
var t=[]
for(var i=0;i<columnCount;i++){
t.push(getColumn(i).title)
}
return t
}
onTitlesChanged:{
for(var i=0;i<titles.length;i++){
if(curTitles.indexOf(titles[i])==-1){
var column = addColumn(columnComponent)
column.title=titles[i]
column.role=titles[i]
}
}
for(var i=curTitles.length-1;i>=0;i--){
if(titles.indexOf(curTitles[i])==-1){
removeColumn(i)
}
}
}
}
}
It also correctly updates drag-drop-reordered columns.
Another way to do it with an Instantiator:
TableView{
id: view
model: tableViewModel
Instantiator{
model: someStringList
onObjectAdded: view.addColumn(object)
onObjectRemoved: view.removeColumn(object)
delegate: TableViewColumn{
width: 100
title: modelData
role: modelData
}
}
}
This code (and the one from #palfi) cause some warning in the console:
For each column created there is:
qml: TableView::insertColumn(): you cannot add a column to multiple views
And then when column are removed it produce this warning:
file:///usr/lib/qt/qml/QtQuick/Controls/Private/BasicTableView.qml:297: Error: Invalid attempt to destroy() an indestructible object
If someone has a better solution, please post it!