본문 바로가기
Python/파이썬 프로그래밍 연습

사건 주도적 프로그래밍

by 가므자 2012. 4. 24.

지금까지 배치 지향적 프로그램을 살펴보았습니다. 프로그램은 배치 지향적일 수 있습니다. 일단 시작하고 무언가를 한 다음 멈춥니다. 또는 사건 주도적일 수 있습니다. 일단 시작하고 사건을 기다리고 사건으로 멈추라고 명령할 때만 멈춥니다. 어떻게 사건 주도적 프로그램을 만드는가? 두 가지 방법을 살펴보겠습니다 - 먼저 사건 환경을 흉내낸 다음 아주 간단한 GUI 프로그램을 만들겠습니다. 구이 프로그램은 운영체제와 환경을 이용하여 사건을 만들어 냅니다.

사건 회돌이 따라하기

사건 주도적 프로그램마다 회돌이가 어디엔가 있어서 사건을 잡아 처리합니다. 사건은 사실상 거의 모든 GUI 프로그램에서 그런 것처럼 운영체제에 의하여 발생되거나 또는 카메라 등등의 임베드 제어 시스템에서 그런 것처럼 프로그램 그 자체가 사건을 찾아 다니기도 합니다.

정확하게 한 가지 유형의 사건만 - 키보드 입력 - 기다려서 종료 사건을 받을 때까지 그 결과를 처리하는 프로그램을 만들겠습니다. 종료 사건은 스페이스 키로 하겠습니다. 들어오는 사건을 아주 간단하게 처리하겠습니다 - 그 키에 대한 ASCII 코드를 인쇄해 보겠습니다. 파이썬을 사용하려고 하는데 한 번에 하나씩 키를 읽기 위하여 쉽게 사용할 수 있는 getch()라는 멋진 함수가 파이썬에 들어 있기 때문입니다. 이 함수는 운영 체제에 따라 두 가지 변종을 사용할 수 있습니다. 리눅스를 사용하고 있다면 curses 모듈에 있고, 윈도우즈를 사용한다면 msvcrt 모듈에 있습니다. 윈도우즈 버전을 사용하겠지만 리눅스에 있다면 그냥 msvcrtcurses.stdscr로 바꾸면 잘 작동하리라 생각합니다. 이에 관해서는 윈도우즈 코드를 다룬 후에 좀 더 자세하게 연구해 보겠습니다.

먼저 키가 탐지되면 호출될 사건 처리자 함수를 구현하고 단순히 사건 수집 회돌이를 시작하는 메인 프로그램 몸체를 구현합니다. 그리고 유효한 사건이 탐지되면 사건 처리 함수를 호출합니다.

import msvcrt

 

def doKeyEvent(key):

    if key == '\x00' or key == '\xe0': # ASCII가 아님

       key = msvcrt.getch() # 두 번째 문자를 가져온다

    print ord(key)

 

def doQuitEvent(key):

    raise SystemExit

 

 

# 먼저, 지저분한 화면을 깨뜻하게 청소한 다음

# 사용자에게 종료하려면 어떻게 해야 하는지 알려준다

lines = 25 # 화면 줄 개수를 설정한다

for line in range(lines): print

 

print "끝내려면 스페이스키를 치시오..."

print

 

# 이제 주회돌이가 "영원히" 실행된다

while True:

   ky = msvcrt.getch()

   length = len(ky)

   if length != 0:

      # 사건을 사건 처리 함수에 보낸다

      if ky == " ": # 종료 사건을 점검한다

         doQuitEvent(ky)

      else:

         doKeyEvent(ky)

메인 몸체는 사건으로 무슨 짓을 하든 관심이 없습니다. 그저 사건을 모아서 사건 처리자에게 건넬 뿐입니다. 이렇게 사건나포와 사건처리를 따로 가르는 것이 사건 주도적 프로그래밍의 핵심 특징입니다.

