Thursday, 17 February 2022

Implementing QQuickAsyncImageProvider in Pyside; No such signal QObject::finished()

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):
    done = Signal(QImage)

    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:

  1. QQuickImageResponse defines the finished signal, and
  2. I get the same error message even when I comment out self.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?



from Implementing QQuickAsyncImageProvider in Pyside; No such signal QObject::finished()

No comments:

Post a Comment