Displaying a video stream in QLabel with PySide

2019-02-10 23:46发布

问题:

Can anybody point me in the right direction on how to create a new QMovie "provider" in PySide?

I have a video stream that I want to display as simply as possible (no audio, just a sequence of frames with an unknown and variable framerate). This example seems perfect except that my video is coming from an unconventional source. It's not a file but a network stream in a format that is not standardized. I can easily write code that receives each frame and my idea is to create a "QMovie provider" so that I can just display this stream on a label like in the example above.

My first thought was to just subclass QMovie and overwrite a few functions there but I started having second thoughts about that when reading the documentation since I don't know what I should do about the "device" my instance would be reading from.

I noticed in the aforementioned documentation that QMovie uses QImageReader so my next thought was to extend that class and have it read frames from my stream. That poses similar questions however, for example, what should I do with the "supportedImageFormats()" function?

I've been experimenting with just directly updating the image on my QLabel every time I receive a new frame but then I've been getting the error "QPixmap: It is not safe to use pixmaps outside the GUI thread".

So basically I'm a little stumped and would really appreciate any pointers or tutorials on how to get a QLabel to display my video stream in a PySide application.

回答1:

For future reference here's how I managed to get this working.

Using the signals and slot mechanism, the following application works. The signal/slot mechanism seems to figure out that the image that gets created inside the up_camera_callback function and emitted to the CameraDisplay.updateFrame function is coming from a different thread and takes the necessary precautions.

class CameraDisplay(QtGui.QLabel):
  def __init__(self):
    super(CameraDisplay, self).__init__()

  def updateFrame(self, image):
    self.setPixmap(QtGui.QPixmap.fromImage(image))

class ControlCenter(QtGui.QWidget):
  up_camera_signal = QtCore.Signal(QtGui.QImage)
  up_camera = None

  def __init__(self):
    super(ControlCenter, self).__init__()
    self.up_camera = CameraDisplay()
    self.up_camera_signal.connect(self.up_camera.updateFrame)

    grid = QtGui.QGridLayout()
    grid.setSpacing(10)

    grid.addWidget(self.up_camera, 0, 0)

    self.setLayout(grid)

    self.setGeometry(300, 300, 350, 300)
    self.setWindowTitle('Control Center')
    self.show()

  def up_camera_callback(self, data):
    '''This function gets called by an external thread'''
    try:
      image = QtGui.QImage(data.data, data.width, data.height, QtGui.QImage.Format_RGB888)
      self.up_camera_signal.emit(image)

    except Exception, e:
      print(e)

if __name__ == "__main__":
  app = QtGui.QApplication(sys.argv)
  ex = ControlCenter()
  sys.exit(app.exec_())