Reputation: 6388
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.
Upvotes: 1
Views: 2215
Reputation: 2168
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);
}
};
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);
}
Upvotes: 3
Reputation: 8920
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();
}
}
Upvotes: 1