문제파악
주식 기본정보를 가져오기 위해선 로그인이 선행되어야 한다.
하지만 로그인하는데 잠깐의 시간이 걸리고, 그 시간보다 빨리 코드가 실행되면 오류가 발생한다.
sosoeasy.tistory.com/413에서는 로그인을 확인하는 이벤트 함수에 주식 기본정보를 요청하는 함수를 넣어서 이 문제를 해결했다.
이번 장에서는 QeventLoop를 통해 문제를 해결한다
QeventLoop란?
특정 시점에서 특정 조건이 발생해야지만 다음 코드를 진행할 수 있게 만들어 주는 함수이다.
다음과 같이 세가지 단계로 구분된다.
1. loop객체 생성
self.login_event_loop = QEventLoop()
2. 조건생성
self.login_event_loop.exit()
3. 코드진행 ( 2.에서 self.login_event_loop.exit() 가 실행되어야지만 다음 코드가 진행된다)
self.login_event_loop.exec_()
아래의 예시에서 각 단계별 역할을 자세히 알아본다.
실제예시
아래와 같이 두 함수가 있다.
self.login() # 로그인함수
self.getData() # 정보받아오는 함수
여기서 우리는 로그인이 확실히 성공한 후에 새로운 데이터를 받아오는 작업( self.getData() )을 수행해야한다.
1. 먼저 로그인과 관련된 이벤트루프를 생성한다. (1. loop객체 생성)
self.login_event_loop = QEventLoop()
2. 로그인 완료 시 실행되는 로그인 이벤트함수 맨 마지막줄에 2.조선생성문을 아래와 같이 넣는다
# 로그인 확인 이벤트
def myOnEventConnect(self, nErrCode):
if nErrCode == 0:
print('로그인 성공')
else:
print('로그인 실패')
self.login_event_loop.exit()
3. 그리고 아래와 같이 login함수에 3.코드진행을 넣는다.
def login(self):
# 로그인 요청함수
print("[로그인 요청]")
self.dynamicCall("CommConnect()")
self.login_event_loop.exec_() #login_event_loop.exit()가 실행되어야 끝남
그러면 로그인 확인 이벤트 함수(myOnEventConnect)가 끝나면서 self.login_event_loop.exit()가 실행되어야지만 login()함수가 끝난다. 즉 로그인이 되기 전까진 login()함수가 끝나지 않는 것이다.
따라서 self.login()함수 다음줄에 있는 self.getData()함수를 실행할때는 로그인이 되어있음을 보장받을 수 있다.
소스코드
import sys
from PyQt5.QAxContainer import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class KiwoomAPI(QAxWidget):
def __init__(self):
super().__init__()
# QAxWidget 객체 (키움 api연결)
# 키움api연동, 아래의 코드와 똑같음
# self.kiwoom = QAxWidget("KHOPENAPI.KHOpenAPICtrl.1")
self.setControl("KHOPENAPI.KHOpenAPICtrl.1")
self.login_event_loop = QEventLoop()
# 이벤트함수
self.OnEventConnect.connect(self.myOnEventConnect) # 로그인 이벤트
self.OnReceiveTrData.connect(self.myOnReceiveTrData) # 데이터요청 이벤트
# 작업함수
self.login()
self.getData()
# 로그인 확인 이벤트
def myOnEventConnect(self, nErrCode):
if nErrCode == 0:
print('로그인 성공')
else:
print('로그인 실패')
self.login_event_loop.exit()
# 기본정보 받기 이벤트
def myOnReceiveTrData(self, sScrNo, sRQName, sTrCode, sRecordName, sPreNext, nDataLength, sErrorCode, sMessage, sSplmMsg):
print("데이터 받기 시작")
# sScrNo(화면번호), sRQName(사용자구분), sTrCode(Tran명), sRecordName(레코드명), sPreNext(연속조회 유무) , 나머지 4개는 현재버전에서 사용x
if sRQName == 'test_opt10001':
name = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, 0, "종목명")
volume = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, 0, "거래량")
price = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, 0, "현재가")
# ( strTrCode(tran코드), strRecordName(레코드명), nIndex(복수데이터 인덱스), strItemName(아이템명) )
# 아이템명은 koa studio에 해당 tr의 output 에서 확인가능
print(name.lstrip(), volume.lstrip(), price.lstrip())
# 로그인
def login(self):
# 로그인 요청함수
print("[로그인 요청]")
self.dynamicCall("CommConnect()")
self.login_event_loop.exec_() #login_event_loop.exit()가 실행되어야 끝남
# 데이터요청
def getData(self):
# 데이터 요청 함수
print("[데이터요청]")
self.dynamicCall("SetInputValue(QString, QString)", "종목코드", "005385") # (아이템명, 입력값)
self.dynamicCall("CommRqData(QString, QString, QString, QString)", "test_opt10001", "opt10001", "0", "0101")
# ( sRQName(사용자구분), sTrCode(Tran명), nPrevNext(0:조회 2:연속), sScreenNo(화면번호) )
# TR종류는 KOA스튜디오에서 확인가능
# 화면목록은 KOA스튜디옹서 확인가능
if __name__ == "__main__":
app = QApplication(sys.argv)
test = KiwoomAPI()
app.exec_()