Loading animated gif data in QMovie

2.4k views Asked by At

I'm very new to Qt (specifically PySide) and I'm trying write a script that loads an animated gif from file into a QByteArray and then into a QMovie. The reason for going from file to the QByteArray is because I cannot keep that gif file in memory. I want to be able to store the animated gif in such a way that it can be written out to a JSON file later (hence the QByteArray). I've tried using ekhumoro's answer from here and although no errors showed up, the animated gif also doesn't show up. (There could be something there but I don't see anything.) My code, in a nutshell, looks like this:

data = open("img.gif", "rb").read()
self.bArray = QtCore.QByteArray(data)
self.bBuffer = QtCore.QBuffer(self.bArray)
self.bBuffer.open(QtCore.QIODevice.ReadOnly)
self.movie = QtGui.QMovie(self.bBuffer, 'GIF')
self.movieLabel.setMovie(self.movie) # a QLabel
self.movie.start()

I want to store the contents of self.bArray to a JSON file later.

I can see the animated gif when I give the QMovie constructor the file path but then I won't be able to save the contents of the gif to a JSON file.

I'm wondering if the data is not being read in properly or not being passed to QMovie properly.

Any ideas?

Thanks!

2

There are 2 answers

4
ekhumoro On BEST ANSWER

This looks like a PySide bug, as the same code works perfectly fine in PyQt.

The bug seems to be in the QMovie constructor, which does not read anything from the device passed to it. A work-around is to set the device explicitly, like this:

import sys
from PySide import QtCore, QtGui
# from PyQt4 import QtCore, QtGui

app = QtGui.QApplication(sys.argv)

data = open('anim.gif', 'rb').read()
a = QtCore.QByteArray(data)
b = QtCore.QBuffer(a)

print('open: %s' % b.open(QtCore.QIODevice.ReadOnly))

m = QtGui.QMovie()
m.setFormat('GIF')
m.setDevice(b)

print('valid: %s' % m.isValid())

w = QtGui.QLabel()
w.setMovie(m)
m.start()

w.resize(500, 500)
w.show()
app.exec_()

print('pos: %s' % b.pos())
0
ba361006 On

it seems that the solution from ekhumoro is for PyQt4 and the gif will freeze at the first frame.

I just want to share the code that is able to work on Python3 and PyQt5 and to read a .py file that contains the base64 encoded .gif file so that this can even package image into a .exe file through pyinstaller.

here is the code, cheers!

from PyQt5 import QtWidgets
from PyQt5 import QtGui
from PyQt5 import QtCore

import base64

def cv2ImageToBase64String(variableName, imagePath, outputPath):
    """
    Parameters: imagePath [str]  -> path of image including file extension
                outputPath [str] -> path of .py file including file extension
    """    
    # read image as binary
    with open(imagePath, "rb") as image:
        data = image.read()

    # turn binary data into string by base64
    base64Image = base64.b64encode(data)

    # save it in a .py file with a variable called "image"
    with open(outputPath, "a") as f:
        f.write(f"{variableName}={base64Image}\n")


class Main(QtWidgets.QDialog):
    def __init__(self, dataInByte):
        # inherit and init QDialog
        super().__init__()

        # layout setting
        layout = QtWidgets.QVBoxLayout()
        label  = QtWidgets.QLabel()
        self.setLayout(layout)
        layout.addWidget(label)
        
        # decode and transform the data type
        rawData = base64.b64decode(dataInByte.decode())
        data    = QtCore.QByteArray(rawData)
        buffer  = QtCore.QBuffer(data)

        # it seems like a bug here that it shows nothing since we put buffer into QMovie like the example below
        # example: movie = QtGui.QMovie(buffer)
        movie = QtGui.QMovie()
        movie.setDevice(buffer)
        
        # start movie
        label.setMovie(movie)
        movie.start()
        self.exec_()

if __name__ == "__main__":
    # step 1: encode your gif by base64
    cv2ImageToBase64String(variableName="dataInByte", imagePath = "./src/Assets/loading.gif", outputPath = "./encodedGif.py")

    # step 2: load your gif from .py file
    from encodedGif import dataInByte

    # step 3: start the app
    app = QtWidgets.QApplication([])
    main = Main(dataInByte = dataInByte)