ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 파이썬 윈도우 메시지 펌프 처리 예제
    개발/파이썬 2018.12.08 10:19

    파이썬을 이용해서 윈도우 메시지펌프를 하는 간단한 예제 코드 입니다 

    우선 전체 코드를 먼저 본 다음 일부 중요 함수들 설명 이어가겠습니다.


    메시지 펌프 코드는 아래 사이트를 참고 했습니다.

    Processing Windows Messages Using MsgWaitForMultipleObjects


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    import sys
     
    import pythoncom
    from PyQt5.QtWidgets import *
    import win32event
     
     
    StopEvent = win32event.CreateEvent(None, 00, None)
    ################################################
    # 테스트를 위한 메인 화면
    class MyWindow(QMainWindow):
        def __init__(self):
            super().__init__()
     
     
            self.setWindowTitle("Test")
            self.setGeometry(300300300180)
     
            nH = 20
     
            btnStart = QPushButton('strat Pump', self)
            btnStart.move(20, nH)
            btnStart.clicked.connect(self.btnStartPump)
            nH += 50
     
            btnStop = QPushButton('stop Pump', self)
            btnStop.move(20, nH)
            btnStop.clicked.connect(self.btnStopPump)
            nH += 50
     
            btnExit = QPushButton('종료', self)
            btnExit.move(20, nH)
            btnExit.clicked.connect(self.btnExit_clicked)
            nH += 50
     
        def MessagePump(sef, timeout):
            waitables = [StopEvent]
            while 1:
                rc = win32event.MsgWaitForMultipleObjects(
                    waitables,
                    0,  # Wait for all = false, so it waits for anyone
                    timeout, #(or win32event.INFINITE)
                    win32event.QS_ALLEVENTS)  # Accepts all input
     
                if rc == win32event.WAIT_OBJECT_0:
                    # Our first event listed, the StopEvent, was triggered, so we must exit
                    print('stop event')
                    break
     
                elif rc == win32event.WAIT_OBJECT_0 + len(waitables):
                    # A windows message is waiting - take care of it. (Don't ask me
                    # why a WAIT_OBJECT_MSG isn't defined < WAIT_OBJECT_0...!).
                    # This message-serving MUST be done for COM, DDE, and other
                    # Windowsy things to work properly!
                    print('pump')
                    if pythoncom.PumpWaitingMessages():
                        break  # we received a wm_quit message
                elif rc == win32event.WAIT_TIMEOUT:
                    print('timeout')
                    return
                    pass
                else:
                    print('exception')
                    raise RuntimeError("unexpected win32wait return value")
     
        def btnStartPump(self):
            self.MessagePump(5000)
            return
     
        def btnStopPump(self):
            win32event.SetEvent(StopEvent)
            return
     
     
        def btnExit_clicked(self):
            exit()
            return
     
     
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        myWindow = MyWindow()
        myWindow.show()
        app.exec_()
    cs


    메시지 펌프를 하는 실제 코드는 아래 함수 입니다.  

    • MsgWaitForMultipleObjects 라는 윈도우 API 를 이용해서 윈도우 메시지 큐에 메시지가 올 때까지 기다립니다. 파라미터로 이벤트 리스트를 보내게 되는데 윈도우 메시지 말고도 지정한 이벤트 리스트 중 하나라도 시그널이 발생하면 대기를 멈추고 리턴합니다. 
    • if rc == win32event.WAIT_OBJECT_0:  같은 코드들은 이벤트 리스트 배열에 들어 있는 항목들 만큼 반복해서 어느 이벤트에서 시그널이 발생했는지 확인하는 용도 입니다.  이번 예제에서는 이벤트(StopEvent) 를 하나만 등록했고 그 이벤트가 발생(Signal) 할 경우 처리를 하면 됩니다. 
    • rc == win32event.WAIT_OBJECT_0 + len(waitables):  실제 윈도우 메시지가 들어온 경우 처리 해야 할 로직으로 여기서는 pythoncom.PumpWaitingMessages() 함수를 호출해서 윈도우 메시지를 가져와 실행하게 됩니다.
    • rc == win32event.WAIT_TIMEOUT : 무한대로 기다릴 수도 있지만 특정 시간이 경과 하면 MsgWaitForMultipleObjects  호출이 리턴되도록 할 수 있습니다
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    def MessagePump(sef, timeout):
        waitables = [StopEvent]
        while 1:
            rc = win32event.MsgWaitForMultipleObjects(
                waitables,
                0,  # Wait for all = false, so it waits for anyone
                timeout, #(or win32event.INFINITE)
                win32event.QS_ALLEVENTS)  # Accepts all input
     
            if rc == win32event.WAIT_OBJECT_0:
                # Our first event listed, the StopEvent, was triggered, so we must exit
                print('stop event')
                break
     
            elif rc == win32event.WAIT_OBJECT_0 + len(waitables):
                # A windows message is waiting - take care of it. (Don't ask me
                # why a WAIT_OBJECT_MSG isn't defined < WAIT_OBJECT_0...!).
                # This message-serving MUST be done for COM, DDE, and other
                # Windowsy things to work properly!
                print('pump')
                if pythoncom.PumpWaitingMessages():
                    break  # we received a wm_quit message
            elif rc == win32event.WAIT_TIMEOUT:
                print('timeout')
                return
                pass
            else:
                print('exception')
                raise RuntimeError("unexpected win32wait return value")
    cs


    메시지 펌프를 시작 시키고 이벤트를 발생 시켜 중단하는 코드는 아래에 있습니다.

    • win32event.CreateEvent 를 통해 새로운 이벤트를 생성합니다.
    • win32event.SetEvent(StopEvent)로 Signal 을 발생시킵니다.


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # 이벤트 생성
    StopEvent = win32event.CreateEvent(None, 00, None)
     
    # 중략...
        # 메시지 펌프 시작
    def btnStartPump(self):
        self.MessagePump(5000)
        return
     
    def btnStopPump(self):
        win32event.SetEvent(StopEvent)
        return
     
    cs



    위 예제 코드를 실행 시켜 보면 start pump 버튼이 눌려진 상태에서 메시지펌핑이 시작 되고, 아직 함수를 빠져나오지 않았는데도 메시지 펌핑에 의해 버튼이 계속 눌려지는 것을 확인할 수 있습니다 

    PYQT 가 이미 내부적으로 윈도우 메시지 펌핑을 하고 있기 때문에 이렇게 이중으로 메시지 펌핑을 하는 경우 약간의 문제들이 생길 수 있어 조심스럽게 코딩해야 합니다. 


    댓글 0

Designed by black7375.