SetProperty for loaded QML component without using SetContextProperty - initialization

I have a C++ plugin system, where a QQmlComponent is created and a qml file is loaded when the user requests a new plugin instance.
Currently I am using setContextProperty() to tell QML about a QObject that is needed for proper initialization.
mEngine->rootContext()->setContextProperty("controller", QVariant::fromValue(mController));
mComponent = new QQmlComponent(mEngine);
mComponent->loadUrl(QUrl{ "qrc:///MyPlugin.qml" });
The problem is, when instantiating a second plugin, both will use the controller of the second one because "controller" is global in QML.
Repeater {
model: controller.numEntries
Is there a way to set a context property locally (only for the current instance)?
I found solutions using setProperty() or QQmlIncubator and setInitialState(), but they all seem to require an object that was already created from my component. But in my plugin I only define the component, which is loaded in the main application through a Loader item. So, when trying these approaches, I always ended up in setting the value in a copy of the item, but not the one being created in my backend.
How can I get access to a property of the component that is created in QML?
mComponent->findChild<QQuickItem*>("controller");
does not give me any results, even if I defined the property in MyPlugin.qml.

Maybe you can create a QObject based class and have a slot and instead of using property you can call slot to slot create a new property in c++ and return it to QML
ControllerCreator.h
#ifndef CONTROLLERCREATOR_H
#define CONTROLLERCREATOR_H
#include <QObject>
class ControllerCreator : public QObject {
Q_OBJECT
public:
explicit ControllerCreator(QObject *parent = nullptr);
signals:
public slots:
int propertyCreator();
private:
int m_example;
};
#endif // CONTROLLERCREATOR_H
ControllerCreator.cpp
#include "ControllerCreator.h"
ControllerCreator::ControllerCreator(QObject *parent)
: QObject(parent), m_example(0)
{
}
int ControllerCreator::propertyCreator()
{
m_example++;
return m_example;
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "ControllerCreator.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
ControllerCreator controllerCreator;
engine.rootContext()->setContextProperty("creator", &controllerCreator);
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Column{
anchors.fill: parent
Text{
text: creator.propertyCreator()
color: "blue"
}
Text{
text: creator.propertyCreator()
color: "red"
}
Text{
text: creator.propertyCreator()
color: "green"
}
}
}

Related

QtQuick's ListView fails to take ownership of QAbstractItemModel object

Based on Qt documentation, whenever a QObject pointer type is passed from C++ code to QML, via a Q_INVOKABLE method, there is a set of rules that determine who is responsible for the lifetime of that pointer. Should the QObject be parentless, implicitly the QML engine is responsible for taking ownership of the pointer.
In my scenario, I want my frontend UI to represent a list model which is generated/provided by the backend C++ code. My assumption is that the pointer will stay alive as long as there is a reference to it by the QML code. The code below shows the trimmed down test case:
Main.cpp
#include <QAbstractItemModel>
#include <QDebug>
#include <QGuiApplication>
#include <QObject>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QStringListModel>
class MyStringListModel : public QStringListModel
{
Q_OBJECT
public:
explicit MyStringListModel(const QStringList &strings, QObject* parent=nullptr) : QStringListModel(strings, parent)
{
qDebug() << "Creation";
}
virtual ~MyStringListModel() override
{
qDebug() << "Destruction";
}
};
class Backend : public QObject
{
Q_OBJECT
public:
Backend(QObject* parent=nullptr) : QObject(parent)
{
}
Q_INVOKABLE QAbstractItemModel* createModel() const
{
static const QStringList months = {
tr("January"),
tr("February"),
tr("March"),
tr("April"),
tr("May"),
tr("June"),
tr("July"),
tr("August"),
tr("September"),
tr("October"),
tr("November"),
tr("December"),
};
return new MyStringListModel(months);
}
};
int main(int argc, char* argv[])
{
QGuiApplication application(argc, argv);
qmlRegisterType<QAbstractItemModel>();
Backend backend;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("backend", &backend);
engine.load("qrc:///ui/main.qml");
return application.exec();
}
#include "main.moc"
Main.qml
import QtQuick 2.10
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.1
ApplicationWindow {
id: window
width: 200
height: 250
visible: true
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
ListView {
Layout.fillWidth: true
Layout.fillHeight: true
model: backend.createModel()
delegate: Text {
anchors.horizontalCenter: parent.horizontalCenter
text: model.display
}
}
Button {
Layout.alignment: Qt.AlignCenter
text: qsTr("Garbage Collect")
onClicked: gc()
}
}
}
This is a screenshot of the program:
The moment the user clicks on the button, the garbage collector runs and destroys the model ptr (destruction is evident by the "Creation" and "Destruction" output in the stdout).
I'm curious to know why the pointer was destroyed? I've noticed that it didn't set the ListView as its parent, which is fair enough, I thought that the QML engine would have used some form of reference pointer to try keep track of who still holds a reference to it. Is there a document which gives greater insight into the way in which garbage collection / ownership is implemented.
Likewise, is there a better way of structuring this code while still meeting the demands of passing a parentless QObject back to QML.
It seems that the reason for the destruction is because the object is not being referenced in QML, for example if it is assigned to a property the garbage collector will not affect it:
ApplicationWindow {
id: window
width: 200
height: 250
visible: true
property var mymodel: backend.createModel()
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
ListView {
Layout.fillWidth: true
Layout.fillHeight: true
model: mymodel
delegate: Text {
anchors.horizontalCenter: parent.horizontalCenter
text: display
}
}
Button {
Layout.alignment: Qt.AlignCenter
text: qsTr("Garbage Collect")
onClicked: gc()
}
}
}

