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