· KLDP.org · KLDP.net · KLDP Wiki · KLDP BBS ·
Programming With Wx Windows

Copyright(c) 1999 - 2001 David Beech

원문

1. Programming with wxWindows - 첫 단계


1.1. 소개

지금부터 wxWindows 클래스 라이브러리를 이용한 새롭고 쉬운 윈도우즈 프로그램 제작법을 살펴보고자 한다. 엄밀히 말하면 wxWindows는 라이브러리 수준을 뛰어넘으며 완전한 응용 프로그램을 만들 수 있는 개발 환경을 제공한다. wxWindows의 장점은 여기서 그치지 않는데, wxWindows 그 자체가 cross-platform 라이브러리이기 때문이다. wxWindows 개발자는 다음과 같이 말하고 있다:

{{| wxWindows는 무엇인가? wxWindows는 다양한 플랫폼에 대해 똑같이 적용되며 사용하기 쉬운 GUI 개발 API를 제공한다. 각각의 플랫폼에서 사용하는 저수준 라이브러리와의 링크를 통해, wxWindows를 통해 개발된 응용프로그램은 각 플랫폼에 맞는 룩앤필을 제공한다. 또한 온라인 도움말, 네트워크 프로그래밍, 스트림 제어, 클립보드, 드래그/드롭, 멀티 쓰레딩, 다양한 이미지 파일에 대한 읽고 쓰기 기능, 데이타베이스지원, HTML 보기 및 출력기능 등과 같은 최고 수준의 GUI 기능들이 포함되어있다. |}}

wxWindows는 매우 고품질의 저작물로, 실제 몇몇 사람들은 돈을 주고 살만하다하여 "상업용 품질"이라는 말을 쓰기도 한다. 그러나 초보자에게는 어려울 수 있다는 wxWindows에 대한 비판 역시 사실이다: 실제로 입문하는 과정이 약간 복잡하다. 이러한 비판에 대해서 다음과 같은 긍정적인 답변을 해줄 수 있다. 프로그래머가 클래스와 함수, 프로그래밍 테크닉과 같은 wxWindows의 아키텍쳐에 대한 개념을 잡게된다면 유용한 응용프로그램을 짜는 일은 상대적으로 쉬워지기 때문에 입문 단계의 어려움을 충분히 보상받는다. 또한 경제적으로도 장점을 갖고 있는데, wxWindows는 거의 모든 응용프로그램을 개발할 수 있는 풍부한 프래임워크이기 때문이다.

여기까지 읽으면서 추측했겠지만, 나는 wxWindows의 열열한 지지자이며 이 웹페이지를 통해 당신과 wxWindows에 대한 열정을 교류하길바란다.

이 글에서 나는 기초부터 차근차근 예제 프로그램을 통해 wxWindows의 구조와 클래스에 대해서 설명하려고 한다. 이 문서를 읽고 나면 wxWindows에 대한 기초를 다질 수 있을 것이다.

시작에 앞서 몇가지 사항을 일러두고자 한다.

  • 나는 특별히 훌륭한 프로그래머도 아니며 wxWindows를 통달하지도 않았다. 그러나 나는 글재주가 좀 있으며 복잡한 주제들을 명확히 설명할 수 있다고 확신한다.
  • 많은 부분은 아니지만 C와 C++에 대한 사전 이해가 필요하다. wxWindows를 배워가다보면 당신의 C/C++ 실력도 많이 향상될 것이다. 당신이 이 웹사이트의 C++과 관련한 문서들을 대부분 탐독했다면 아마도 별 문제가 없을 것이다.
  • 객체지향 프로그래밍과 클래스에 대한 사전 이해가 필요하다. 이 또한 이 웹사이트의 문서가 도움이 될 것이다.
  • 초기 작업은 MS 윈도우 환경 (Win95/98/2K) 에서 이루어졌다. MingW 패키지와 wxWindows로 개발 환경을 구성하였다.

만약 당신이 C++ 프로그램에 익숙하지 않지만 wxWindows를 사용하여 실제 GUI 프로그램을 어서 빨리 개발하고싶다면 이 문서는 매우 큰 도움이 될 것이다. 자 함께 가보자!

1.2. wxWindows의 구조와 첫번째 프로그램

여기가 출발점이기 때문에, 모든 걸 매우 간단하게 시작하겠다. wxWindows와의 첫 대면은 그 가장 단순한 모습이다. 계속 공부를 해가면서 프래임워크의 세부사항을 하나씩 살펴볼 것이다.

wxWindows GUI 프로그램은 다음으로 구성되어있다.

  • 어플리케이션 객체 - wxApp 클래스의 인스턴스이다
  • 프래임 객체 - wxFrame 클래스의 인스턴스이며 프래임은 메뉴바, 상태바, 아이콘등을 가질 수 있다
  • 프래임 객체는 텍스트 컨트롤이나 버튼, 분리자등의 다른 객채를 담을 수 있는 컨테이너이다



가장 간단한 프로그램은 빈 프래임 하나로만 이루어져있으며, 이것이 그 첫번째 기본 예제이다. 이 예제가 어떤 유용성이 있는지 궁금해 하는 것이 당연하다. 프래임만 달랑 있는 예제이지만 교육적가치는 충분하다. 그래서 우리가 이것을 제일 처음으로 살펴보는 것이다. 이 단순한 프로그램은 윈도우 프로그램으로서 모든 유용한 행동할 수 있다. 이 프로그램은 시스템 메뉴를 갖고 있으며, 이동시킬 수도 있고, 크기를 조절할 수 있고, 또한 없앨 수 도 있다.

이 프로그램이 대단해보이지는 않겠지만, 우리가 손수 작성한 것이다. 이 프로그램은 또한 우리에게 시스템 자체의 GUI를 보여준다. 이 경우는 Windows 98인데, wxWindows가 크로스플랫폼 프래임워크임을 기억한다면 Linux, OS2, Mac과 같은 다른 플랫폼에서도 똑같이 적용된 다는 것을 짐작할 수 있다.

우리의 첫번째 기초 예제에 대한 소스코드는 다음과 같다.


#ifndef BASIC_H
#define BASIC_H

class BasicApplication : public wxApp
{
  public:
    virtual bool OnInit();
};

class BasicFrame : public wxFrame
{
  public:
    BasicFrame( const wxChar *title,
                int xpos, int ypos,
                int width, int height);
    ~BasicFrame();

};
#endif



이것은 헤더 파일이다. 이 기초 응용프로그램은 2개의 클래스를 선언한다:

  • wxApp로 부터 상속된 BasicApplication
  • wxFrame으로 부터 상속된 BasicFrame

BasicFrame을 위한 생성자와 소멸자를 선언했지만, BasicApplication에 대해서는 OnInit() 멤버함수를 오버라이딩할 뿐이다. 오버라이딩한 멤버함수이기 때문에 가상으로 선언된 점을 잊지말자.


#include <wx/wx.h>
#include "basic.h"

IMPLEMENT_APP(BasicApplication)

bool BasicApplication::OnInit()
{
  BasicFrame
   *frame = new BasicFrame("Basic", 50, 50, 450, 300);

  frame->Show(TRUE);
  SetTopWindow(frame);
  return TRUE;
}

BasicFrame::BasicFrame
             (const wxChar *title,
                    int xpos, int ypos,
                    int width, int height)
             : wxFrame
                ( (wxFrame *) NULL,
                   -1,
                   title,
                   wxPoint(xpos, ypos),
                   wxSize(width, height)
                )
{

}

BasicFrame::~BasicFrame()
{

}



이것은 basic.h에 선언한 클래스의 정의와 구현을 담고있다.

첫번째로 눈에 띠는 것은 다음의 매크로이다.

IMPLEMENT_APP(BasicApplication)

우리의 관점에서 보면 이 매크로는 BasicApplication 객체를 생성하고 응용프로그램의 시작점을 제공한다. 이것은 int WINAPI WinMain( ... ) 함수가 하는 모든 일을 대체한다.

OnInit() 멤버함수는 적절한 x, y 값과 윈도우의 폭, 높이, 그리고 제목을 갖는 BasicFrame의 인스턴스를 생성한다. 그리고 나서 프래임을 보이기 위해 Show() 멤버함수를 호출한다. wxFrame 클래스를 살펴보면 Show() 멤버함수를 찾을 수 없는데 이것은 wxFrame이 wxWindow로 부터 상속되어서 wxWindow의 멤버함수인 Show() 멤버함수를 함께 물려받았기 때문이다. 많은 클래스가 다른 클래스로 부터 상속되었기 때문에, 부모 클래스의 멤버함수를 간과하지 말아야한다. 초보자라면 이점을 꼭 기억해야 한다.

OnInit()는 이어 wxApp의 멤버 함수인 SetTopWindow()를 호출한다. 생성자와 소멸자에는 다른 새로운 내용을 첨가하지 않았다.


#include "wx/msw/wx.rc"



마지막으로 리소스 파일이다. 이 간단한 예제의 경우 어떤 특별한 리소스가 사용되지는 않지만, 최소한 다음 리소스 파일은 항상 포함해야 한다.


#include "wx/msw/wx.rc"


이 후에 예제에서는 우리가 작성한 리소스를 첨가 할 것이다. 이제 남은 일은 빌드하여 프로그램을 실행해 보는 것이다.

1.3. MingW를 이용하여 wxWindows 프로그램 빌드하기



wxWindows 배포판은 많은 샘플파일과 다양한 플랫폼에서 샘플 파일을 컴파일할 수 있게 해주는 Makefile을 포함하고 있다. 사용자가 빌드 옵션등 세부사항을 신경쓰지 않아도 된다는 점에서 이 Makefile을 이용하는 것이 프로그램을 빌드하는 훌륭한 방법임에도 불구하고, 내 경우는 몇가지 혼란을 겪었음을 인정해야만 하겠다. 실제로 단점 또한 있었는데, 빌드시 헤더 파일과 라이브러리의 경로를 명시해야만 하는 것이었다.

나는 wxWindows의 헤더 파일과 라이브러리 파일을 각각의 표준 위치에 보관하는 것이 더 도움이 된다는 것을 알아냈다. 라이브러리를 빌드하여 설치한 후 나는 wx 헤더 파일을 표준 위치로 이동 시켰다. 내 윈도우 시스템에서는 MingW 트리에 위치하며 리눅스 시스템에서는 /usr 또는 /usr/local 트리에 위치한다. (/usr/lib, /usr/include 또는 /usr/local/lib, /usr/local/include).

이렇게 라이브러리와 헤더파일의 재배치는 초보자들을 혼란스럽게하는 것들 중 하나를 없애주는 장점이 있다. 이렇게 해서 예제를 빌드하기 위한 다음의 Makefile을 쓸 수 있게 된다.


PROGRAM = basic
OBJECTS = ${PROGRAM}.o ${PROGRAM}_resources.o
RC = windres.exe
CC = g++

INCLUDES =

CCSW1 = --pipe -fvtable-thunks -c -D_X86_=1 -DWIN32 -D_WIN32 -DWINVER=0x0400 -D__WIN95__ \
-D__GNUWIN32__ -D__WIN32__ -DSTRICT  -D__WXMSW__ -D__WINDOWS__\
-Wall -fno-pcc-struct-return -O2 -fno-rtti -fno-exceptions

CCSW2 = --pipe -fvtable-thunks -Wl,--subsystem,windows -mwindows

LIBS  = -lwx -lxpm -lcomctl32 -ladvapi32 -lwsock32 -lole32 -loleaut32 -luuid

RESSW = --include-dir c:/gcc-2.95.2-1/i386-mingw32msvc/include \
        --define __WIN32__ --define __WIN95__ --define __GNUWIN32__

.SUFFIXES: .o .cpp

all:    ${OBJECTS}
$(CC) -o $(PROGRAM) ${OBJECTS} ${CCSW2} ${LIBS}

.cpp.o:
$(CC) ${CCSW1} ${INCLUDES} -c -o $@ $<

${PROGRAM}_resources.o:
$(RC) ${RESSW} ${PROGRAM}.rc $@

.PHONY : clean

clean:
echo cleaning up
rm $(OBJECTS)
rm *.$$$$$$
rm ${PROGRAM}.exe



프로그램을 빌드하기 위해 적당한 디렉토리에 소스파일들을 생성한다.

  • basic.h
  • basic.cpp
  • basic_resources.rc
  • Makefile
파일이 저장된 디렉토리에서 make를 실행한다.

  • make
프로그램을 실행한다.

  • basic
파일이 필요할 경우 다음을 내려받을 수 있다 ([http]http://www.bzzt.net/downloads/wxbasic0.zip).

역자 주

  • wx-config 라는 프로그램을 사용해도 됩니다.
  • wx-config --cxxflags
  • wx-config --libs
  • ex) gcc -o outfile sourcefile wx-config --cxxflags --libs

1.4. 사용한 wxWindows 클래스


우리는 오직 2개의 클래스만을 사용했다:

  • wxApp
  • wxFrame
이로부터 우리 자신의 클래스인 BasicApplication과 BasicFrame을 유도했다. 이제 이 두 클래스에 대해서 자세히 알아보려고 한다. 단, 더욱 완전한 클래스 설명은 wxWindows 문서를 참조하길 바란다.

  • wxApp

wxApp 클래스는 30개가 넘는 참조가능한 멤버를 가지고 있지만, 이 단계에서 각각을 자세히 살펴볼 필요는 없다. 추후에 wxWindows 이벤트 시스템을 다룰 때, wxApp에 대해서 자세히 알아보기로 하자.

  • wxFrame

wxFrame 클래스가 하는 일은 "눈에 보이기" 때문에 시간을 할애하여 각각의 속성에 대해 알아보기로 하자.

wxFrame 생성자


wxFrame
( wxWindow* parent,
  wxWindowID id,
  const wxString& title,
  const wxPoint& pos = wxDefaultPosition,
  const wxSize& size = wxDefaultSize,
  long style = wxDEFAULT_FRAME_STYLE,
  const wxString& name = "frame"
)



우리가 만든 BasicFrame의 생성자는 제목, x/y 좌표, 폭, 그리고 높이 이렇게 오직 4개의 매개변수를 갖는다. wxFrame의 생성자는 그 밖의 매개변수들을 갖는다: wxWindow 포인터, 스타일, 이름, wxWindowID. wxWindowID는 정수로 -1일 경우 기본값이 사용된 것이다. 때때로 당신이 지정한 ID로 설정하는 것이 유용할 때가 있다. 포인터는 부모 윈도우가 1개일 경우, 부모 윈도우를 가리킨다. 우리의 경우 이 포인터가 NULL을 가리키는데 부모윈도우가 없기 때문이다. 이름 매개변수는 ID와 같이 프로그래머에 의해서 지정될 수 있으며 프래임이 참조되고 있다면 유용하다. 프래임 스타일은 매우 다양한 값을 가질 수 있다:

윈도우 스타일

  • wxDEFAULT_FRAME_STYLE. wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxRESIZE_BOX | wxSYSTEM_MENU | wxCAPTION로 정의 된다.
  • wxICONIZE 아이콘화(최소화)된 프래임을 보여준다. Windows에서만 사용가능.
  • wxCAPTION 프래임에 캡션을 넣는다.
  • wxMINIMIZE wxICONIZE과 같다. Windows에서만 사용가능.
  • wxMINIMIZE_BOX 프레임에 최소화된 박스를 표시한다.
  • wxMAXIMIZE 최대화된 프래임을 표시한다. Windows에서만 사용가능.
  • wxMAXIMIZE_BOX 프레임에 최대화된 박스를 표시한다.
  • wxSTAY_ON_TOP 항상 최상위로 유지한다. Windows에서만 사용가능.
  • wxSYSTEM_MENU 시스템 메뉴를 표시한다.
  • wxSIMPLE_BORDER 외곽 경계선과 장식을 하지 않는다. GTK와 Windows에서만 사용가능.
  • wxRESIZE_BORDER 윈도우 주위에 크기조절 가능한 외곽선을 표시한다. Unix에서만 사용가능.
  • wxFRAME_FLOAT_ON_PARENT 프래임이 부모 윈도우의 위에 위치하도록 하며 작업표시줄에 나타나지 않는다. 이 스타일이 없을 경우, 프래임은 최상위 윈도우로 생성되며 부모 윈도우에 의해서 가려질 수 있으며 작업표시줄에 프래임 제목이 표시된다. Windows와 GTK에서 사용가능.
  • wxFRAME_TOOL_WINDOW 작은 제목막대를 갖는 프래임이 생성된다. 프레임 제목은 작업표시줄에 나타나지 않으며, Windows에서만 사용가능하다.

프래임 스타일은 다른 스타일과 OR 연산하여 사용할 수 있다. 우리가 만든 BasicFrame과 같이 스타일 정의가 없을 경우, 기본값이 적용된다. 즉 BasicFrame은 wxMINIMIZE_BOX와 wxMAXIMIZE_BOX, wxSYSTEM_MENU, wxCAPTION, wxRESIZE_BOX의 스타일 속성을 갖게 된다.

이렇게 바꾸어 본다면 ...


BasicFrame::BasicFrame
             (const wxChar *title,
                    int xpos, int ypos,
                    int width, int height)
             : wxFrame
                ( (wxFrame *) NULL,
                   -1,
                   title,
                   wxPoint(xpos, ypos),
                   wxSize(width, height),
                   wxSIMPLE_BORDER
                )
...



여기서 보듯, wxFrame 생성자에 스타일 인자를 넘기도록 BasicFrame 생성자를 바꾸면 다른 스타일의 효과를 볼 수 있다.

24개에 달하는 wxFrame의 멤버중 멤버함수는 다음과 같다:

  • CreateStatusBar
  • CreateToolBar
  • GetTitle
  • SetIcon
  • SetMenuBar
  • SetStatusBar
  • SetStatusText
  • SetToolBar
  • SetTitle

당신은 앞으로 공부해가다보면 이들 대부분을 사용할 것이지만, 지금은 SetTitleGetTitle 멤버 함수에 대해서 연습을 해보자.

프래임 제목과 프래임 이름


BasicFrame::BasicFrame
             (const wxChar *title,
              int xpos, int ypos,
              int width, int height)
             : wxFrame( (wxFrame *) NULL,
                         -1,
                         title,
                         wxPoint(xpos, ypos),
                         wxSize(width, height),
                         wxDEFAULT_FRAME_STYLE,
                         "Fred"
                      )
{

}



이름 인수를 wxFrame 생성자로 넘길 수 있도록 BasicFrame 생성자를 수정하였다. 이제 다음 줄을 BasicApplication::OnInit() 함수 내 frame->Show(true)줄 앞에 추가하자.


  frame->SetTitle(frame->GetTitle() + " " + frame->GetName());



프로그램을 빌드하고 실행해보면 그 효과를 볼 수 있을 것이다. GetName() 멤버함수는 어디서 온 것일까?

1.5. 요약

첫번째 학습에서는 상당한 양의 기초를 쌓았다. 당신은 wxWindows 프래임워크의 기초를 살펴봤으며 아키텍쳐에 대한 어떤 감을 잡았을 것이다. wxWindows 문서의 클래스 레퍼런스를 찾아 보는 것을 잊지 말길 바란다. 처음에 혼란스러울지 모르나 연습을 통해 상당부분 해결할 수 있다. 또한 시간이 있을 때 wxWindows의 소스코드를 탐독해볼 것을 권한다. 그 안에서 많은 유용한 주석문들을 발견할 수 있을 것이며 또한 이 훌륭한 패키지가 담고 있는 것을 확실히 이해할 수 있는 출발 점이 될 것이다.

2. Programming with wxWindows - 컨트롤 사용하기


2.1. 소개


앞 세션에서는 wxWindows의 기본 골격을 살펴보았고, 가장 간단한 응용프로그램을 만들어 보았다. 공부를 많이 한 것은 아니지만, 일반적인 윈도우 프로그램의 기본 기능을 알게 되었다. 다음 단계는 몇가지 기능을 추가해보는 것으로, 이를 통해 별로 유용해보이지는 않지만, 중요한 무엇인가를 개발할 것이며 wxWindows의 프래임워크에 대해 더욱 배우게 될 것이다. 이 세션에서는 텍스트 컨트롤, 메뉴, 상태바를 BasicFrame에 추가하려고 한다. 먼저 텍스트 컨트롤을 추가해보자.

2.2. 텍스트 컨트롤 추가하기

텍스트 컨트롤을 갖는 응용프로그램이 여기 나와있다.


텍스트 컨트롤은 텍스트 편집기에 필요한 모든 기능을 제공해준다. 예를 들면 커서 이동, 텍스트 삽입, 선택과 삭제, 잘라내기, 복사, 붙여넣기 등이다. 또한 텍스트 컨트롤에 몇가지 간단한 기능을 더해주면 파일 열기, 저장, 새 이름으로 저장, 되돌리기, 다시하기, 폰트 선택등의 기능을 수행할 수 있다.

wxTextCtrl의 가장 간단한 스타일은 단 한개의 편집 줄만을 갖는 것으로, 이것은 프로그램 실행 도중, 사용자로부터 데이터를 입력 받을 때 대화상자에서 이용되곤한다. 여러 줄을 다룰 수 있도록 텍스트 컨트롤을 수정하는 것은 단순히 스타일을 고침으로써 해결된다. 또한 많은 양의 데이타를 다뤄야 하는 텍스트 컨트롤을 만드는 것 역시 스타일을 수정함으로써 얻어질 수 있다.

다음은 텍스트 컨트롤을 갖는 BasicFrame에 대한 소스코드이다.


#ifndef BASIC_H
#define BASIC_H

class BasicApplication : public wxApp
{
  public:
    virtual bool OnInit();
};

class BasicFrame : public wxFrame
{
  public:
    BasicFrame( const wxChar *title,
                int xpos, int ypos,
                int width, int height);
    ~BasicFrame();
    wxTextCtrl *theText;

};
#endif



다음 줄을 제외하고 헤더파일은 거의 바뀐점이 없다.


    wxTextCtrl *theText;



이것은 theText라는 wxTextCtrl 포인터를 선언하는 것이다.


#include <wx/wx.h>
#include "basic.h"

IMPLEMENT_APP(BasicApplication)

bool BasicApplication::OnInit()
{
  BasicFrame *frame
    = new BasicFrame
       ("wxWindows Basic Steps - Step 1:"
        " A simple application",
         50, 50, 200, 200);

  frame->Show(TRUE);
  SetTopWindow(frame);
  return TRUE;
}

BasicFrame::BasicFrame
             (const wxChar *title,
              int xpos, int ypos,
              int width, int height)
             : wxFrame( (wxFrame *) NULL, -1, title,
                        wxPoint(xpos, ypos),
                        wxSize(width, height))
{
  theText = (wxTextCtrl *) NULL;

  theText
   = new wxTextCtrl
  ( this,
    -1,
    wxString("This is a text control\n\n"
             "The text control supports"
                   " basic text editing operations\n"
             "along with copy, cut, paste, "
                   "delete, select all and undo.\n\n"
             "Right click on the control"
                   " to see the pop-up menu.\n"
            ),
    wxDefaultPosition,
    wxDefaultSize,
    wxTE_MULTILINE
  );
}

BasicFrame::~BasicFrame()
{

}



생성자를 살펴보면, 몇가지 기본 매개변수로 텍스트 컨트롤이 만들어짐을 알 수 있다. 여기서 다음 줄을 눈여겨보자.


theText = (wxTextCtrl *) NULL;