How to call slot with QFlags argument from QML

I have an enum defined in a QObject with a few values, and I am registering the enum as QFlags as the Qt documentation specifies. I have registered the enum and the QObject as metatypes that I can access just fine from QML.
The problem is that once I have a C++ QObject slot defined that has the QFlags as an argument it doesn't get an error when it is called, but instead passes in the first defined value in the enum (ie. its value is that of the enum entry with the number 0).
It is hard to describe, so I created a small working example (using C++11/Qt 5.7). When you run it and click anywhere in the window that opens, QFlags<QMLThing::MyEnum>(VALA) is printed out, even though in main.qml I am calling thing.doThing(QMLThing.VALC).
I started by creating a "Qt Quick Application" in QtCreator. Then added a class called "QMLThing". Here is the source code for each file:
QMLThing.hpp
#ifndef QMLTHING_HPP
#define QMLTHING_HPP
#include <QObject>
class QMLThing : public QObject
{
Q_OBJECT
public:
enum MyEnum {
VALA = 0,
VALB = 1,
VALC = 2,
VALD = 4,
};
Q_ENUM(MyEnum)
Q_DECLARE_FLAGS(MyEnums, MyEnum)
public:
explicit QMLThing(QObject *parent = 0);
public slots:
void doThing(QMLThing::MyEnums val);
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QMLThing::MyEnums)
Q_DECLARE_METATYPE(QMLThing::MyEnums)
#endif // QMLTHING_HPP
QMLThing.cpp
#include "QMLThing.hpp"
#include <QDebug>
QMLThing::QMLThing(QObject *parent) : QObject(parent)
{}
void QMLThing::doThing(QMLThing::MyEnums val)
{
qDebug() << val;
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "QMLThing.hpp"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<QMLThing>("stuff", 1, 0, "QMLThing");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
main.qml
import QtQuick 2.7
import QtQuick.Window 2.2
import stuff 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
MouseArea {
anchors.fill: parent
onClicked: {
thing.doThing(QMLThing.VALC)
}
}
Text {
text: qsTr("Click here and look in the terminal")
anchors.centerIn: parent
}
QMLThing {
id: thing
}
}
This seems a lot like a bug, but maybe I'm just missing something.
You're missing Q_FLAG(MyEnums):
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include <QObject>
class QMLThing : public QObject
{
Q_OBJECT
public:
enum MyEnum {
VALA = 0,
VALB = 1,
VALC = 2,
VALD = 4,
VALE = VALC | VALD
};
Q_DECLARE_FLAGS(MyEnums, MyEnum)
Q_FLAG(MyEnums)
public:
explicit QMLThing(QObject *parent = 0) :
QObject(parent)
{
}
public slots:
void doThing(QMLThing::MyEnums val)
{
qDebug() << val;
}
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QMLThing::MyEnums)
Q_DECLARE_METATYPE(QMLThing::MyEnums)
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<QMLThing>("stuff", 1, 0, "QMLThing");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
#include "main.moc"
main.qml:
import QtQuick 2.7
import QtQuick.Window 2.2
import stuff 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
MouseArea {
anchors.fill: parent
onClicked: {
thing.doThing(QMLThing.VALC)
thing.doThing(QMLThing.VALC | QMLThing.VALD)
}
}
Text {
text: qsTr("Click here and look in the terminal")
anchors.centerIn: parent
}
QMLThing {
id: thing
}
}
As mentioned here, you don't need to use Q_ENUM():
Note: The Q_FLAG macro takes care of registering individual flag
values with the meta-object system, so it is unnecessary to use
Q_ENUM() in addition to this macro.
Not sure exactly what is going on but first of all:
public:
enum MyEnum {
VALA,
VALB,
VALC,
VALD,
};
You need to remove the last coma.
I would also recommend to set at least the first enum, to a certain value, usually 0 so you know where you are going but no need to set the following enum items as they will be auto-incremented from the last one set.
Last, I'm not entirely sure about the QMLThing.ValC, shouldn't it be QMLThing::MyEnums::ValC ?

