Home > Software design >  How to emit Signal from nested QML page to python
How to emit Signal from nested QML page to python

Time:08-09

in my QML/python app I can emit signal from main.qml to the python code. But now In main.qml I added StackLayout for loading another page1.qml. In that page1.qml is button, now I want to emit signal from this button to the python.

I use this method for emit signals from main.qml file to the python: But do not know how to emit it from nested page1.qml

main.py

from PySide2.QtCore import QObject, QUrl, Slot, Signal, Qt
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine

class Foo(QObject):
    @Slot(str)
    def test_slot(self, input_string : str):
        print(input_string)

if __name__ == "__main__":
    import os
    import sys

    app = QGuiApplication()
    foo = Foo()
    engine = QQmlApplicationEngine()
    
    #CHANGES: line excluded engine.rootContext().setContextProperty("foo", foo)
    
    qml_file = "main.qml"
    current_dir = os.path.dirname(os.path.realpath(__file__))
    filename = os.path.join(current_dir, qml_file)
    engine.load(QUrl.fromLocalFile(filename))
    if not engine.rootObjects():
        sys.exit(-1)
    
    #CHANGES: connect QML signal to Python slot
    engine.rootObjects()[0].test_signal.connect(foo.test_slot, type=Qt.ConnectionType.QueuedConnection)
    
    sys.exit(app.exec_())

main.qml

import QtQuick 2.13
import QtQuick.Controls 2.13

ApplicationWindow {
    visible: true
    
    //CHANGES: declare signal
    signal test_signal(string input_string)

    Button {
        anchors.centerIn: parent
        text: "Example"

        //CHANGES: emit signal
        onClicked: test_signal("Test string")
    }
}

https://stackoverflow.com/a/69595659/5166312

Thank you very much.

CodePudding user response:

  1. Add objectName string property for nested pages (included the case if they are in separate files):
    objectName: "page1_objname"
    
  2. Access nested pages from Python (or C ) backend
    qmlpage1 = engine.rootObjects()[0].findChild(QObject, "page1_objname")
    
  3. Connect slot and signal as usually in Python or C backend (you already have code in your question or in my answer https://stackoverflow.com/a/69595659/5166312).
    qmlpage1.page1_signal.connect(
       test_object.test_slot3,
       type=Qt.ConnectionType.QueuedConnection)
    

The whole project with StackLayout, two nested pages, 3 signals and 3 slots in my test example on https://github.com/Ornstein89/pyside6-qml-slotsignal.

main.py

# This Python file uses the following encoding: utf-8
import sys
from pathlib import Path

from PySide6.QtCore import QObject, QUrl, Slot, Signal, Qt
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine

class TestClass(QObject):
    '''
    Object - slot-owner and signal-acceptor
    '''
    @Slot(str)
    def test_slot1(self, input_string : str):
        print(input_string)
    @Slot(str)
    def test_slot2(self, input_string : str):
        print(input_string)
    @Slot(str)
    def test_slot3(self, input_string : str):
        print(input_string)

if __name__ == "__main__":
    app = QGuiApplication(sys.argv)
    engine = QQmlApplicationEngine()
    test_object = TestClass()
    qml_file = Path(__file__).resolve().parent / "main.qml"
    engine.load(qml_file)
    if not engine.rootObjects():
        sys.exit(-1)

    # !!! connect ApplicationWindow.mainapp_signal() to test_object.test_slot1
    engine.rootObjects()[0]\
        .mainapp_signal.connect(
            test_object.test_slot1,
            type=Qt.ConnectionType.QueuedConnection)

    # !!! access nested page1 from python backend
    qmlpage1 = engine.rootObjects()[0].findChild(QObject, "page1_objname")

    # !!! and connect MyPage1.page1_signal() to test_object.test_slot2
    qmlpage1.page1_signal.connect(
            test_object.test_slot2,
            type=Qt.ConnectionType.QueuedConnection)

    # !!! access nested page2 from python backend
    qmlpage2 = engine.rootObjects()[0].findChild(QObject, "page2_objname")

    # !!! and connect MyPage2.page2_signal() to test_object.test_slot3
    qmlpage2.page2_signal.connect(
            test_object.test_slot3,
            type=Qt.ConnectionType.QueuedConnection)

    sys.exit(app.exec())

main.qml

import QtQuick
import QtQuick.Window
import QtQuick.Controls
import QtQuick.Layouts

ApplicationWindow {
    width: 480
    height: 640
    visible: true
    title: qsTr("Example for Stackoverflow")

    // !!! singal №1 in root QML Object - most easy to connect in main.py
    signal mainapp_signal(string input_string)

    StackLayout {
        id : stacklayout
        anchors.fill: parent
        MyPage1 {
            id: page1
        }
        MyPage2 {
            id: page2
        }
    }
}

MyPage1.qml

import QtQuick
import QtQuick.Window
import QtQuick.Controls
import QtQuick.Layouts

Page{
    id: page1

    // !!! singal №2 in nested QML Object
    signal page1_signal(string input_string)

    // !!! important - this name is used
    // to access page1 from C   or Python backend
    objectName: "page1_objname"

    Button{
        text: "Go to page 2"
        anchors.centerIn: parent
        onClicked: {
            stacklayout.currentIndex = 1
            // call root object signal from nested object
            mainapp_signal("mainapp_signal() from page1");
            // call nested object signal from same object
            page1_signal("page1_signal() from page1");
            // call another nested object signal
            page2.page2_signal("page2_signal() from page1");
        }
    }
}

MyPage2.qml

import QtQuick
import QtQuick.Window
import QtQuick.Controls
import QtQuick.Layouts

Page{
    id: page2

    // !!! singal №3 in nested QML Object
    signal page2_signal(string input_string)

    // !!! important - this name is used
    // to access page2 from C   or Python backend
    objectName: "page2_objname"

    Button{
        text: "Go to page 1"
        anchors.centerIn: parent
        onClicked: {
            stacklayout.currentIndex = 0
        }
    }
}

CodePudding user response:

You can add a Conections element to use signals of loaded component.
Look at documentation here.

Also, you can declare a component for your page1.qml item and use sourceComponent property of Loader. In this way you can use signal inside component to call an outer method. Look at documentation here.

  • Related