대신증권 CYBOS PLUS 프로그램 구현 (4) - 이벤트 처리 함수 보완하기
프로그램 구현 목표
- 로그인 함수 이벤트 처리기 생성하기
- 이벤트 처리 함수 보완하기
지난 게시글에서 GUI와 파이썬 코드를 연결하는 기능을 구현하였기 때문에 이번 게시글에서부터는 여러 모듈을 활용하여 다양한 기능을 사용하는 코드를 구현해보고자 했으나 이벤트 처리에 있어서 다소 보완해야 할 부분들이 있어서 코드 구현을 잠깐 멈추고 이벤트 처리기를 보완하는 코드를 구현해보고자 한다. 이전에 `win32com` 모듈에서 제공하는 `WithEvents` 함수를 사용하여 이벤트를 처리하는 방법에 대해 살펴보았는데, 동일한 방식에 따라 로그인 함수에 대한 이벤트 처리기도 구현해보고 그 안에서 보완해야 할 부분에 대해서도 함께 구축하도록 하자.
로그인 함수 이벤트 처리기 생성하기
우리가 Boss.py 파일 내에서 제작한 로그인 함수는 기본적으로 `class login:`이라는 이름으로 제작되어 있다. 이 클래스는 어디까지나 대신증권의 Cybos Plus에 로그인할 아이디와 비밀번호를 전달하고 로그인을 시도하도록 하는 기능만 수행할 뿐, 그 어떠한 기능도 수행하지 않는다. 다시 말해, 로그인 여부를 확인하는 함수는 `class cybos(QMainWindow, main_ui):` 내부에 있는 함수인 `def _open_cybosplus(self):`가 수행한다는 것이다. 그런데 이 함수는 로그인 여부를 확인할 때에는 정작 CpUtil.py 파일 내에 있는 `class CpCybos:`라는 이름의 클래스 내부에 있는 `def _IsConnect(self):` 함수를 사용한다. 우리가 이전에 DsCbo1.py 파일 내에 있는 `StockMst` 모듈의 이벤트 처리기를 DsCbo1.py 파일 내부에 생성해주었듯이, 로그인과 관련된 함수가 있는 CpUtil.py 파일 내에 있는 `CpCybos` 모듈의 이벤트 처리기는 CpUtil.py 파일 내부에 생성해주어야 한다. 그 구조는 이전에 생성했던 것과 동일하다.
아래와 같이 코드를 구현한 후에 프로그램을 실행시켜 로그인을 시도하고 그 결과를 확인해보도록 하자.
※ Line: 33~41
## CpUtil.py ##
import win32com.client
class CpCybos:
def __init__(self):
self.cybos = win32com.client.Dispatch("CpUtil.CpCybos") ## COM 연결
self.handlers = win32com.client.WithEvents(self.cybos, event_handler_CpUtil)
self.handlers.set_instance(self.cybos)
def _IsConnect(self):
value = self.cybos.IsConnect
if value == 1:
print(f"[통신결과:{value}] 서버와의 연결에 성공했습니다.")
return True
elif value == 0:
print(f"[통신결과:{value}] 서버와의 연결에 실패했습니다.")
return False
def _ServerType(self):
"""
0: 연결 끊김
1: CybosPlus 서버
2:HTS 보통 서버(1번 제외)
"""
value = self.cybos.ServerType
if value == 0:
print(f"[서버종류:{value}] 서버와의 연결이 끊겼습니다.")
elif value == 1:
print(f"[서버종류:{value}] CybosPlus 서버와의 연결에 성공했습니다.")
elif value == 2:
print(f"[서버종류:{value}] HTS 보통 서버와의 연결에 성공했습니다.")
class event_handler_CpUtil:
"""서버와의 접속이 종료된 케이스를 확인할 수 없는 상태"""
def set_instance(self, disp):
self.disp = disp
def OnDisConnect(self, disp):
print(f"{disp}")
print("Raised OnDisConnet Event")
▶ 실행 결과 확인하기
[통신결과:1] 서버와의 연결에 성공했습니다.
로그인되어 있습니다.
self.client:<win32com.gen_py.CpDib 1.0 Type Library.IDib instance at 0x151140816>
Raised Received Event
[A005930] 삼성전자
전일종가:78300, 현재가:77700
시가:77700, 고가:78400, 저가:77500
이벤트 처리 함수 보완하기
위의 실행 결과를 보고 보완해야 할 부분이 떠올랐다면 벌써 프로그램 구조를 훤히 꿰뚫고 있다고 보아도 무방할 것 같다. 현재 구조를 보면 `def set_instance(self, client):` 부분에서 사전에 전달받은 COM 구문의 내용만 확인하고 있는데, 실질적으로 우리는 그 내용만 보고 어떤 COM인지 확인할 수 없기 때문에 `def set_instance(self, client):` 함수에서 다른 인자를 추가적으로 받아서 확인해줘야 한다. 일단 추가적으로 수신받을 인자의 이름을 `object`라고 하고 함수를 아래와 같이 수정하도록 하자.
※ Line: 4, 6, 7
## CpUtil.py ##
class event_handler_CpUtil:
"""서버와의 접속이 종료된 케이스를 확인할 수 없는 상태"""
def set_instance(self, disp, object):
self.disp = disp
self.object = object
print(f"self.object:{self.object}")
def OnDisConnect(self, disp):
print(f"{disp}")
print("Raised OnDisConnet Event")
그리고 다시 `class CpCybos:`로 돌아와서, 이전에 `self.handlers.set_instance(self.cybos)`로만 작성했던 구문에서 해당 함수의 인자로 전달해줄 `object`를 전달해주어야 하는데, 이 `object` 변수가 갖는 의미는 무엇일까? 바로 인스턴스로 생성해두었던 COM 모듈의 이름이라고 보면 된다. 여기서 `self.object`를 하나의 인자로 또 받아내는 이유는 바로 그 인스턴스가 생성시키는 이벤트가 어떠한 인스턴스로부터 발생했는지를 구분해내는 데에 그 목적이 있다고 보면 된다. 현재 작성하고 있는 CpUtil.py 파일 내부에서 사용하고 있는 `CpUtil.CpCybos` 모듈은 `OnDisConnect`라는 이벤트만 발생시킬 수 있겠지만, 다른 모듈로 확장시켜서 본다면 하나의 모듈이 하나의 이벤트만 발생시키지 않을 수 있다.
예를 들어, CpUtil.py 파일 내부에서는 `CpUtil.CpCybos` 모듈 외에도 `CpUtil.CpStockCode`나 `CpUtil.CpCodeMgr` 등과 같은 여러 가지 모듈을 사용하고 있는데, 가령 이 세 가지 모듈이 모두 다 `OnReceived`라는 이벤트를 생성시킨다고 가정해보도록 하자. 그렇다면 이 때 우리는 각기 다른 방식으로 데이터를 수신받아야 하는데, 현재 코드 구조 상에서는 이벤트가 발생했을 때 그 이벤트를 발생시킨 모듈이 어떠한 모듈인지 확인할 수 없다는 맹점이 있다. 따라서 이벤트를 발생시킨 모듈이 어떠한 모듈인지를 확인하는 기능을 `object`라는 변수가 수행하는 것이다.
이제 이벤트 처리기에 COM에 대한 인스턴스를 전달하는 코드 라인에서 `object`를 전달해주도록 하자. 가장 먼저 보완할 클래스는 `class CpCybos` 내부의 로그인 함수이다. 아래의 코드와 실행 결과를 확인해보자. 실행 결과를 보면 우리가 Line: 8에서 전달해준 "CpCybos"가 정상적으로 출력되고 있다는 것을 확인할 수 있다.
※ Line: 8
## CpUtil.py ##
import win32com.client
class CpCybos:
def __init__(self):
self.cybos = win32com.client.Dispatch("CpUtil.CpCybos") ## COM 연결
self.handlers = win32com.client.WithEvents(self.cybos, event_handler_CpUtil)
self.handlers.set_instance(self.cybos, "CpCybos")
def _IsConnect(self):
value = self.cybos.IsConnect
if value == 1:
print(f"[통신결과:{value}] 서버와의 연결에 성공했습니다.")
return True
elif value == 0:
print(f"[통신결과:{value}] 서버와의 연결에 실패했습니다.")
return False
def _ServerType(self):
"""
0: 연결 끊김
1: CybosPlus 서버
2:HTS 보통 서버(1번 제외)
"""
value = self.cybos.ServerType
if value == 0:
print(f"[서버종류:{value}] 서버와의 연결이 끊겼습니다.")
elif value == 1:
print(f"[서버종류:{value}] CybosPlus 서버와의 연결에 성공했습니다.")
elif value == 2:
print(f"[서버종류:{value}] HTS 보통 서버와의 연결에 성공했습니다.")
class event_handler_CpUtil:
"""서버와의 접속이 종료된 케이스를 확인할 수 없는 상태"""
def set_instance(self, disp, object):
self.disp = disp
self.object = object
print(f"self.object:{self.object}")
def OnDisConnect(self, disp):
print(f"{disp}")
print("Raised OnDisConnet Event")
▶ 실행 결과 확인하기
self.object:CpCybos
[통신결과:1] 서버와의 연결에 성공했습니다.
로그인되어 있습니다.
self.client:<win32com.gen_py.CpDib 1.0 Type Library.IDib instance at 0x139934064>
Raised Received Event
[A005930] 삼성전자
전일종가:78300, 현재가:77700
시가:77700, 고가:78400, 저가:77500
이제 마지막으로 `def OnDisConnect(self):` 함수 내부에서 `self.object` 변수를 통해 OnDisConnect라는 이벤트를 발생시킨 COM 인스턴스가 CpUtil.CpCybos인 경우에만 해당 이벤트를 실행시키도록 수정해주자.
※ Line: 9
class event_handler_CpUtil:
"""서버와의 접속이 종료된 케이스를 확인할 수 없는 상태"""
def set_instance(self, disp, object):
self.disp = disp
self.object = object
print(f"self.object:{self.object}")
def OnDisConnect(self, disp):
if self.object == "CpCybos":
print(f"{disp}")
print("Raised OnDisConnet Event")
이제 다시 이전에 작성했던 DsCbo1.py 파일 내부에 있는 `class event_handler` 내부의 코드도 이와 동일한 논리 구조에 따라 동작하도록 해당 클래스의 `def set_instance()` 함수가 `object`라는 인자로 추가로 받도록 하고 `def OnReceived` 함수 내부에서는 `self.object`라는 변수를 가지고 이벤트를 발생시킨 COM 인스턴스를 구분하도록 하여 이벤트를 처리할 수 있도록 수정해주도록 하자.
## 기존 코드 ##
## DsCbo1.py ##
import win32com.client
class StockMst:
def __init__(self):
self.stockmst = win32com.client.Dispatch("DsCbo1.StockMst") ## COM 연결
self.handler = win32com.client.WithEvents(self.stockmst, event_handler)
def _StockMst(self, item_code):
"""주식종목현재가 반환"""
self.stockmst.SetInputValue(0, item_code)
self.handler.set_instance(self.stockmst)
self.stockmst.BlockRequest()
class event_handler:
def set_instance(self, disp):
self.disp = disp
print(f"self.client:{self.disp}")
def OnReceived(self):
print("Raised Received Event")
item_code = self.disp.GetHeaderValue(0)
item_name = self.disp.GetHeaderValue(1)
y_close = self.disp.GetHeaderValue(10)
now = self.disp.GetHeaderValue(11)
open = self.disp.GetHeaderValue(13)
high = self.disp.GetHeaderValue(14)
low = self.disp.GetHeaderValue(15)
print(f"[{item_code}] {item_name}")
print(f" 전일종가:{y_close}, 현재가:{now} ")
print(f" 시가:{open}, 고가:{high}, 저가:{low}")
## 수정 후 코드 ##
## DsCbo1.py ##
import win32com.client
class StockMst:
def __init__(self):
self.stockmst = win32com.client.Dispatch("DsCbo1.StockMst") ## COM 연결
self.handler = win32com.client.WithEvents(self.stockmst, event_handler)
def _StockMst(self, item_code):
"""주식종목현재가 반환"""
self.stockmst.SetInputValue(0, item_code)
self.handler.set_instance(self.stockmst, "StockMst")
self.stockmst.BlockRequest()
class event_handler:
def set_instance(self, disp, object):
self.disp = disp
self.object = object
print(f"self.client:{self.disp}")
print(f"self.object:{self.object}")
def OnReceived(self):
if self.object == "StockMst":
print("Raised Received Event")
item_code = self.disp.GetHeaderValue(0)
item_name = self.disp.GetHeaderValue(1)
y_close = self.disp.GetHeaderValue(10)
now = self.disp.GetHeaderValue(11)
open = self.disp.GetHeaderValue(13)
high = self.disp.GetHeaderValue(14)
low = self.disp.GetHeaderValue(15)
print(f"[{item_code}] {item_name}")
print(f" 전일종가:{y_close}, 현재가:{now} ")
print(f" 시가:{open}, 고가:{high}, 저가:{low}")
▶ 실행 결과 확인하기
[통신결과:0] 서버와의 연결에 실패했습니다.
[통신결과:0] 서버와의 연결에 실패했습니다.
로그인되어 있지 않습니다. 로그인을 시도합니다.
[통신결과:0] 서버와의 연결에 실패했습니다.
[통신결과:1] 서버와의 연결에 성공했습니다.
[통신결과:1] 서버와의 연결에 성공했습니다.
로그인되었습니다. 반복문에서 나갑니다.
self.client:<win32com.gen_py.CpDib 1.0 Type Library.IDib instance at 0x146946352>
self.object:StockMst
Raised Received Event
[A005930] 삼성전자
전일종가:78300, 현재가:77700
시가:77700, 고가:78400, 저가:77500
'AUTO TRADE > [대신증권] CYBOS PLUS' 카테고리의 다른 글
대신증권 CYBOS PLUS 프로그램 구현 (6) - 전종목 종목코드 조회하기 ② (0) | 2024.08.26 |
---|---|
대신증권 CYBOS PLUS 프로그램 구현 (5) - 전종목 종목코드 조회하기 ① (0) | 2024.08.25 |
대신증권 CYBOS PLUS 프로그램 구현 (3) - GUI와 연결하기 (0) | 2024.08.18 |
대신증권 CYBOS PLUS 프로그램 구현 (2) - 자동로그인 구현하기 ② (0) | 2024.08.18 |
대신증권 CYBOS PLUS 프로그램 구현 (1) - 자동 로그인 기능 구현하기 ① (0) | 2024.08.18 |
소중한 공감 감사합니다