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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 15:35:23