PyQt5秒表程序点击启动按钮后崩溃求助
PyQt5秒表程序点击启动后崩溃的解决方案
问题描述
我从视频获取了一段实现Start、Stop、Reset、Lap功能的秒表代码,但每次点击启动按钮,鼠标显示加载圈后程序就自行关闭。代码如下:
import time import thread6 from PyQt5 import uic from PyQt5.QtWidgets import * class MyGUI(QMainWindow): def __init__(self): super(MyGUI, self).__init__() uic.loadUi("stopwatch.ui", self) self.show() self.running = False self.started = False self.passed = 0 self.previous_passed = 0 self.lap = 1 self.pushButton.clicked.connect(self.start_stop) self.pushButton_2.clicked.connect(self.lap_reset) self.label_2.setStyleSheet("border: 10px solid transparent") def start_stop(self): if self.running: self.running = False self.pushButton.setText("Resume") self.pushButton_2.setText("Reset") else: self.running = True self.pushButton.setText("Stop") self.pushButton_2.setText("Lap") self.pushButton_2.setEnabled(True) threading.Thread(target=self.stopwatch).start() def lap_reset(self): pass def format_time_string(self, time_passed): secs = time_passed % 60 mins = time_passed // 60 hours = mins // 60 return f"{int(hours):02d}:{int(mins):02d}:{int(secs):02d}:{int((self.passed % 1) * 100):02d}" def stopwatch(self): start = time.time() if self.started: until_now = self.passed else: until_now = 0 self.started = True while self.running: self.passed = time.time() - start + until_now self.label.setText(self.format_time_string(self.passed)) def main(): app = QApplication([]) window = MyGUI() app.exec_() if __name__ == "__main__": main()
问题原因及修复方案
1. 线程模块导入错误
代码中使用threading.Thread创建线程,但只导入了无用的thread6,未导入threading模块,运行时会触发NameError直接崩溃。
修复:删除import thread6,添加import threading。
2. 子线程直接操作UI元素
PyQt严格要求所有UI操作必须在主线程执行,子线程中调用self.label.setText()会触发线程安全冲突,导致程序崩溃。
修复:使用pyqtSignal定义信号,子线程通过信号通知主线程更新UI,避免跨线程操作UI。
3. 时间格式化逻辑错误
format_time_string函数参数为time_passed,但内部用self.passed计算毫秒部分,既逻辑混乱又可能引发线程竞争问题。
修复:统一使用传入的time_passed参数计算所有时间维度。
4. 循环无延时导致资源耗尽
while self.running循环没有任何延时,会占用100%CPU资源,引发程序卡顿甚至崩溃。
修复:在循环内添加time.sleep(0.01),控制每秒更新100次,平衡精度与资源占用。
修复后的完整代码
import time import threading from PyQt5 import uic from PyQt5.QtWidgets import * from PyQt5.QtCore import pyqtSignal class MyGUI(QMainWindow): # 定义更新UI的信号 update_time_signal = pyqtSignal(str) def __init__(self): super(MyGUI, self).__init__() uic.loadUi("stopwatch.ui", self) self.show() self.running = False self.started = False self.passed = 0 self.previous_passed = 0 self.lap = 1 self.pushButton.clicked.connect(self.start_stop) self.pushButton_2.clicked.connect(self.lap_reset) self.label_2.setStyleSheet("border: 10px solid transparent") # 绑定信号到UI更新槽函数 self.update_time_signal.connect(self.update_time_label) def start_stop(self): if self.running: self.running = False self.pushButton.setText("Resume") self.pushButton_2.setText("Reset") else: self.running = True self.pushButton.setText("Stop") self.pushButton_2.setText("Lap") self.pushButton_2.setEnabled(True) # 创建守护线程,程序退出时自动终止 threading.Thread(target=self.stopwatch, daemon=True).start() def lap_reset(self): if not self.running: # 重置功能 self.running = False self.started = False self.passed = 0 self.previous_passed = 0 self.lap = 1 self.pushButton.setText("Start") self.pushButton_2.setText("Lap") self.pushButton_2.setEnabled(False) self.update_time_signal.emit(self.format_time_string(0)) self.label_2.setText("") else: # 计次功能 lap_time = self.format_time_string(self.passed - self.previous_passed) self.label_2.setText(f"Lap {self.lap}: {lap_time}") self.previous_passed = self.passed self.lap += 1 def format_time_string(self, time_passed): hours = int(time_passed // 3600) mins = int((time_passed % 3600) // 60) secs = int(time_passed % 60) ms = int((time_passed % 1) * 100) return f"{hours:02d}:{mins:02d}:{secs:02d}:{ms:02d}" def update_time_label(self, time_str): # 主线程中安全更新UI self.label.setText(time_str) def stopwatch(self): start = time.time() until_now = self.passed if self.started else 0 self.started = True while self.running: current_time = time.time() - start + until_now self.passed = current_time # 通过信号传递时间字符串给主线程 self.update_time_signal.emit(self.format_time_string(current_time)) time.sleep(0.01) def main(): app = QApplication([]) window = MyGUI() app.exec_() if __name__ == "__main__": main()
额外优化说明
- 添加
daemon=True创建守护线程,确保程序退出时子线程自动终止,避免内存泄漏。 - 完善
lap_reset函数逻辑,实现了完整的Reset和Lap功能。 - 使用
pyqtSignal实现线程间安全通信,彻底解决UI操作的线程冲突问题。
内容的提问来源于stack exchange,提问作者user12485898
相关产品推荐
相关产品推荐

