PyQt5 QML Signal to Python Slot?

11,081

Solution 1

qml_rectangle.mousePressEvent is not a signal, it's an event handler that is called on mouse events, so you can't connect to it. You could just replace it with your handler function (qml_rectangle.mousePressEvent = on_qml_mouse_clicked), but that's not a very clean way of working with Qt.

The better way would be to define a signal in your qml file and emit it from the rectangle's onClicked handler:

import QtQuick 2.0

Rectangle {
    signal clicked()
    MouseArea {
        anchors.fill: parent
        onClicked: {
           parent.clicked()  // emit the parent's signal
        }
    }
}

Then you can just connect to it from your python code:

...
def on_qml_mouse_clicked():
    print('mouse clicked')

qml_rectangle.clicked.connect(on_qml_mouse_clicked)
...

Solution 2

I would recommend sub classing QQuickView and setting a property on its root context, for example MainWindow. Now all you need to do is adding functions in that class with decorations such as @pyqtSlot('QString'), and then you can set the event handler with onClicked: MainWindow.FunctionName(Arguments_According_To_Decoration)

Then your main.py would look like this

#!/bin/env python3
# -*- coding: utf-8 -*-
from PyQt5.QtCore    import pyqtSlot
from PyQt5.QtCore    import QUrl
from PyQt5.QtQuick   import QQuickView
from PyQt5.QtWidgets import QApplication
import sys

class MainWindow(QQuickView):
    def __init__(self):
        super().__init__()
        self.setSource(QUrl('sample.qml'))
        self.rootContext().setContextProperty("MainWindow", self)
        self.show()

    @pyqtSlot('QString')
    def Print(self, value):
        print(value)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = MainWindow()
    sys.exit(app.exec_())

And sample.qml like so

import QtQuick          2.0
import QtQuick.Controls 2.2

Rectangle {
    width: 200; height: 200

    Button {
      text: "print Hello World"
      onClicked: MainWindow.Print('hello world')
    }
}

You can find more information in the docs

http://pyqt.sourceforge.net/Docs/PyQt5/signals_slots.html

Share:
11,081
berg
Author by

berg

Updated on June 27, 2022

Comments

  • berg
    berg almost 2 years

    How can a python method/slot be connected to a QML signal? It looks like QtObject.connect() used to work in PyQt4 but it's no longer available in PyQt5.

    #Sample QML File (stack.qml)
    
    import QtQuick 2.0
    
    Rectangle {
        MouseArea {
            anchors.fill: parent
            onClicked: {
               // relay this to python
            }
        }
    }
    

    --

    #Sample Python File
    from PyQt5.QtCore import QUrl
    from PyQt5.QtGui import QGuiApplication
    from PyQt5.QtQuick import QQuickView
    
    if __name__ == '__main__':
        import os
        import sys
    
        app = QGuiApplication(sys.argv)
    
        view = QQuickView()
        view.setWidth(500)
        view.setHeight(500)
        view.setTitle('Hello PyQt')
        view.setResizeMode(QQuickView.SizeRootObjectToView)
        view.setSource(QUrl.fromLocalFile(os.path.join(os.path.dirname(__file__),'stack.qml')))
    
        def on_qml_mouse_clicked(mouse_event):
            print 'mouse clicked'
    
        view.show()
        qml_rectangle = view.rootObject()
    
        # this technique doesn't work #############################
        qml_rectangle.mousePressEvent.connect(on_qml_mouse_clicked)
    
        sys.exit(app.exec_())
    

    Some of the PyQT examples pass an object into the QML context via "setContextProperty" and then relay QML events to slots on that object but that approach seems roundabout. Is there a better way?