Skip to content

Feature request: Support QImage and QQuickImageProvider #233

Description

@rakeshksr

JuliaDisplay works fine for displaying content, but adding support for QImage and QQuickImageProvider would be a great improvement. Supporting QImage would allow direct image data transfer, and adding QQuickImageProvider support would enable use of image:// sources in QML backed by Julia functions. This may improve performance, simplify workflows, and make QML.jl more aligned with native Qt/QML capabilities, especially for real-time visualization and scientific applications.

Activity

  1. barche commented on Feb 13, 2026

    @barche
    Collaborator

    This could indeed be a good idea, do you have an example in QML on how this would be used?

  2. rakeshksr commented on Feb 14, 2026

    @rakeshksr
    Author

    Currently, my use case involves displaying image that are generated dynamically based on user input.

    With assistance from Gemini, I have created an example demonstrating real-time edge detection using OpenCV integrated with QML in Python.

    edge_detect.py

    import sys
    
    import cv2
    import numpy as np
    from PySide6.QtCore import QObject, QSize, Qt, QTimer, Signal, Slot
    from PySide6.QtGui import QGuiApplication, QImage
    from PySide6.QtQml import QQmlApplicationEngine
    from PySide6.QtQuick import QQuickImageProvider
    
    
    class ImageProvider(QQuickImageProvider):
        def __init__(self):
            super().__init__(QQuickImageProvider.Image)
            self.image = QImage()
    
        def requestImage(self, id, size, requestedSize):
            if self.image.isNull():
                return QImage(640, 480, QImage.Format_RGB32)
            return self.image
    
    
    class VideoProcessor(QObject):
        imageChanged = Signal()
    
        def __init__(self, provider):
            super().__init__()
            self.provider = provider
            self.cap = cv2.VideoCapture(0)
    
            self.timer = QTimer()
            self.timer.timeout.connect(self.update_frame)
            self.timer.start(30)
    
        @Slot()
        def update_frame(self):
            ret, frame = self.cap.read()
            if not ret:
                return
    
            # Example: Simple Canny Edge Detection
            gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            edges = cv2.Canny(gray, 100, 200)
    
            detection_overlay = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)
    
            output = cv2.addWeighted(frame, 0.7, detection_overlay, 0.3, 0)
    
            rgb_frame = cv2.cvtColor(output, cv2.COLOR_BGR2RGB)
            h, w, ch = rgb_frame.shape
    
            # Create QImage and store in provider
            qt_img = QImage(rgb_frame.data, w, h, ch * w, QImage.Format_RGB888)
            self.provider.image = qt_img.copy()  # Copy to avoid memory issues
            self.imageChanged.emit()
    
    
    if __name__ == "__main__":
        app = QGuiApplication(sys.argv)
        engine = QQmlApplicationEngine()
    
        stream_provider = ImageProvider()
        engine.addImageProvider("live_stream", stream_provider)
    
        processor = VideoProcessor(stream_provider)
    
        engine.rootContext().setContextProperty("videoProcessor", processor)
    
        engine.load("edge_detect.qml")
        if not engine.rootObjects():
            sys.exit(-1)
    
        sys.exit(app.exec())

    edge_detect.qml

    import QtQuick
    import QtQuick.Window
    import QtQuick.Controls
    
    Window {
        width: 800
        height: 600
        visible: true
        title: "OpenCV + QML Live Detection"
        color: "#222"
    
        property int frameCounter: 0
    
        Column {
            anchors.centerIn: parent
            spacing: 10
    
            Text {
                text: "Live Edge Detection"
                color: "white"
                font.pixelSize: 24
                anchors.horizontalCenter: parent.horizontalCenter
            }
    
            Image {
                id: liveView
                width: 640
                height: 480
                fillMode: Image.PreserveAspectFit
    
                // The "id=" part forces a reload by changing the URL
                source: "image://live_stream/camera?id=" + frameCounter
                cache: false
            }
        }
    
        Connections {
            target: videoProcessor
            function onImageChanged() {
                frameCounter++
            }
        }
    }
    
  3. barche commented on Jun 21, 2026

    @barche
    Collaborator

    This has been in QML.jl for a while, but now there is also an extensive example on how to use it: https://github.com/barche/QmlJuliaExamples/blob/master/basic/imageprovider.jl

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions