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

QAxWidget实例调用QEventLoop::exec报错及程序挂起问题排查

问题解决方法

根因分析

你遇到的问题本质是嵌套事件循环的实例管理冲突:

  1. 首次调用comm_connect()时启动第一个QEventLoop,_on_event_connect回调是在这个循环的运行上下文中触发的,此时第一个循环的exec()调用还未完全返回
  2. 你在回调内调用unloop()后立刻触发新的请求,复用同一个QEventLoop实例调用第二次exec(),就会弹出"已经调用exec()"的错误
  3. 后续你改为每次调用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.09 19:21:01