You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复PyQt5启动屏(Splash Screen)实现中的异常问题?

PyQt5启动屏双进度条问题修复

需求背景

开发项目时需实现带GIF的PyQt5启动屏,包含两个进度条:

  • 单个菜单加载进度条:跟踪当前菜单加载进度,菜单加载完成后延迟清零
  • 整体加载进度条:显示全部菜单加载的整体进度,启动屏需在整体进度到100%后关闭

当前存在的问题

  • 整体进度条在加载完第一个菜单后停留在33%,后续菜单加载时不再更新
  • 第三个菜单加载时,单菜单进度条仅到78%,启动屏就提前关闭了

原代码

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_())

问题原因及修复方案

问题1:整体进度条不更新

原因:update_process_bar中仅用current_menu-1计算已完成菜单占比,但current_menu仅在整个菜单加载完成后才+1,导致加载后续菜单时整体进度停滞。
修复:

  • 整体进度改为:(已完成菜单数 + 当前菜单进度占比) / 总菜单数 * 100,实时反映加载状态
  • 移除错误的valueProcess叠加逻辑,直接使用修正后的整体进度值

问题2:第三个菜单进度条未到100%就关闭

原因:

  1. 40个条目用int(100/40)取整后每次加2,循环39次仅累加78,未到100
  2. 菜单加载完成后直接触发启动屏关闭,无进度条延迟清零逻辑
    修复:
  • 单菜单进度改用(i+1)/条目数*100浮点计算后取整,避免累加误差
  • 菜单加载完成后强制设为100%,再用QTimer延迟1秒清零
  • 调整启动屏关闭条件,确保整体进度到100%后再关闭

修改后的完整代码

import sys
import time
from PyQt5.QtWidgets import QApplication, QDialog, QProgressBar, QMainWindow, QVBoxLayout
from PyQt5.QtCore import Qt, QTimer, 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.all_menus_loaded = False
        self.menu_reset_timer = QTimer()
        self.menu_reset_timer.setSingleShot(True)
        self.menu_reset_timer.timeout.connect(self.reset_menu_progress)

        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(50)  # 提高刷新频率,让进度条更流畅

    def load_next_menu(self):
        self.valueMenu = 0
        self.menu_reset_timer.stop()

        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()

        # 菜单加载完成后强制设为100%,延迟1秒清零
        self.update_menu_progress(100)
        self.menu_reset_timer.start(1000)

        self.current_menu += 1
        if self.current_menu > self.menu_count:
            self.timerMenu.stop()
            self.all_menus_loaded = True

    def load_menu_one(self):
        menu_one_items = 10
        for i in range(menu_one_items):
            time.sleep(0.5)
            self.valueMenu = int((i + 1) / menu_one_items * 100)
            self.update_menu_progress(self.valueMenu)
            QCoreApplication.processEvents()

    def load_menu_two(self):
        menu_two_items = 4
        for i in range(menu_two_items):
            time.sleep(0.5)
            self.valueMenu = int((i + 1) / menu_two_items * 100)
            self.update_menu_progress(self.valueMenu)
            QCoreApplication.processEvents()

    def load_menu_three(self):
        menu_three_items = 40
        for i in range(menu_three_items):
            time.sleep(0.5)
            self.valueMenu = int((i + 1) / menu_three_items * 100)
            self.update_menu_progress(self.valueMenu)
            QCoreApplication.processEvents()

    def update_menu_progress(self, value):
        self.splash_screen.progressBarMenu.setValue(value)

    def reset_menu_progress(self):
        self.valueMenu = 0
        self.update_menu_progress(self.valueMenu)

    def update_process_bar(self):
        if self.all_menus_loaded:
            self.valueProcess = 100
        else:
            completed_menus = self.current_menu - 1
            current_menu_progress = self.valueMenu / 100
            self.valueProcess = int((completed_menus + current_menu_progress) / self.menu_count * 100)

        self.splash_screen.progressBarProcess.setValue(self.valueProcess)

        # 整体进度到100%且所有菜单加载完成后,延迟关闭启动屏
        if self.all_menus_loaded and self.valueProcess == 100:
            QTimer.singleShot(500, self.close_splash_and_show_main)

    def close_splash_and_show_main(self):
        self.timerProcess.stop()
        self.splash_screen.close()
        self.show_main_window()

    def show_main_window(self):
        self.setWindowTitle('Main Window')
        self.show()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MyApp()
    window.show()
    sys.exit(app.exec_())

关键改进点

  • 修正整体进度计算逻辑,实时反映当前加载状态
  • 单菜单进度改用浮点计算,避免整数累加误差,加载完成后强制设为100%
  • 添加单菜单进度条延迟清零功能,符合需求
  • 调整启动屏关闭条件,确保整体进度到100%后再关闭,增加短暂延迟提升用户体验
  • 提高进度条刷新频率,让动画更流畅

内容的提问来源于stack exchange,提问作者Rithik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 03:52:03