1절. GUI 프로그래밍의 필요를 느끼게 되다.

필자는 주로 Linux/Unix 환경에서 시스템/네트웍 프로그래밍 작업을 해왔으며, Windows 계열로는 학생시절에 아주 잠깐 볼랜드 C++ 을 이용해서 노트패드(얼마나 만들기 쉬운지 아시는분은 다 아시죠 --;) 한번만들어 본게 윈도우즈(GUI) 프로그래밍의 처음이자 마지막이였다.(Linux 쪽에서는 Qt 쪽을 좀 보긴 했었지만)

그러다가 최근들어 업무상의 이유로 GUI 플밍을 해야될 처지에 놓이게 되었다. 꼭 업무상의 이유가 아니더라도, GUI 플밍의 기본개념은 익혀 놓아야 겠다고 그동안 생각하고 있었는데, 마침 잘되었다 싶어 어떤 도구를 사용하는게 좋을지 선택하기 위해 여기 저기 뒤적이기 시작했다.


1.1절. 선택기준

어떤 도구를 선택하기 위해서는 선택을 위한 명확한 기준을 가지고 있어야 한다. 필자는 다음과 같은 요건들을 만족시키는 도구를 원했다.

배우기 쉬워야 한다.

GUI는 개인적으로 전략적 공략(공부) 대상이 아니였다. 필자의 전략적 공략 대상은 시스템/네트웍 관련 부분이였고, GUI는 어느정도 이해하고 구성할수 있을정도 까지만 할생각이였음으로, 강력하지만 이해가 어렵고, 복잡한 그런 도구를 선택하고 싶지는 않았다. 비록 강력함이 조금 덜하더라도, 이해하고 배우기 쉬운 도구를 선택하길 원했다. 거기에 덧붙여 부족한 강력함을 메울수 있도록 c/c++ 과 잘 연결되는 도구라면 금상첨화일 것이다.

기업환경에 적용시킬수 있어야 한다.

사용될 도구는 취미생활로 즐길수도 있지만, 필요할경우 업무에 적용될지도 모른다. 그러므로 기업(enterprise)환경에서의 적용가능성도 염두에 두어야 했다.

크로스 플랫폼 환경 지향

역시 GUI 하면 Windows 환경을 따로 떼어 놓고 생각할수 없다. 그러면서 동시에 Linux환경도 지원하고 싶었다. "크로스 플랫폼은 환상이다" 라고 주장하는 분들도 많기는 하지만 최대한 크로스 플랫폼에 가깝운 도구를 필요로 했다.

라이센스 문제

이왕이면 공개된 도구를 사용하는게 좋다. 공개되지 않았다 하더라도 저렴한 가격에 라이센스 획득이 가능해야 한다.

재미있어야 한다

선택한 도구가 너무 구태의연하거나 구습에 얽매여 있을경우 공부하다 지쳐버릴수도 있으므로, 적당히 재미있어야 했다.

차세대 전투기 선정 방식으로 각각의 요건에 대한 가중치를 매겨본다면, 가중치의 순서는 (쉽고 > 크로스 플랫폼지향적이며 > 재미있고 > 기업환경적용가능성 > 까다롭지 않은 라이센스) 순이였다.


1.2절. wxPython 을 선택 하다

위의 조건을 만족시키는 도구를 찾기위해 조사를 해본결과 최종적으로 Qt와 wxPython, Gtk 로 선택의 폭을 좁힐 수 있게 되었다. Qt와 Gtk 는 각각 C++, C로 이루어졌음으로 그동안 익혀왔던 프로그래밍 기술을 사용할수 있다는 점과 이 두 언어가(C/C++)이 거의 산업표준이라는 점, 꽤 재미있을거라는 점이 맘에 들었다. 게다가 Qt의 경우는 크로스 플랫폼 환경을 지원하고 있으며, 개인적으로 좋아하고 몇번 다루어본 경험이 있다는것도 장점으로 작용했다.

