Qt for Python/Connecting QML Signals: Difference between revisions
No edit summary |
m (CristianMaureiraFredes moved page Qt for Python - Connecting QML Signals to Qt for Python/Connecting QML Signals) |
||
(One intermediate revision by the same user not shown) | |||
Line 2: | Line 2: | ||
[[Category:Developing_with_Qt::Qt Quick]] | [[Category:Developing_with_Qt::Qt Quick]] | ||
This page describes a few alternative approaches for connecting signals between QML and PySide. Simple illustrative examples about the signal connectivity are also provided in the [https://code.qt.io/cgit/pyside/pyside-setup.git/tree/examples/declarative/signals pyside-setup repository]. | |||
This page describes a few alternative approaches for connecting signals between QML and PySide. Simple illustrative examples about the signal connectivity are also provided in the [ | |||
== Connecting signals from QML to Python == | == Connecting signals from QML to Python == | ||
Line 13: | Line 12: | ||
=== Explicitly calling a Python slot from QML === | === Explicitly calling a Python slot from QML === | ||
If the Python object is exposed to QML using <code>setContextProperty</code>, you can call any slot of the object explicitly from QML, as shown in [ | If the Python object is exposed to QML using <code>setContextProperty</code>, you can call any slot of the object explicitly from QML, as shown in [https://code.qt.io/cgit/pyside/pyside-setup.git/tree/examples/declarative/signals/qmltopy1 qmltopy1]. First, you define a class in Python, inheriting from QObject: | ||
< | <syntaxhighlight lang="python" line="line"> | ||
class Console(QtCore.QObject): | class Console(QtCore.QObject): | ||
@QtCore.Slot(str) | |||
def outputStr(self, s): | |||
print(s) | |||
</ | </syntaxhighlight> | ||
Then, the Python object is instantiated and connected to the QML context: | Then, the Python object is instantiated and connected to the QML context: | ||
< | <syntaxhighlight lang="python" line="line"> | ||
con = Console() | con = Console() | ||
view = QtDeclarative.QDeclarativeView() | view = QtDeclarative.QDeclarativeView() | ||
context = view.rootContext() | context = view.rootContext() | ||
context.setContextProperty("con", con) | context.setContextProperty("con", con) | ||
</ | </syntaxhighlight> | ||
After this, the object is accessible in QML and any slot can be called just like a function: | After this, the object is accessible in QML and any slot can be called just like a function: | ||
< | <syntaxhighlight lang="javascript" line="line"> | ||
MouseArea { | MouseArea { | ||
onClicked: { | onClicked: { | ||
Line 39: | Line 38: | ||
} | } | ||
} | } | ||
</ | </syntaxhighlight> | ||
=== Returning a value from a slot === | === Returning a value from a slot === | ||
It is possible for slots to return values to the QML caller (see example [ | It is possible for slots to return values to the QML caller (see example [https://code.qt.io/cgit/pyside/pyside-setup.git/tree/examples/declarative/signals/qmltopy2 qmltopy2]). In this case, the Python slot needs to define an explicit return type: | ||
< | <syntaxhighlight lang="python" line="line"> | ||
@QtCore.Slot(result=int) | |||
def val(self): | |||
self.r = self.r + 10 | |||
return self.r | |||
</ | </syntaxhighlight> | ||
Then the Python object is exposed to QML, after which it can be called just like a function: | Then the Python object is exposed to QML, after which it can be called just like a function: | ||
< | <syntaxhighlight lang="javascript" line="line"> | ||
onClicked: { | |||
helloText.rotation = rotatevalue.val() | |||
} | |||
</ | </syntaxhighlight> | ||
=== Connecting signals from QML to Python using a top-level QML signal === | === Connecting signals from QML to Python using a top-level QML signal === | ||
If you prefer to handle the signal connection in Python, the simplest way to do it is to declare a top-level signal in QML and connect that to a Python slot as shown in [ | If you prefer to handle the signal connection in Python, the simplest way to do it is to declare a top-level signal in QML and connect that to a Python slot as shown in [https://code.qt.io/cgit/pyside/pyside-setup.git/tree/examples/declarative/signals/qmltopy3 example qmltopy3]. | ||
First, in the top-level QML item, declare the signal: | First, in the top-level QML item, declare the signal: | ||
< | <syntaxhighlight lang="javascript" line="line"> | ||
Rectangle { | Rectangle { | ||
id: page | |||
signal textRotationChanged(double rot) | signal textRotationChanged(double rot) | ||
[…] | […] | ||
</ | </syntaxhighlight> | ||
Then, in some other QML item, make some other signal handler emit this signal: | Then, in some other QML item, make some other signal handler emit this signal: | ||
< | <syntaxhighlight lang="javascript" line="line"> | ||
onRotationChanged: textRotationChanged(rotation) | |||
</ | </syntaxhighlight> | ||
Finally, in Python, acquire the QML root object and connect the signal: | Finally, in Python, acquire the QML root object and connect the signal: | ||
< | <syntaxhighlight lang="python" line="line"> | ||
root = view.rootObject() | |||
root.textRotationChanged.connect(sayThis) | |||
</ | </syntaxhighlight> | ||
This approach has the benefit of hiding the Python class behaviour from QML. On the other hand, this approach requires the Python code to have some knowledge of the QML item contents. | This approach has the benefit of hiding the Python class behaviour from QML. On the other hand, this approach requires the Python code to have some knowledge of the QML item contents. | ||
Line 92: | Line 91: | ||
=== Connecting a signal from a specific QML item to Python. === | === Connecting a signal from a specific QML item to Python. === | ||
It is also possible to acquire a specific QML item and connect a signal directly to it as illustrated in [ | It is also possible to acquire a specific QML item and connect a signal directly to it as illustrated in [https://code.qt.io/cgit/pyside/pyside-setup.git/tree/examples/declarative/signals/qmltopy4 example qmltopy4]. | ||
In principle, this approach doesn't require any extra consideration in QML. Unfortunately, in practice it may be difficult to find the proper QML items without assigning them object names: | In principle, this approach doesn't require any extra consideration in QML. Unfortunately, in practice it may be difficult to find the proper QML items without assigning them object names: | ||
< | <syntaxhighlight lang="javascript" line="line"> | ||
MouseArea { | |||
id: buttonMouseArea | |||
objectName: "buttonMouseArea" | |||
anchors.fill: parent | |||
} | |||
</ | </syntaxhighlight> | ||
Then, in Python, it is simple to find the desired item using <code>findChild</code> and connect a signal to it: | Then, in Python, it is simple to find the desired item using <code>findChild</code> and connect a signal to it: | ||
< | <syntaxhighlight lang="python" line="line"> | ||
button = root.findChild(QtCore.QObject,"buttonMouseArea") | |||
button.clicked.connect(lambda: sayThis("clicked button (signal directly connected)")) | |||
</ | </syntaxhighlight> | ||
Although initially tempting, this approach ties the QML and Python codebases pretty tightly to each other. Also, having to define the object names in QML makes the approach less than optimal. | Although initially tempting, this approach ties the QML and Python codebases pretty tightly to each other. Also, having to define the object names in QML makes the approach less than optimal. | ||
Line 115: | Line 114: | ||
== Connecting signals from Python to QML == | == Connecting signals from Python to QML == | ||
It is also possible to connect signals from Python to QML when e.g. changes in the model data need to be reflected in the UI. [ | It is also possible to connect signals from Python to QML when e.g. changes in the model data need to be reflected in the UI. [https://code.qt.io/cgit/pyside/pyside-setup.git/tree/examples/declarative/signals/pytoqml1 Example pytoqml1] illustrates how to do that. | ||
First, a function is defined in QML: | First, a function is defined in QML: | ||
< | <syntaxhighlight lang="javascript" line="line"> | ||
function updateRotater() { | |||
rotater.angle = rotater.angle + 45 | |||
} | |||
</ | </syntaxhighlight> | ||
QML functions appear as slots in Python, so it's a simple matter to connect a signal to them: | QML functions appear as slots in Python, so it's a simple matter to connect a signal to them: | ||
< | <syntaxhighlight lang="python" line="line"> | ||
timer.timeout.connect(root.updateRotater) | |||
</ | </syntaxhighlight> | ||
This allows for fairly clean and straightforward delivery of signals from Python to QML. | This allows for fairly clean and straightforward delivery of signals from Python to QML. |
Latest revision as of 10:16, 9 February 2021
This page describes a few alternative approaches for connecting signals between QML and PySide. Simple illustrative examples about the signal connectivity are also provided in the pyside-setup repository.
Connecting signals from QML to Python
Connecting a signal from QML to Python is the most common use case. This allows for example connecting button clicks and other user interface events in QML to the backend logic written in Python.
There are multiple alternative methods for connecting QML signals to Python. The methods are not mutually exclusive; any of them can be used in a single program (and even for a single signal, if need be).
Explicitly calling a Python slot from QML
If the Python object is exposed to QML using
setContextProperty
, you can call any slot of the object explicitly from QML, as shown in qmltopy1. First, you define a class in Python, inheriting from QObject:
class Console(QtCore.QObject):
@QtCore.Slot(str)
def outputStr(self, s):
print(s)
Then, the Python object is instantiated and connected to the QML context:
con = Console()
view = QtDeclarative.QDeclarativeView()
context = view.rootContext()
context.setContextProperty("con", con)
After this, the object is accessible in QML and any slot can be called just like a function:
MouseArea {
onClicked: {
con.outputStr("Hello, world!")
}
}
Returning a value from a slot
It is possible for slots to return values to the QML caller (see example qmltopy2). In this case, the Python slot needs to define an explicit return type:
@QtCore.Slot(result=int)
def val(self):
self.r = self.r + 10
return self.r
Then the Python object is exposed to QML, after which it can be called just like a function:
onClicked: {
helloText.rotation = rotatevalue.val()
}
Connecting signals from QML to Python using a top-level QML signal
If you prefer to handle the signal connection in Python, the simplest way to do it is to declare a top-level signal in QML and connect that to a Python slot as shown in example qmltopy3.
First, in the top-level QML item, declare the signal:
Rectangle {
id: page
signal textRotationChanged(double rot)
[…]
Then, in some other QML item, make some other signal handler emit this signal:
onRotationChanged: textRotationChanged(rotation)
Finally, in Python, acquire the QML root object and connect the signal:
root = view.rootObject()
root.textRotationChanged.connect(sayThis)
This approach has the benefit of hiding the Python class behaviour from QML. On the other hand, this approach requires the Python code to have some knowledge of the QML item contents.
Connecting a signal from a specific QML item to Python.
It is also possible to acquire a specific QML item and connect a signal directly to it as illustrated in example qmltopy4.
In principle, this approach doesn't require any extra consideration in QML. Unfortunately, in practice it may be difficult to find the proper QML items without assigning them object names:
MouseArea {
id: buttonMouseArea
objectName: "buttonMouseArea"
anchors.fill: parent
}
Then, in Python, it is simple to find the desired item using
findChild
and connect a signal to it:
button = root.findChild(QtCore.QObject,"buttonMouseArea")
button.clicked.connect(lambda: sayThis("clicked button (signal directly connected)"))
Although initially tempting, this approach ties the QML and Python codebases pretty tightly to each other. Also, having to define the object names in QML makes the approach less than optimal.
Connecting signals from Python to QML
It is also possible to connect signals from Python to QML when e.g. changes in the model data need to be reflected in the UI. Example pytoqml1 illustrates how to do that.
First, a function is defined in QML:
function updateRotater() {
rotater.angle = rotater.angle + 45
}
QML functions appear as slots in Python, so it's a simple matter to connect a signal to them:
timer.timeout.connect(root.updateRotater)
This allows for fairly clean and straightforward delivery of signals from Python to QML.