Invoking c++ slots from plasmoid qml - c++

New question for you guys.
I have a simple kde (kf5) plasmoid, with a label and two buttons.
I have a C++ class behind the scenes, and I am currently able to send signals from C++ to qml.
The problem: I need to send signals from the qml buttons to the C++ class.
Usually this could be done by using the standard Qt/qml objects like QQuickView and so on, but in my case I have no main.cpp.
This is my C++ class header. Using a QTimer, I emit the textChanged_sig signal, which tells the qml to refresh the label's value:
class MyPlasmoid : public Plasma::Applet
{
Q_OBJECT
Q_PROPERTY(QString currentText READ currentText NOTIFY textChanged_sig)
public:
MyPlasmoid( QObject *parent, const QVariantList &args );
~MyPlasmoid();
QString currentText() const;
signals:
void textChanged_sig();
private:
QString m_currentText;
}
This is the plasmoid main.qml:
import QtQuick 2.1
import QtQuick.Layouts 1.1
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.components 2.0 as PlasmaComponents
Item {
Plasmoid.fullRepresentation: ColumnLayout {
anchors.fill: parent
PlasmaComponents.Label {
text: plasmoid.nativeInterface.currentText
}
PlasmaComponents.Button {
iconSource: Qt.resolvedUrl("../images/start")
onClicked: {
console.log("start!") *** HERE
}
}
}
}
The PlasmaComponents.Label item contains the correct value of the c++ field m_currentText.
*** HERE I need to emit some signal (or invoke a c++ method, would have the same effect).
Any hint?

Since you can access the currentText property through plasmoid.nativeInterface that object is almost certainly an instance of your C++ applet class, i.e. a MyPlasmoid instance.
So if your MyPlasmoid has a slot, it can be called as a function on the plasmoid.nativeInterface object
in C++
class MyPlasmoid : public Plasma::Applet
{
Q_OBJECT
public slots:
void doSomething();
};
in QML
onClicked: plasmoid.nativeInterface.doSomething()

Related

How to hook up to the onClick event of a QML item from C++ side

Scenario:
I have a Qt app which runs on Qt 5.9.4 commercial edition. Its a QtQuick and QML based application which is running on iOS and Android.
I have a QML item on the UI like this:
SomeItem {
text: qsTr("Some Item")
objectName: "someitem"
visible: false
onClicked: {
console.log("Some item was clicked")
}
}
I have a C++ function which can easily control the properties of SomeItem.
void MyCppClass::Func() {
QQuickItem *someItem = qml_engine->rootObjects()[0]->findChild<QQuickItem*>("someitem");
someItem->setVisible(true); // this works
// How to listen to someItem's onClick event here
}
Question:
I want to listen to the onClick event of someItem in a C++ method or a lambda without changing anything in the QML. Basically hook up to the onClick signal signal of someItem from C++ side itself. How can I do it?
The method to use to interact can be dangerous in the general case because the life cycle of the items depends on QML, so make sure it does not happen. Going to your request and I assume that MyCppClass inherits from QObject or a child class you must create a slot and use the old connection syntax:
*.h
class MyCppClass: public QObject
{
...
private slots:
void on_clicked();
...
};
*.cpp
void MyCppClass::Func() {
QQuickItem *someItem = qml_engine->rootObjects()[0]->findChild<QQuickItem*>("someitem");
if(!someItem)
return;
someItem->setVisible(true); // this works
connect(someItem, SIGNAL(clicked()), this, SLOT(on_clicked()));
}
void MyCppClass::on_clicked(){
qDebug()<<"on clicked";
}

How to use a custom Qt C++ type with a QML slot?