Gtk 역시 매우 훌륭하긴 하지만, Qt에 비해 그리 좋아하지 않고, 다루어본적도 없었으며, 크로스 플래스 플랫폼 환경의 지원이 3개의 도구중 가장 취약했다. 기반언어가 C라는 것도 좀 맘에 들지 않았다.

결국 wxPython 을 선택했다. 이유는 가장 배우기 쉽고, 가장 재미있을 것 같았기 때문이였다. Python 이 스크립트 언어인 관계로 쏘쓰코드가 보이는 점 때문에 기업환경에 적용하기가 조금 애매할것 같기도 했으나, C나 C++로 중요코드를 감추는 것으로 어느정도 해결이 가능할것이라고 생각했다. 다행히 python은 다른 언어들과 쉽게 결합할수 있는 특징을 지니고 있다. 그리고 어차피 전문적인 GUI 플밍을 목적으로 하는것이 아니였음으로 쏘쓰코드가 보이는 점이 크게 문제일거라고는 생각지 않았다. 게다가 기반언어인 Python 은 꽤 오래 다루어 왔었고, 꽤 재미있고 훌륭한 언어라고 생각해오고 있었다. 그리고 3개의 도구중 가장 크로스 플랫폼 환경을 완벽하게 지원하고 있었다.

wxPython 이 비록 "기업환경적용" 측면에서 가장 낮은 점수를 받기는 했지만, 나머지 분야에서 최고의 점수를 받음으로 "GUI 응용" 제작을 위한 도구로 낙점되게 되었다.


2절. wxPython 프로그래밍

이번장에서는 wxPython에 대한 간단한 설명과 함께, tutorial 방식으로 wxPthon 프로그래밍이 방법에 대해서 알아보도록 할것이다. 여기에 있는 문서의 많은 내용은 wxpython.orgwiki.wxpython.org의 내용을 참고하고 있다.

이 글은 Python 에 대한 기본적인 이해를 하고 있다는 가정하에 만들어졌다. Python을 접해보지 않았다면, 우선 파이썬 튜토리얼를 읽어보고 바란다. C와 C++ 혹은 다른언어에 어느정도 익숙하다면, 2-3 일정도에 파이쓴 코드를 충분히 이해할수 있게 될것이다.

앞으로 다루게 될 내용은 Linux os 의 Red Hat 계열을 기준으로 설명되어 진다. 코드들 역시 위 환경에서 테스트되었다. Windows 환경에서의 설명은 시간이 되는대로 테스트를 해본다음에 보강 설명하도록 하겠다.


2.1절. wxPython 에 대하여

wxPython 은 Python 프로그래밍언어에 기반한 GUI toolkit(저작도구)로, 구조적이고 배우기 쉬운 Python 언어를 이용해서 graphical user interface 를 간단하고 빠른시간에 제작가능하도록 도와준다. wxPython 은 크로스 플랫폼 GUI 라이브러리인 wxWindows 를 Python 에서 지원가능하도록 c++ 을 이용해서 제작한것이다.

wxPython 는 크로스 플랫폼 툴킷이다. 즉 하나의 플랫폼에서 작동가능하도록 만든 쏘쓰는 (대부분의경우) 수정없이 다른 플랫폼에 이식이 가능하다. 현재 wxPython은 윈도우와 대부분의 Unix및 Unix 호환 시스템에서 작동한다. 매킨토시 버젼은 현재 pre-alpha 로, 조만간 지원가능할것으로 생각된다.

Python 과 마찬가지로 wxWindows역시 Open Source 모델이다. 그러므로 쏘쓰를 이용해서 무엇을 만들든지 아무런 제한이 없으며, wxWindows 쏘쓰 자체를 수정 하더라도 전혀 문제가 되지 않는다. 또한 이들을 기반으로 만들어진 wxPython 역시 공개이다. wxWindows 는 현재 2.0 안정버젼이 공개되어 있으며 Windows 3.1/95/98/NT와 Unix GTK/Motif/Lesstif, 매킨토시 버젼을 지원한다.