Qml QTouchDevice

I am new in Qt. I am working on windows desktop application and using Qt with qml. On PC that hasn't QTouchDevices, splitter between component (element that allow you resize component on window) works good with mouse (screenshot "Good distance"), but if screen is touchscreen I have next problem, please look on screenshot "Wrong distance".
My application shouldn't support any touch device. So how to disable this Qt feature? I need the same behavior like on device without touch screen.
Wrong distance
Good distance
I have tried disable touch device using privet methods using next sample:
QWindowSystemInterface::unregisterTouchDevice(QTouchDevice::devices().first());
This works, but QWindowSystemInterface is private class and it disable touchscreen. And one more in QTCreator splitters work fine, exactly as I need.
If you can't patch Qt, the only way that I can think of would be to iterate through the children, searching for the MouseArea. For example, suppose you had this QML:
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.1
ApplicationWindow {
width: 600
height: 400
visible: true
property alias splitView: splitView
SplitView {
id: splitView
anchors.fill: parent
Rectangle {
width: 200
Layout.maximumWidth: 400
color: "lightblue"
Text {
text: "View 1"
anchors.centerIn: parent
}
}
Rectangle {
id: centerItem
Layout.minimumWidth: 50
Layout.fillWidth: true
color: "lightgray"
Text {
text: "View 2"
anchors.centerIn: parent
}
}
}
}
You could then print out the object tree of the SplitView like so:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickItem>
#include <QDebug>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QObject *window = engine.rootObjects().first();
QQuickItem *splitView = window->property("splitView").value<QQuickItem*>();
splitView->dumpObjectTree();
return app.exec();
}
That gives you:
SplitView_QMLTYPE_1::
QQmlComponent::
QQuickSystemPalette::
QObject_QML_2::
QQmlComponent::
QQuickItem::
QQuickItem::
QQuickItem::
QQuickLoader_QML_3::
QObject_QML_4::
QQuickMouseArea_QML_5::
QQuickRectangle::
QQmlContext::
QQuickItem::
QQmlComponentAttached::
QQuickRectangle::
QQuickText::
QQuickLayoutAttached::
QQuickRectangle::
QQuickText::
QQuickLayoutAttached::
QQuickLayoutAttached::
QObject::dumpObjectTree() prints out metaObject->className(), so we know to look for an object whose metaObject has a className matching that:
Then:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickItem>
#include <QScreen>
#include <QWindow>
#include <QDebug>
QQuickItem *findMouseArea(QQuickItem *item)
{
foreach (QQuickItem *childItem, item->childItems()) {
if (QString(childItem->metaObject()->className()).startsWith(QStringLiteral("QQuickMouseArea_QML"))) {
return childItem;
} else {
QQuickItem *mouseArea = findMouseArea(childItem);
if (mouseArea) {
return mouseArea;
}
}
}
return 0;
}
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QWindow *window = qobject_cast<QWindow*>(engine.rootObjects().first());
QQuickItem *splitView = window->property("splitView").value<QQuickItem*>();
QQuickItem *mouseArea = findMouseArea(splitView);
mouseArea->setProperty("defaultMargin", QVariant(window->screen()->physicalDotsPerInch() / 25.4));
return app.exec();
}
Apparently, Screen::pixelDensity is calculated using the physical dots per inch of the screen, divided by 25.4, so we copy that as well. You could use any other value there.
You will need to adapt the code should a second MouseArea ever be introduced, for example.
It's still largely dependent on private API, but it doesn't touch Qt code, at least.
Quick&Easy: enable/disable mouse and touch synthesizing
No need to patch things or doing any crazy tree lookups, just enable or disable mouse or touch synthesizing.
Qt::AA_SynthesizeTouchForUnhandledMouseEvents
Qt::AA_SynthesizeMouseForUnhandledTouchEvents
Just disable what you don't want, either mouse or touch to accept only authentic events.

