How can I fix issues with my PyQt5 splash screen implementation?
I am working on a project that requires a splash screen displaying a GIF file while loading menus. Each menu can have a different number of items, and the time needed to load each menu can vary. I want to include two progress bars: one tracking the progress of the menu loading and one displaying the overall progress of the entire process. The first progress bar should clear its value after a time delay, even when the menu loading completed before that time. The splash screen should only close after the second progress bar reaches 100%.
Currently, my implementation has the following problems:
Here is the code I am using:
import sys
import time
from PyQt5.QtWidgets import QApplication, QWidget, QDialog, QProgressBar, QMainWindow, QVBoxLayout
from PyQt5.QtCore import Qt, QTimer, QEventLoop, QCoreApplication
class SplashScreen(QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle('Splash Screen Example')
self.setFixedSize(800, 400)
self.setWindowFlag(Qt.FramelessWindowHint)
self.setWindowFlag(Qt.WindowStaysOnTopHint)
self.initUI()
def initUI(self):
layout = QVBoxLayout(self)
self.progressBarMenu = QProgressBar()
self.progressBarProcess = QProgressBar()
layout.addWidget(self.progressBarMenu)
layout.addWidget(self.progressBarProcess)
self.setStyleSheet('''
QProgressBar {
background-color: #DA7B93;
color: rgb(200, 200, 200);
border-style: none;
border-radius: 10px;
text-align: center;
font-size: 20px;
}
QProgressBar::chunk {
border-radius: 10px;
background-color: qlineargradient(spread:pad x1:0, x2:1, y1:0.511364, y2:0.523, stop:0 #1C3334, stop:1 #376E6F);
}
''')
class MyApp(QMainWindow):
def __init__(self):
super().__init__()
self.valueMenu = 0
self.valueProcess = 0
self.window_width, self.window_height = 800, 600
self.setMinimumSize(self.window_width, self.window_height)
self.splash_screen = SplashScreen()
self.setCentralWidget(self.splash_screen)
self.menu_count = 3
self.current_menu = 1
self.timerMenu = QTimer()
self.timerMenu.timeout.connect(self.load_next_menu)
self.timerMenu.start(1000)
self.timerProcess = QTimer()
self.timerProcess.timeout.connect(self.update_process_bar)
self.timerProcess.start(1000)
self.start_time = time.time()
self.estimated_time = 10 # Estimated time in seconds for each menu
self.remaining_time = self.estimated_time
self.overall_progress = 0
self.is_menu_loaded = False
def load_next_menu(self):
self.valueMenu = 0
if self.current_menu == 1:
self.load_menu_one()
elif self.current_menu == 2:
self.load_menu_two()
elif self.current_menu == 3:
self.load_menu_three()
self.update_menu_progress(self.valueMenu)
self.current_menu += 1
if self.current_menu > self.menu_count:
self.timerMenu.stop()
self.update_menu_progress(100)
self.is_menu_loaded = True
def load_menu_one(self):
menu_one_items = 10 # Number of items in menu one
for i in range(menu_one_items):
time.sleep(0.5)
self.valueMenu += int(100 / menu_one_items)
self.update_menu_progress(self.valueMenu)
QCoreApplication.processEvents()
def load_menu_two(self):
menu_two_items = 4 # Number of items in menu two
for i in range(menu_two_items):
time.sleep(0.5)
self.valueMenu += int(100 / menu_two_items)
self.update_menu_progress(self.valueMenu)
QCoreApplication.processEvents()
def load_menu_three(self):
menu_three_items = 40 # Number of items in menu three
for i in range(menu_three_items):
time.sleep(0.5)
self.valueMenu += int(100 / menu_three_items)
self.update_menu_progress(self.valueMenu)
QCoreApplication.processEvents()
def update_menu_progress(self, value):
self.splash_screen.progressBarMenu.setValue(value)
def update_process_bar(self):
elapsed_time = int(time.time() - self.start_time)
self.remaining_time = self.estimated_time - elapsed_time
if self.current_menu > self.menu_count:
self.overall_progress = 100
else:
self.overall_progress = int((self.current_menu - 1) / self.menu_count * 100)
if self.remaining_time <= 0:
self.remaining_time = 0
self.timerProcess.stop()
self.valueProcess = int(self.overall_progress + (self.valueMenu / self.menu_count))
self.splash_screen.progressBarProcess.setValue(self.valueProcess)
# print(f"Elapsed Time: {elapsed_time}s")
# print(f"Estimated Time: {self.estimated_time}s")
# print(f"Remaining Time: {self.remaining_time}s")
if self.current_menu > self.menu_count and self.is_menu_loaded:
self.timerProcess.stop()
self.splash_screen.close()
self.show_main_window()
def show_main_window(self):
pass
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec_())
There are several issues with your implementation.
time.sleep
to simulate loading);overall_progress
will always be 100 even if loading is not completed, which is exactly your case: since you are loading 10+4+40 items with a basic 0.5s delay for each item, resulting in 27 seconds;The above means that:
So, the "simple" solution to your issue is to not stop the timerProcess
timer before the loading has finished.
Unfortunately, this is not the least of your problems, as your implementation has many other important issues.
The following are strictly related to the issue at hand:
self.valueMenu
addition is unreliable and imprecise, since it's using int()
: a more appropriate solution would be to always use floating values for the addition, and set the progress bar value using round()
;These are not that relevant to the specific problem, but are still quite important:
Qt.SplashScreen
window flag, or, if you want the "splash screen" to be "embedded" in the window, set it as a child and outside of any layout, and eventually update its geometry in that window's resizeEvent()
(see this related answer);