이 프로그램에서는 위 줄은 불필요하지만, 포인터의 안전을 위함임을 기억하자. 프로그램이 복잡해질수록, 아직 존재하지 않는 객체를 가리키는 포인터를 종종 선언하게 된다. 때때로, 포인터가 가리킬 대상을 만들어 내기 전에 그 포인터를 사용하게 되는 함정에 빠진다. 초기화되지 않은 포인터의 사용은 짜증나고 황당한 결과를 초래할 수 있다. 그렇게 되면 몇시간이 될지 모르는 귀중한 당신의 시간을 낭비할 수 있다. As a matter of course you should always initialize any pointer you have declared in an interface file. Other people will say "It doesn't matter, I intend to init the thing straight away!". I say - ignore them and avoid disturbing the even tenor of your life with bad pointers. I do this out of a sense of brotherly concern for your well-being.

다음과 같이 텍스트 컨트롤 생성자를 호출할 때, 매개 변수로 this 포인터를 넘기는데 이것은 부모윈도우에 대한 참조로 this는 이미 생성되었거나 생성중인 부모 frame을 가리키고 있다.


theText = new wxTextCtrl( ... )



2.3. 텍스트 컨트롤 클래스 - wxTextCtrl

wxTextCtrl 생성자


wxTextCtrl
( wxWindow* parent,
  wxWindowID id,
  const wxString& value = "",
  const wxPoint& pos,
  const wxSize& size = wxDefaultSize,
  long style = 0,
  const wxValidator& validator,
  const wxString& name = "text"
)



만약 당신이 이 생성자와 이전 예제를 비교해보면, 이전 세션에서 BasicFrame을 만들었을때와 똑같이 많은 기본값을 사용했음을 볼 수 있다. 생성자 선언 방식의 예처럼, 이 프래임워크에 관해 배운 많은 새로운 것들은 다른 프래임 웍에 대해서 똑같이 적용될 수 있다.

당신은 이미 wxFrame 생성자에 대해서 배웠으며, wxTextCtrl 생성자의 경우도 매개변수의 순서와 형이 wxFrame 생성자와 비슷함을 알 수 있을 것이다. wxTextCtrl은 텍스트에만 적용되는 wxValidator 클래스라는 새로운 매개변수를 소개하고 있다. 여기서는 이 클래스에 대해 자세히 다루지 않겠다. 이것은 꽤 복잡하다. validator는 프로그램 데이타 구조와 컨트롤 사이에서 전달되는 데이타를 확인하기 위해서 사용된다고 알아두면 충분하다. 예를 들면 validator는 텍스트만 입력받거나 혹은 숫자만 입력받도록 입력을 제한할 때도 사용될 수 있다. wxTextCtrl 생성자의 나머지 매개변수는 wxFrame와 똑같다.

  • wxWindow* parent - 부모 윈도우를 가리키는 포인터. 프래임은 NULL일 수 있으나 wxTextCtrl은 NULL 값을 가질 수 없다
  • wxWindowID id - 컨트롤의 ID
  • const wxString& value = "" - 초기값 (예제를 참조),
  • const wxPoint& pos - 왼쪽 상단의 x, y 좌표
  • const wxSize& size = wxDefaultSize - 높이와 폭,
  • long style = 0 - 스타일 (아래를 참조),
  • const wxString& name = "text" - 컨트롤의 이름

wxTextCtrl 윈도우 스타일

  • wxTE_PROCESS_ENTER - wxEVENT_TYPE_TEXT_ENTER_COMMAND 메시지를 생성한다 (그렇지 않으면 키입력은 컨트롤에 의해서 내부적으로 처리되거나 대화상자 컨트롤 사이 이동을 위해서 사용된다)
  • wxTE_PROCESS_TAB - TAB이 입력되면 컨트롤는 EVT_CHAR 메시지를 받는다 - 일반적으로 TAB은 다음 컨트롤로 이동하는데 이용된다. 이 스타일을 갖는 컨트롤의 경우 다음 컨트롤로 이동할 때 여전히 Ctrl-Enter를 이용할 수 있다.
  • wxTE_MULTILINE - 여러 줄을 갖는 텍스트 컨트롤을 만든다.
  • wxTE_PASSWORD - 텍스트가 *표시로 반향된다.
  • wxTE_READONLY - 편집을 불가능하게 한다.
  • wxHSCROLL - 수평 스크롤바를 만든다. GTK+에서는 효과가 없다.
  • wxTE_RICH - Win95/98에서 32K 제한이 없는 텍스트 컨트롤을 만든다. WinNT와 같은 다른 플랫폼에서는 이러한 제한이 아예 없다.

프래임 스타일 처럼 텍스트 스타일도 하나 이상 OR 연산할 수 있는데, 기본값은 0으로 폭과 높이에 관계없이 한줄의 텍스트 컨트롤을 생성한다. 가능한 스타일 조합과 불가능한 조합을 알려면 몇가지에 대해서 실험해봐야 한다. 한 예로 wxTE_MULTILINE | wxTE_PASSWORD는 가능할까?

프로그램 빌드하기:

하나의 디렉토리에 소스파일을 만든다.

  • basic.h
  • basic.cpp
  • basic_resources.rc
  • Makefile

make를 실행한 후, 프로그램 실행! * make * basic

원하면 예제 파일을 다음에서 내려받을 수 있다 .

2.4. 메뉴바 더하기

프래임과 텍스트 컨트롤은 작지만 상당한 기능을 제공하지만, 실제 응용프로그램 작성까지는 여전히 몇 발자국이 남아있다. 추가할 다음 컨트롤은 메뉴바로 사용자가 프로그램에 각 명령을 전달할 수 있도록 해준다. 헤더 파일은 다음 추가 사항을 제외하고는 바뀐점이 없다:


enum
{ BASIC_EXIT     = 1,
  BASIC_OPEN   = 100,
  BASIC_ABOUT = 200
};



몇몇 괄호로 싸인 부분은 바뀐 점이 없기 때문에 생략하였음을 알려둔다. enum 선언문은 메뉴 항목과 연결시키기 위해 선언되었다.

다음 줄은 두개의 새로운 데이타 멤버를 갖는 BasicFrame 클래스를 선언한다.


