How to bind buttons in Qt Quick to Python PyQt 5

10,447

Solution 1

If you name the button, you can connect to its onClick signal, or to a custom signal that it emits in onClicked. Example:

ApplicationWindow {
 title: qsTr("Test Invoke")
 width: 200
 height: 100

 Button {
  signal messageRequired
  objectName: "myButton"
  y : 70
  text : "About"
  onClicked: messageRequired()

 }
}

Note the signal in Button and the objectName property. Then the Python code just before exec could be for example:

def myFunction():
    print 'handler called'

button = win.findChild(QObject, "myButton")
button.messageRequired.connect(myFunction)
button.clicked.connect(myFunction) # works too

Note that in the Button above, onClicked just emits the messageRequired signal, so it is better to drop the custom signal and connect to clicked directly. Both onClicked() and any slots connected to clicked will get called when you click button.

Solution 2

There is another solution which uses a Python model (QObject) in QML.

Python

engine = QQmlApplicationEngine()
engine.load("main.qml")

class Greeter(QObject):
    @pyqtSlot(str)
    def sayHello(self, name):
        print("Hello, " + name)

ctx = engine.rootContext()
ctx.setContextProperty("greeter", Greeter())

QML

Button {
    onCliked: {
        greeter.sayHello("Alice")
    }
}

Ref.

  1. https://wiki.qt.io/Qt_for_Python/Connecting_QML_Signals
Share:
10,447
Daivid
Author by

Daivid

Updated on June 28, 2022

Comments

  • Daivid
    Daivid almost 2 years

    How can bind the buttons I create in a .qml script to python PyQt5 code?

    example: python:

    import sys
    from PyQt5.QtCore import QObject, QUrl, Qt
    from PyQt5.QtWidgets import QApplication
    from PyQt5.QtQml import QQmlApplicationEngine
    
    if __name__ == "__main__":
      app = QApplication(sys.argv)
      engine = QQmlApplicationEngine()
      ctx = engine.rootContext()
      ctx.setContextProperty("main", engine)
    
      engine.load('test.qml')
    
      win = engine.rootObjects()[0]
      win.show()
      sys.exit(app.exec_())
    

    qml:

    import QtQuick 2.2
    import QtQuick.Window 2.1
    import QtQuick.Controls 1.2
    import QtQuick.Dialogs 1.1
    
    ApplicationWindow {
     title: qsTr("Test Invoke")
    
     width: 200
     height: 100
    
     Button{
      y : 70
      text : "About"
      onClicked: {
       print('Hello')
      }
    
     }
    }
    

    How can I do something with Python when the button is clicked? Also: Does anyone has a good resource of examples or doc. about pyqt + qml (qt quick)?

  • Daivid
    Daivid almost 10 years
    The older example didn't work. PyQt5 doesn't have the QDeclarativeView class, as said here; Finally, I couldn't find anything similar to what I'm trying to do in the demos.
  • Daivid
    Daivid almost 10 years
    All I got is: Traceback (most recent call last): File ".\main.py", line 29, in <module> win.myButton.messageRequired.connect(myFunction) AttributeError: 'QWindow' object has no attribute 'myButton'
  • Daivid
    Daivid almost 10 years
    Oh! changing your result a little bit makes it work! If I put the "signal messageRequired" as a direct child attribute of ApplicationWindow, I can capture the signal in python. Do you know how to capture the signal from the signal specified from the button? @Schollii
  • Oliver
    Oliver almost 10 years
    @Daivid I have updated: to find the button you have to use the objectName property (id won't do), and signal in Button works.
  • Daivid
    Daivid almost 10 years
    that works. But I tried something different: using win.findChild(QObject, 'anotherButton') that is inside an inner hierarchy of the QML. Then it didn't work. Do I have to iterate over the children of win to find it? What I did to work around this was to declare the signals inside ApplicationWindow and use in the buttons.
  • Oliver
    Oliver almost 10 years
    @Daivid Strange, because findChild by default (no 3rd argument) searches recursively. If findChild can't find it, it's either not there, the type is wrong, or the objectName has a type or is wrong. Check those things (it's unfortunate that findChild doesn't search id's as well, or that there isn't an option to search id or even better, to specify which attribute to use to find the child). I would post another question showing the inner hierarchy and how findChild works at top level but not inner.
  • bootchk
    bootchk over 9 years
    That might be a bad architecture. The QML might be a UI layer on top of a model or business logic layer in Python. The model layer should not know that a button is controlling it, since then you could easily change the control to say a gesture or other control. The UI layer should know that there is some model that it controls and views. To do this, you implement some model class in Python and register it with QML. Then in the QML you can connect the clicked signal of the button to some slot of the model instance. I struggled with this too: github.com/bootchk/demoQMLPyQt.git