I have a C++ class that emits a signal. I want that signal to be delivered to QML.
I set the object as a context property of qml application engine root context.
My C++ class
// Sample.h
class Sample : public QObject
{
Q_OBJECT
public:
explicit Sample(QObject *parent = nullptr);
public slots:
void emitSomething();
signals:
void emitted();
public slots:
};
And the implementation
// Sample.cpp
Sample::Sample(QObject *parent) : QObject(parent)
{
}
void Sample::emitSomething()
{
emit emitted();
}
My main implementation. This is very similar to the code provided by qt creator.
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
Sample sample;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("obj", &sample);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
QTimer::singleShot(1000, &sample, &Sample::emitSomething);
return app.exec();
}
The qml implementation is
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Connections {
target: obj
onEmitted: function() {
console.log("received")
}
}
}
When I run the code, emitSomething() slot is called, but I don't see emitted() signal in qml.
I didn't have version 5.9, but I tried it with 5.10.1. In that case, the text did not get printed to the console. I fixed it by changing the syntax on the signal handler. (Just remove function().)
Connections {
target: obj
onEmitted: {
console.log("received")
}
}
Related
I got this basic project:
main.qml
Rectangle{
Text {
id: text1
objectName: "showStr"
property string _textField: "passive"
text: _textField
}
Button{
id: _button
signal buttClick(string str)
anchors.top: text1.bottom
text: "Button"
onClicked:
{
_button.buttClick("state: active")
}
}
}
myClass.cpp
class myClass : public QObject{
Q_OBJECT
public:
explicit myClass(QObject *parent = nullptr): QObject(parent) {}
public slots:
void setTextProperty(const QString& s) {this->str = s;}
void getStrToQObj()
{
//TODO: set updated str into qml
}
signals:
void strChanged(const QString& time);
private:
QString str;
};
main.cpp
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
QQmlComponent component(&engine,
QUrl(QLatin1String("../myProj/qml/main.qml")));
QObject* item = component.create();
QObject* showTime = item->findChild<QObject*>("showTime");
QObject* butt = item->findChild<QObject*>("start");
myClass mc(item);
QObject::connect(butt, SIGNAL(buttClick(QString)),
&mc, SLOT(setTextProperty(QString)));
return app.exec();
}
Connection of Qml signal buttClick to setTextProperty(const QString& s) works fine and myClass::str is changed.The question is how to connect Text property _textField to update every time, when myClass::str is changed?
Do not export objects from QML to C++, instead do the opposite: export the C ++ objects to QML since this way the connection is simple and there is no need to use the old connection syntax:
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
myClass mc;
engine.rootContext()->setContextProperty("mc", &mc);
QQmlComponent component(&engine,
QUrl(QLatin1String("../myProj/qml/main.qml")));
QObject* item = component.create();
return app.exec();
}
Rectangle {
Text {
id: text1
text: "passive"
}
Button {
id: _button
anchors.top: text1.bottom
text: "Button"
onClicked: {
mc.setTextProperty("state: active");
}
}
Connections {
function onTimeChanged(text) {
text1.text = text;
}
target: mc
}
}
It is old syntax of connection, but according to separations logiv from GUI, and for ones who do not know JS well,turns out you can call parent() method to you class because it is inherit QObject class. This return to you Qobject* you put in constructor of your class, and then you can findChild and work with it:
void myClass::f(){
//do fancy stuff with your myClass::str
emit activityChanged(this->parent())
}
void getStrToQObj(QObject* qo)
{
auto tmp = qo->findChild<QObject*>("showTime");
tmp->setProperty("_textField", this->str);
}
And connect this in main.cpp like:
QObject::connect(&mc, SIGNAL(strChanged(QObject*)),
&mc, SLOT(getStrToQObj(QObject*)));
I have a small problem. Can someone show me how can I update a qml text from c++. I have an example using threads but I don't want to apply this method because I don't know how to set a
parameter in run() function.To fully understand me here is my code.In the main function when I start the thread I want to put my custom text or a string variable that has a text.
thread.h
#ifndef THREAD_H
#define THREAD_H
#include <QThread>
class Thread : public QThread
{
Q_OBJECT
public:
Thread(QObject *parent=nullptr);
~Thread() override;
Q_SLOT void stop();
Q_SIGNAL bool textChanged(const QString & text);
protected:
void run() override;
};
#endif // THREAD_H
thread.c
#include "thread.h"
#include <QDebug>
Thread::Thread(QObject *parent):
QThread(parent)
{
}
Thread::~Thread()
{
}
void Thread::stop()
{
requestInterruption();
wait();
}
void Thread::run() //here I want to specify a QString variable such that in main function to call the function with my text, and not specified the text from here
{
int i=0;
while(!isInterruptionRequested()){
QString text;
text = QString("I changed the text"); // I don't want to declare from here the text.
Q_EMIT textChanged(text);
QThread::msleep(20);
qDebug() <<i++;
}
}
main.cpp
...
Thread thread;
QQmlApplicationEngine engine;
QObject::connect(&app, &QGuiApplication::aboutToQuit, &thread, &Thread::stop);
thread.start();
engine.rootContext()->setContextProperty("thread", &thread);
engine.load(QUrl("qrc:/main.qml"));
thread.stop();
...
main.qml
.....
Text {
objectName: "myLabel"
id: txt
width: 200
height: 29
color: "#faf9f9"
text: qsTr("Text")
font.pixelSize: 12
}
Connections{
target: thread
onTextChanged: txt.text = text
}
.....
You can send data from C++ to qml using signals, like this:
//main.cpp
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
// Class init
YourClass yourObject;
// Embedding C++ Objects into QML with Context Properties
QQmlContext* ctx = engine.rootContext();
ctx->setContextProperty("yourObject", &yourObject);
return app.exec();
}
//main.qml
import QtQuick 2.6
Window {
id: mainWindow
Connections {
target: gapi
onSignalData: {
console.log("Data: " + data)
textToChange.text = "Changed to: " + data
}
}
Text {
id: textToChange
text: "beforeChange"
}
}
//yourClass.h
class YourClass : public QObject
{
signals:
// Signal from YourClass to QML
void signalData(QString data);
}
//yourClass.cpp
emit signalData("Hello QML"); // Signal from GAPI to QML
This page https://felgo.com/cross-platform-development/how-to-expose-a-qt-cpp-class-with-signals-and-slots-to-qml have a complete tutorial about "How to Expose a Qt C++ Class with Signals and Slots to QML"
I have a situation where I need to add keyboard shortcuts to an application that is mainly comprised of a QtQuick user interface. The version of Qt Quick is unfortunately locked to Qt5.3 and Shortcuts (the way we need them) were only introduced in Qt5.5 and Qt5.7 respectively.
So, as a solution, I wrote an event filter that functions similarly to QShortcut (can't use QShortcut, hence the event filter).
Does anybody know how to install and use this eventfilter in QML?
One way would be to expose a singleton type to QML:
#include <QtGui>
#include <QtQml>
class ShortcutListener : public QObject
{
Q_OBJECT
public:
ShortcutListener(QObject *parent = nullptr) :
QObject(parent)
{
}
Q_INVOKABLE void listenTo(QObject *object)
{
if (!object)
return;
object->installEventFilter(this);
}
bool eventFilter(QObject *object, QEvent *event) override
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
qDebug() << "key" << keyEvent->key() << "pressed on" << object;
return true;
}
return false;
}
};
static QObject *shortcutListenerInstance(QQmlEngine *, QJSEngine *engine)
{
return new ShortcutListener(engine);
}
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterSingletonType<ShortcutListener>("App", 1, 0, "ShortcutListener", shortcutListenerInstance);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
#include "main.moc"
main.qml:
import QtQuick 2.5
import QtQuick.Window 2.2
import App 1.0
Window {
id: window
width: 640
height: 480
visible: true
Component.onCompleted: ShortcutListener.listenTo(window)
}
If you have several different listeners, you could also do it declaratively by adding an object property to ShortcutListener that it would install an event filter on when set.
For more info, see Integrating QML and C++.
I just began learning about Qt programming and found myself struggling with the signals and slot mechanism while making a simple test application.
I have two QML files: main.qml and grocery.qml.
I have a header file that manages the signals and slots of the application:
applicationamanger.h
#ifndef APPLICATIONMANAGER_H
#define APPLICATIONMANAGER_H
#include <QObject>
#include <QDebug>
class ApplicationManager : public QObject
{
Q_OBJECT
public:
explicit ApplicationManager(QObject *parent = 0);
signals:
void newGroceryItem();
public slots:
void addGroceryItem();
};
#endif // APPLICATIONMANAGER_H
The user starts the application on main.qml and when pressing a button loads grocery.qml
On grocery.qml I have an image that can be clicked by the user and emits the signal newGroceryItem()
MouseArea {
id: okBtnClkd
anchors.fill: parent
Connections {
onClicked: {
newGroceryItem()
}
}
}
And the main application main.cpp connects the signals but says it can't find the signal newGroceryItem() as it doesn't belong to main.qml. So my question is, how can I establish the connection to the slot addGroceryItem() from the secondary qml file grocery.qml?
EDIT:
As requested here're the contents of main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickWindow>
#include <QQmlContext>
#include "applicationmanager.h"
int main(int argc, char *argv[]){
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QObject *topLevel = engine.rootObjects().value(0);
QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);
ApplicationManager applicationManager;
QObject::connect(window, SIGNAL(newGroceryItem()), &applicationManager, SLOT(addGroceryItem()));
return app.exec();
}
And the contents of grocery.qml
import QtQuick 2.5
import QtQuick.Controls 1.4
ApplicationWindow {
//Declaration of signals.
signal newGroceryItem()
width: 300
height: 400
visible: true
TextField {
id: product
x: 14
y: 111
width: 270
height: 22
}
Text {
id: text1
x: 14
y: 81
width: 124
height: 24
text: qsTr("Product")
font.pixelSize: 20
}
Image {
id: okBtn
x: 99
y: 290
width: 100
height: 100
source: "img/main/okBtn.png"
MouseArea {
id: okBtnClkd
anchors.fill: parent
Connections {
onClicked: {
newGroceryItem()
}
}
}
}
}
Any method of a QObject-derived type is accessible from QML code if it is:
a public method flagged with the Q_INVOKABLE macro
a method that is a public Qt SLOT
So there are two ways to access addGroceryItem(). The first one is to use Q_INVOKABLE macro as follows:
class ApplicationManager : public QObject
{
Q_OBJECT
public:
explicit ApplicationManager(QObject *parent = 0);
Q_INVOKABLE void addGroceryItem();
};
The second way is to use public slots as follows:
class ApplicationManager : public QObject
{
Q_OBJECT
public:
explicit ApplicationManager(QObject *parent = 0);
public slots:
void addGroceryItem();
};
If you are creating your class object in C++ then you should set the object as the context data for main.qml as follows:
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
QQmlEngine engine;
ApplicationManager app;
engine.rootContext()->setContextProperty("applicationManager", &app);
QQmlComponent component(&engine, QUrl::fromLocalFile("main.qml"));
component.create();
return app.exec();
}
Now you can access the ApplicationManager object created in main.cpp from main.qml as follows:
MouseArea {
id: okBtnClkd
anchors.fill: parent
onClicked: {
applicationManager.addGroceryItem();
}
}
For more information see here.
I would like to change a the color of a rectangle when I click a button. They are both in the main.qml file. I'd like to send a signal to C++ backend to change the color of the rectangle. I can't seem to figure it out from the code given in the documentation
main.qml:
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
ApplicationWindow {
title: qsTr("Hello World")
width: 640
height: 480
visible: true
id:root
signal mysignal()
Rectangle{
anchors.left: parent.left
anchors.top: parent.top
height : 100
width : 100
}
Button
{
id: mybutton
anchors.right:parent.right
anchors.top:parent.top
height: 30
width: 50
onClicked:root.mysignal()
}
}
main.cpp:
#include <QApplication>
#include <QQmlApplicationEngine>
#include<QtDebug>
#include <QQuickView>
class MyClass : public QObject
{
Q_OBJECT
public slots:
void cppSlot() {
qDebug() << "Called the C++ slot with message:";
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MyClass myClass;
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QPushButton *mybutton = engine.findChild("mybutton");
engine.connect(mybutton, SIGNAL(mySignal()),
&myClass, SLOT(cppSlot()));
return app.exec();
}
Any help would be appreciated!
QPushButton *mybutton = engine.findChild("mybutton");
First, QObject::findChild finds QObjects by object name, not id (which is local to a context anyhow). Hence in QML you need something like:
objectName: "mybutton"
Second, I think you need to perform that findChild not on the engine itself, but on its root objects as returned from QQmlApplicationEngine::rootObjects().
// assuming there IS a first child
engine.rootObjects().at(0)->findChild<QObject*>("myButton");
Third, a Button in QML is represented by a type that you don't have available in C++. So you can't just assign the result to a QPushButton *, but you need to stick with the generic QObject *.
I had to create a seperate class and header file and then connect it to the signal in main.cpp
main.cpp
#include <QApplication>
#include <QQmlApplicationEngine>
#include<QtDebug>
#include <QQuickView>
#include<QPushButton>
#include<myclass.h>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QObject *topLevel = engine.rootObjects().at(0);
QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);
MyClass myClass;
QObject::connect(window,
SIGNAL(mysignal()),
&myClass,
SLOT(cppSlot())
);
return app.exec();
}
myclass.h
#ifndef MYCLASS
#define MYCLASS
#include <QObject>
#include <QDebug>
class MyClass : public QObject
{
Q_OBJECT
public slots:
void cppSlot();
};
#endif // MYCLASS
myclass.cpp
#include<myclass.h>
void MyClass::cppSlot(){
qDebug()<<"Trying";
}