python 과 wxWindows, wxPython 에 대한 자세한 내용은 아래의 사이트들을 참고하기 바란다.


2.2절. wxPython 을 위해 필요한것들

요즘의 배포본은 기본적으로 wxPython 프로그래밍 가능한 환경으로 제공되므로 Linux 설치와 동시에 프로그래밍이 가능할것이다. 그렇지만 만약의 경우란것이 있으므로 간단하게 어떠한 패키지들이 필요한지 정도는 언급하고 넘어가도록 하겟다.

  • python

  • wxPython

  • wxGTK


2.3절. Hello World

역시 뭐든지 처음시작할때, "Hello World" 만큼 좋은게 없는것 같다. 우선 wxPython이 제대로 작동하는지 알아보기 위해서, 가장 간단한 어플리케이션을 하나 만들어 보도록 하겠다.

#!/usr/bin/python

from wxPython.wx import * 

class MyFrame(wxApp):
    def OnInit(self):
        frame = wxFrame(NULL, -1, "Hello World")
        frame.Show(true)
        self.SetTopWindow(frame)
        return true

app = MyFrame(0)
app.MainLoop()
			
이걸 실행시키면 다음과 같은 프로그램이 실행된다.

그림 1. Hello World

가장먼저 wxPython 을 import 시켜서 wxPython 에서 제공하는 각종 클래스와 함수들을 사용가능하도록 만든다.

모든 wxPython 어플리케이션은 wxApp 클래스에서 제공하는 OnInit 메서드를 통해서 만들어진다. OnInit 은 Windows 를 만들고, 프로그램이 처음에 시작하면서 필요로하는 각종 초기화 작업을 실행하게 된다. 그리고 wxFrame 를 이용하여 window (parent window 혹은 main frame)를 만들면된다. 처음에 윈도를 만들때 보통은 생성자(constructor)를 이용하여 윈도의 위치와 크기를 지정해주지만, 여기에서는 프로그램을 최소한 간소화 시키기 위해서, 이를 생략했다. 생략할경우에는 기본값으로 만들어지게 된다.

마지막 2개의 라인은 아마도 모든 wxPython 프로그램에 공통적으로 포함될 것이다. 마지막 2개의 라인에서 우리가 만든 어플리케이션 클래스의 instance 를 생성하고 MainLoop()메서드를 통해서 만든 클래스를 호출하게 된다. MainLoop 는 wxPython 어플리케이션의 핵심 요소로, 이벤트를 프로세싱하고 어플리케이션에 포함되어 있는 여러가지 윈도우에 이벤트를 전달하는 등의 일을 수행한다. 다행이도 우리는 이러한 과정이 일어나는 세부적인 내용을 몰라도 된다. wxPython 이 알아서 처리해 주기 때문이다. 우리는 단지 사용하기만 하면 된다.


2.4절. 실전 GUI 어플리케이션 제작

그럼 이제 실제적인 GUI 응용 프로그램을 만들어 보도록 하겠다. 보통 GUI 프로그램은 몇가지 공통적인 유저 인터페이스를 가지는데, 타이틀바, 메뉴바, 메인작업창, 상태바, 스크롤바등이다. 이러한 것들은 대부분의 GUI 응용프로그램에 있어서 거의 필수적으로 사용되는 것들이다. 아래는 이러한 요소들을 가지는 표준적인 GUI 프로그램의 단적인 모습을 보여준다.

그림 2. GUI 응용 프로그램

여러가지 GUI 응용프로그램중에서 editor 가 표준적인 GUI에 가장 충실한 화면을 보여주며 또한 구현하기가 대체로 수월하기 때문에, 이번 장에서는 wxPython 을 이용해서 editor 를 구현을 해보도록 하겠다.

