Issue
I have a loading GIF, and I want to show it as a loading screen till a specific method is finished. My problem is that the GIF won't keep running because there is another function working, and that GIF should keep running till that function is done.
import sys
import time
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QMovie
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow
def wait_till_function_done():
time.sleep(5)
class Window(QMainWindow):
def __init__(self):
super().__init__()
class Splash:
def showSplash(self):
label = QLabel()
label.setWindowFlag(Qt.SplashScreen)
# label for displaying GIF
self.movie = QMovie(".../Loading.gif")
label.setMovie(self.movie)
self.movie.start()
label.show()
# -------
# Simulate the operation that is talking about.
# The GIF should keep working and playing till the following simulation is done.
# -------
# But The GIF stop running and that is because the following line block Qt event
wait_function_is_done() # this line simulate this sentence: "keep showing loading screen till the operation is done"
if __name__ == "__main__":
app = QApplication(sys.argv)
main_window = Window()
splash_obj = Splash()
# start the Splash screen
splash_obj.showSplash()
# when it's done, it will be closed and main_app showen
main_window.show()
app.exec_()
I've tried to use QThread
with the loading gif, so it will change the gif frames while that method/operation is running (and should be closed when that function is done). But that didn't work out:
- The frames are not changing. (I can use
app.processEvents()
so the frames can be changed). - It is stuck in the while loop.
class GIFThread(QThread):
signal = pyqtSignal()
def __init__(self):
super(GIFThread, self).__init__()
class Splash:
def showSplash(self):
.....
# init thread
thread = GIFThread()
thread.signal.connect(self.change_gif_frame)
thread.start()
# start changing GIF frames
thread.signal.emit()
# -------
# Simulate the operation.
time.sleep(5)
# this should stop the thread and break the while loop
thread.quit()
def change_gif_frame(self):
while True:
self.movie.jumpToNextFrame()
Solution
It's probably best to use QSplashScreen for this, and use QThread to run the app initialisation in the background. If necessary, the splash-screen can be updated with progress messages via a custom signal. Here is a basic demo that shows how to implement that:
import sys
from PyQt5.QtCore import pyqtSignal, Qt, QThread
from PyQt5.QtGui import QMovie
from PyQt5.QtWidgets import QApplication, QSplashScreen, QMainWindow
class Worker(QThread):
progressChanged = pyqtSignal(int)
def run(self):
for count in range(6):
self.progressChanged.emit(count)
self.sleep(1)
self.progressChanged.emit(-1)
class Window(QMainWindow):
def __init__(self):
super().__init__()
class SplashScreen(QSplashScreen):
def __init__(self, filepath, flags=0):
super().__init__(flags=Qt.WindowFlags(flags))
self.movie = QMovie(filepath, parent=self)
self.movie.frameChanged.connect(self.handleFrameChange)
self.movie.start()
def updateProgress(self, count=0):
if count == 0:
message = 'Starting...'
elif count > 0:
message = f'Processing... {count}'
else:
message = 'Finished!'
self.showMessage(
message, Qt.AlignHCenter | Qt.AlignBottom, Qt.white)
def handleFrameChange(self):
pixmap = self.movie.currentPixmap()
self.setPixmap(pixmap)
self.setMask(pixmap.mask())
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
splash = SplashScreen('anim.gif', Qt.WindowStaysOnTopHint)
worker = Worker()
worker.progressChanged.connect(splash.updateProgress)
worker.finished.connect(
lambda: (splash.finish(window), window.show()))
splash.show()
worker.start()
app.exec_()
Answered By - ekhumoro
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.