How to connect a Qt Quick button click to a c++ method

I've just started to learn about Qt Mobile programming with Qt Quick 2.0 and I've been on google the whole day and its driving me crazy so here goes.
I have got just your standard qt quick mobile app that qt makes for you. Here is all the files.
(I'm brand new to this so these might be noob qu's, sorry)
Ok so here is my list of I Don't Knows:
how on earth do I connect this class method to the button click in the qml file.
Would it be possible to connect the same click and pass params through the click from the qml to the method and if so how would I do it?
and also I was wondering if I could link the class to the qt quick like a regular QWidget class for instance:
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
SomeClass *sc = new SomeClass();
QtQuick2ApplicationViewer viewer;
// this I guess would be more like the Widget method but this i really dont know
// about, just a question I'm throwing out there
viewer. (Link qt quick qml to object "sc"(Containing all my processes))
viewer.setMainQmlFile(QStringLiteral("qml/Final2/main.qml"));
viewer.showExpanded();
return app.exec();
}
The main(.)cpp file
#include <QtGui/QGuiApplication>
#include "qtquick2applicationviewer.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QtQuick2ApplicationViewer viewer;
viewer.setMainQmlFile(QStringLiteral("qml/Final2/main.qml"));
viewer.showExpanded();
return app.exec();
}
This is just the button element from my main.qml
Button {
id: btn
y: 443
height: 39
text: "Click Me"
anchors.bottom: parent.bottom
anchors.bottomMargin: 8
anchors.right: parent.right
anchors.rightMargin: 25
anchors.left: parent.left
anchors.leftMargin: 15
}
This is just some random class header :
#ifndef SOMECLASS_H
#define SOMECLASS_H
#include <QObject>
#include <QDebug>>
class SomeClass : public QObject
{
Q_OBJECT
public:
explicit SomeClass(QObject *parent = 0);
signals:
public slots:
void buttonClicked();
void buttonClicked(QString &in);
};
#endif // SOMECLASS_H
Of course the cpp file :
#include "someclass.h"
SomeClass::SomeClass(QObject *parent) :
QObject(parent)
{
}
void SomeClass::buttonClicked()
{
// do something
}
void SomeClass::buttonClicked(QString &in)
{
qDebug() << "Your string was : " << in;
}
I really appreciate all the help.
Thank you.
First, you need to export the SomeClass object to QtQuick (is that your third question?):
SomeClass sc;
viewer.rootContext()->setContextProperty(QStringLiteral("_someObject"), &sc);
This makes the sc object available in QtQuick, under the name "_someObject".
Then, in the QML use it like this:
Button {
....
onClicked: {
_someObject.buttonClicked(); //_someObject is the name used in setContextProperty.
}
}
This assumes the Button has a signal clicked() that's emitted on click/touch. Not knowing which Button component you use, I can't check that.
To pass an argument, just do
onClicked: {
_someObject.buttonClicked("Something");
}