wxMenuBar  *menuBar;
wxMenu     *fileMenu;



하나는 메뉴바이고 다른 하나는 메뉴이다. 물론 메뉴바는 프래임의 가장 윗부분에 위치한다. 메뉴는 드롭다운 메뉴이다.


#include <wx/wx.h>
#include "basic.h"

IMPLEMENT_APP(BasicApplication)

bool BasicApplication::OnInit()
{ ...
  return TRUE;
}

BasicFrame::BasicFrame
             ( ... )
{
  theText = (wxTextCtrl *) NULL;
  menuBar  = (wxMenuBar *) NULL;
  fileMenu = (wxMenu *)NULL;

  theText = new wxTextCtrl( ... );

  fileMenu = new wxMenu;
  fileMenu->Append(BASIC_OPEN,  "&Open file");
  fileMenu->Append(BASIC_ABOUT, "&About");
  fileMenu->AppendSeparator();
  fileMenu->Append(BASIC_EXIT,  "E&xit");

  menuBar = new wxMenuBar;
  menuBar->Append(fileMenu, "&File");
  SetMenuBar(menuBar);
}

BasicFrame::~BasicFrame()
 ...



프로그램 파일에는 다음을 추가하였다. 역시 포인터의 안전을 위함이다.


  menuBar  = (wxMenuBar *) NULL;
  fileMenu = (wxMenu *)NULL;



wxMenu의 인스턴스를 생성한다.


  fileMenu = new wxMenu;



새로운 파일 메뉴에 항목을 추가하기위해서 Append()와 AppendSeparator()를 사용한다.


  fileMenu->Append(BASIC_OPEN,  "&Open file");
  fileMenu->Append(BASIC_ABOUT, "&About");
  fileMenu->AppendSeparator();
  fileMenu->Append(BASIC_EXIT,  "E&xit");



마지막으로 wxMenuBar의 인스턴스를 생성하며 파일 메뉴를 여기에 추가한 후, 프레임에 메뉴바를 추가하기 위해서 BasicFrame의 멤버함수인 SetMenuBar()를 호출한다.


  menuBar = new wxMenuBar;
  menuBar->Append(fileMenu, "&File");
  SetMenuBar(menuBar);



이제 "E&xit"나 "&File"에서 사용한 앰퍼샌드 &가 무엇을 의미하는지 알아보자. 이것은 메뉴바나 메뉴에서 사용될 단축키를 의미한다. 이시점에서 당신은 우리가 메뉴를 클릭하거나 Alf-F를 눌렀을 때 메뉴를 보여주는 것외 별다른 일을 하지 않는다고 생각할 것이다. It will soon though when we look at events, meanwhile we need to add a statusbar.

빌드 하는 법:

한 디렉토리에 다음 소스파일을 만든다.

  • basic.h
  • basic.cpp
  • basic_resources.rc
  • Makefile

여기서 make를 실행하고, 프로그램을 실행한다.

  • make
  • basic
다음에서 소스파일을 얻을 수 있다 .

2.5. 상태바 추가하기

상태바를 추가하는 것은 이보다 더 간단할 수 없다. 다음줄을 추가한다:


CreateStatusBar(3);




  SetMenuBar(menuBar);
  CreateStatusBar(3);



이것으로 BasicFrame에 3개 필드를 갖는 상태바가 추가된다. 우리는 wxFrame의 SetStatusBarText("문자열",정수)를 이용하여 필드에 문자열을 써넣는다. 상태바가 3개의 필드를 갖고 있기 때문에 가능한 정수값은 0, 1, 또는 2 이다. 앞서 나온 예제를 다음으로 수정해보면 0번째 상태바 필드에 Append()멤버함수의 3번째 인수가 표시됨을 알 수 있다.


  fileMenu->Append(BASIC_OPEN,  "&Open file", "Open an existing file");
  fileMenu->Append(BASIC_ABOUT, "&About", "Who wrote this!");
  fileMenu->AppendSeparator();
  fileMenu->Append(BASIC_EXIT,  "E&xit", "Stop wasting time.");



2.6. 사용한 wxWindows 클래스

우리는 wxMenuBar와 wxMenu를 새로 알게되었고, 상태바를 생성/조정을 위해서 CreateStatusBar() 멤버함수를 사용하였다. wxMenuBar와 wxMenu에 대해 자세히 알아보는 것은 뒤로 미루고 다음번에는 메뉴 이벤트에 프로그램이 응답하는 방법에 대해서 다룰 것이다.

2.7. 요약


생각해 볼것들

  • 이 세션의 간단한 예제를 가져다가 몇가지 메뉴항목을 더 추가해보자.
  • 왜 다음과 같은 초기화를 하는가? theText = (wxTextCtrl *) NULL;
  • frame->Show(TRUE); 와 frame.Show(TRUE); 의 차이는 무엇인가?

이 세션에서는 몇가지 일반적인 윈도우 기능을 첨가함으로써 기본 응용프로그램을 더욱 발전시켰다. 흥미로운 것은 그다지 힘들지 않았다는 것이다. 이는 wxWindows가 잘 고안된 프래임웍임을 반증한다. 또한 우리는 텍스트 컨트롤을 쉽게 수정할 수 있음을 보았고, wxMenu 클래스의 유연성에 대해 힌트를 얻었다.

3. Programming with wxWindows - 이벤트 사용하기

3.1. 소개


오래전 나는 두개의 다른 운영체제에서 작업을 하였다. 하나는 시분할 시스템으로 운영체제 스케줄러가 각 사용자에서 컴퓨터 리소스를 사용할 수 있는 시간을 할당해주도록 되어있다. 다른 하나는 이벤트 드라이브 시스템으로, 이것은 일종의 산업용 프로세서 제어기인데 프로세스는 각 사용자가 리소스를 공유하도록 기다려주지 않는다. 대신 시스템은 갑작스런 원자로 노심의 용융과 같은 외부이벤트에 빠르게 응답하도록 고안되었다. 어떤 면에서 보면, GUI 시스템은 이벤드 드라이브 모델을 시분할 모델에 결합했다고 볼 수 있다.

