I am trying run a gif animation by utilizing QThread. I am using QThreading becuase I have a progress bar (not shown), code (not shown), and animations. Threading allows me to run each process synchronously without interfering with each other.
The code is broken into three scripts (run_gui.py, gui_test.py, test.py). I am using @property and @.setter to allow myself to call the animation in my test.py script to easily start, stop, and set different anamations when I want to. Using this three script method also allows me to reuse the code in other applications.
My .ui is a simple design. It has an ok
and cancel
button to start and exit the app. It also has a label named “animation_label” where the loaded .gif should be displayed.
The tool runs with no errors, but the .gif loops indefinetly. It does not appear that the self._movie.finished.connect(self.restart_movie)
is executing. What am I missing?
run_gui.py
import sys
from PyQt5 import uic
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel
from PyQt5.QtGui import QMovie
from gui import gui_test
def main():
app = QApplication(sys.argv)
UI()
app.exec()
class AnimationThread(QThread):
started = pyqtSignal()
finished = pyqtSignal()
setAnimation = pyqtSignal(QMovie)
def __init__(self, label: QLabel, parent=None):
super().__init__(parent)
self._label = label
self._movie_parms = None
self._movie = None
self._loop = 1
self._current_loop = 0
def restart_movie(self):
print("function running")
"""Restart the GIF when it finishes and increase current loop by 1."""
if self._current_loop < self._loop:
self._movie.stop()
else:
self._movie.start()
self._current_loop += 1
def run(self):
self.started.emit()
self.finished.emit()
if self._movie:
self._movie.start()
self._movie.finished.connect(self.restart_movie)
@property
def animation(self):
return self._movie_parms
@animation.setter
def animation(self, value):
movie_path, loop = value
self._loop = loop
self._movie = QMovie(movie_path)
self._label.setMovie(self._movie)
self.setAnimation.emit(self._movie)
self.run()
class WorkerThread(QThread):
# This is the thread that each tool's main code will use
started = pyqtSignal()
finished = pyqtSignal()
def __init__(self, function, args):
super().__init__()
self.function = function
self.args = args
def run(self):
self.function(self.args)
def start(self):
self.started.emit()
def finish(self):
self.finished.emit()
class UI(QMainWindow):
def __init__(self):
super(UI, self).__init__()
# Load the gui from its ui file.
uic.loadUi("./animation.ui", self)
if not QApplication.instance():
app = QApplication(sys.argv)
else:
app = QApplication.instance()
app.setQuitOnLastWindowClosed(True)
# Connect up the buttons.
gui_test.setup_buttons(self)
# Show the application GUI.
self.show()
if __name__ == '__main__':
main()
gui_test.py
import os
import sys
from PyQt5.QtCore import QThread
from PyQt5.QtWidgets import QLabel
sys.path.append(
os.path.dirname(
os.path.dirname(
os.path.abspath(__file__)))
)
from test import main_testing
import test_run_gui
def run_main(args):
try:
main_testing(*args)
except Exception as e:
print(e)
def setup_buttons(ui):
def configure_setup():
ui.ok_cancel.accepted.connect(operation_run_test)
ui.ok_cancel.rejected.connect(close)
def close():
sys.exit()
def operation_run_test():
label = ui.findChild(QLabel, "animation_label")
label.setFixedSize(800, 600)
label.setScaledContents(True)
# Create the AnimationThread but do not start it here
animation = test_run_gui.AnimationThread(label)
animation.run()
args = (animation,)
# Pass the animation thread instance to the worker thread
ui.thread = QThread()
ui.worker = test_run_gui.WorkerThread(run_main, args)
ui.worker.moveToThread(ui.thread)
ui.thread.started.connect(ui.worker.run)
ui.worker.finished.connect(ui.thread.quit)
ui.thread.start()
configure_setup()
test.py
def main_testing(animation):
animation.animation = ['./animations/test.gif', 1]
6