passing signal with parameters from qml to c++

I dont know how to pass parameters from QML file to c++ file in Qt.
QML code:
import QtQuick 1.1
Rectangle{
id:loin
height: 272
width:480
property alias loguid:loginuid
signal sigHome()
Rectangle{
id:rect1
width:parent.width-80
height:24
TextInput {
id:loginuid
maximumLength: 16
width: maximumLength * 20
focus: false
validator: RegExpValidator { regExp: /\d+/ }
KeyNavigation.down: login1
}
}
Button{
id: login1
x: 195
y: 187
height:30;
focus:false
border.color:"black"
opacity: activeFocus ? 1.0 : 0.5
Text{
text:"LOGIN"
anchors.horizontalCenter:login1.horizontalCenter;
anchors.verticalCenter:login1.verticalCenter;
}
Keys.onReturnPressed: {
if(loginuid.text < 1000000000000000)
{
text1.opacity=0.1
error1.visible=true
errorText.text="\n enter valid 16 digit number\n"
errorOk.focus=true
loginuid.focus=false
}
else{
loginuid.focus=false
loin.sigHome()
}
}
}
}
c++ code:
#include <QApplication>
#include <QDeclarativeView>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
int uid;
QDeclarativeView view;
view.setSource(QUrl::fromLocalFile("main.qml"));
view.show();
return app.exec();
}
When I press the login button rect1.text content shud go to main.cpp file and uid in the main.cpp get dat value. Something like this uid=rect1.text.
How to do it?
I wouldn't try to listen for a QML signal from the C++ side. Calling a C++ method with arguments is much easier and achieves the same:
To do so you have to:
define a slot or invokable method accepting the required arguments
register the class carrying the method with the declarative engine
then you can set an instance of this class as a property of your root context and finally call this method from QML
This topic is also well covered in the official documentation.
Thanks, sebasgo, your response helped me. I used signals and slots to communicate.
I created a signal in main.qml.
signal info(string msg)
and in login page
else{
info(loginUid.text)
loginuid.focus=false
loin.sigHome()
}
and in main.cpp I connected it to d slot
main.cpp goes like this
#include <QtGui>
#include <QApplication>
#include <QDeclarativeView>
#include <QtDeclarative>
class DeclarativeView : public QDeclarativeView
{
Q_OBJECT
public:
DeclarativeView(const QUrl & source) : QDeclarativeView(source)
{
}
public slots:
void readText(QString quid)
{
qdebug<<quid;
}
};
#include "main.moc"
int main(int argc, char *argv[])
{
QString file = "main.qml";
QApplication app(argc, argv);
DeclarativeView view(QUrl::fromLocalFile(file));
QDeclarativeItem *item = qobject_cast<QDeclarativeItem *>(view.rootObject());
QObject::connect(item, SIGNAL(info(QString)), &view, SLOT(readText(QString)));
view.show();
return app.exec();
}
Create a GUI controller C++ class:
class UiController : public QObject
{
Q_OBJECT
public:
UiController();
virtual ~UiController();
public slots:
void cal_daysoff__onDoubleClicked(const QDate& date);
};
In QML file you define, say, a calendar control in which you connect a signal to a slot in the controller:
Calendar{
id: cal_daysoff
Layout.fillWidth: true
Layout.fillHeight: true
onDoubleClicked: UiController.cal_daysoff__onDoubleClicked(date)
}
In main file, when launching the QML interface, connect the interface to the controller:
#include "uicontroller.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlApplicationEngine engine;
UiController control;
engine.rootContext()->setContextProperty("UiController", &control);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}

Resources