I'm attempting to implement a subclass of QQuickAsyncImageProvider
in Pyside6, referring to the official example. I'm already aware of the potential hurdle that, unlike in the C++ example code, with PySide it's not possible to inherit from both QRunnable
and QObject
. In order to send a signal from my QRunnable
I'm instead using an intermediary QObject
, as suggested here. That part works fine.
Here's my attempt so far at using this arrangement along with my own subclass of QQuickAsyncImageProvider
:
from PySide6.QtGui import QGuiApplication, QImage
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtCore import QObject, QRunnable, QThreadPool, Signal
from PySide6.QtQuick import (QQuickImageResponse, QQuickAsyncImageProvider,
QQuickTextureFactory)
class Signaller(QObject):
done = Signal(QImage)
class AsyncImageResponseRunnable(QRunnable):
def __init__(self):
super().__init__()
self.signaller = Signaller()
self.done = self.signaller.done
def run(self):
image = QImage(400, 400, QImage.Format_RGB32)
image.fill('red')
self.done.emit(image)
class AsyncImageResponse(QQuickImageResponse):
def __init__(self):
super().__init__()
runnable = AsyncImageResponseRunnable()
runnable.done.connect(self.handle_done)
pool = QThreadPool.globalInstance()
pool.start(runnable)
def handle_done(self, image):
self.image = image
self.finished.emit()
def textureFactory(self):
return QQuickTextureFactory.textureFactoryForImage(self.image)
class AsyncImageProvider(QQuickAsyncImageProvider):
def requestImageResponse(self, image_id, requested_size):
return AsyncImageResponse()
if __name__ == "__main__":
app = QGuiApplication()
engine = QQmlApplicationEngine()
engine.addImageProvider('async_test', AsyncImageProvider())
engine.load('test.qml')
app.exec()
//test.qml
import QtQuick
Window {
width: 1200
height: 800
visible: true
Image {
source: 'image://async_test/test'
}
}
This causes a segmentation fault—and some of the time, immediately beforehand, I get this error message:
qt.core.qobject.connect: QObject::connect: No such signal QObject::finished()
At first glance, this seems like it would be related to the self.finished.emit()
line. However:
QQuickImageResponse
defines the finished
signal, andself.finished.emit()
, which seems to indicate something internal must be attempting to call it.So, what am I missing here? How do I avoid the segmentation fault and get my asynchronous image provider functional?
I reported this in case it was a bug, and one of the maintainers realized that the issue is that the AsyncImageResponse
gets immediately deleted. The workaround for this is to hold a reference to it in the AsyncImageProvider
:
class AsyncImageProvider(QQuickAsyncImageProvider):
def requestImageResponse(self, image_id, requested_size):
self.response = AsyncImageResponse()
return self.response
They've also proposed a change that may make this unnecessary in the future.