QML signals connecting - c++

I started writing application in QML (using QtQuick 1.1 with Qt 4.8.1) and I have a few questions about signals. In my project there are following files:
main.qml:
Rectangle {
signal sigExit()
width: 800
height: 600
Text {
text: qsTr("Hello World")
anchors.centerIn: parent
}
MouseArea {
anchors.fill: parent
onClicked: {
sigExit();
Qt.quit();
}
}
Button
{
x: 10
y: parent.height-height-5
text: "someText"
}
}
Button.qml:
Rectangle {
signal buttonsig()
width: 60
//(...)
MouseArea
{
anchors.fill: parent
onClicked: buttonsig();
}
}
When I want to connect signal from main.qml to C++ slot, I do:
main.cpp :
QmlApplicationViewer viewer;
viewer.setOrientation(QmlApplicationViewer::ScreenOrientationAuto);
viewer.setMainQmlFile(QLatin1String("qml/MyProject/main.qml"));
viewer.showExpanded();
MyClass* obj = new MyClass;
QObject* item = qobject_cast<QObject*>(viewer.rootObject());
QObject::connect(item, SIGNAL(sigExit()), obj, SLOT(onExitWindow()));
and it works. But what when I want to connect sigbutton() from Button.qml to C++ slot? It will be something like that?
QObject *rect = item->findChild<QObject*>("Button");
QObject::connect(rect, SIGNAL(buttonsig()), obj, SLOT(onExitWindow()));
And the second issue: how can I connect sigbutton() to main.qml (for example, I want to change position of my buttons after clicking them)?

You will also need to have the objectName property of your Button item if you want to access it :
Button {
id: myButton
objectName: "myButton"
x: 10
y: parent.height-height-5
text: "someText"
}
Now you can access it by :
QObject *rect = item->findChild<QObject*>("myButton");
Regarding the second question, you can use Connections object to connect buttonsig() to some QML signal handler in main.qml :
Rectangle {
signal sigExit()
width: 800
height: 600
Connections{
target: myButton
onButtonsig :
{
...
}
}
Text {
text: qsTr("Hello World")
anchors.centerIn: parent
}
MouseArea {
anchors.fill: parent
onClicked: {
sigExit();
Qt.quit();
}
}
Button
{
id: myButton
x: 10
y: parent.height-height-5
text: "someText"
}
}
Note that the signal handler's name should be on<Signal> (First letter of signal letter Capital). Also Button should have an id to address it in Connections.

Accessing loaded qml elements, casting them and connecting their signals to your C++ slots is perferctly possible. But this method should be avoided in your production code.
See this warning from Qt docs.
So what's the way to call a C++ slot from qml side? You can register the object whose slot needs to be called with qml engine as a context property. Once registered, these context properties can be accessed anywhere from QML side.
Slots of objects registered as context properties can be called directly in your signal handler in QML example: onClicked:{<contextPropertyName>.<slotName>()}
Or, you can connect a QML signal with context property object's slot directly using Connections type. Please see this documentation
For details about registering context properties, please see Embedding C++ objects into QML with context properties.
If you want to see some examples, see my answers to these questions.
Qt Signals and Slots - nothing happens and Setting object type property in QML from C++

Related

Return from child qml to main one

