Custom Return Type Signals Not Received in QML Despite Being Emitted
-
I'm working on a PySide6 project where I need to return a custom type from a Python method to QML. I've registered the type and can see the signals being emitted in Python, but they're not being received in QML.
Here's my minimal example:
# data_response.py from PySide6.QtCore import QObject, Signal, Property class DataResponse(QObject): dataReceived = Signal(str) errorOccurred = Signal(str) def __init__(self, parent=None): super().__init__(parent) self._data = "" def set_data(self, data: str): print(f"Setting data: {data}") # This prints self.dataReceived.emit(data) print("Signal emitted") # This prints # data_api.py from PySide6.QtCore import QObject, Slot from PySide6.QtQml import qmlRegisterUncreatableType class DataApi(QObject): def __init__(self, parent=None): super().__init__(parent) qmlRegisterUncreatableType( DataResponse, "CustomTypes", 1, 0, "DataResponse", "DataResponse cannot be created from QML" ) @Slot(str, result=DataResponse) def fetch_data(self, query: str) -> DataResponse: response = DataResponse(self) response.set_data(f"Data for query: {query}") return response
QML usage:
import QtQuick import CustomTypes 1.0 Item { Button { onClicked: { var response = dataApi.fetch_data("test") response.dataReceived.connect(function(data) { console.log("Data received:", data) // Never prints }) } } }
Debug output shows:
Setting data: Data for query: test Signal emitted
The Python side is clearly emitting the signal (and I can verify this with debug prints), but the QML side never receives it. I suspect this might be related to signal timing or object lifetime, but I'm not sure.
What am I missing? How can I properly handle signals from a returned Python object in QML?
Environment:
- Python 3.10
- PySide6 6.7.2
- Debian 12
Any help would be greatly appreciated!
-
Slots can have return values (so can signals, but let's not get there), and there doesn't seem to be an
@Invokable
decorator in Python.
The QML code in theonClicked
signal handler has the correct syntax for imperatively connecting a signal to a function in QML ( https://doc.qt.io/qt-6/qtqml-syntax-signals.html#connecting-signals-to-methods-and-signals ).There doesn't seem to be much wrong in the current code.
What does theconnect
call returns? Is theresponse
object returned byfetch_data
valid in QML? Can you try to print it? And add a property to it and print that too?EDIT: Reading your code again, it just appears that the
dataReceived
signal is emitted before yourfetch_data
returns (and thus before QML can establish the connection). -
Hi,
First thing: slots, in principle, do not have return values, if you want that, use invokable methods.Python is an exception it seems to this rule when implementing invokable functions. Found it in the documentation
What strikes me in your code is that you are using Python syntax in your QML code.It seems you want to implement your own QNetworkAccessManager class, is that correct ?
-
Slots can have return values (so can signals, but let's not get there), and there doesn't seem to be an
@Invokable
decorator in Python.
The QML code in theonClicked
signal handler has the correct syntax for imperatively connecting a signal to a function in QML ( https://doc.qt.io/qt-6/qtqml-syntax-signals.html#connecting-signals-to-methods-and-signals ).There doesn't seem to be much wrong in the current code.
What does theconnect
call returns? Is theresponse
object returned byfetch_data
valid in QML? Can you try to print it? And add a property to it and print that too?EDIT: Reading your code again, it just appears that the
dataReceived
signal is emitted before yourfetch_data
returns (and thus before QML can establish the connection). -
@GrecKo while I agree on the fact that slot can have return value, it's really not how they are usually implemented nor used hence I wouldn't go that route as mostly nobody would be using them that way. I reworded my answer.
Ok, so, doing some more searches, it seems the correct way to do Invokable in Python is to, in fact, use slots with return value. It's in the documentation.
Thanks for the pointer on the declarative connection style. It's something I either missed or had forgotten about.
-
-