1

I'm trying to embed a SpoutGL (link to bindings) program that receives graphics into a Qt (PySide6) window. How can I do that?

The only example I have found uses PyGame to create a surface and then a image of it is converted into a QImage, but that is not real time processing. The other example uses the PyGame event loop in order to receive data in real time, but I would have to embed the PyGame window into a QWidget which is not good.

I have this code:

import time

import SpoutGL
import contextlib
from OpenGL.GL import *

from PySide6.QtCore import QObject, Signal, Qt, QThread
from PySide6.QtGui import QImage, QPixmap
from PySide6.QtWidgets import QMainWindow, QLabel, QWidget, QVBoxLayout, QApplication

with contextlib.redirect_stdout(None):
    import pygame

DISPLAY_WIDTH = 800
DISPLAY_HEIGHT = 600
SENDER_NAME = "SpoutGL-texture-test"


def setProjection(width, height):
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    glOrtho(0, width, height, 0, 1, -1)
    glMatrixMode(GL_MODELVIEW)


def drawSquare(width, height):
    glEnable(GL_TEXTURE_2D)
    glBegin(GL_QUADS)

    glTexCoord(0, 0)
    glVertex2f(0, 0)

    glTexCoord(1, 0)
    glVertex2f(width, 0)

    glTexCoord(1, 1)
    glVertex2f(width, height)

    glTexCoord(0, 1)
    glVertex2f(0, height)

    glEnd()
    glDisable(GL_TEXTURE_2D)


class SpoutWorker(QObject):
    send_image = Signal(QImage)

    def run(self):
        # Initialise screen
        pygame.init()

        pygame.display.set_mode((DISPLAY_WIDTH, DISPLAY_HEIGHT),
                                            pygame.OPENGL | pygame.HIDDEN)

        receiveTextureID = glGenTextures(1)
        glBindTexture(GL_TEXTURE_2D, receiveTextureID)
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0,
                         DISPLAY_WIDTH, DISPLAY_HEIGHT, 0)
        setProjection(DISPLAY_WIDTH, DISPLAY_HEIGHT)
        glClearColor(0.0, 0.0, 0.0, 1.0)

        with SpoutGL.SpoutReceiver() as receiver:
            receiver.setReceiverName(SENDER_NAME)

            width = 0
            height = 0

            while not QThread.currentThread().isInterruptionRequested():
                result = receiver.receiveTexture(
                    receiveTextureID, GL_TEXTURE_2D, False, 0)

                if receiver.isUpdated():
                    width = receiver.getSenderWidth()
                    height = receiver.getSenderHeight()

                    print("Updated")

                    # Initialize or update texture size
                    glActiveTexture(GL_TEXTURE0)
                    glBindTexture(GL_TEXTURE_2D, receiveTextureID)
                    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width,
                                 height, 0, GL_RGBA, GL_UNSIGNED_BYTE, None)

                    setProjection(width, height)

                glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

                # Draw texture
                glActiveTexture(GL_TEXTURE0)
                glBindTexture(GL_TEXTURE_2D, receiveTextureID)
                drawSquare(width, height)

                # Get the display image
                image_str = pygame.image.tostring(pygame.display.get_surface(), "RGB", 1)
                buffer = QImage(image_str, DISPLAY_WIDTH, DISPLAY_HEIGHT, QImage.Format_RGB888)
                if buffer.isNull():
                    print("Buffer is null")
                    continue

                self.send_image.emit(buffer)

                # Wait until the next frame is ready
                # Wait time is in milliseconds; note that 0 will return immediately
                receiver.waitFrameSync(SENDER_NAME, 1000)
                time.sleep(0.1)

        print("Thread finished")


class SpoutReceiver(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)

        self.main_widget = QWidget()
        self.setCentralWidget(self.main_widget)

        self.main_layout = QVBoxLayout()
        self.main_widget.setLayout(self.main_layout)

        self.spout_worker = SpoutWorker()
        self.spout_worker.send_image.connect(self.update_image)
        self.spout_worker_thread = QThread()
        self.spout_worker.moveToThread(self.spout_worker_thread)
        self.spout_worker_thread.started.connect(self.spout_worker.run)
        self.spout_worker_thread.start()

        self.image_label = QLabel()
        self.image_label.setAlignment(Qt.AlignCenter)
        self.main_layout.addWidget(self.image_label)

    def update_image(self, image):
        flipped_image = image.mirrored()
        qpixmap = QPixmap.fromImage(flipped_image)
        self.image_label.setPixmap(qpixmap)

    def closeEvent(self, event):
        self.spout_worker_thread.requestInterruption()
        self.spout_worker_thread.quit()
        self.spout_worker_thread.wait()
        QMainWindow.closeEvent(self, event)


if __name__ == "__main__":
    app = QApplication()
    window = SpoutReceiver()
    window.show()
    app.exec()

It uses a QThread to run PyGame at same time and SpoutGL to receive the graphics. However, although receiveTexture returns True, no image is received

Jaime02
  • 299
  • 7
  • 21

0 Answers0