열아홉번째 날: Qt 사용하기

저자

@yukinpl - 아는 것이 아무것도 없는 Perl 초보, eriny.net을 운영하고 있다.

시작하며

Linux에서의 GUI / Perl 에서의 GUI

Linux에서 GUI 툴킷하면 두 가지, GTK+Qt가 떠오르시나요? 그러나 펄에서는 Qt가 유독 힘을 쓰지 못하는 것 같습니다. GUI를 표현하기 위한 펄 라이브러리로는 wxPerl, Tk, Perl/Qt와 Perl/KDE, gtk2-perl 등이 있습니다. 그 중에서도 윈도우 사용자는 wxPerl을, Linux 사용자는 GTK+를 선호하는 것 같습니다. 대체 왜 그럴까요? 아무래도 Qt는 안정화 되어있다보니 모듈이 지속적으로 업데이트 되고 있지 않고 잘 알려진 모듈도 흔하지 않은 것이 가장 큰 이유인 것 같습니다. 더불어 라이센스 문제도 있겠지요(원인을 잘 아신다면 제게 알려주세요! :). 개인적으로는 Linux에서 C++ 프로그램 개발 시 GTK+보다 Qt로 더 많이 작업한 편이라 친근하기도 하고 펄에서 널리 사용되 못하는 것 같아 소개해보려 합니다.

준비물

펄에서 Qt를 사용하려면 필요한 항목은 다음과 같습니다.

데비안 계열의 리눅스를 사용하고 있다면 다음 명령을 이용해서 개발 의존 패키지를 설치합니다.

$ sudo apt-get install build-essentials cmake libqt4-dev

필요한 모듈은 다음과 같습니다.

직접 CPAN을 이용해서 설치한다면 다음 명령을 이용해서 모듈을 설치합니다.

$ sudo cpan Alien::SmokeQt List::MoreUtils Qt

사용자 계정으로 모듈을 설치하는 방법을 정확하게 알고 있거나 perlbrew를 이용해서 자신만의 Perl을 사용하고 있다면 다음 명령을 이용해서 모듈을 설치합니다.

$ cpan Alien::SmokeQt List::MoreUtils Qt

문제 해결

설치 순서

모듈 설치가 실패하는 경우 다음 순서로 하나씩 모듈 설치를 진행하면서 어느 부분에서 문제가 생겼는지 확인하도록 합니다.

libperl.so 확인

Qt 모듈을 설치하려면 사용하는 펄이 공유 라이브러리 형태로 빌드되어 있어야 합니다. 시스템에서 제공하는 대부분의 펄은 공유 라이브러리 형태로 빌드 되어 있습니다. 자신만의 펄을 사용하는 경우는 펄 빌드 옵션에서 useshrplib 항목을 확인 후 자신이 설치한 펄 디렉터리 내부에 libperl.so 파일이 있는지 확인해야 합니다.

$ perl -V | grep useshrplib
    config_args='
        -de -Dprefix=/home/askdna/perl5/perlbrew/perls/perl-5.18.1-so
        -Dusethreads -Duseshrplib -Dcccdlflags=-fPIC
        -Aeval:scriptdir=/home/askdna/perl5/perlbrew/perls/perl-5.18.1-so/bin'
    libc=, so=so, useshrplib=true, libperl=libperl.so

PERL5LIB 설정

빌드 후 테스트 진행시 Qt4 모듈Qt 모듈의 문제로 스스로가 빌드한 모듈을 제대로 찾지 못하는 경우가 있습니다. 각각의 모듈 빌드 디렉터리에서 blib/lib 디렉터리와 blib/arch 디렉터리를 검색할 수 있도록 설정해주어야 하는데 가장 간단한 방법은 PERL5LIB 환경 변수를 설정하는 것입니다.

$ export PERL5LIB=/path/to/blib/lib:/path/to/blib/arch:$PERL5LIB
$ make test

테스트가 끝나면 PERL5LIB 환경 변수에서 추가한 두 항목을 제거하도록 합니다.

qstringlistmodel.tqabstractitemmodel.t 테스트 실패

현재 Qt4 모듈Qt 모듈의 문제로 인해 상속받은 가상 함수를 호출하는 테스트는 실패하는 경우가 있습니다. 따라서 다음 파일 목록을 확인하고 해당 테스트에서 실패할 경우 일단은 강제로 설치를 진행하도록 합니다.

$ rgrep 'Qt::this()->SUPER::' Qt4-0.99.0 Qt-0.96.0
Qt4-0.99.0/qtgui/t/qstringlistmodel.t:        return Qt::this()->SUPER::buddy($index);
Qt4-0.99.0/qtgui/t/qstringlistmodel.t:        return Qt::this()->SUPER::flags($index);
Qt4-0.99.0/qtcore/t/qabstractitemmodel.t:        return Qt::this()->SUPER::buddy($index);
Qt4-0.99.0/qtcore/t/qabstractitemmodel.t:        return Qt::this()->SUPER::flags($index);
Qt-0.96.0/qtgui/t/qstringlistmodel.t:        return Qt::this()->SUPER::buddy($index);
Qt-0.96.0/qtgui/t/qstringlistmodel.t:        return Qt::this()->SUPER::flags($index);
Qt-0.96.0/qtcore/t/qabstractitemmodel.t:        return Qt::this()->SUPER::buddy($index);
Qt-0.96.0/qtcore/t/qabstractitemmodel.t:        return Qt::this()->SUPER::flags($index);

사용해보자!

다음 예제를 실행해 보죠.

#!/usr/bin/env perl

use strict;
use warnings;

use QtCore4;
use QtGui4;
use QtCore4::debug qw( ambiguous );