고지: 키가 ASCII 코드가 아니면 - 예를 들어 기능 키이면 - 키보드에서 두 번째 문자를 가져올 필요가 있습니다. 왜냐하면 이런 특수키는 실제로는 한 쌍의 바이트를 생성하는데 getch 함수는 오직 한 번에 한 바이트만 받기 때문입니다. 해당 실제 값은 두 번째 바이트입니다.

리눅스 프로그래머는 위의 코드르 다음과 같이 수정하여 사용할 수 있습니다:

import curses

 

# curses 모듈은 화면을 초기화할 필요가 있다.

# 그래야 통제할 수 있기 때문이다. 화면을 msvcrt에 할당한다.

# 나머지 코드는 원래대로 작동하기 때문에,

# 마치 화면처럼 변수를 사용하면 된다!

msvcrt = curses.initscr()

 

# 나머지 코드는 여기에 둔다...

 

curses.endwin()

 

curses.endwin()은 화면을 다시 정상으로 복구합니다. 이것이 없다면 화면은 이상하게 표시될 것입니다. 혹 커서가 안 보이거나 줄갈이 문자 등등이 없거나 할 수 있습니다. Ctrl-D로 파이썬을 빠져 나와서 다음 리눅스 명령어를 사용하면 고칠 수 있습니다.

$ stty echo -nl

모든 것이 정상으로 돌아왔기를 바랍니다.

많은 프로젝트에 사용하기 위해 이를 작업틀로 만들려면 아마도 시작할 때 initialization 함수를 호출하고 끝날 때 cleanup 함수를 호출해야 할 것입니다. 그러면 프로그래머는 회돌이 부분을 사용하면서 자신만의 초기화 함수와 처리 함수 그리고 정리 함수를 제공할 수 있습니다.

그것이 바로 정확하게 대부분의 GUI 유형 환경이 하는 일입니다. GUI 환경에서 회돌이 부분은 운영체제 환경이나 작업틀에 임베드되고 어플리케이션은 의무적으로 사건 처리 함수들을 사건 회돌이에 어떻게든 끼워 넣어 제공합니다.

파이썬 Tkinter GUI 라이브러리를 탐험하면서 실제로 작동하는 모습을 살펴봅시다.

GUI 프로그램

이 연습문제에서 파이썬 Tkinter 툴킷을 사용하겠습니다. 이는 Tk 툴킷을 둘러싼 파이썬 포장자입니다. 이 툴킷은 원래 Tcl 확장으로 작성되었고 Perl에서도 사용할 수 있습니다. 파이썬 버전은 객체 지향적 작업틀로서 생각건대 원래의 절차적인 Tk 버전에 비해 상당히 더 사용하기 쉽습니다. GUI 주제에서 GUI 프로그래밍의 원리를 더욱 자세하게 살펴보겠습니다.

이 주제에서는 GUI 측면에 너무 주력하지 않겠습니다. 그 보다는 프로그래밍 스타일에 초점을 두고 싶습니다 - Tkinter를 사용하여 사건 회돌이를 처리하고 프로그래머에게 최초 GUI를 만들고 사건이 도착하는대로 처리하는 일을 맡기겠습니다.

예제에서 KeysApp 어플리케이션 클래스를 만듭니다. 이 클래스는 __init__ 메쏘드에서 GUI를 만듭니다. 그리고 스페이스키를 doQuitEvent 메쏘드에 묶습니다(binds). 또한 필수적인 doQuitEvent 메쏘드를 정의합니다.

GUI 자체는 단순히 텍스트 엔트리 위젯(widget)으로 구성되며 기본 행위는 타자된 문자를 화면에 표시하는 것입니다.

어플리케이션 클래스를 만드는 일은 OO 사건 주도적 환경에서 아주 흔한 일입니다. 왜냐하면 프로그램에 전송되는 사건의 개념과 객체에 건내지는 메시지의 개념 사이에 많은 유사점이 있기 때문입니다. 두 개념은 서로 아주 쉽게 대응됩니다. 사건 처리 함수는 그리하여 어플리케이션 클래스의 메쏘드가 됩니다.