I want to use my C++ type registered in QML as parameter in QML slot. So I already have C++ signal which has QString type parameter:
emit newMessage(myString);
and QML slot:
onNewMessage: {
console.log(myString);
}
Now I want to pass C++ type registered in QML as parameter of this slot.
I found similar question, but I don't understand how I can use this vice versa. Any help will be useful.
So Lets say you wanted to pass a QObject derived file back and forth from C++ to QML via a signal/slot setup.
class MyClass : public QObject
{
Q_OBJECT
public:
explicit MyClass(QObject *parent = 0);
signals:
void sendSignal(MyClass* anotherMyClass)
public slots:
void send_signals() { emit sendSignal(this); }
};
If you have a C++ type called MyClass which is a QObject-based type, and you successfully did
qmlRegisterType<MyClass>("com.myapp", 1, 0, "MyClass");
Then In QML all you have to do is this:
import QtQuick 2.0
import com.myapp
Item {
MyClass {
id: myInstance
Component.OnCompleted: {
// connect signal to javascript function
myInstance.sendSignal.connect( myInstance.receiveSignal);
}
function receiveSignal(mySentObject) {
// here is your QObject sent via Signal
var receivedObject = mySentObject;
// Now to send it back
// (will cause an endless loop)
myInstance.sendSignal(receivedObject);
// or you could do this to perform
// the same endless loop
receivedObject.send_signals();
// or you could do this to perform
// the same endless loop
receivedObject.sendSignal(myInstance)
}
}
}
Summary:
You can just send any QObject-derived object as long as its registered with the QML engine as the actual object, you just treat it like any other type in QML
Hope this helps

How to get data from QML to C++?

I try to write some application using FileDialog and I have to get file urls in my C++ class. I try to do this in this way:
FileDialog {
id: fileDialog
objectName: "fileDialog"
selectMultiple: true
signal getFiles(var urls)
title: qsTr("Open file")
nameFilters: [qsTr("MP3 files (*.mp3)"), qsTr("All files (*.*)")]
onAccepted: getFiles(fileDialog.fileUrls)
}
........................................
class MyClass : public QObject
{
Q_OBJECT
public slots:
void addToPlaylist (const QList<QUrl> & urls){
for(int i = 0; i < urls.length(); ++i)
qDebug() << "Get\n";
}
};
............................................
QObject *fileDialog = root->findChild<QObject *>("column")->findChild<QObject *>("row")->findChild<QObject *>("openButton")->findChild<QObject*>("fileDialog");
MyClass myClass;
QObject::connect(fileDialog, SIGNAL(getFiles(QVariant)), &myClass, SLOT(addToPlaylist(QList<QUrl>)));
I found solution but I do not understand. Can someone explain me in my example?
just a question (and maybe solution), why do you need to define a
signal in QML for this? can’t you simply call the c++ slot and pass
the URL list directly?
Well,you are trying to pass a list of urls from qml to c++. These are the ways.
1)Make sure that you make an object of MyClass available to QML by calling QQmlContext::setContextProperty() and registering that object.
Example:
QQuickView view;
MyClass myObj;
view.rootContext()->setContextProperty("myObj", &myObj);
Then you can call your MyClass::addToPlaylist() from QML like this.
FileDialog{onAccepted: myObj.addToPlaylist(fileDialog.fileUrls);}
Reference: Embedding C++ Objects into QML with Context Properties
2)Make the FileDialog instance emit a signal whenever user selects some files from QML. Now this signal needs to be connected to the slot of an object of MyClass. In order to do this, you need to have pointers to both FileDialog instance and object of MyClass object.
You are getting pointer to an instance of FileDialog by doing this.
QObject *fileDialog = root->findChild<QObject *>("column")->findChild<QObject *>("row")->findChild<QObject *>("openButton")->findChild<QObject*>("fileDialog");
Now you are connecting the signal and slot. Thats it.

Post HTTP request from qt5 using qml