my $app    = Qt::Application(\@ARGV);
my $button = Qt::PushButton( 'Hello, world!', undef );

$button->resize( 200, 30 );
$button->show;

exit $app->exec;

앞의 예제를 실행했을 때의 결과는 다음과 같습니다.

버튼 생성 예제 그림 1. 버튼 생성 예제 (원본)

여기까지 잘 진행되었다면 Qt를 사용하는 것에 무리가 없을 것입니다.

SIGNAL과 SLOT

Qt를 사용하다보면 자주 만나게 되는 함수가 바로 Qt::Object::connect() 입니다. 그리고 SIGNALSLOT이라는 개념을 이해해야합니다. 이벤트 지향 프로그래밍을 이해하고 있다면 간단한 내용입니다. SIGNAL은 이벤트의 발생 조건이고, SLOT은 이벤트 처리자라고 생각하면 됩니다. 그리고 connect() 함수가 이 둘을 묶어주는 역할을 합니다. 버튼을 클릭하면 프로그램이 종료되는 간단한 예제를 살펴보죠.

#!/usr/bin/env perl

use strict;
use warnings;

use QtCore4;
use QtGui4;
use QtCore4::debug qw(ambiguous);

main();

sub main {
    my $app     = Qt::Application(\@ARGV);
    my $btnQuit = Qt::PushButton("Quit");

    $btnQuit->resize( 200, 30 );
    Qt::Object::connect( $btnQuit, SIGNAL "clicked()", $app, SLOT "quit()" );

    $btnQuit->show;
    return $app->exec;
}

Qt::Object::connect() 함수는 이벤트 발생 객체, 이벤트, 이벤트 처리 객체, 이벤트 처리자를 인자로 받습니다.

화면 구성 #1

지금까지의 예제는 하나의 컨트롤(PushButton)만 만들었는데 버젓한 화면을 구성하려면 어떻게 해야할까요? 기본적으로 제공되는 컨트롤은 부모 컨트롤를 설정할 수 있습니다. 다음은 $btnQuit PushButton$window Widget에 붙이는 예제입니다.

#!/usr/bin/env perl

use strict;
use warnings;

use QtCore4;
use QtGui4;
use QtCore4::debug qw(ambiguous);

main();

sub main {
    my $app    = Qt::Application(\@ARGV);
    my $window = Qt::Widget;

    $window->resize( 200, 50 );

    my $btnQuit= Qt::PushButton( "Quit", $window );
    $btnQuit->setGeometry( 10, 10, 180, 30 );

    Qt::Object::connect( $btnQuit, SIGNAL "clicked()", $app, SLOT "quit()" );
    $btnQuit->show;

    return $app->exec;
}

화면 구성 #2

여기까지 다다르면 무언가 만들려고 할 때 고민이 한 가지 생깁니다. Widget에 컨트롤을 붙이기는 했는데 버튼을 눌렀을 때 기존 컨트롤의 동작이 아닌 다른 동작을 수행하려면 어떻게 해야할 까요? Widget의 형태를 미리 만들어 둘 수는 없을까요? 바로 이런 경우 사용하는 것이 상속(Subclassing)입니다. Qt의 위젯을 상속하는 방법은 다음과 같습니다.

use QtCore4::isa qw( Qt::Widget );

앞의 코드와 같이 QtCore4::isa 함수를 사용해 상속받을 객체를 선택합니다. 그 후 생성자인 NEW() 메소드를 재정의(overloading)합니다.

sub NEW {
    shift->SUPER::NEW(@_);
}

이로써 기본적인 상속 과정은 끝납니다. 더 필요한 부분이 있다면 NEW() 메소드를 수정하거나 다른 메소드를 추가합니다. 이 때 기본적인 SIGNAL과 SLOT 이외의 것을 추가할 경우 다음과 같은 방법으로 명시해야 한다는 점을 유의해야 합니다.

use QtCore4::slots viewMessage => [];

이제 버튼을 눌렀을 때 Hello World를 보여주는 프로그램을 작성해 봅시다.

#!/usr/bin/env perl

use strict;
use warnings;

package ExWidget;

use QtCore4;
use QtGui4;
use QtCore4::debug qw( ambiguous );
use QtCore4::isa   qw( Qt::Widget );
use QtCore4::slots viewMessage => [];

sub NEW {
    shift->SUPER::NEW(@_);
}

sub viewMessage {
    my $msgbox = Qt::MessageBox();
    $msgbox->setText("Hello World");

    return $msgbox->exec;
}

package main;

use QtCore4;
use QtGui4;
use QtCore4::debug qw( ambiguous );
use ExWidget;

main();

sub main {
    my $app    = Qt::Application(\@ARGV);
    my $window = ExWidget();

    $window->resize( 200, 50 );

    my $btnQuit= Qt::PushButton( "View", $window );
    $btnQuit->setGeometry( 10, 10, 180, 30 );

    Qt::Object::connect( $btnQuit, SIGNAL "clicked()", $window, SLOT "viewMessage()" );

    $window->show;
    return $app->exec;
}

실행한 결과는 다음과 같습니다.

상속받은 창을 띄우는 예제 그림 2. 상속받은 창을 띄우는 예제 (원본)

정리하며

Qt에 대한 모든 내용을 다루기보다는 기본적인 설치와 사용 방법에 중점을 두어보았습니다. 기사를 읽어보면 아시겠지만(물론 이미 알고 계시겠지만 :) Qt가 그리 어려운 GUI 프레임워크는 아닙니다. 개인적으로는 Qt가 펄에서도 많은 사랑을 받았으면 좋겠습니다.

Enjoy Your Perl! ;-)

blog comments powered by Disqus