클래스를 정의하였으므로 그냥 실체를 만들면 됩니다. 다음 그 실체를 주회돌이 메시지에 보냅니다.

코드는 다음과 같이 보입니다:

 

# from X import * 형태를 이용하였으므로,

# tkinter.xxx와 같이 앞에다 접두사를 붙이지 않아도 된다.

from Tkinter import *

 

# GUI를 정의하는 어플리케이션 클래스와

# 사건 처리 메쏘드를 정의한다.

class KeysApp(Frame):

    def __init__(self): # 구성자를 이용하여 GUI를 구축한다.

        Frame.__init__(self)

        self.txtBox = Text(self)

        self.txtBox.bind("<space>", self.doQuitEvent)

        self.txtBox.pack()

        self.pack()

 

    def doQuitEvent(self,event):

        import sys

        sys.exit()

       

 

# 이제 실체를 만들고 사건 회돌이를 시작한다.

myApp = KeysApp()

myApp.mainloop()

 

키 사건 처리자는 구현하지도 않았음을 주목하세요! 그것은 Text 위젯의 기본 행위가 눌린 키를 인쇄하는 것이기 때문입니다. 그렇지만 그것은 우리의 프로그램이 실제로는 콘솔 버전과 기능적으로 동등하지 않다는 뜻입니다. 콘솔 버전에서는 모든 키의 ASCII 코드를 인쇄했습니다. 여기에서 한 것처럼 인쇄가능한 숫자 문자 키만 인쇄하는 대신에 말입니다. 모든 키눌림을 잡아서 똑같이 일을 하지 못할 이유가 없습니다. 그렇게 하려면 다음 줄을 __init__ 메쏘드 안에 추가하면 됩니다:

self.txtBox.bind("<Key>", self.doKeyEvent)

그리고 다음 메쏘드는 그 사건을 처리합니다:

def doKeyEvent(self,event):

    str = "%d\n" % event.keycode

    self.txtBox.insert(END, str)

    return "break"

고지 1: 키 값은 사건의 keycode 필드에 저장됩니다. 이 사실을 알아내기 위하여 Tkinter.py의 소스 코드를 보아야 했습니다... 호기심은 프로그래머가 지녀야할 핵심 자질이라는 지적이 기억나십니까?!

고지 2: return "break" 줄은 Tkinter에게 그 위젯에 대하여 기본 사건 처리를 요청하지 말라고 알려주는 마법의 신호입니다. 이 줄이 없다면, 텍스트 박스는 ASCII 코드를 화면에 보여주고 다음에 실제 타자된 문자를 보여주는데, 이 문자는 우리가 원하는 문자가 아닙니다.

지금 당장은 Tkinter에 관하여 이 정도로 충분합니다. 이번 주제는 Tkinter 자습서가 목적이 아니며, 그것은 다음 주제에서 다루겠습니다. Tk Tkinter의 사용법을 다룬 책들이 많이 있습니다.

 

기억해야 할 것

· 사건 회돌이는 자신이 탐지한 사건에 신경쓰지 않는다.

· 사건 처리자는 한 번에 하나의 사건만 처리한다.

· Tkinter같은 작업틀은 사건 회돌이를 제공하며 종종 기본 사건 처리자도 제공한다.

· 웹 브라우저는 배치 코딩 스타일과 시건 주도적 코딩 스타일을 모두 제공한다. 또는 둘을 혼합한 스타일도 제공한다.

 

출처 : http://coreapython.hosting.paran.com/tutor/index.htm

'Python > 파이썬 프로그래밍 연습' 카테고리의 다른 글

파이썬으로 간단한 메모장 만들기  (0) 2012.04.25
Tkinter GUI 프로그래밍  (0) 2012.04.24
객체 지향 프로그래밍  (0) 2012.04.24
정규 표현식  (0) 2012.04.24
이름공간  (0) 2012.04.24

댓글