Hi everyone,
I am trying to send the http post request from my qt app. I have read alot and still struggling to get some concepts of signals and slots. Would be nice if somebody can help me out from here..
here is my qml code snippet:
TextField { id: u_name; placeholderText: userText(); Layout.fillWidth: true; style: StyleTextField {} }
TextField { id: p_text; echoMode: TextInput.Password; Layout.fillWidth: true; style: StyleTextField {} }
Button {
id: signInButton
text: "Sign In";
style: StyleButton {}
Layout.fillWidth: true;
//Layout.alignment: Qt.AlignTop;
signal esLoginClicked()
onClicked: {
if (u_name.text.length) Settings.userText = u_name.text;
if (p_text.text.length) Settings.passText = p_text.text;
signInButton.esLoginClicked().connect(esLogin(u_name.text, p_text.text));
page_stack.pop();
}
}
Here I am trying to get username and password from user and want to pass it to slot "esLogin" that I have declared in my header file using signal esLoginCLicked() which I have created here only. My header files looks like this...
Q_OBJECT
Q_PROPERTY(QString userText READ userText WRITE setUserText NOTIFY userTextChanged)
Q_PROPERTY(QString passText READ passText WRITE setPassText NOTIFY passTextChanged)
public:
static esQuickSettings *instance(void);
public:
QString userText(void);
QString passText(void);
// void esLoginClicked(void);
// void esLoginClicked(const QString& userText, const QString passText);
public:
void setUserText(const QString& user);
void setPassText(const QString& passt);
void esLogin(const QString& userText, const QString& passText);
signals:
void userTextChanged(void);
void passTextChanged(void);
but somehow I am not able to make it work and missing some key concept here to make signal and slot work.
P.S: I want to take input from QML and put in slot which will have the definition in cpp file respective to header.
There are (at least) two ways to address this issue, but I will only let you know one of them based on the comment discussion.
Connect the QML signal to the C++ slot.
main.qml
...
Button {
id: signInButton
// This is necessary for finding this nested item in C++
objectName: "SignInButtonObjectName"
...
}
...
main.cpp
...
QQmlEngine engine;
QQmlComponent component(&engine, "main.qml");
QObject *object = component.create();
QObject *childObject = object->findChild<QObject*>("SignInButtonObjectName");
Foo foo;
QObject::connect(childObject, SIGNAL(esLoginClicked(const QString&, const QString&)), &foo, SLOT(esLogin(const QString&, const QString&)));
...
The other approach would be to call the C++ slot in your qml code when the signal happens to be emitted which is probably even simpler. In that case, you would make the method below either Q_INVOKABLE or even better: a slot.
void esLogin(const QString& userText, const QString& passText);
Then, you would need to make sure that this method is exposed to qml via context properties, namely: you would make the class a context property which would be available to qml for calling like foo.esLogin() in your desired qml signal handler.

how to access events of a specific QML control from c++

