갑자기 perl로 GUI를 만들고 싶은 마음이 들어서 이런 저런 GUI를 보다가 WX가 가장 맘에 들어서 WX를 선택했습니다. 위젯도 많고 작성도 편리하다는 느낌이 들어서 요녀석으로 기억에 예전에 TCL로 짯었는데 그것보다 좀더 간결한 듯한 느낌이 들기도 합니다. wx는 python c++ perl등 대부분의 언어로 바인딩 되어 있어서 한번 배워두면 다른 언어에서 활용하기도 편리할 것 같구요
Wx어플리케이션은 다음과 같은 순서로 만들어 집니다.
1. 어플리케이션 생성
2. 위젯들을 생성하고 배열
3. 이벤트 만들기
4. 이벤트에서 설정한 함수 작성 (이벤트 만들 때 sub{}를 만들어서 할수도 있습니다.)
그럼 좀더 자세히 볼까요?를 클릭하시면 글이 계속 이어집니다 ^^
그럼 좀더 자세히 볼까요?
어플리케이션 생성과 위젯 생성은 다음과 같이 이루어 집니다.
Wx::SimpleApp의 경우에는 다음과 같이 하시면 됩니다.
my $m = Wx::SimpleApp -> new;
Wx::APP의 경우에는 다음과 같이 하시면 됩니다.
use base qw(Wx::App); #Wx의 App를 상속 받는다. App에는 어플리케이션의 뼈대가 숨어 있습니다.
sub OnInit{ # 모든 어플리케이션은 생성자가 불려지면서 OnInit를 실행합니다.
my $self = shift;#자기 자신을 인자로 받는다
#... 프레임및 위젯 생성
Wx::Frame, Wx::TextCtrl, Wx::StaticText, Wx::Button 등이 되겟습니다. 위젯의 이름을 써주시면 됩니다.
위젯ID :
위젯은 각각의 ID를 가지게 됩니다. -1을 써주면 랜덤하게 생성해 주는 것 같습니다.
라벨 :
위젯별로 다르겟지만 위젯의 스트링부분이 체워지게 됩니다. Frame은 타이틀, TextCtrl은 기본 값, StaticText는 라벨의 값이 들어가게 됩니다. 이처럼 위젯별로 달라집니다.
위치 :
부모위젯의 어디에 위치될지를 정하게 됩니다. Frame의 경우에는 부모가 없으므로 화면의 어디에 위치가 될지가 정해지게 됩니다. [가로, 세로]로 만약 뒤에 크기나 위젯스타일이 없다면 생략이 가능하지만 뒤에 붙는게 있을때는 DEFAULT_POSITION을 값대신 넣어줍니다.
크기 :
크기는 위젯의 크기를 말합니다. 이 것 역시 [가로,세로]로 넣어주게 되며 위와 같은 이유로 뒤에 다른 인자가 있을 때 생략시에는 DEFAULT_SIZE 를 사용해 줍니다.
스타일 :
위젯별로 다양한 옵션을 줄 수 있습니다. 예를 들면 TextCtrl에 wxTE_MULTILINE옵션을 사용하면 한줄이 아닌 여러줄 을 입력할 수 있습니다.
ex) my $text_input = new Wx::TextCtrl($f, 1, 'hello', [10,10], DEFAULT_POSITION, DEFAULT_SIZE, wxTE_MULTILINE);
아래코드에서 예제를 살펴보면
my $f = Wx::Frame -> new (undef, -1, "nan1004au.tistory.com",[10,10], [250,100]);
my $text_input = new Wx::TextCtrl($f, 1, 'hello', [10,10]);
my $statictext = Wx::StaticText->new( $f, 1, "I Love NamSa", [100,10]);
my $button1 = Wx::Button -> new ($f, -1, "라벨변경", [100,40] );
이제는 이벤트에 대해서 알아보겟습니다.
이벤트는 버튼이 눌렸다거나 메뉴가 클릭되었다거나 하는 특정 이벤트에 대해서 어떤 행동을 취하도록 합니다. 아래와 같은 형식으로 이루어지게 됩니다.
EVT_ooo(부모위젯, 이벤트위젯, 실행함수);
좀더 자세히 들여다 보면
부모위젯 :
위젯이 위치하고 있는 부모를 지정한다.
이벤트위젯 :
이벤트를 줄 위젯을 선택한다.
실행함수 : 이벤트가 일어났을 때 실행할 함수를 정해준다 \&함수명 또는 sub{코드}로 그 안에 직접 함수를 선언해 줄 수 있다.
이벤트의 예제를 확인해보죠
EVT_BUTTON(
$f,#상위 위젯
한 $button1,#이벤트 대상이 되는 위젯
sub{#버튼을 누르면 실행될 함수 이처럼 말고 \&button_event처럼 직접 함수를 선언해서 사용도 가능
my $string = $text_input->GetValue();#$text_input에서 입력된 것을 가져욤
$statictext->SetLabel($string);#$statictext의 라벨을 바꿈
}
);
EVT_MENU($f, $save_menu, \&save_menu_event);
EVT_MENU($f, $r_save_menu, \&r_save_menu_event);
EVT_MENU($f, $open_menu, \&open_menu_event);
그럼 간단한 프로그램 몇개를 볼까요?
아래의 예제는 Wx로 텍스트박스에 입력을 받고 라벨변경 버튼을 누르면 라벨이 바뀌도록 만들었습니다.
코use strict;
use warnings;
use Wx;
use Wx::Event qw(EVT_BUTTON EVT_MENU);
my $m = Wx::SimpleApp -> new; #상속받지 않아도 바로 사용가능하다.
my $f = Wx::Frame -> new (undef, -1, "nan1004au.tistory.com",[10,10], [250,100]);#프레임생성
#라벨
my $statictext = Wx::StaticText->new(
$f, #부모위젯
1,#이 위젯
"I Love NamSa",#라벨
[100,10],#위치
);
#한줄 입력
my $text_input = new Wx::TextCtrl($f, 1, 'hello', [10,10]);
#버튼
my $button1 = Wx::Button -> new (
$f,
-1,
"라벨변경",
[100,40],
);
#버튼을 눌럿을 때 이벤트 설정
EVT_BUTTON(
$f,#상위 위젯
$button1,#이벤트 대상이 되는 위젯
sub{#버튼을 누르면 실행될 함수 이처럼 말고 \&button_event처럼 직접 함수를 선언해서 사용도 가능
my $string = $text_input->GetValue();#$text_input에서 입력된 것을 가져욤
$statictext->SetLabel($string);#$statictext의 라벨을 바꿈
}
);
$f -> Show();
$m -> MainLoop ();
프로그램을 설명하는 것보다 주석을 보시면서 이해하시는게 빠를 것 같습니다. 이번에는 얼마전에 개인용도로 사용하려고 만든 한자풀이 프로그램을 GUI화 합니다.
이 프로그램에는 상단에 메뉴바를 넣었고 About,파일열기,파일저장,멀티라인텍스트박스 등을 넣어 봤습니다.
#!/usr/bin/perl
use strict;
use warnings;
use Wx;
use Wx qw(:everything);
use Wx::Event qw(EVT_MENU EVT_BUTTON);
my $file_path; #작업파일의 패스가 들어 있다.
#초기 창을 만든다
my $App = Wx::SimpleApp->new;
#프레임의 생성
my $f = Wx::Frame -> new(
undef,#상위 위젯이 있어야 하지만 프레임이 최상위임으로 undef
-1, #자신의 위젯 아이디
'한자풀이',#프레임의 타이틀
[20,20], #프레임의 위치 x, y
[400,500],#프레임의 크기 width, height
my $open;
#파일메뉴를 생성
my $file_menu = Wx::Menu->new; #하나의 메뉴를 생성
$file_menu->AppendSeparator();#메뉴에서 가운데 ---으로 나누는것 표시
my $save_menu = $file_menu->Append(-1,'저장');#서브메뉴
my $r_save_menu = $file_menu->Append(-1,'다른이른으로 저장');
my $open_menu = $file_menu->Append(-1,'열기');
$file_menu->AppendSeparator();
my $quit_menu = $file_menu->Append(-1,'종료');
#about
my $about_menu = Wx::Menu->new; #하나의 메뉴를 생성
my $about = $about_menu->Append(-1,'About');
#여기가지 2개의 메뉴를 만든다.
#메뉴바에 메뉴를 추가해준다.
my $bar = Wx::MenuBar->new; #메뉴바 생성
#Append(위에서 만든메뉴, 메뉴바의 라벨)
$bar->Append($file_menu,'파일'); #메뉴바에 메뉴생성
$bar->Append($about_menu,'About');
$f->SetMenuBar($bar);#프레임에 메뉴를 세팅한다.
#이벤트 설정
#EVT_MENU(상위위젯, 메뉴, 함수);
EVT_MENU($f, $save_menu, \&save_menu_event);
EVT_MENU($f, $r_save_menu, \&r_save_menu_event);
EVT_MENU($f, $open_menu, \&open_menu_event);
#exit를 사용해 종료하면 에러나더군요 이런식으로 종료이벤트를 만들어 주시기를
EVT_MENU($f, $quit_menu, sub {$_[0]->Close( 1 )});
EVT_MENU($f, $about, \&about_event);
=cut
아래와 같은 식으로 속해 있다.
메뉴를 생성하고 각각에 서브메뉴를 넣고
메뉴바를 생성하고 그 안에 메뉴를 추가한다.
프레임에 메뉴를 넣는다.
그리고 각각의 메뉴에 EVT_MENU를 이용하여 이벤트를 추가한다.
=cut
#한자를 입력받을 곳을 만든다.
my $hanja= Wx::TextCtrl->new(
$f, #상위 위젯
-1, #위젯 id
'', #글씨의 초기값
[10,10],#위치
[380,100],#크기
wxTE_MULTILINE#이 부분에 스타일을 입힌다. 여기에서는 기본적으로는 한줄만 입력하연 TextCtrl을 여러줄로 입력하는 스타일로 바꾸었다
);
#해석된 한자가 있는 곳
my $hanja_t= Wx::TextCtrl->new(
$f,
-1,
'',
[10,140],
[380,100],
wxTE_MULTILINE
);
#지금까지 해석됫던게 모두 보이는 곳
my $hanja_v= Wx::TextCtrl->new(
$f,
-1,
'',
[10,250],
[380,210],
wxTE_MULTILINE
);
#입력받은 한자를 변환하자는 함수가 실행하도록 하는 버튼
my $button = Wx::Button->new(
$f,
-1,
'한자풀이',
[10,110]
);
#버튼 이벤트 생성
EVT_BUTTON(
$f,
$button,
\&hanja
);
#한자 해석을 하는 함수
sub hanja{
use LWP::Simple;
my $string = $hanja->GetValue;#$hanja에서 텍스트를 가져온다
my $str_len = length($string);
my $newstring = "";
for(my $i=0;$i<$str_len;$i++){
my $str_pice = substr $string, $i, 1 . "\n";
if($str_pice =~ /([\x{4E00}-\x{9FA5}]|[\x{F900}-\x{FA2D}])/){
my $url = "http://hanja.naver.com/hanja?q=" . $str_pice;
my $content = get($url);
$content =~ /<dd><strong>(.*)<\/strong>/;
$str_pice .= "(" .$1. ")";
$newstring .= $str_pice;
}else{
$newstring .= $str_pice;
}
}
$hanja_t->Clear();#$hanja_t를 초기화
$hanja_t->AppendText($newstring);#$hanja_t에 새로운 스트링을 입력한다
$hanja_v->AppendText($string . "\n\n");#여기는 계속 추가하는 방식으로 할 것이므로 지우지 않고 추가
$hanja_v->AppendText($newstring . "\n=============================================\n");#해석할 때마다 구분자를 넣어준다
}
#이벤트 서브루틴 작성
#저장 메뉴를 눌럿을 때 이 이벤트 실행 파일 저장 다이얼로그를 뛰움
sub save_menu_event{
my $prevdir ="./";#저장메뉴가 열리는 곳 현제위치로
my $prevfile='';#열려서 선택되어 있는 파일
unless($file_path){#파일패스가 있으면 그대로 사용한다
my( $this, $event ) = @_;#자신과 이벤트를 받아 온다
my $dialog = Wx::FileDialog->new #파일에 관한 다이어로그 창
( $this,#상위 위젯
"해석 저장하기", #다이얼로그 타이틀
$prevdir, $prevfile,#기본적으로 선택할 디렉토리와 파일
"|*.txt|All files (*.*)|Text files (*.txt)", #확장자의 설정
wxFD_SAVE #스타일의 설정 wxFD_OPEN은 열기가 됩.
);
if( $dialog->ShowModal != wxID_CANCEL ) {#다이얼로그가 성공적으로 선택됫나?
my $string = $hanja_v->GetValue;#$hanja_v에서 문자를 가져옴
my $path = $dialog->GetPath;#방금 선택한 파일의 path를 가져옴
#파일을 저장한다
open FILE, ">$path";
print FILE $string;
close FILE;
$file_path = $path;
}
$dialog->Destroy; #다이얼로그 종료
}else{#이미 한번 연적이 있을 경우 종료
my $string = $hanja_v->GetValue;
open FILE, ">$file_path";
print FILE $string;
close FILE;
}
}
#다른이름으로 저장 위와 같음 대신 $file_path가 있어도 실행
sub r_save_menu_event{
my $prevdir ="./";
my $prevfile='';
my( $this, $event ) = @_;
my $dialog = Wx::FileDialog->new #파일에 관한 다이어로그 창
( $this, # Parent du Dialog, ici la Frame
"해석 저장하기", # Titre du Dialog
$prevdir, $prevfile, # Nom de fichier et répertoire par défaut à l'ouverture
"|*.txt|All files (*.*)|Text files (*.txt)", #확장자의 설정
wxFD_SAVE #스타일의 설정 wxFD_OPEN은 열기가 됩.
);
if( $dialog->ShowModal != wxID_CANCEL ) {
my $string = $hanja_v->GetValue;
my $path = $dialog->GetPath;
open FILE, ">$path";
print FILE $string;
close FILE;
$file_path = $path;
}
$dialog->Destroy;
}
#열기 몇가지 빼고는 위의 열기코드와 같음
sub open_menu_event{
my $prevdir ="./";
my $prevfile='';
my( $this, $event ) = @_;
my $dialog = Wx::FileDialog->new #파일에 관한 다이어로그 창
( $this,
"해석 저장하기",
$prevdir, $prevfile,
"|*.txt|All files (*.*)|Text files (*.txt)", #확장자의 설정
wxFD_OPEN #이부분의 스트알 빼고는 거의 같음
);
if( $dialog->ShowModal != wxID_CANCEL ) {
my $path = $dialog->GetPath;
open FILE, "<$path";
while(<FILE>){
$hanja_v->AppendText( $_ );#파일을 불러와 문자열을 $hanja_v에
}
close FILE;
$file_path = $path;
}
$dialog->Destroy;
}
#프로그램 정보를 출력
sub about_event{
my ($self) = @_;
my $info = Wx::AboutDialogInfo->new;#프로그램에 관한 정보를 소개하는 다이얼로그이다.
$info->SetName( 'HanJa' );#프로그램 네임
#$info->SetVersion( $demeter->version );
$info->SetDescription( "한자에 약하신분들을 문서의 한자를 풀어주는 프로그램입니다");#프로그램 설명
$info->SetCopyright( 'by NamSa' );#카피라이트
#개발 웹사이트
$info->SetWebSite( 'http://nan1004au.tistory.com', 'The NamSa web site' );
#개발자정보
$info->SetDevelopers( ["NamSa <nan1004au\@gmail.com>\n",
] );
#아트웍
my $artwork = <<'EOH'
Wx를 공부할겸 이전에 웹버전과 콘솔버전으로
개인적인 목적으로 만들었었던 한자 해석 프로그램은
GUI화 해봅니다.
확실히 GUI화 하니까 좀더 사용이 편리하네요.
WX가지고 놀게 참 많은 것 같습니다.
앞으로 좀 더 퀼리티 있는 프로그램을 만들어 보겟습니다.
EOH
;
#아트웍추가
$info -> AddArtist($artwork);
#artist세팅
Wx::AboutBox( $info );
}
#창을 보여주고 GUI 프로그램을 시작한다.
$f->Show();
$App -> MainLoop ();
Cross-Platform GUI Programming with wxWidgets 라는 책에 보면 자세히 설명 되어 있네요. ebook으로 보고 있는데 wx를 다룰때는 괜찬은 듯 싶습니다. c++기반의 책이기는 하지만요.