QAxWidget实例调用QEventLoop::exec报错及程序挂起问题排查
问题解决方法
根因分析
你遇到的问题本质是嵌套事件循环的实例管理冲突:
- 首次调用
comm_connect()时启动第一个QEventLoop,_on_event_connect回调是在这个循环的运行上下文中触发的,此时第一个循环的exec()调用还未完全返回 - 你在回调内调用
unloop()后立刻触发新的请求,复用同一个QEventLoop实例调用第二次exec(),就会弹出"已经调用exec()"的错误 - 后续你改为每次调用loop时新建实例,但是未处理多层嵌套场景:如果在某一层的回调中再次发起请求开启新循环,旧的循环实例会被新实例覆盖,调用
unloop()时只能退出最新的循环,更早的循环永远收不到exit信号,就会导致程序无报错挂起
解决方案
改用栈结构管理所有嵌套的QEventLoop实例,每开一个新循环压入栈,退出时弹出栈顶的当前循环处理,修改OpenApi类的相关代码如下:
class OpenApi(QAxWidget): def __init__(self): super().__init__() self.setControl("KHOPENAPI.KHOpenAPICtrl.1") self.OnEventConnect.connect(self._on_event_connect) self.OnReceiveTrData.connect(self._on_receive_tr_data) # 用栈存储嵌套的事件循环实例 self._loop_stack = [] def loop(self): new_loop = QEventLoop() self._loop_stack.append(new_loop) new_loop.exec() def unloop(self): if not self._loop_stack: logger.info("no running loop, exit skipped") return current_loop = self._loop_stack.pop() if current_loop.isRunning(): logger.info("-->exit") current_loop.exit() else: logger.info("current loop already stopped, exit skipped") # 其余原有代码保持不变即可
额外优化建议
- 避免在回调函数中直接发起新的阻塞请求,可通过
QTimer.singleShot(0, 目标函数)将请求逻辑投递到主事件队列,等待当前回调上下文完全释放后再执行,减少嵌套层级 - 你使用的是Kiwoom证券开放API,该OCX本身有严格的接口调用频率限制,短时间内连续发起请求会被接口静默拦截,也会导致事件循环无响应挂起,建议添加请求间隔控制,每次请求间隔至少1秒以上
内容的提问来源于stack exchange,提问作者DWONH
相关产品推荐
相关产品推荐

