2

I'm testing out an application and the UI uses PyQt4 with Pygame embedded into it. It uses a timer to "update" itself so to speak and in the timerEvent function Pygame attempts to retrieve all detected events. Issue is, Pygame isn't detecting any events.

Here's a minimalist version of my code

#!/etc/python2.7
from PyQt4 import QtGui
from PyQt4 import QtCore
import pygame
import sys

class ImageWidget(QtGui.QWidget):
    def __init__(self,surface,parent=None):
        super(ImageWidget,self).__init__(parent)
        w=surface.get_width()
        h=surface.get_height()
        self.data=surface.get_buffer().raw
        self.image=QtGui.QImage(self.data,w,h,QtGui.QImage.Format_RGB32)

        self.surface = surface

        self.timer = QtCore.QBasicTimer()
        self.timer.start(500, self)

    def timerEvent(self, event):
        w=self.surface.get_width()
        h=self.surface.get_height()
        self.data=self.surface.get_buffer().raw
        self.image=QtGui.QImage(self.data,w,h,QtGui.QImage.Format_RGB32)
        self.update()

        for ev in pygame.event.get():
            if ev.type == pygame.MOUSEBUTTONDOWN:
                print "Mouse down"

    def paintEvent(self,event):
        qp=QtGui.QPainter()
        qp.begin(self)
        qp.drawImage(0,0,self.image)
        qp.end()


class MainWindow(QtGui.QMainWindow):
    def __init__(self,surface,parent=None):
        super(MainWindow,self).__init__(parent)
        self.setCentralWidget(ImageWidget(surface))



pygame.init()
s=pygame.Surface((640,480))
s.fill((64,128,192,224))
pygame.draw.circle(s,(255,255,255,255),(100,100),50)

app=QtGui.QApplication(sys.argv)
w=MainWindow(s)
w.show()
app.exec_()

How can I get Pygame events while the Pygame window is embedded in a PyQt application?

Edgecase
  • 673
  • 1
  • 7
  • 23

3 Answers3

1

A functional code implementation:

import time
import contextlib
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


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

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

        screen = pygame.display.set_mode((DISPLAY_WIDTH, DISPLAY_HEIGHT),
                                            pygame.OPENGL)

        # Fill background
        background = pygame.Surface(screen.get_size())
        background = background.convert()
        background.fill((250, 250, 250))

        # Display some text
        font = pygame.font.Font(None, 36)
        text = font.render("Hello There", 1, (10, 10, 10))
        textpos = text.get_rect()
        textpos.centerx = background.get_rect().centerx
        background.blit(text, textpos)

        # Blit everything to the screen
        screen.blit(background, (0, 0))

        while not QThread.currentThread().isInterruptionRequested():
            screen.blit(background, (0, 0))

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

            self.send_image.emit(buffer)
            time.sleep(0.1)
        print("Thread finished")


class PygameReceiver(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.pygame_worker = PygameWorker()
        self.pygame_worker.send_image.connect(self.update_image)
        self.pygame_worker_thread = QThread()
        self.pygame_worker.moveToThread(self.pygame_worker_thread)
        self.pygame_worker_thread.started.connect(self.pygame_worker.run)
        self.pygame_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):
        qpixmap = QPixmap.fromImage(image)
        self.image_label.setPixmap(qpixmap)

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


if __name__ == "__main__":
    app = QApplication()
    window = PygameReceiver()
    window.show()
    app.exec()
Jaime02
  • 299
  • 7
  • 21
0

You can not.


TL; DR;

You cannot and should not combine 2 libraries that have their own event loop, for example now the Qt eventloop is blocking the pygame event loop.

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • 1
    @Rabbid76 The first part of your answer is the same as what I indicated, in the other parts you only indicate possible solutions. In the case of pygame window it is possible to use it in Qt but it is an unnecessary effort since Qt can do the same as pygame. For this reason I point out that it is not good, and although it is possible it is better to only use Qt or only pygame. – eyllanesc Dec 12 '21 at 18:16
0

Fist of all do not mix frameworks. The frameworks may interact poorly or completely conflict with one another. Getting it to work on your system doesn't mean it will work on another system or with a different version of any of the frameworks. Mixing frameworks always means some kind of undefined behavior.

In your example your create an image (pygame.Surface) with the Pygame library and display it in QWidget. You never create a Pygame window. Therefore the Pygame event handling cannot work. You need to use Qts event handling.

Anyway, if you just want to do some image processing or draw some pictures and display them in a Qt application, I suggest using OpenCV (cv2). This library is designed for powerful image manipulation and the images can be viewed nicely using a Qt user interface.

Rabbid76
  • 202,892
  • 27
  • 131
  • 174