메뉴 바로가기 검색 및 카테고리 바로가기 본문 바로가기

한빛출판네트워크

IT/모바일

파이썬카드(PythonCard)와 파이크러스트(PyCrust)

한빛미디어

|

2002-10-24

|

by HANBIT

9,085

저자: 패트린 오브라이언(Patrick O"Brien), 역 전순재

파이썬 애플리케이션용으로 그래픽 사용자 인터페이스(GUI)를 개발하는 작업은 거의 지루하고, 시간이 많이 들어갈 뿐만 아니라 확실하지 못한 처리과정을 거친다. 이런 상황은 파이썬 프로그래머들이 파이썬을 사용한 대부분의 다른 소프트웨어 개발 측면들을 기술하는 것과는 정확하게 반대 양상을 기술했다고 볼 수도 있다. 그렇다면 GUI 애플리케이션 작성을 그토록 까다롭게 만드는 원인은 무엇인가? 상황을 개선하기 위한 묘책은 있는 것인가? 이 기사는 그런 문제에 대한 해답을 추구하고자 작성되었다. 파이썬카드(PythonCard) 프로젝트가 이러한 이슈들에 접근하는 법을 본 기사에서 살펴볼 수 있을 것이다.

(wxWindows의 파이썬 버전인) 파이썬카드는 wxPython이라는 GUI 도구모음용 크로스-플랫폼 애플리케이션 프레임워크이다. 파이썬카드 프로젝트는 파이썬의 강력한 힘과 단순명료함, 그리고 넓은 표현력을 구이(GUI) 개발의 영역 안으로 도입하는 데 목적을 두고 있다. 이 프로젝트는 현재 프로토타입(prototype) 단계이다. 그러나 이와 같은 초기의 형태임에도 불구하고 수 많은 애플리케이션이 파이썬카드로 개발되고 있다. 그렇지만 이 시점에서, 다양한 접근법들을 검토하고 구이(GUI) 애플리케이션 개발을 더 쉽고, 더 빠르고 더 "파이썬적"으로 만들어 주는 도구들을 개발함에 따라서 상황은 유동적으로 변할 수 있다.

일단 프로그래머들이 구이(GUI) 개발 중에 자주 직면하는 문제들부터 시작하자. 예를 들면 위젯 생성, 컴포넌트 조감, 이벤트 핸들링, 실행시간 상호작용등과 같은 문제들 말이다. 그렇게 하는 동안, 우리는 wxPython 도구모음의 특징들에 초점을 맞추게 될 것이다. 그러나 모든 구이 도구모음이 서로 다르기 때문에 똑같이 적용할 특징들이 없음에도 불구하고, 우리가 다루어 볼 여러 문제들은 어느 구이(GUI) 도구모음에나 똑 같이 적용될 것이라는 사실에 주목할 가치가 있다. 이 기사에서 진짜로 다루고자 하는 문제는 파이썬카드 프로젝트가 어떻게 이러한 문제들에 접근하는 방법과 여러분이 지금 개발하고 있는 갖가지 애플리케이션에 혜택을 과연 누릴 수 있는가에 맞춰진다. WxPython에 결점이 없다고 생각한다는 사실에도 주목할 만한 가치가 있다. 사실, 우리가 wxPython을 기반으로 애플리케이션을 구축하기로 결정한 것은 wxPython이 파이썬에서 사용할 수 있는 가장 강력한 크로스-플랫폼 구이(GUI) 도구모음이라고 생각하기 때문이다. 우리의 의도는 파이썬카드가 아직 캡슐화해 넣지 않은 능력이나 특징들에 접근할 필요가 있을 때, 그 대용품으로 wxPython 코드를 직접 작성할 능력을 포기하지 않고서도 그저 초보 구이(GUI) 프로그래머들이 더 쉽게 그리고 더 편안하게 wxPython에 접근하도록 해주는 것이다.

이러한 이슈들을 더듬어 보면서, Counter라고 불리는 간단한 애플리케이션의 소스 코드를 보여 주겠다. 특정한 차이점을 강조하기 위해, 그 코드를 wxPython과 PythonCard의 형태로 보여 줄 것이다. Counter 애플리케이션을 만드는 과정은 댄 샤퍼(Dan Shafer)의 탁월한 파이썬카드 튜토리얼에 기술되어 있다. 결과로 나온 애플리케이션은 텍스트 콘트롤 안에 표시되는 수치 값을 늘리거나 줄이고, 재설정하는 버튼과 메뉴 선택을 가지고 있는데, 이는 아래 그림과 같이 보인다.

내장된(Embedded) 파이썬 셸

구이 개발 과정이 불명료하다고 언급한 바가 있는데, 아마도 그 점부터 제일 먼저 설명해야 할 것 같다. 그 의미는 단순히 그래픽 인터페이스를 구성하고 있는 다양한 위젯들 안에서 무슨 일이 진행되고 있는지 가늠하기가 어렵다는 뜻일 뿐이다. 파이썬은 경이로운 실행시간 내부검사(introspect) 능력이 있다. 따라서 대부분의 비-구이 애플리케이션에서는 모듈들을 파이썬 셸 안으로 적재해 넣고, 몇 개의 명령어를 타자해 넣으며, 객체의 결과 상태를 살펴 보는 것은 상대적으로 쉬운 일이다. 그러나 이벤트-주도형 특성을 가지는 구이(GUI) 애플리케이션에서는 이렇게 하기가 어려우며, 결론적으로 불명료한 객체들과 행위라는 결과에 이른다. 그래픽 객체의 상태 역시도 똑같이 용이하게 검사(inspect)를 할 수 있으면 좋을 것이다. 애플리케이션이 실행되고 있는 동안에, 구이 컴포넌트들을 파이썬 셸에서 조작할 수 있다면 도움이 될 것이다. 그러한 능력들이 파이썬카드에 파이크러스트(PyCrust)라는 그래픽 파이썬 셸의 형태로 처음부터 갖추어졌다.

파이크러스트(PyCrust)는 wxPython 도구모음을 이용하여 파이썬으로 작성되었기 때문에, 어떤 wxPython 애플리케이션에도 그 애플리케이션의 이벤트 루프(event loop)와 충돌하지 않고서 내장될 수 있다. 파이크러스트의 모듈 디자인 덕분에 파이썬 명령어 셸이나 네임스페이스 뷰어(namespace viewer)를 내장할 수 있으며 여러분이 만든 wxPython 애플리케이션의 어떤 부분도 노출시킬 수 있다. 파이크러스트 셸을 포함한 최소로 작은 애플리케이션은 다음과 같이 보인다.
from wxPython.wx import * 
 
from PyCrust.shell import Shell 
 
class ShellFrame(wxFrame): 
    def __init__(self, parent=None, id=-1, title="PyCrust Minimus"): 
        wxFrame.__init__(self, parent, id, title) 
        self.shell = Shell(parent=self) 
 
class App(wxApp): 
    def OnInit(self): 
        self.frame = ShellFrame() 
        self.frame.Show(true) 
        self.SetTopWindow(self.frame) 
        return true 
 
def main(): 
    application = App(0) 
    application.MainLoop() 
 
if __name__ == "__main__": 
    main() 
파이썬카드는 이것을 한 단계 더 발전시켜 모든 파이썬카드 애플리케이션에 자동으로 파이크러스트 셸과 네임스페이스 뷰어를 포함한다. 게다가 이벤트 메시지 감시기, 로그기록기, 특성 편집기 등도 포함한다. 파이썬카드로 구축된 애플리케이션의 사용자는 보통 이러한 실행시간 도구들을 볼 수 없지만, 표준 모둠의 명령어 라인 선택사항으로 그 도구들을 보여줄 수 있다. 실행시간 도구모음은 특히 개발중이나 디버깅 중에 편리하다. 두 경우 모두 구이(GUI) 개발 환경에서 자주 결여되고 있는 투명함을 제공한다. 예를 들어 아래 그림은 모든 실행시간 도구들이 적재된 Counter 예제를 보여 주고 있다.

파이크러스트 셸을 애플리케이션에서 할 수 있게 만들어주는 코드(그리고 그 반대도)는 다음과 같이 간단하다.
class PyCrustFrame(MyMiniFrame): 
    def __init__(self, parent, ID, title, pos, size, parentApp): 
        MyMiniFrame.__init__(self, parent, ID, title, pos, size) 
         
        parentApp.shell = PyCrust.shell.Shell(self, -1) 
        parentApp.shell.interp.locals["pcapp"] = parentApp 
        self.parentApp = parentApp 
 
        wx.EVT_CLOSE(self, self.onCloseMe) 
 
    def onCloseMe(self, evt): 
        self.Show(0) 
셸 프레임(shell frame)이 wxPython MiniFrame으로 만들어진다. 파이크러스트 셸이 그 프레임에 부착된다. 애플리케이션 그 자체는 그 셸의 지역 네임스페이스에 "pcapp"으로 부착된다. 위의 그림에서 보듯이, 지금부터는 애플리케이션이 실행되는 동안에, 그 애플리케이션에 접근할 수 있다. 따라서 창 그리고 그 창의 메뉴와 버튼, 그 메뉴와 버튼의 메소드와 속성 등등에 접근할 수 있다. 이 덕분에 파이크러스트 파이썬 셸에서 거의 모든 파이썬 명령어들을 실행할 수가 있다.


Programming Python, 2nd Edition

참고 도서

Programming Python, 2nd Edition
Mark Lutz




자동적인 이벤트 벤딩(Automatic Event Binding)

지겹고 에러가 난발하는 경향이 있는 또다른 구이(GUI) 개발 영역으로는 마우스 버튼을 클릭한다든가 하는 사용자행위나 이벤트 카운터를 증가시키는 일과 같은 수행될 행위와 서로 연관짓는 분야가 있다. 이런 것을 "이벤트(event)을 핸들러(handler)에 묶는다"라고 한다. 이것은 아주 흔한 연산작업이기 때문에, 파이썬카드(PythonCard)라는 관례를 만들어서 이런 묶기 작업이 최소한의 코딩으로 이루어지고 일정한 형태로 일어나도록 만들었다. 무슨 말인지 정확하게 이해하려면, 코드를 보면서 표준 wxPython 접근법 위에 어떻게 파이썬카드의 접근법이 구축되었는지를 알아 볼 필요가 있다.

Counter 애플리케이션을 직접적으로 wxPython 코딩한다면 다음과 같이 보일 것이다.
from wxPython import wx
 
ID_FILE_EXIT = wx.NewId()  
ID_COUNTER_INCREMENT = wx.NewId()  
ID_COUNTER_DECREMENT = wx.NewId()  
ID_COUNTER_RESET = wx.NewId()  
 
class MyApp(wx.wxApp): 
 
    def OnInit(self): 
        frame = wx.wxFrame(wx.NULL, -1, "PythonCard Counter Tutorial", size=(204, 160)) 
        self.frame = frame 
        panel = wx.wxPanel(frame, -1) 
 
        self.resetBtn = wx.wxButton(panel, -1, "Reset", (10, 68)) 
        self.decrBtn = wx.wxButton(panel, -1, "Decrement", (10, 38)) 
        self.incrBtn = wx.wxButton(panel, -1, "Increment", (10, 8)) 
         
        # 이 이벤트 엮기(binding)는 파이썬카드 프레임워크에 의해 자동으로 이루어진다.
        wx.EVT_BUTTON(panel, self.resetBtn.GetId(), self.OnResetMouseClick) 
        wx.EVT_BUTTON(panel, self.decrBtn.GetId(), self.OnDecrMouseClick) 
        wx.EVT_BUTTON(panel, self.incrBtn.GetId(), self.OnIncrMouseClick) 
 
        # 사후 초기화(post initialization) 
        self.incrBtn.SetDefault() 
 
        self.field1 = wx.wxTextCtrl(panel, -1, "42", (127, 19), (55, 46),  
            wx.wxTE_READONLY) 
 
        # 콘트롤 하나가 만들어지고 나면, 파이썬카드는 컴포넌트가 지원하는 속성들에 대하여
        # 사후 초기화(post initialization)를 단행한다. wxPython에서 이러한 과정들은 추가로 실행되는 작업이다. 
        font = self.field1.GetFont() 
        font.SetPointSize(24) 
        font.SetFaceName("MS Sans Serif") 
        font.SetFamily(wx.wxSWISS) 
        self.field1.SetFont(font) 
 
        # "File" 메뉴를 만들어라
        file_menu = wx.wxMenu() 
        file_menu.Append(ID_FILE_EXIT, "E&xit\tAlt+X") 
 
        # "Counter" 메뉴를 만들어라
        counter_menu = wx.wxMenu() 
        counter_menu.Append(ID_COUNTER_INCREMENT, "Increment") 
        counter_menu.Append(ID_COUNTER_DECREMENT, "Decrement") 
        counter_menu.Append(ID_COUNTER_RESET, "Reset") 
 
        # 이제 방금 만든 두 개의 메뉴를 보유할 메뉴 바가 필요하다.
        menu_bar = wx.wxMenuBar() 
        menu_bar.Append(file_menu, "&File") 
        menu_bar.Append(counter_menu, "Counter") 
 
        # 메뉴 바를 설정하라 (시스템에 작업완료 사실을 알린다) 
        frame.SetMenuBar(menu_bar) 
  
        # EVT_MENU를 사용하여, 각 메뉴 항목에 대한 식별자(identifier)를
        # 그 메뉴 항목이 선택될 때 호출될 메소드와 연관짓는다. 
        wx.EVT_MENU(self, ID_FILE_EXIT, self.OnFileExit) 
 
        # 버튼들에 대하여 정의된 메소드들을 재사용할 수 있다
        wx.EVT_MENU(self, ID_COUNTER_INCREMENT, self.OnIncrMouseClick) 
        wx.EVT_MENU(self, ID_COUNTER_DECREMENT, self.OnDecrMouseClick) 
        wx.EVT_MENU(self, ID_COUNTER_RESET, self.OnResetMouseClick) 
 
        frame.Show(1) 
        self.SetTopWindow(frame) 
 
        return 1 
 
    def OnIncrMouseClick(self, event): 
        endValue = int(self.field1.GetValue()) + 1 
        self.field1.SetValue(str(endValue)) 
 
    def OnDecrMouseClick(self, event): 
        endValue = int(self.field1.GetValue()) - 1 
        self.field1.SetValue(str(endValue)) 
 
    def OnResetMouseClick(self, event): 
        self.field1.SetValue("0") 
 
    def OnFileExit(self, event): 
        self.frame.Close() 
 
app = MyApp(0) 
app.MainLoop() 
보시다시피 이벤트(events)들과 그 핸들러(handlers)들을 연관짓는데 상당한 부담(overhead)이 수반된다. 똑같은 애플리케이션을 파이썬카드로 코딩하면 실제로 두 개의 파일로 나뉘어진다. 한 파일은 애플리케이션 로직을 담고 있고 다른 하나는 모든 구이(GUI) 컴포넌트들의 속성들을 기술하는 리소스(resource) 파일이다. 다른 말로 하면, 형태와 기능을 분리하고 있는 것이다. 애플리케이션 코드는 다음과 같다.
from PythonCardPrototype import model 
 
class Counter(model.Background): 
 
    def on_menuFileExit_select(self, event): 
        self.Close() 
        
    def on_menuCounterIncrement_select(self, event): 
        startValue = int(self.components.field1.text) 
        endValue = startValue + 1 
        self.components.field1.text = str(endValue) 
 
    def on_menuCounterDecrement_select(self, event): 
        startValue = int(self.components.field1.text) 
        endValue = startValue - 1 
        self.components.field1.text = str(endValue) 
 
    def on_menuCounterReset_select(self, event): 
        self.components.field1.text = "0" 
 
    def on_incrBtn_mouseClick(self, event): 
        startValue = int(self.components.field1.text) 
        endValue = startValue + 1 
        self.components.field1.text = str(endValue) 
 
    def on_decrBtn_mouseClick(self, event): 
        startValue = int(self.components.field1.text) 
        endValue = startValue - 1 
        self.components.field1.text = str(endValue) 
 
    def on_resetBtn_mouseClick(self, event): 
        self.components.field1.text = "0" 
 
if __name__ == "__main__": 
    app = model.PythonCardApp(Counter) 
    app.MainLoop() 
어떻게 파이썬카드가 이벤트들을 적절한 핸들러로 엮을까? wxPython 예제를 살펴 보면, 어느 정도는 일관성을 가지고 이벤트 핸들러의 이름을 짓고 있는 것을 볼 수 있다. 예를 들어 "OnIncrMouseClick"과 "OnDecrMouseClick"과 같이 말이다. 이런 일관성이 필수적인 것은 아니지만, 관행상 상당히 전형적이다. 파이썬카드는 일관성 있는 이름짓기 전략의 이점을 활용하여 자동으로 핸들러와 그 핸들러를 촉발시키는 이벤트에 연관짓는다.

그런 이름짓기 전략은 다음과 같이 작동한다. 파이썬카드 프레임워크는 모든 wxPython 이벤트를 가로챈다. 각 이벤트 하나당, 파이썬카드 프레임워크는 예를 들어 "incrBtn"라는 이름의 버튼과 같은, 그 이벤트를 촉발시킨 컴포넌트의 이름을 살펴본다. 그런 후, 밑줄로 세 조각으로 분리된 정보를 가진 메소드 이름을 찾는다. 접두사로 "on"이 오고 다음에 그 컴포넌트의 이름이 따르며, 그 다음은 "mouseClick"과 같은 이벤트의 이름이 온다. 일치가 되면 그 코드는 실행된다. 이것을 자동 이벤트 바인딩(automatic event binding)이라 한다.

리소스 편집(Resource Editing)

Counter 애플리케이션에 대한 리소스 파일은 다음과 같이 보인다. 리소스 파일의 구조는 어떤 파이썬 프로그래머도 읽을 수 있고 필요하다면 수작업으로 편집도 가능하지만, 대게 파이썬카드에서 제공하는 그래픽 리소스 편집 도구를 사용하여 유지보수된다.
{"stack":{"type":"Stack", 
          "name":"Counter", 
    "backgrounds": [ 
    {"type":"Background", 
         "name":"bgcounter", 
          "title":"PythonCard Counter Tutorial", 
          "size":(204, 160), 
 
        "menubar": {"type":"MenuBar", 
         "menus": [ 
             {"type":"Menu", 
             "name":"menuFile", 
             "label":"&File", 
             "items": [ 
                  {"type":"MenuItem", 
                   "name":"menuFileExit", 
                   "label":"E&xit\tAlt+X", 
                  }, 
              ] 
             }, 
             {"type":"Menu", 
             "name":"menuCounter", 
             "label":"Counter", 
             "items": [ 
                  {"type":"MenuItem", 
                   "name":"menuCounterIncrement", 
                   "label":"Increment", 
                  }, 
                  {"type":"MenuItem", 
                   "name":"menuCounterDecrement", 
                   "label":"Decrement", 
                  }, 
                  {"type":"MenuItem", 
                   "name":"menuCounterReset", 
                   "label":"Reset", 
                  }, 
              ] 
             }, 
         ] 
     }, 
         "components": [ 
 
{"type":"Button",  
    "name":"resetBtn",  
    "position":(10, 68),  
    "label":"Reset",  
    }, 
 
{"type":"Button",  
    "name":"decrBtn",  
    "position":(10, 38),  
    "label":"Decrement",  
    }, 
 
{"type":"Button",  
    "name":"incrBtn",  
    "position":(10, 8),  
    "default":1,  
    "label":"Increment",  
    }, 
 
{"type":"TextField",  
    "name":"field1",  
    "position":(127, 19),  
    "size":(55, 46),  
    "editable":0,  
    "font":{"size": 24, "family": "sansSerif"},  
    "text":"42",  
    }, 
 
] # end components 
} # end background 
] # end backgrounds 
} } 
파이썬카드 애플리케이션을 애플리케이션 코드와 애플리케이션 리소스 파일 두 부분으로 나눔으로써, 구이(GUI) 개발에 관련된 권태와 시간소비를 약간이나마 제거하였다. 동시에, 리소스 정보를 표준 파이썬 구조(사전, 리스트, 터플, 등등)로 저장하면 편집 도구를 추가로 만들 수 있거나 파이썬으로 작성된 맞춤 유틸리티로 리소스 파일을 조작할 수 있다. 리소스 편집기를 한 번 살펴보고 어떻게 작동하는지 알아보자.

리소스 편집기(Resource Editor)는 파이썬카드에 포함된 개발 도구들 중의 하나이다. 게다가 개발 도구중에는 코드 편집기, 텍스트 편집기, 파일 탐색기도 포함된다. 리소스 편집기 뒤에 숨은 개념은 상당히 단순하다. 표준 파이썬 구조를 사용한 보편적인 용어로 구이(GUI) 객체들이 기술되어 있는 리소스 파일을 읽고 쓴다. 그 리소스 편집기 안에서, 구이 위젯들과 그들의 특성들을 만들어 내고 변경하는 것을 허용한다. 그리고 자동으로 리소스 파일을 애플리케이션과 연관지어서 그 애플리케이션이 시작될 때 리소스 파일이 적재되어 인터페이스를 구축하는데 사용되도록 만든다는 것이다.

다음 그림은 Counter 애플리케이션 리소스 파일이 적재된 리소스 편집기를 보여준다.

그리고 다음의 또다른 스크린 샷은 컴포넌트 메뉴 옵션을 보여준다.

(당연히 궁금하게 여기겠지만 리소스 편집기는 그 자체로 자신의 리소스 파일을 가진 파이썬카드 애플리케이션이다. 사실, 리소스 편집기는 바로 자신의 리소스 파일을 편집하는데 사용된다.)

맺는 말

그래픽 사용자 인터페이스 개발은 복합적인 업무이며 파이썬카드로 나아가는 것이 올바른 방향이라고 생각한다. 알란 케이(Alan Kay)가 처음 제시한 말을 인용하면 다음과 같다. "단순한 일은 당연히 단순하게 처리해야 하고, 복잡한 일은 복잡하게 처리할 수 있어야 한다.(Simple things should be simple and complex things should be possible)" 최선을 다해 우리는 그 모토(motto)를 파이썬으로 하는 구이(GUI) 개발에 적용시키려 하고 있다. 동시에, 아직도 탐구해야 할 영역이 많이 남아 있다. 파이썬카드 메일링 리스트에서 최근 토론은 조감을 통제하는 크기조정자(sizer)의 사용과 복합 컴포넌트들의 생성, 애플리케이션 세션들 사이의 데이터를 투명하게 유지하는 능력에 집중되고 있다. 어쨌든 간에 여러분이 이러한 노력들에 참여하기를 바라는 바이다. 프로젝트 정보는 http://www.pythoncard.org/에서 완벽하게 알아 볼 수 있다. OSCON 2002에 참석할 생각이 있다면 본인이 참가하는 튜토리얼에도 와주기 바란다. 그리고 마지막으로, 이 프로젝트가 가능하도록 해준 Python과 wxPython에 감사하는 바이다.
패트릭 오브라이언(Patrick O"Brien)은 Orbtech사의 소프트웨어 개발자이다. 그는 파이썬 프로그래밍 언어를 사용하는 애플리케이션 개발을 전문으로 한다. 그는 파이크러스트(PyCrust)를 작성했을 뿐만 아니라 파이썬카드(PythonCard) 프로젝트의 개발자이기도 하다.
TAG :
댓글 입력
자료실