2.4.1절. edit component 를 추가시키자

응용프로그램을 만드는 대부분의 경우에 있어서, 아마도 당신은 main frame(parent window) 를 용도에 맞게 최적화 시켜서 사용하길 원할것이다. 그래서 wxFrame 을 주로 사용하게 된다. wxFrame 는 가장 기본적인 main frame 만을 제공해주는 대신, 사용자가 필요로 하는 많은 사항들을 직접 추가 시켜 최적화 시키기 쉽도록 만들어준다.

#!/usr/bin/python

from wxPython.wx import *

class MainWindow(wxFrame):
    def __init__(self, parent, id, title):
        wxFrame.__init__(self, parent, -4, title, size = (200, 100),
                style=wxDEFAULT_FRAME_STYLE|wxNO_FULL_REPAINT_ON_RESIZE)
        self.control = wxTextCtrl(self, 1, style=wxTE_MULTILINE)
        self.Show(true)

app = wxPySimpleApp()
frame = MainWindow(None, -1, "small editor")
frame.Show(1)
app.MainLoop()

위의 코드는 매우 간단하다. 가장먼저 wxFrame 으로부터 상속을 받은 MainWindow 라는 클래스를 정의 하고 __init__ 메서드를 오버로딩한다. 이 메서드 안에서 wxTextCtrl 이란 간단한 함수를 사용했는데, 이 함수는 문서편집을 위한 text 창을 만들어준다. 위의 코드를 실행시켜서 문서편집 작업을 해보면, 단순히 text 입력외에도 복사(Ctrl+c), 붙이기(Ctrl+v), 삭제(Ctrl+x) 와 같은 기본적인 편집기능까지 제공하고 있음을 알수 있다.

그림 3. editor component


2.4.2절. 메뉴와 상태바의 추가

모든 GUI 어플리케이션은 메뉴바와 상태바를 가지고 있다. 위의 프로그램이 메뉴와 상태바를 지원하도록 쏘쓰코드를 수정해 보자.

#!/usr/bin/python

from wxPython.wx import *

ID_ABOUT = 101
ID_EXIT = 110

class MainWindow(wxFrame):
    def __init__(self, parent, id, title):
        wxFrame.__init__(self, parent, -4, title, size = (400, 500),
                style=wxDEFAULT_FRAME_STYLE|wxNO_FULL_REPAINT_ON_RESIZE)
        self.control = wxTextCtrl(self, 1, style=wxTE_MULTILINE)
        self.CreateStatusBar()

        filemenu = wxMenu()
        filemenu.Append(ID_ABOUT, "&About", "프로그램에 대한 정보")
        filemenu.AppendSeparator()
        filemenu.Append(ID_EXIT, "E&xit", "프로그램종료")

        menuBar = wxMenuBar()
        menuBar.Append(filemenu, "&파일")
        self.SetMenuBar(menuBar)

        self.Show(true)

app = wxPySimpleApp()
frame = MainWindow(None, -1, "small editor")
frame.Show(1)
app.MainLoop()
				

그림 4. Simple Editor_1


2.4.3절. 이벤트 다루기

위의 프로그램을 외형적인 측면으로만 본다면, GUI 어플리케이션이 가져야할 최소한의 요건을 충족시키고 있음을 알수 있다. 그러나 내부적인 기능구현은 전혀 이루어지지 않고 있다.

GUI 프로그램에서 기능의 실행은 마우스로 버튼을 클릭함으로써 대부분 발생한다. 메뉴에서 "Quit" 버튼을 클릭하면 "프로그램 종료" 기능이 수행되어야 하며, "Help" 버튼을 클릭하면 "도움말 창"을 화면에 보여줘야 한다. 위의 프로그램역시 "About" 메뉴를 선택해서 마우스를 클릭하면 "프로그램에 대한설명"을 보여줘야 하고, "Exit" 메뉴를 클릭하면 "프로그램을 종료" 시켜야 한다.