"이벤트"는 프로그램 외부에서 발생한 어떤 흥미로운 사건을 일컫는다. 이벤트는 단축기를 누르거나 메뉴항목을 선택함으로써 발생할 수 있고, 또 타이머 작동 완료시나 통신포트에 데이타가 대기할 때, 시스템 종료 메시지를 받았을 때와 같이 프로그램 외부의 컴퓨터 환경의 변화로부터 발생할 수 있다. 이벤트의 발생원이 무엇이든간에 이것들은 적절히 다뤄질 필요가있다.

내가 이 글을 쓰는 동안, 내 윈도우 98 시스템에는 10개의 응용프로그램이 올려져있다. 이 편집기를 제외한 다른 응용프로그램은 때때로 하드디스크 불이 깜밖이는 외에 그다지 많은 일을 하는 것 같지않다. 그러나, 내가 작업표시줄의 어떤 응용프로그램을 클릭한다면, 그 응용프로그램은 활성화되면 작업할 준비를 한다. 작업표시줄은 마우스 클릭 이벤트를 수신하고, 이벤트와 관련된 정보를 운영체제가 관리하는 메시지큐에 전달한다. 그리고 해당하는 응용프로그램은 메시지를 받게 된다. <somehow the appropriate application gets a message to say it's been called.>

일반적인 이벤트 핸들링 프로그래밍 테크닉은 콜백 함수를 이용하는 것이다. 아래는 [http]FLTK에서 콜백 함수를 이용하는 예이다 (FLTK는 크로스-플랫폼 GUI 프레임웍의 일종이다):


