Reputation: 105
I have some expirience using Qt/C++ and now I want to switch to PySide2 + QML. I want to connect ui signals, such as clicking a button, to python slot
I have seen many examples, but they all differ, i guess PyQt/PySide is changing quickly now
Can you provide me modern and clean way of connecting a QML signal to PySide Slot? For example clicking a Button to printing some text in python console. Here's my simple code example
main.py
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine
def test_slot(string): # pseudo slot
print(string)
if __name__ == "__main__":
app = QGuiApplication()
engine = QQmlApplicationEngine('main.qml')
exit(app.exec_())
main.qml
import QtQuick 2.13
import QtQuick.Controls 2.13
ApplicationWindow {
visible: true
Button {
anchors.centerIn: parent
text: "Example"
onClicked: test_slot("Test") //pseudo signal
}
}
Upvotes: 2
Views: 3507
Reputation: 676
The eyllanesc's solution is direct synchronous call of method test_slot
. It is good when test_slot
is small and fast. But if it includes a lot of operations, QML GUI will be suspended each time until test_slot
is returned.
The mosth Qt-like way is slot-signal metaobject connection (see #CHANGES
and //CHANGES
below):
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_())
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")
}
}
Upvotes: 2
Reputation: 243937
The best practice in these cases is to create a QObject, export it to QML and make the connection there as it is also done in C++.
main.py
from PySide2.QtCore import QObject, QUrl, Slot
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine
class Foo(QObject):
@Slot(str)
def test_slot(self, string):
print(string)
if __name__ == "__main__":
import os
import sys
app = QGuiApplication()
foo = Foo()
engine = QQmlApplicationEngine()
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)
sys.exit(app.exec_())
main.qml
import QtQuick 2.13
import QtQuick.Controls 2.13
ApplicationWindow {
visible: true
Button {
anchors.centerIn: parent
text: "Example"
onClicked: foo.test_slot("Test")
}
}
Note: All C++/QML good practices also apply in Python/QML with minimal changes and restrictions.
Upvotes: 5