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

PyQt5 Dialog调用self.close()后while循环仍运行解决方法

问题根源

循环无法随窗口关闭终止,是三处核心写法错误导致的:

  • run()方法内的while True是同步阻塞逻辑,直接占住Qt主线程,包括窗口关闭在内的所有UI事件都无法被正常分发处理
  • 原closeEvent方法内直接调用self.close()会递归触发关闭事件,逻辑完全错误,根本不会执行到终止循环的分支
  • 按Q退出的判断分支里SettingsWindow.close只是获取了类方法的引用,没有加括号实际调用,就算执行到该行也不会产生任何效果

修复方案

核心是给循环增加可控的终止标记,同时给Qt事件循环留出处理UI操作的时间:

  1. 给SettingsWindow增加实例属性self.running作为循环开关,初始化时设为True
  2. 重写closeEvent逻辑:窗口触发关闭时先把self.running设为False,再正常接收关闭事件,禁止递归调用self.close()
  3. 将原while True死循环改为判断self.running状态,标记为False时自动退出循环
  4. 循环内增加QApplication.processEvents()调用,给Qt留出处理滑块拖动、窗口点击、关闭请求等事件的时间,避免UI完全卡死
  5. 修正按Q退出的逻辑,触发时直接修改运行标记即可,不需要直接调用窗口关闭方法
  6. 循环退出后主动销毁OpenCV打开的所有窗口,避免进程残留

修正后可运行代码
import sys
import cv2
from PyQt5.QtWidgets import QMainWindow, QDialog, QApplication
# 代码中用到的全局常量B_H_MIN/B_H_MAX/B_S_MIN/B_S_MAX/R_H_MIN/R_H_MAX/gtarget/CROP_SIZE请提前定义

class UI(QMainWindow):
    def __init__(self):
        super(UI, self).__init__()
        #xxxxxx MORE CODE xxxxx
        uic.loadUi("BTS.ui", self)
        self.action_Open_Settings.triggered.connect(lambda: self.open_settings_window('BAS'))
        self.show()
        
    def open_settings_window(self, target):
        s_window = SettingsWindow(target)
        s_window.exec()
    

class SettingsWindow(QDialog):
    def __init__(self, target):
        super(SettingsWindow,self).__init__()
        uic.loadUi('set.ui', self)
        self.setWindowTitle(target)
        # 初始化循环运行标记
        self.running = True

        # 加载对应配置
        if target == 'BAS':
            self.H_MIN = B_H_MIN
            self.H_MAX = B_H_MAX
            self.S_MIN = B_S_MIN
            self.S_MAX = B_S_MAX
            self.Area_Value_Text.setEnabled(False)
            self.Peri_Value_Text.setEnabled(False)
        else:  
            self.H_MIN = R_H_MIN
            self.H_MAX = R_H_MAX
            
        self.show()
        self.run(target)

    def run(self, target):
        global gtarget
        global CROP_SIZE

        ref_image = "./set_ref.jpg"
        img = cv2.imread(ref_image)

        # 用标记控制循环,替代死循环
        while self.running:
            print('running')
            frame = cv2.resize(img, (1280, 720))
            h_min = self.horizontalSlider_Hue_Min.value()
            h_max = self.horizontalSlider_Hue_Max.value()

            #******** 原有OpenCV图像处理逻辑保持不变 *************

            # 让出主线程时间片处理UI事件
            QApplication.processEvents()

            # 按Q键时主动终止循环
            if cv2.waitKey(1) & 0xFF == ord('q'):
                self.running = False
                break
        
        # 循环退出后清理OpenCV窗口
        cv2.destroyAllWindows()

    def closeEvent(self, event):
        # 窗口关闭时首先终止循环
        self.running = False
        # 正常响应关闭事件
        event.accept()
        
if __name__ == "__main__":
    app = QApplication(sys.argv)
    UIWindow = UI()
    app.exec_()

可选优化

如果后续OpenCV图像处理逻辑单帧耗时超过50ms,上述processEvents的写法还是可能造成UI卡顿,建议将OpenCV检测逻辑放到QThread子线程中运行,通过Qt信号槽和主线程UI做数据交互,从根本上避免阻塞主事件循环。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 23:57:35