Fl_Menu_Item menuitems[] =
 { { "&File", 0,0,0, FL_SUBMENU},
    { "&New", FL_ALT + 'n', (Fl_Callback *) new_cb },
    ...
...

void new_cb(void)
{
  if (changed)
   if ( !check_save() ) return;

  filename[0] = '\0';
  input->value("");
  set_changed(0);

}



이 프로그램 조각은 new_cb 함수와 연결된 메뉴 항목을 보여준다. 함수는 FI_Callback형을 가리키는 포인터로 형전환되었다. 응용프로그램이 운영체제로 부터 New 메뉴항목이 선택되었음을 지시하는 메시지를 받게되면, new_cb()를 호출한다. Win32 API도 이와 비슷한 기작을 갖고 있다. 이벤트에 응답해야하는 윈도우는 CALLBACK 형 함수로 정의된 함수를 갖는다.

wxWindows와 Microsoft MFC(Microsoft Foundation Classes) 프레임워크는 최소한 프로그래머 입장에서 보면 둘다 다른 테크닉을 사용한다. 물론 내부적으로는 콜백 기작이 여전히 사용되고 있다. 이 기작은 이벤트 테이블이라고 부르며, 이벤트 핸들링을 매우 간단히 다룰수 있게 해준다.

3.2. wxWindows 이벤트 테이블



BEGIN_EVENT_TABLE (BasicFrame, wxFrame)
  EVT_MENU ( BASIC_EXIT,  BasicFrame::OnExit)
  EVT_MENU ( BASIC_ABOUT, BasicFrame::OnAbout)
  EVT_MENU ( BASIC_OPEN,  BasicFrame::OnOpenFile)
END_EVENT_TABLE()



여기서 보듯, wxWindows 이벤트 테이블을 매우 간단하다. 이것은 매크로 집합으로 첫번째 줄은 이벤트 테이블 시작을 의미하고, 다음 3줄은 이벤트 상수와 클래스 멤버함수를 연결하는 부분이며, 마지막줄은 이벤트 테이블의 끝을 의미한다. 이벤트 테이블은 BASIC_OPEN과 같은 상수로 인식되는 이벤트를 BasicFrame::OnOpenFile과 같은 멤버함수에 맵핑한다.

우리는 하나의 프로그램안에 여러개의 이벤트 테이블을 작성할 수 있는데, 이를 위해서는 각 클래스가 사용하는 이벤트 테이블이 어떤것인지 구분하는 것이 필요하다. BEGIN_EVENT_TABLE 매크로는 이 이벤트 테이블이 wxFrame으로부터 유도된 BasicFrame에 속해있음을 선언한다. 또, 클래스가 이벤트 테이블을 사용하고 있음을 명확히 해줘야하는데, 이를 위해 헤더파일의 클래스 원형 내에 DECLARE_EVENT_TABLE() 매크로를 명시한다. 마지막으로 이벤트를 처리할 멤버함수를 작성해야만 한다. 다음 예를 살펴보자:


#ifndef BASIC_H
#define BASIC_H

#include <wx/wx.h>

static const wxChar
 *TITLE =
    _T("Basic - Step 3: Responding to events");

class BasicApplication : public wxApp
{
  public:
virtual bool OnInit();
};

class BasicFrame : public wxFrame
{
  public:
BasicFrame( const wxChar *title,
                    int xpos, int ypos,
                    int width, int height);
~BasicFrame();

wxTextCtrl *theText;
wxMenuBar  *menuBar;
wxMenu     *fileMenu;

void OnOpenFile (wxCommandEvent & event);
void OnAbout    (wxCommandEvent & event);
void OnExit     (wxCommandEvent & event);

  DECLARE_EVENT_TABLE()
};

enum
{ BASIC_EXIT   =   1,
  BASIC_OPEN= 100,
  BASIC_ABOUT= 200
};
#endif



이것은 헤더 파일로, 이전에 비해 새로 추가된 내용이 상당히 있다. 추가된 내용 전부가 이벤트와 관련된 것은 아니다.

첫번째로 주목할 점은 다음 부분이다:


static const wxChar
 *TITLE =
   _T("Basic - Step 3: Responding to events");



이 부분에서는 char를 가리키는 상수, 정적, 포인터를 선언했다라고 추측할 수 있다. 이 특별한 예제에서는 변수를 정적으로 선언한것이 큰 의미가 있지는 않다. 왜냐하면, 일반적으로 정적선언은 함수 호출간 값의 유지(내부 정적)나 다른 파일로 부터의 변수 은닉(외부 정적)을 보장하기 위해 이용하기 때문이다. 우리는 이 예제 파일을 다른 곳에 사용하지 않기때문에, 정적 메모리 클래스는 중복된다. 그럼에도 불구하고 정적변수로써 외부 또는 전역 변수(즉, 함수 외부에서 선언된 변수)를 선언하는 것은 좋은 연습이다. 그 이유는 우리가 이 파일을 다른 곳에 사용할 수 있기에 이름의 충돌 가능성을 사전 차단하기 위해서이다. 정적변수에 대해서는 외부, 정적, 자동 변수에 관한 참고문헌을 읽어보길 권한다.

매크로 _T는 또 다른 매크로 wxTRANSLATE의 약어로 로케일 정보와 관련이 있으나, 내가 잘 모른다! 다음은 이벤트 테이블에 대해서 살펴보겠다:

왼쪽을 보면 3개의 멤버함수가 있는데, 각각은 void 형이고, 인수로 wxCommandEvent를 갖는다. 이것들은 실제로는 우리의 콜백함수이다. 또 DECLARE_EVENT_TABLE()을 볼 수 있는데, 이 매크로는 컴파일러에게 이 클래스가 이벤트 테이블을 갖고 있음을 알려주게된다. 마지막으로, enum 선언이 있는데, 이 상수들을 이벤트의 인식자로 사용하기 위함이다.


#include "basic.h"

IMPLEMENT_APP(BasicApplication)

bool BasicApplication::OnInit()
{
 ...
}

BasicFrame::BasicFrame
 ...
{
   ...

  fileMenu = new wxMenu;
  fileMenu->Append(BASIC_OPEN, "&Open file");
  fileMenu->Append(BASIC_ABOUT,"&About");
  fileMenu->AppendSeparator();
  fileMenu->Append(BASIC_EXIT, "E&xit");

  menuBar = new wxMenuBar;
  menuBar->Append(fileMenu, "&File");
  SetMenuBar(menuBar);
  CreateStatusBar(3);
}

BasicFrame::~BasicFrame()
 ...

BEGIN_EVENT_TABLE (BasicFrame, wxFrame)
  EVT_MENU ( BASIC_EXIT,  BasicFrame::OnExit)
  EVT_MENU ( BASIC_ABOUT, BasicFrame::OnAbout)
  EVT_MENU ( BASIC_OPEN,  BasicFrame::OnOpenFile)
END_EVENT_TABLE()

void BasicFrame::OnOpenFile (wxCommandEvent & event)
{
  theText->LoadFile("data.txt");
}


void BasicFrame::OnAbout (wxCommandEvent & event)
{ wxString t = TITLE;

  t.append( _T("\nDB 2001"));

  wxMessageDialog
   aboutDialog
    ( this, t, "About Basic", wxOK | wxCANCEL);
  aboutDialog.ShowModal();
}

void BasicFrame::OnExit (wxCommandEvent & event)
{
  Close(TRUE);
}



왼쪽은 클래스를 구현한 소스파일이다. 이벤트 맵핑을 위한 테이블과 이벤트 핸들링 멤버함수를 제외하고는 바뀐점이 없다. wxWindows는 프로그래머로 하여금 다른 종류의 이벤트를 구분할 수 있도록 각각 다른 맵핑 매크로를 제공한다. 위 예제는 메뉴 이벤트를 다루기 위해서 이 테이블의 경우 세개의 EVT_MENU() 매크로를 포함하고 있다. 그 외의 매크로들로는

  • EVT_BUTTON
  • EVT_CHECKBOX
  • EVT_RADIOBUTTON
  • EVT_SLIDER
  • EVT_SCROLLBAR
등이 있는데, 이들에 대해서는 나중에 배우기로 하고, 여기는 기초에 대해서 집중하도록 하겠다.

OnOpenFile 멤버함수는 wxTextCtrl의 유용함을 보여준다. wxTextCtrl은 파일을 직접 콘트롤로 불러오는 멤버함수를 가지고 있다. 우리가 파일 메뉴의 "Open file" 항목을 클릭하면, 이로부터 이벤트가 트리거되며 운영체제는 이 이벤트를 잡아서 우리 프로그램이 적절한 함수(파일을 theText로 불러오기)를 호출하도록 알려준다.

OnAbout 멤버함수는 새로운 클래스 wxMessageDialog를 사용한다. 이 클래스와 wxCommandEvent에 대해서는 나중에 이 세션에서 다룰것이다.

소스코드가 필요하면 다음에서 내려받을 수 있다 .

3.3. 요약

생각해 볼것들

wxWindows는 윈도우 크기 변경을 돕기위해서 다음 이벤트 매크로를 제공한다:

EVT_SIZE ( BasicFrame::OnSize )

이 매크로에서는 크기변경이 현재 윈도우에 적용되는 것으로 간주하여 이벤트 ID가 명시되지 않았음을 주목하라.

위 예제에 크기 변경 이벤트를 추가하고, OnSize 이벤트 핸들러를 만들고 , 핸들러 안에 윈도우의 크기를 알아내고 제목 막대에 그 크기를 표시할 수 있도록 코드를 추가하라.

힌트: wxString 클래스와 wxString::Printf() 맴버함수가 필요할 것이다.

해답이 필요하면 다음에서 내려받을 수 있다 .

이 세션이 매우 중요하지만, 나는 짧고 제한된 범위 내에서 다루었다. 오히려 이런 점이 wxWindows 이벤트에 대한 훌륭한 기초를 다지게 도와주었을 것이라 본다. 이벤트를 다루는 것은 꽤 복잡한 일이고, 설사 wxWindows 프레임웍이 더 쉽게 고안되었음에도 불구하고 여전히 복잡스러운 점이 있다. 다음 세션에서는 공통 대화상자에 대해서 살펴보도록 하겠다.





sponsored by andamiro
sponsored by cdnetworks
sponsored by HP

Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2006-04-25 00:32:50
Processing time 0.0274 sec