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
Related
I have some data structure updated in c++ layer. I have to display it in qml and save changes from qml layer to c++ structures. I hope there is a declarative approach to do it but I in desperate to find it.
Here is the part of code:
C++ header:
#ifndef NODEINFO_H
#define NODEINFO_H
#include <QObject>
#include <QString>
class NodeInfo : public QObject {
Q_OBJECT
Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged)
public:
NodeInfo(QObject *parent = 0);
virtual ~NodeInfo() {}
const QString& label() const;
void setLabel(const QString& val);
signals:
void labelChanged();
private:
QString d_label;
};
#endif // NODEINFO_H
C++ body:
#include "nodeinfo.h"
#include <QDebug>
NodeInfo::NodeInfo(QObject *parent) : QObject(parent), d_label("Test string") {
}
const QString &NodeInfo::label() const {
qDebug() << "NodeInfo::label: getter";
return d_label;
}
void NodeInfo::setLabel(const QString &val) {
qDebug() << "NodeInfo::label: setter - " << val;
d_label = val;
emit labelChanged();
}
main.cpp
#include <QGuiApplication>
#include <QDebug>
#include <QQmlContext>
#include <QQuickView>
#include <QQmlApplicationEngine>
#include "nodeinfo.h"
int main(int argc, char *argv[]) {
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
// qmlRegisterType<NodeInfo>("NodeInfo", 1, 0, "NodeInfo");
NodeInfo nodeDescr;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("nodeData", &nodeDescr);
const QUrl url(QStringLiteral("qrc:/main.qml"));
engine.load(url);
QObject *root = engine.rootObjects().value(0);
if (QWindow *window = qobject_cast<QWindow *>(root))
window->show();
else
return -1;
return app.exec();
}
Qml code:
import QtQuick 2.15
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
ApplicationWindow {
id: root
width: 360
height: 520
visible: true
// property alias a_label: nodeData.label
Column {
anchors.fill: parent
TextInput {
id: simpleTxt
text: nodeData.label
}
Text {
id: txt
text: nodeData.label
}
Button {
text: "writeProp"
onClicked: nodeData.label = simpleTxt.text
}
// Binding {
// target: nodeData
// property: "label"
// value: simpleTxt.text
// }
}
}
So when I'm editing text in TextInput it should automatically set property in c++ code but it do not. Only if I press button.
There is the Binding way as you see in comments and it works but I it's not a true way I hope.
Let's imagine if I have 15-30 or more data fields in my c++ structure and it's full rubbish if I must do 30 Bindings such way or if I need to write signal/slot on each data field and connect them.
But what is right way?
Any ideas appreciated
A simpler solution is to assign the signal associated to the property text:
Text {
id: txt
text: nodeData.label
onTextChanged: {
if(nodeData.label != simpleTxt.text)
nodeData.label = simpleTxt.text
}
}
text: nodeData.label sets binding nodeData.label --> text (one direction). I.e. text is updated whenever nodeData.label is changed. When the user types some text in the field, this binding is destroyed.
So if you want to update nodeData.label when the user changes text, you need to use onTextChaged event.
Text {
onTextChanged: nodeData.label = text
}
One more note: you need to check if the property is really changed before emitting the appropriate changed signal. So the code should be something like this:
void NodeInfo::setLabel(const QString &val) {
qDebug() << "NodeInfo::label: setter - " << val;
if (d_label != val)
{
d_label = val;
emit labelChanged();
}
}
This will prevent your code from endless binding loops.
I'm very new to Qt and have issues passing my Model to my View.
My view features a bunch of buttons and a Map with some markers whose latitudes/longitudes come from my Model.
Clicking on buttons should update the markers on the map (delete some and/or display new ones).
The problem is : When my model (a QList) gets updated on the C++ side, the QML side doesn't.
(I know this kind of question seems to have already been asked, but after reading the different answers, I can't get a clear view of whether I can get away with a smarter way of calling setContextProperty() or if I have to use things like emit signals and bind properties, which I also can't get a clear view of after reading a little documentation)
The architecture is the following :
A main class with a QApplication instantiation and a MainWindow (MainWindow being a custom QMainWindow class). App gets executed and Window gets shown.
A Mapwidget class (custom QQuickWidget class) with an updateMap() method that :
Reacts to button clicks on the user interface
Updates the Model (the QList)
Uses the setContextProperty() method to pass the updated Model to
the View
The MainWindow class has a Mapwidget attribute
Things I have tried so far :
When making a call to setContextProperty() in the Mapwidget Constructor before calling the setSource() method, the Model is taken into consideration. So the syntax I'm using for passing the Model into the View ought to be correct. The problem seems to be that any call to setContextProperty() afterwards (in this case : in the updateMap() method) isn't passed to the QML File.
Calling the setContextProperty() on different levels (Mapwidget class, MainWindow class), the results are the same, it's never taken into account after the application's first launch.
I have tested the Model and know for a fact that it does get updated inside the updateMap() method, it just seems like the update isn't transfered to the QML File.
QML File :
Item {
width: 1200
height: 1000
visible: true
Plugin {
id: osmPlugin
name: "osm"
}
Map {
id: map
anchors.fill: parent
plugin: osmPlugin
center: QtPositioning.coordinate(45.782074, 4.871263)
zoomLevel: 5
MapItemView {
model : myModel
delegate: MapQuickItem {
coordinate:QtPositioning.coordinate(
model.modelData.lat,model.modelData.lon)
sourceItem: Image {
id:image_1
source: <picturePath>
}
anchorPoint.x: image_1.width / 2
anchorPoint.y: image_1.height / 2
}
}
}
Mapwidget Class :
mapwidget::mapwidget(QWidget *parent) : QQuickWidget(parent)
{
this->setSource(QUrl(QStringLiteral("qrc:/main.qml")));
}
void mapwidget::updateMap(QList<QObject *> &data)
{
/**
DO OPERATIONS TO UPDATE data
Each append has the following form :
data.append(new DataObject(someLatitude, someLongitude))
*/
this->rootContext()->setContextProperty("myModel", QVariant::fromValue(data));
}
In the updateMap() method, the QObjects appended to the list are of a custom Class DataObject :
class DataObject : public QObject
{
Q_OBJECT
Q_PROPERTY(double lat READ lat WRITE setLat)
Q_PROPERTY(double lon READ lon WRITE setLon)
public:
explicit DataObject(QObject *parent = nullptr);
DataObject(double latitude, double longitude, QObject *parent =
nullptr);
void setLat(double latitude);
void setLon(double longitude);
double lat() const;
double lon() const;
double d_lat;
double d_lon;
}
Why can't the View see the updated Model even after a call to setContextProperty() ?
Thank you for your help
The name passed to you through setContextProperty(...) is an alias to the object that you pass, in the case of the binding of model: myModel it is made between the objects, in your case when you pass a new object with the same alias no longer the initial binding is valid since they are different objects, it is something similar to:
T *t = new T;
connect(t, &T::foo_signal, obj, &U::foo_slot);
t = new T;
Although both objects have the same alias (t) it does not imply that the connection persists with the second object.
The solution is to use the same object that notifies the update to QML, and in this case the solution is to implement a custom QAbstractListModel:
CoordinateModel class
// coordinatemodel.h
#ifndef COORDINATEMODEL_H
#define COORDINATEMODEL_H
#include <QAbstractListModel>
#include <QGeoCoordinate>
class CoordinateModel : public QAbstractListModel
{
Q_OBJECT
public:
enum{
PositionRole = Qt::UserRole + 1000
};
explicit CoordinateModel(QObject *parent = nullptr);
void insert(int index, const QGeoCoordinate & coordinate);
void append(const QGeoCoordinate & coordinate);
void clear();
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:
QList<QGeoCoordinate> m_coordinates;
};
#endif // COORDINATEMODEL_H
// coordinatemodel.cpp
#include "coordinatemodel.h"
CoordinateModel::CoordinateModel(QObject *parent)
: QAbstractListModel(parent)
{
}
void CoordinateModel::insert(int index, const QGeoCoordinate &coordinate){
int i = index;
if(index < 0) // prepend
i = 0;
else if (index >= rowCount()) // append
i = rowCount();
beginInsertRows(QModelIndex(), i, i);
m_coordinates.insert(i, coordinate);
endInsertRows();
}
void CoordinateModel::append(const QGeoCoordinate &coordinate){
insert(rowCount(), coordinate);
}
void CoordinateModel::clear(){
beginResetModel();
m_coordinates.clear();
endResetModel();
}
int CoordinateModel::rowCount(const QModelIndex &parent) const{
if (parent.isValid())
return 0;
return m_coordinates.count();
}
QVariant CoordinateModel::data(const QModelIndex &index, int role) const{
if (index.row() < 0 || index.row() >= m_coordinates.count())
return QVariant();
if (!index.isValid())
return QVariant();
const QGeoCoordinate &coordinate = m_coordinates[index.row()];
if(role == PositionRole)
return QVariant::fromValue(coordinate);
return QVariant();
}
QHash<int, QByteArray> CoordinateModel::roleNames() const{
QHash<int, QByteArray> roles;
roles[PositionRole] = "position";
return roles;
}
MapWidget class
// mapwidget.h
#ifndef MAPWIDGET_H
#define MAPWIDGET_H
#include <QQuickWidget>
class CoordinateModel;
class MapWidget : public QQuickWidget
{
public:
MapWidget(QWidget *parent=nullptr);
CoordinateModel *model() const;
private:
CoordinateModel *m_model;
};
#endif // MAPWIDGET_H
// mapwidget.cpp
#include "coordinatemodel.h"
#include "mapwidget.h"
#include <QQmlContext>
MapWidget::MapWidget(QWidget *parent):
QQuickWidget(parent),
m_model(new CoordinateModel{this})
{
rootContext()->setContextProperty("myModel", m_model);
setSource(QUrl(QStringLiteral("qrc:/main.qml")));
}
CoordinateModel *MapWidget::model() const
{
return m_model;
}
And then you can use it as:
MapWidget w;
w.model()->append(QGeoCoordinate(45.782074, -6.871263));
w.model()->append(QGeoCoordinate(50.782074, -1.871263));
w.model()->append(QGeoCoordinate(55.782074, 4.871263));
w.model()->append(QGeoCoordinate(45.782074, 4.871263));
w.model()->append(QGeoCoordinate(50.782074, 4.871263));
w.model()->append(QGeoCoordinate(55.782074, 4.871263));
main.qml
import QtQuick 2.12
import QtLocation 5.12
import QtPositioning 5.12
Item {
width: 1200
height: 1000
visible: true
Plugin {
id: osmPlugin
name: "osm"
}
Map {
id: map
anchors.fill: parent
plugin: osmPlugin
center: QtPositioning.coordinate(45.782074, 4.871263)
zoomLevel: 5
MapItemView {
model : myModel
delegate: MapQuickItem {
coordinate: model.position
sourceItem: Image {
id: image_1
source: "http://maps.gstatic.com/mapfiles/ridefinder-images/mm_20_red.png"
}
anchorPoint.x: image_1.width / 2
anchorPoint.y: image_1.height / 2
}
}
}
}
The complete example is here.
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
}
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 a simple BB10 app with a QML front end.
The GUI consists of a couple of buttons and a label
Page {
Container {
Label {
text: app.alarmCount()
}
Button {
text: qsTr("Resend Notification")
onClicked: {
app.resendNotification();
}
}
Button {
text: qsTr("Stop Service")
onClicked: {
app.stopService();
}
}
Button {
text: qsTr("Kill Service")
onClicked: {
app.killService();
}
}
}
}
And the C++ class
class ApplicationUI: public QObject
{
Q_OBJECT
Q_PROPERTY(QString alarmCount READ alarmCount NOTIFY AlarmUpdate)
public:
ApplicationUI();
virtual ~ApplicationUI() { }
Q_INVOKABLE void resendNotification();
Q_INVOKABLE void stopService();
Q_INVOKABLE void killService();
QString alarmCount() const;
void setAlamCount(int newCount);
signals:
void AlarmUpdate();
private:
bb::system::InvokeManager* m_invokeManager;
QString m_alarmCountDisplay;
};
and the hopefully relevant bit of the class
QString ApplicationUI::alarmCount() const
{
return m_alarmCountDisplay;
}
void ApplicationUI::setAlamCount(int newCount)
{
m_alarmCountDisplay = QString("%1 Alarms").arg(newCount);
emit AlarmUpdate();
}
My problem is the label never displays the alarm count string property. I have set a breakpoint on the emit and can see it's getting called and on the alarmCount() getter and can see that's returning the correct value but my front end never actually shows a value for the label.
You did not actually make a binding to the variable. Correct binding will look like:
text: app.alarmCount
But in your code it is:
text: app.alarmCount()
With your code it makes an error because you can't access any method of Q_OBJECT which is not Q_INVOKABLE or public slot. But even if you make such mark to your methods it means that you get alarmCount property only one single time and it will not be updated since you did not make a binding but just one method call.