PyQt5 Dialog调用self.close()后while循环仍运行解决方法
问题根源
循环无法随窗口关闭终止,是三处核心写法错误导致的:
run()方法内的while True是同步阻塞逻辑,直接占住Qt主线程,包括窗口关闭在内的所有UI事件都无法被正常分发处理- 原
closeEvent方法内直接调用self.close()会递归触发关闭事件,逻辑完全错误,根本不会执行到终止循环的分支 - 按Q退出的判断分支里
SettingsWindow.close只是获取了类方法的引用,没有加括号实际调用,就算执行到该行也不会产生任何效果
修复方案
核心是给循环增加可控的终止标记,同时给Qt事件循环留出处理UI操作的时间:
- 给
SettingsWindow增加实例属性self.running作为循环开关,初始化时设为True - 重写
closeEvent逻辑:窗口触发关闭时先把self.running设为False,再正常接收关闭事件,禁止递归调用self.close() - 将原
while True死循环改为判断self.running状态,标记为False时自动退出循环 - 循环内增加
QApplication.processEvents()调用,给Qt留出处理滑块拖动、窗口点击、关闭请求等事件的时间,避免UI完全卡死 - 修正按Q退出的逻辑,触发时直接修改运行标记即可,不需要直接调用窗口关闭方法
- 循环退出后主动销毁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
相关产品推荐
相关产品推荐

