0

I'm working on terminal based PyQt application using Windows OCX api. And I'm having trouble over implement(calling exec) QEventLoop in QAxWidget instance.

If I call exec() in main QApplication main loop, everthing is fine. But, calling exec() in instance of QAxWidget which is instantiated in main loop, it does not work as expected.

When exec() called(_on_event_connect in code), error message showed up like

"QEventLoop:exec: instance 0x18479d8 has already called exec()"

And on the moment of calling exit(), QEventLoop seems not running. And the application is hang after this.

The below is simplified example of my code, It tries call QAxWidget method "dynamicCall" as soon as log-in process finished. It consists of pyQt signal/slot mechanism, get event from API server. Please understand that you can not execute this code because of it needs specific APIs installation and registered user-id. But I hope you can see the points of problem.

main.py

import sys
from PyQt5.QtWidgets import QApplication

from api import OpenApi

class Main():
    def __init__(self):

    self.api = OpenApi()
    self.api.comm_connect()
    # self.api.req_basic_stock_info("035420") -> if I call here, works well

if __name__ == "__main__":
    app = QApplication(sys.argv)
    main = Main()
    sys.exit(app.exec_())

api.py

import sys

from PyQt5.QtWidgets import *
from PyQt5.QAxContainer import *
from PyQt5.QtCore import *

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._qloop = QEventLoop()

    def loop(self):
        if not self._qloop.isRunning():
            logger.info(f"-->exec")
            self._qloop.exec()
        else:
            logger.info("exec skipped")

    def unloop(self):
        if self._qloop.isRunning():
            logger.info("-->exit")
            self._qloop.exit()
        else:
            logger.info(f"exit skipped")

    def comm_connect(self):
        self.dynamicCall("CommConnect()")
        self.loop()

    def comm_rq_data(self, rqname, trcode, next, screen_no):
        self.dynamicCall("CommRqData(QString, QString, int, QString)", rqname, trcode, next, screen_no)
        self.loop()

    def _on_event_connect(self, err_code):
        if err_code == 0:
            logger.info("logged in")
        else:
            logger.info("log-in failed")
        self.unloop()

        # If I call this function here, QEventLoop fails
        
        self.req_basic_stock_info("000660")    

    def _on_receive_tr_data(self, screen_no, rqname, trcode, record_name, next,
                        unused1, unused2, unused3, unused4):
        logger.info(f"OnReceivTrData: {rqname}")
        self.unloop()

    def req_basic_stock_info(self, code):
        # I want to call this function inside instance on certain condition.
        self.set_input_value("item", code)
        scrno = "2000"
        self.comm_rq_data("ITEM_INFO_REQ", "opt10080", "0", scrno)

And output is like below.

12:42:53,54 test INFO -->exec

12:42:58,961 test INFO logged in

12:42:58,962 test INFO -->exit

12:42:58,977 test INFO -->exec

QEventLoop::exec: instance 0x35c34a0 has already called exec()

12:42:59,33 test INFO OnReceivTrData:ITEM_INFO_REQ

12:42:59,35 test INFO exit skipped

As eyllanesc told me, I changed functions related assign/deassign QEventLoop like below. And it works.

def loop(self):
    self._qloop = QEventLoop()
    self._qloop.exec()

def unloop(self):
    try:
        self._qloop.exit()
    except AttributeError as e:
        logger.error(e)

update

I'm afraid this issue has not been solved yet. It was successful when req_basic_stock_info function is called on _on_event_connect event, But I call it at another event function, it hang again without any error message. There is no difference in calling mechanism.

DWONH
  • 1
  • 3

1 Answers1

1

Calling the exit() function does not imply that the QEventLoop can be reused, therefore the error message is signaled. In this case it is better to use a new QEventLoop:

def unloop(self):
    if self._qloop.isRunning():
        logger.info("-->exit")
        self._qloop.exit()
        self._qloop = QEventLoop()
    else:
        logger.info(f"exit skipped")
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Great! After I changed loop() function to declare new QEventLoop, It works. But one question remained, why it didn't matter when it called in main loop(main.py)? – DWONH Aug 05 '21 at 07:16
  • @DWONH Is that the event loop does not start automatically but starts using the main eventloop (in your case QApplication), so in the case of "main.py" even that evenloop did not start so it did not cause that problem. – eyllanesc Aug 05 '21 at 07:21
  • It was too early to judge it as solved, unfortunately eventloop is hang again on another call, though first call after login event was successful. I have no idea what makes difference. – DWONH Aug 05 '21 at 11:01