이러한 하나하나의 사건(마우스를 움직임, 클릭, 더블클릭, 드래그, 드롭 ...) 을 "이벤트"라고 한다. GUI 프로그램에서는 이러한 이벤트를 받아서, 어떤종류의 이벤트인지를 확인하고 거기에 적당한 함수를 호출시켜야한다. 이러한 이벤트 처리작업은 GUI 프로그래밍에 있어서 가장 어려운 부분이 될것이다. 마우스 위치가 어디에 있는지 확인해야하고, 클릭을 했을경우 클릭 위치가 어디인지, 선택된 메뉴범위 내에서 클릭되었는지 등등을 처리할수 있어야 하기 때문이다. 이러한 이벤트 처리를 위해서 Motif나 GTK 같은경우는 callback 함수(함수에 대한 포인터)를 호출하는 방식을 사용하며, Qt 같은경우는 signal/slot 기법을 사용한다. 어쨋든 우리는 wxpython 이 내부적으로 어떻게 이러한 이벤트를 처리하는지에 신경쓸필요 없이, wxpython 에서 제공하는 간단한 인터페이스를 이용해서 아주 쉽게 이벤트를 처리할수 있다.

#!/usr/bin/python

from wxPython.wx import *

ID_ABOUT = 101
ID_EXIT = 110

class MainWindow(wxFrame):
    def __init__(self, parent, id, title):
        wxFrame.__init__(self, parent, -4, title, size = (400, 500),
                style=wxDEFAULT_FRAME_STYLE|wxNO_FULL_REPAINT_ON_RESIZE)
        self.control = wxTextCtrl(self, 1, style=wxTE_MULTILINE)
        self.CreateStatusBar()

        filemenu = wxMenu()
        filemenu.Append(ID_ABOUT, "&About", "프로그램에 대한 정보")
        filemenu.AppendSeparator()
        filemenu.Append(ID_EXIT, "E&xit", "프로그램종료")

        menuBar = wxMenuBar()
        menuBar.Append(filemenu, "&파일")
        self.SetMenuBar(menuBar)

        EVT_MENU(self, ID_ABOUT, self.OnAbout)
        EVT_MENU(self, ID_EXIT, self.OnExit)

        self.Show(true)
    def OnAbout(self, e):
        d = wxMessageDialog(self, "샘플 프로그램\n"
                          "Made in wxPython",
                          "이 프로그램에 대하여", wxOK|wxICON_INFORMATION)
        d.ShowModal()
        d.Destroy()

    def OnExit(self, e):
        self.Close(true)

app = wxPySimpleApp()
frame = MainWindow(None, -1, "small editor")
frame.Show(1)
app.MainLoop()
				

wxPyton 에서의 이벤트발생했을때, 필요한 메서드를 부르는건 EVT_* 를 이용한다.

EVT_MENU(self, ID_ABOUT, self.OnAbout)
				
메뉴에서 ID_ABOUT 이벤트를 발생시키면, 이 이벤트는 윈도우 자신에게 보내지고, self.OnAbout 메서드를 호출하게 된다.


2.4.4절. 파일 불러오기 및 저장하기

이제 파일불러오기와 저장하기 기능만 구현하면 editor 완성이다. 기능의 구현은 간단하다 "메뉴바"에 "Open", "Save" 메뉴를 추가한다음에 Open, Save 버튼을 눌렀을경우의 이벤트에 대한 메서드만 만들면 된다.

#!/usr/bin/python

from wxPython.wx import * 

ID_ABOUT = 101
ID_EXIT = 110
ID_OPEN = 120
ID_SAVE = 130

