PyQt5多页面切换多次后程序卡顿问题求解
Hey, let's tackle this performance issue you're seeing with page switches. The root cause here is that every time you switch pages, you're calling setupUi() which recreates all the UI widgets from scratch. Over time, this leads to unnecessary memory overhead, widget creation/destruction cycles, and eventually slowdowns.
Here's a targeted optimization plan using Qt's built-in QStackedWidget (designed exactly for multi-page apps) to avoid recreating widgets every time:
Step 1: Refactor UI Classes to be Reusable Widgets
Instead of inheriting from QObject, make your UI classes inherit from QWidget so they can be treated as self-contained page components. We'll also initialize widgets once in the constructor, not on every page switch.
Modified main.py:
from PyQt5.QtCore import * from PyQt5.QtWidgets import * class Ui_MainWindow(QWidget): settingSignal = pyqtSignal() position = 3 # Use instance variable instead of global def __init__(self, parent=None): super().__init__(parent) self.setupUi() # Initialize widgets once on creation def setupUi(self): self.setObjectName("MainWindowPage") self.frame = QFrame(self) self.frame.setObjectName("frame") self.frame.setGeometry(QRect(0, 0, 1920, 1080)) self.frame.setStyleSheet("background-color: rgb(255, 255, 255);") self.frame.setFrameShape(QFrame.StyledPanel) self.frame.setFrameShadow(QFrame.Raised) self.optical = QLabel(self.frame) self.upgrade = QLabel(self.frame) self.menu = QLabel(self.frame) self.calibration = QLabel(self.frame) self.media = QLabel(self.frame) self.imageChange(self.position) QMetaObject.connectSlotsByName(self) def imageChange(self, Pos): self.menu.setGeometry(QRect(160, 410, 200, 200)) self.upgrade.setGeometry(QRect(510, 410, 200, 200)) self.optical.setGeometry(QRect(860, 410, 200, 200)) self.calibration.setGeometry(QRect(1210, 410, 200, 200)) self.media.setGeometry(QRect(1520, 410, 200, 200)) self.optical.setStyleSheet("background-color: rgb(0, 0, 0);") self.calibration.setStyleSheet("background-color: rgb(0, 0, 0);") self.menu.setStyleSheet("background-color: rgb(0, 0, 0);") self.media.setStyleSheet("background-color: rgb(0, 0, 0);") self.upgrade.setStyleSheet("background-color: rgb(0, 0, 0);") if Pos == 1: self.menu.setGeometry(QRect(110, 360, 300, 300)) self.menu.setStyleSheet("background-color: rgb(255, 0, 255);") elif Pos == 2: self.upgrade.setGeometry(QRect(460, 360, 300, 300)) self.upgrade.setStyleSheet("background-color: rgb(255, 0, 255);") elif Pos == 3: self.optical.setGeometry(QRect(810, 360, 300, 300)) self.optical.setStyleSheet("background-color: rgb(255, 0, 255);") elif Pos == 4: self.calibration.setGeometry(QRect(1160, 360, 300, 300)) self.calibration.setStyleSheet("background-color: rgb(255, 0, 255);") elif Pos == 5: self.media.setGeometry(QRect(1470, 360, 300, 300)) self.media.setStyleSheet("background-color: rgb(255, 0, 255);") def Control(self, key): if key == "LEFT": if self.position > 1: self.position -= 1 else: self.position = 1 self.imageChange(self.position) elif key == "RIGHT": if self.position < 5: self.position += 1 else: self.position = 5 self.imageChange(self.position) elif key == "OK": if self.position == 1: self.settingSignal.emit()
Modified setting.py:
from PyQt5.QtCore import * from PyQt5.QtWidgets import * class Ui_SettingWindow(QWidget): mainSignal = pyqtSignal() def __init__(self, parent=None): super().__init__(parent) self.setupUi() def setupUi(self): self.setObjectName("SettingWindowPage") self.centralwidget = QWidget(self) self.centralwidget.setObjectName("centralwidget") self.frame = QFrame(self.centralwidget) self.frame.setObjectName("frame") self.frame.setGeometry(QRect(0, 0, 1920, 1080)) self.frame.setStyleSheet("background-color: rgb(255, 0, 255);") self.frame.setFrameShape(QFrame.StyledPanel) self.frame.setFrameShadow(QFrame.Raised) QMetaObject.connectSlotsByName(self) def Control(self, key): if key == "BACK": self.mainSignal.emit()
Step 2: Use QStackedWidget for Page Switching
QStackedWidget is Qt's native way to manage multiple pages without recreating widgets. It keeps all pages in memory and only shows the active one, eliminating the need to call setupUi() repeatedly.
Modified controller.py:
import sys from PyQt5 import QtCore from PyQt5.QtWidgets import QApplication, QMainWindow, QStackedWidget from ui import main, setting class InitWindow(QMainWindow): keyPressed = QtCore.pyqtSignal(QtCore.QEvent) def __init__(self, parent=None): super(InitWindow, self).__init__(parent) self.setWindowTitle("Multi-Page App") self.setGeometry(0, 0, 1920, 1080) # Create stacked widget to manage pages self.stacked_widget = QStackedWidget(self) self.setCentralWidget(self.stacked_widget) # Initialize both pages ONCE (no more repeated setupUi calls) self.main_page = main.Ui_MainWindow() self.setting_page = setting.Ui_SettingWindow() # Add pages to the stack self.stacked_widget.addWidget(self.main_page) self.stacked_widget.addWidget(self.setting_page) # Connect page switch signals self.main_page.settingSignal.connect(self.switch_to_setting) self.setting_page.mainSignal.connect(self.switch_to_main) self.keyPressed.connect(self.on_key) def switch_to_setting(self): # Simply switch to the existing setting page self.stacked_widget.setCurrentWidget(self.setting_page) def switch_to_main(self): # Simply switch back to the existing main page self.stacked_widget.setCurrentWidget(self.main_page) def keyPressEvent(self, event): super(InitWindow, self).keyPressEvent(event) self.keyPressed.emit(event) def on_key(self, event): remote_key = "" if event.key() == QtCore.Qt.Key_Left: remote_key = "LEFT" elif event.key() == QtCore.Qt.Key_Right: remote_key = "RIGHT" elif event.key() == QtCore.Qt.Key_Return: remote_key = "OK" elif event.key() == QtCore.Qt.Key_Backspace: remote_key = "BACK" # Forward key events to the active page current_page = self.stacked_widget.currentWidget() if current_page == self.main_page: self.main_page.Control(remote_key) elif current_page == self.setting_page: self.setting_page.Control(remote_key) if __name__ == '__main__': app = QApplication(sys.argv) w = InitWindow() w.showFullScreen() sys.exit(app.exec_())
Key Optimizations Explained
- Single Initialization: Each page's widgets are created only once when the app starts, not on every page switch. This eliminates repeated widget creation/destruction overhead.
- QStackedWidget Efficiency: Qt's
QStackedWidgetis optimized for page switching – it only renders the active page and keeps inactive pages in memory without consuming unnecessary resources. - Removed Global Variables: Replaced global
positionandPageIsNowwith instance variables andQStackedWidget's native state tracking, making the code more maintainable and thread-safe. - Simplified Logic: Page switching is now just a matter of changing the active widget in the stack, no more complex setup/teardown code.
Extra Performance Tips
- If your pages contain resource-heavy widgets (like videos, charts, or large datasets), add
showEventandhideEventhandlers to pause/resume resource usage when pages are switched (e.g., pause video playback when the page is hidden). - Avoid running long-running tasks in the UI thread – use
QThreadto offload work and keep the UI responsive.
内容的提问来源于stack exchange,提问作者Mohammad Farahi