Is there a way of accessing signals(such as clicked()) of a QML control such as a button, from c++. Assume that I have the memory address of that specific control. I just want to simulate a click event from c++ code.
Easy. You just create a slot in a C++ object that has a QObject base to it, make sure its registered as a QML type, then you "instantiate" it in the QML document, and connect the desired signal to the C++ object through QML using connect() and handle the logic from the C++ side.
Example:
I have a Rectangle I want to get the onWidthChanged signal from and use it in my class ShapeTracker which tracks when shapes change or whatever
in main.cpp:
#include "shapetracker.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
/* this is where you register ShapeTracker as a QML type that can be
accessed through the QML engine even though its a C++ QObject */
qmlRegisterType<ShapeTracker>("my_cpp_classes", 1, 0, "ShapeTracker");
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
then in
main.qml
import QtQuick 2.6
/* import the C++ class you registered into this QML document */
import my_cpp_classes 1.0
Window {
visible: true
Item {
/* set up a parent item that has a signal which is exposed to
the ShapeTracker object and the Rectangle you want to track */
id: myRootItem
signal rectangleChanged(var newWidth)
/* Heres our special Rectangle from QML that will send a signal when
its width changes */
Rectangle {
id: specialRectangle
width: 250
/* send the signal rectangleChanged(width) from our parent item
root item whenever width changes
*/
onWidthChanged: function() { rectangleChanged(width); }
}
/* Special Button that when clicked enlarges the Rectangle but
10px */
Button {
id: mySpecialButton
onClicked: { click_handler(mouse); }
function click_handler(mouse): {
if (specialRectangle.width < 500)
specialRectangle.width += 10;
}
}
/* Heres the actual ShapeTracker instance, which exists
as a QObject inside of the QML context, but has its methods
which are declared in C++, and exposed to the QML engine */
ShapeTracker {
id: myShapeTracker
/* similar to a constructor, but more like a callback */
Component.onCompleted: {
/* connect our signal from the parent root Item called
"rectangleChanged" to the ShapeTracker's Slot called
"myRectangleChangeHandler" */
myRootItem.rectangleChanged.connect(myShapeTracker.myRectangleChangeHandler);
/* connect send_mouse_click to the click_handler of
the button */
myShapeTracker.send_mouse_click.connect(mySpecialButton.click_handler)
}
}
}
}
in shapetracker.h you simply add a new slot with the name myRectangleChangeHandler and it will receive that signal whenever it is send via QML to be processed via C++
class ShapeTracker : public QObject {
Q_OBJECT
public:
ShapeTracker(QObject *parent = 0 );
signal:
void send_mouse_click(QMouseEvent *event);
public slots:
void myRectangleChangeHandler(QVariant newWidth) {
/* Perform a mouse click on our QML Object mySpecialButton
using QQuickItem::mousePressEvent and sending it via
signal back to QML */
QMouseEvent myEvent(QEvent::MouseButtonPress, QPointF(1,1), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
QMouseEvent* pressEvent = QQuickItem::mousePressEvent(&myEvent);
emit send_mouse_click(pressEvent);
}
};
In Summary, you expose a C++ QObject to QML, then you use
object.signal.connect(cppObject.desired_slot)
To connect them -- all the extra stuff was for a functional example in case anyone needs it later
In reality, you don't even need this functionality because anything happening in an onClick event could just as easily be put into any other property such on
Rectangle {
id: rect
signal customClick(var var1)
onCustomClick : { console.log(var1); }
}
Item {
rect.customClick(1);
}
The easy way would be to call all the receiving SLOTS manually. But that would be tedious and error prone.
You might try implementing a sub-class of QObject that has one slot onClicked() which emits the signal clicked() and use it as a shim between the button and elements controlled by the button. Connect the button clicked() to the new object onClicked() and then connect the new object to the original receivers. Then calling onClicked() would trigger the behavior.
This is a very simple example, and I haven't run it through the compiler.
ButtonShim.hpp
#include <QObject>
class ButtonShim : public QObject {
Q_OBJECT
public:
ButtonShim(QObject *parent = 0);
virtual ~ButtonShim();
public slots:
void onClicked();
signals:
void clicked();
};
ButtonShim.cpp
#include "ButtonShim.hpp"
ButtonShim::ButtonShim(QObject *parent) : QObject(parent) {
}
ButtonShim::~ButtonShim() {
}
void ButtonShim::onClicked() {
// All we do here is emit the clicked signal.
emit clicked();
}
SomeFile.cpp
#include <bb/cascades/Button>
#include "ButtonShim.hpp"
...
ButtonShim * pButtonShim = new ButtonShim(pButton); // pButtonShim will live as long as pButton
bool c = connect(pButton, SIGNAL(clicked()), pButtonShim, SLOT(onClicked()));
c = connect(pButtonShim, SIGNAL(clicked()), pSomeObject, SLOT(onButtonClicked()));
...
// to simulate a click of pButton
pButtonShim->onClicked();
SomeFile.qml
// assuming ButtonShim has been exposed to QML from your application
...
attachedObjects: [
ButtonShim {
id: buttonShim
onClicked: {
clickedLabel.text = "I've been clicked";
}
}
]
...
Label {
id: clickedLabel
text: "I haven't been clicked"
}
Button {
text: "Click Me"
onClicked: {
buttonShim.onClicked();
}
}
I think that you can look at code for tests. There they get object from QML file loaded into engine.
If you have an QObject you can just call signal because, AFAIR
public signals:
void clicked();
is expanded by moc into
public:
void clicked();