class MainWindow(wxFrame):
    def __init__(self, parent, id, title):
        wxFrame.__init__(self, parent, -4, title, size = (400, 500), 
                style=wxDEFAULT_FRAME_STYLE|wxNO_FULL_REPAINT_ON_RESIZE)
        self.control = wxTextCtrl(self, 1, style=wxTE_MULTILINE)
        self.dirname = "."
        self.CreateStatusBar()

        filemenu = wxMenu()
        filemenu.Append(ID_ABOUT, "&About", "프로그램에 대한 정보")
        filemenu.AppendSeparator()
        filemenu.Append(ID_OPEN, "O&pen", "파일열기")
        filemenu.Append(ID_SAVE, "S&ave", "파일저장") 
        filemenu.Append(ID_EXIT, "E&xit", "프로그램종료")

        menuBar = wxMenuBar()
        menuBar.Append(filemenu, "&파일")
        self.SetMenuBar(menuBar)

        EVT_MENU(self, ID_ABOUT, self.OnAbout)
        EVT_MENU(self, ID_EXIT, self.OnExit)
        EVT_MENU(self, ID_OPEN, self.OnOpen)
        EVT_MENU(self, ID_SAVE, self.OnSave)

        self.Show(true)
    def OnAbout(self, e):
        d = wxMessageDialog(self, "샘플 프로그램\n"
                          "Made in wxPython",     
                          "이 프로그램에 대하여", wxOK|wxICON_INFORMATION) 
        d.ShowModal()
        d.Destroy()

    def OnExit(self, e):
        self.Close(true)

    def OnSave(self, e):
        dlg=wxFileDialog(self, "파일저장", self.dirname, "", "*.*", wxOPEN)
        if dlg.ShowModal() == wxID_OK:
            self.filename=dlg.GetFilename();
            self.dirname=dlg.GetDirectory();
            f=open(self.dirname+"/"+self.filename, "w")
            f.write(self.control.GetValue())
            f.close()

        dlg.Destroy()

    def OnOpen(self, e):
        dlg=wxFileDialog(self, "Choose a file", self.dirname, "", "*.*", wxOPEN)
        if dlg.ShowModal() == wxID_OK:
            self.filename=dlg.GetFilename()
            self.dirname=dlg.GetDirectory()
            f=open(self.dirname+"/"+self.filename, "r")
            self.control.SetValue(f.read())
            f.close()
        dlg.Destroy()

app = wxPySimpleApp()
frame = MainWindow(None, -1, "small editor")
frame.Show(1)
app.MainLoop()

				

다음은 "Save" 버튼을 클릭했을때의 "파일 선택 대화상자" 이다.

그림 5. my_editor 실행화면

그림 6. my_editor 실행화면

위의 그림은 코딩된 프로그램의 실행화면이다. 영어는 물론이고 한글입력도 잘되고, 파일불러오기, 파일저장등도 제대로 실행된다.

위의 프로그램은 최소한의 기능을 가지고 작동을 하지만, 완전한 프로그램은 아니다. 파일 저장전에 중복되는 파일인지 검사하는 기능이 없으며, "Exit" 버튼을 클릭했을때 변경된 내용을 저장할것인지 검사하는 기능도 빠져있다. 그밖에도 몇가지 에러처리를 위한 기능들이 빠져있다. 이러한 기능들은 숙제로 남겨두고 이 문서는 여기에서 일단락 짓도록 하겠다.


3절. 결론

이렇게 해서 wxpython 에 대한 간략한 소개와 어떤식으로 프로그래밍이 가능한지에 대해서 알아보았습니다. 다음번 문서에서는 윈도우를 어떻게 구성하는지에 대해서 알아보도록 하겠습니다.

'Python' 카테고리의 다른 글

wxPython  (0) 2007.04.12
WxPython 을 이용한 GUI 응용 제작  (0) 2007.04.12
파이썬 GUI 툴킷 Tkinter 와 wxPython파이썬  (0) 2007.04.12
wxPython?  (0) 2007.04.12
Posted by Real_G