I have several QML files, main one is the one that opens the ApplicationWindow when ever I try to get back from child QML to main, new window is opened again!
how can I prevent of doing this?
I thought that enabling a flag in child QML, but there may be other ways!
I tried stackview in QML
Is there any way to prevent from opening new page when I get back to main QML?
create a loader in main window and call every page into that loader when ever you need to change page just change loader's source
Window {
Loader{
id:myLoader
anchors.fill: parent
source: "LoginPage.qml"
}
Connections{
target: myLoader.item
onBack_clicked:{
loginid = ""
myLoader.source = "LoginPage.qml"
}
onSetting_clicked:{
myLoader.source = "Setting.qml"
}
}
}
and for child qml files : (for me Setting.qml)
Item {
signal back_clicked()
Button {
id: button1
anchors.right: parent.right
anchors.rightMargin: 15
onClicked: {
back_clicked()
}
}
}
but if you want to not destroy old page use SwipeView Or StackView:
SwipeView {
id: swipeView
clip: true
currentIndex: 0
Item{
id:firstPage
clip:true
//your page
}
Item{
id:secondPage
clip:true
//your page
}
}
and to change pages just change currentIndex
swipeView.currentIndex = 1
UPDATE:
StackView {
id: stackView
initialItem: one
}
Component {
id: one
Item{//your first page }
}
Component {
id: two
Item{//your second page }
}
and to push your pages :
stackView.push({ item: two})
//or
stackView.push("MySecondView.qml")
to get back to old or main page just pop it :
stackView.pop()

How to close a window in QML/Javascript code (with C++ code involved)?

I have a QML window declared for example in MyWindow.qml:
Item {
id: thisWindow
width: 500
height: 140
... sub-items that declare the UI of the window ...
And a C++ class that instantiates that QML:
class MyWindow : public QQuickView
...
MyWindow::MyWindow() {
setSource(QUrl("qrc:/MyWindow.qml"));
setFlags(Qt::WindowFlags(Qt::Popup));
}
How do I close that window from Javascript/QML code?
I can't call thisWindow.close(), because it's just an item type in the hierarchy.
You don't need c++ to do that. You can do it with the window attached property straight from QML.
//other imports
import QtQuick.Window 2.2
Item {
id: thisWindow
width: 500
height: 140
//... sub-items that declare the UI of the window ...
MouseArea {
anchors.fill: parent
onClicked: Window.window.close()
}
}
The easiest option is to export the QQuickView to the .qml with setContextProperty():
#include <QQmlEngine>
#include <QQmlContext>
// ...
{
engine()->rootContext()->setContextProperty("view", this);
setSource(QUrl("qrc:/MyWindow.qml"));
setFlags(Qt::WindowFlags(Qt::Popup));
}
And then in QML you can use:
view.close()
Use the Qt global object and follow the instruction here:
http://doc.qt.io/qt-5/qml-qtqml-qt.html#quit-method

QML QWidget container

I'm beginning with QML in Qt Creator and I like too much everything I've read about it but now I found a complication.
See following code:
BLCMainWidget::BLCMainWidget(QWidget *parent) : BLCBaseWidgetControler(parent) {
QQuickView view;
view.setSource(QUrl("qrc:///main.qml"));
QWidget *container = QWidget::createWindowContainer(&view);
QHBoxLayout *layout = new QHBoxLayout;
layout->setSpacing(10);
layout->setAlignment(Qt::AlignHCenter);
layout->setContentsMargins(1, 1, 1, 1);
parent->setStyleSheet("background:QColor(200,100,150);");
layout->addWidget(container);
parent->setLayout(layout);
}
Where parent is my QWidget on QMainWindow of my application, but this code not show my QQuickView container. Obviously the parent in question has a setCentralWidget signed in main() method and I'm already using that concept for non-QML widgets perfectly. How can I fix that to show my QML object containers?
My QML is just a simple concept example:
import QtQuick 2.1
Item {
id: box
width: 640
height: 480
Rectangle {
id: redSquare
width: 30; height: 30
anchors.top: parent.top; anchors.left: parent.left; anchors.margins: 10
color: "green"
Text { text: "!"; font.pixelSize: 16; anchors.centerIn: parent }
}
}
Thanks
If you are using a recent version of Qt, QWidget::createWindoContainer is depricated. Create a QQuickWidget instead, and use it a a normal QWidget.
Take a look at this: QML C++ Integration
and this: Interact QML from C++

How to do an action in Qml every time a user touches the screen?

I am writing an application for an embedded linux touchscreen device using Qt and Qml.
I need to implement a lock screen that appears after 30 seconds of inactivity.
To do this I've implemented a timer in C++, that changes the state of the program after timeout.
Is it possible to refresh this timer on a global scale every time the user touches the screen, so that I do not have to call the timers' start() slot in every touchable element in my program?
I would also like to refresh this timer even when the user touches a part of the screen where there are no buttons/interactive elements.
Something like this main.qml:
Rectangle {
id: mainRect
width: 800
height: 480
//something like this (oversimplified) pseudo code:
onGlobalUserTouch {
timers.startLockTimer()
}
//end pseudocode
Loader {
id: mainLoader
anchors.fill: parent
source: "FirstPage.qml"
Behavior on opacity {PropertyAnimation{duration:250}}
onLoaded: secondLoader.source = ""
}
states:[
State {
name:"SecondPage"
when: (mainCppClass.state == PanelStates.SECOND_PAGE)
PropertyChanges {
target: mainLoader
source: "SecondPage.qml"
}
}
]
}
All I was able to find on the web was how to implement this in either iOS or Android.
If mainRect is your root component, you can just start your timer in its MouseArea.
// touch area of your root component
MouseArea {
anchors.fill: parent
onClicked: {
timers.startLockTimer();
}
// child component
Rectangle {
color: "yellow"
x: 50; y : 50
width: 100; height: 100
}
}
This does not override descendant MouseAreas. Thus, if you touch area where there are no buttons/interactive elements, your startLockTimer will be called.

Simulate clicked QML element from QTest

I'm playing around with QTest and want simulate a mouse click in my test on one of my QML elements that I have in my UI.
So in other words I would like to trigger the onClicked signal handler in my QML code from C++?
This is how my QML looks like:
import QtQuick 2.0
Rectangle
{
objectName: "MessageRectangle"
width: 360
height: 360
Text
{
objectName: "MessageText"
text: qsTr("Hello World")
anchors.centerIn: parent
}
Rectangle
{
id: simplebutton
objectName: "ChangeTextButton"
color: "grey"
width: 150; height: 75
Text
{
id: buttonLabel
anchors.centerIn: parent
text: "button label"
}
MouseArea
{
id: buttonMouseArea
objectName: "ChangeTextButtonMouseArea"
anchors.fill: parent
onClicked:
{
CppApi.setMessage( "Button Clicked" );
}
}
}
}
It is a bit hard to show all the different solutions that I have tried. But I have tried to connect an own class derived from QObject with the QML item and sending click signal to it:
QObject::connect( this, SIGNAL( clicked() ), pItem, SLOT( onClicked() ) );
emit clicked();
I have also tried to call the following way:
QMetaObject::invokeMethod( pButton, "pressedChanged", Qt::DirectConnection );
Where pButton is the QQuickItem which I get when I grab the object named ChangeTextButtonMouseArea.
I have found some similar questions here. But there seems to be something different with the onClicked, somehow. If I print out all the methods that are defined in the QMetaObject that I get from the QQuickItem, then I can't find the onClicked function or the clicked signal.
This answer is somewhat in the correct direction but the the signal I want to send is not created by myself:
C++ SIGNAL to QML SLOT in Qt
I also found this question:
How to simulate mouse clicks in QML?
But I only want to trigger a click event on a specific QML object. Do I really need to calculate the coordinates of my mouse click? Doing that seems a bit complicated since the QML items are only aware of the relative positions to their parents.
Am I trying something that is not possible? Or is the reason that I can't find any answers on the internet because the solution is so obvious?
Found out how to simulate the mouse click of an item. Posting it here in case someone else is trying to do this. It is actually quite simple. Use compute the coordinate to click and use QTest do the actual clicking.
void ClickItem( QQuickItem* pItem, QWindow* pRootWindow )
{
auto oPointF = pItem->mapToScene( QPoint( 0, 0 ) );
auto oPoint = oPointF.toPoint();
oPoint.rx() += pItem->width() / 2;
oPoint.ry() += pItem->height() / 2;
QTest::mouseClick( pRootWindow, Qt::LeftButton, Qt::NoModifier, oPoint );
}