0
votes

I have the source code follow as below:

I have 2 qml files. The second qml file has been called from the maim.qml file. I want to get the text from the second qml file using the signal and slot method.

main.qml file:

ApplicationWindow {
    id: applicationWindow1
    StackLayout {
        id: swipeView
        anchors.left: sideBar.right
        anchors.right: parent.right
        anchors.bottom: tabBar.top
        anchors.top: parent.top
        currentIndex: tabBar.currentIndex
        firstpage{}
        Secondpage{}
        }
    TabBar {
        id: tabBar
        height: 42
        anchors.bottom: parent.bottom
        anchors.left: sideBar.right
        anchors.right: parent.right
        currentIndex: swipeView.currentIndex
        TabButton {
            text: qsTr("firstpage")
        }
        TabButton {
            text: qsTr("Secondpage")
        }
}

secondpage.qml

Item{
    signal submitTextField(string text)

    // this function is our QML slot
    function setTextField(text){
        console.log("setTextField: " + text)
        textField1.text = text
    }

    visible: true
    width: 360
    height: 360

    TextField {
        id: textField1
        x: 31
        y: 169
        placeholderText: qsTr("Enter some text...")
    }

    Button {
        x: 193
        y: 167
        text: qsTr("Uppercase me!")

        onClicked:
            // emit the submitTextField signal
            submitTextField(textField1.text)
    }

}

code C++ : HandleTextField.h file

class HandleTextField : public QObject
{

        Q_OBJECT
    public:
        explicit HandleTextField(QObject *parent = 0) {}

    signals:
        void setTextField(QVariant text);

    public slots:
        void handleSubmitTextField(const QString& in)
       {
        qDebug() << "c++: HandleTextField::handleSubmitTextField:" << in;
        emit setTextField(in.toUpper());  
       }

}

main.cpp file

int main(int argc, char *argv[])
{

    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    QQuickView view;
    view.setSource(QUrl(QStringLiteral("qrc:/secondpage.qml")));
    QObject *item = view.rootObject();

    HandleTextField handleTextField ;

    QObject::connect(item,SIGNAL(submitTextField(QString)),&handleTextField ,SLOT(handleSubmitTextField(QString)));
    QObject::connect(&handleTextField ,SIGNAL(setTextField(QVariant)),item,SLOT(setTextField(QVariant)));

    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;
    return app.exec();

}

-->> When I press the button, there is no action. Please tell me what I'm doing wrong here.

Thanks

2

2 Answers

0
votes

Your first error is that you are thinking that the window created using QQuickView is the same one that is created in main.qml, because no, they are 2 different objects.

There are 2 approaches for QML and C++ to interact:

  • Pulling References from QML : This is the approach that you want to implement but it has many inconveniences because the object is searched using the objectname that you have not used for example, besides that in complex structures it is complicated or impossible to obtain, besides that many QML objects are created and destroyed by what object you get at another time I can point to non-reserved memory, plus there is a dependence of the C ++ code to the QML, so this method is not the best and not recommended.

  • Pushing References to QML: This approach does not have the problems since when exporting the object using setContextProperty the object is visible in all QML since it is global, so if a QML object is created or eliminated it will not generate problems like the previous method, we do not need the objectname and the connection is made as if the object was created in QML.

Having mentioned the above, I will use the second approach:

main.cpp

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);
    HandleTextField handleTextField;
    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty("handleTextField", &handleTextField);
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;
    return app.exec();
}

Secondpage.qml

Item{
    Connections{
        target: handleTextField
        onSetTextField: textField1.text = text // <---
    }
    visible: true
    width: 360
    height: 360
    TextField {
        id: textField1
        x: 31
        y: 169
        placeholderText: qsTr("Enter some text...")
    }
    Button {
        x: 193
        y: 167
        text: qsTr("Uppercase me!")
        onClicked: handleTextField.handleSubmitTextField(textField1.text) // <---
    }
}

main.qml should not be modified.

0
votes

The most obvious issue with the code is that you're never actually using the instance of secondpage which you create and connect() to. Nor the QQuickView in which it is created. The QQmlApplicationEngine is where you load and show your main.qml and inside there you have created a whole new instance of secondpage.

Assuming you have some legitimate reason for using the intermediary HandleTextField class (instead of just connecting your QML signals/slots directly in QML w/out involving C++), then I would suggest an alternate strategy. Looks like you've read at least some of Interacting with QML Objects from C+ but do pay particular attention to the warning displayed in this specific part:

Warning: Although it is possible to access QML objects from C++ and manipulate them, it is not the recommended approach, except for testing and prototyping purposes. ...

To fix your existing code (if choose this route after all):

main.qml

ApplicationWindow {
    id: applicationWindow1
    StackLayout {
        id: swipeView
        ...
        Secondpage {
            objectName: "mySecondPage"  // add an object name
        }
    }
    ...
}

main.cpp

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    // First load the main QML file, which also loads the Secondpage item you want to access.
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    HandleTextField handleTextField;

    // Here we find the "Secondpage" item loaded into main.qml by its objectName property
    QObject *item = engine.rootObjects().first()->findChild<QObject *>("mySecondPage");
    if (item) {
        QObject::connect(item, SIGNAL(submitTextField(QString)), &handleTextField, SLOT(handleSubmitTextField(QString)));
        QObject::connect(&handleTextField, SIGNAL(setTextField(QVariant)), item, SLOT(setTextField(QVariant)));
    }

    return app.exec();
}

Note: code not tested. Check for any warnings from Qt on stderr (console) to make sure those connect() operations actually succeed. Another issue with this approach is that the "old style" Qt connections can only be verified at runtime (vs. "new style" verified at compile time), and you'll only see the warning if you're actively looking at stderr output. But that's the only way to connect to QML signals/slots.