Skip to main content

C++ of the Day #37 - Boost.Signals로 Qt 스타일의 Signal/Slot 따라하기 #1

Qt는 GUI프로그래밍시에 많이 사용되는 라이브러리입니다. 이 라이브러리의 가장 큰 특징은 signal/slot이라는 구조입니다. 어떤 signal이 emit되면 그 signal에 connect되어 있던 slot들이 호출됩니다. 이 방법은 Qt에서 보듯이 특히 GUI widget 프로그래밍시에 매우 유용합니다.

예를 들어 QDialog에 QTextEdit가 두 개 있고 Clear라는 QPushButton이 있어 이 버튼을 사용자가 누를때마다 QTextEdit들의 내용을 지우고 싶다고 한다면 다음과 같이 버튼의 clicked() signal을 TextEdit의 clear() slot에 연결해주기만 하면 됩니다.


connect(btn, SIGNAL(clicked()), text1, SLOT(clear()));
connect(btn, SIGNAL(clicked()), text2, SLOT(clear()));


Qt는 이러한 signal/slot을 구현하기 위해 Meta-Object System을 사용합니다. 이때 meta-object information을 생성하기 위해 moc(meta-object compiler)를 사용하게 됩니다. 따라서 C++ 컴파일러만 가지고는 Qt를 사용하는 코드를 컴파일할 수 없습니다만 Qt의 경우 qmake라는 편리한 make 시스템을 가지고 있어 불편함은 거의 없습니다. ((사실 불편함이 없는 정도가 아니라 portable한 코드를 작성하는데 매우 유용합니다.))

그럼 Qt를 사용하여 signal/slot을 정의하는 방법에 대해 살펴보겠습니다. 만들 클래스들은 Button, Slider, Text, Dialog이고 사용법은 아래와 같이 됩니다.


Button quit;
Slider sldr;
Text text;
Dialog app;

QObject::connect(&quit, SIGNAL(clicked()), &app, SLOT(quit()));
QObject::connect(&sldr, SIGNAL(valueChanged(int)), &text, SLOT(display(int)));

sldr.valueChanged(1);
sldr.valueChanged(2);
quit.clicked();


각 클래스들은 QObject에서 상속을 받아야 하며 클래스의 private 영역에 Q_OBJECT를 선언해야 합니다. Q_OBJECT는 signal이나 slot을 가지는 클래스들에는 반드시 들어가야 합니다. signal들은 구현이 필요 없으며 slot들은 테스트시 확인할 수 있도록 간단한 printout을 넣었습니다.


class Button : public QObject
{
Q_OBJECT
signals:
void clicked();
};

class Slider : public QObject
{
Q_OBJECT
signals:
void valueChanged(int);
};

class Text : public QObject
{
Q_OBJECT
public slots:
void display(int n) {
std::cout << "Text::display(" << n << ")\n";
}
};

class Dialog : public QObject
{
Q_OBJECT
public slots:
void quit() {
std::cout << "Dialog::quit\n";
}
};


이제 이 코드와 위의 사용법에 나온 코드를 컴파일하여 수행하면 다음과 같이 출력됩니다.


Text::display(1)
Text::display(2)
Dialog::quit


Qt의 경우 moc의 도움으로 간단히 signal/slot을 구현할 수 있음을 알 수 있습니다.

다음 글에서는 Boost.Signals를 사용하여 Qt와 유사한 signal/slot 구조를 구현할 수 있는 코드를 작성해 보도록 하겠습니다.

아래 코드는 다음 글에서 작성할 코드를 사용하여 위의 코드를 그대로 따라 작성해본 예제입니다. 특별히 moc과 같은 별도의 툴을 사용하지 않고 C++ 컴파일러만으로 컴파일 가능합니다.


class Button
{
public:
Signal clicked;
};

class Slider
{
public:
Signal valueChanged;
};

class Text
{
public:
void display(int n) {
cout << "Text::display(" << n << ")\n";
}
};

class Dialog
{
public:
void quit() {
cout << "Dialog::quit\n";
}
};


그리고 위의 클래스를 사용하는 코드는 다음과 같습니다.


Button quit;
Slider sldr;
Text text;
Dialog app;

connect(SIGNAL(quit, clicked), SLOT(app, Dialog::quit));
connect(SIGNAL(sldr, valueChanged), SLOT(text, Text::display));

sldr.valueChanged(1);
sldr.valueChanged(2);
quit.clicked();


위의 Qt 예제 코드와 거의 똑같죠? :-) 수행 결과 역시 Qt 예제에서 사용했던 것과 똑같습니다. 그럼 다음 시간에 뵙지요~

Comments

Popular posts from this blog

CodeHighlighter plugin test page.

This post is for testing CodeHighlighter plugin which uses GeSHi as a fontifier engine. ((Those code blocks are acquired from Google Code Search .)) ((For more supported languages, go CodeHighlighter plugin or GeSHi homepage.)) C++ (<pre lang="cpp" lineno="1">) class nsScannerBufferList { public: /** * Buffer objects are directly followed by a data segment. The start * of the data segment is determined by increment the |this| pointer * by 1 unit. */ class Buffer : public PRCList { public: Buffer() { ++index_; } PHP (<pre lang="php" lineno="4">) for ($i = 0; $i $value = ord( $utf8_string[ $i ] ); if ( $value < 128 ) { // ASCII $unicode .= chr($value); } else { if ( count( $values ) == 0 ) { $num_octets = ( $value } $values[] = $value; Lisp (<pre lang="lisp">) ;;; Assignment (define-caller-pattern setq ((:star var fo...

Textiler plugin test page

This post is for testing Textiler plugin . This plugin uses Textile engine (version 2.0.0). The sample text is come from Textile test page. (Note that the result will be vary according to your CSS options.) Supported wiki syntax Rendering result h2{color:green}. This is a title h3. This is a subhead p{color:red}. This is some text of dubious character. Isn't the use of "quotes" just lazy writing -- and theft of 'intellectual property' besides? I think the time has come to see a block quote. bq[fr]. This is a block quote. I'll admit it's not the most exciting block quote ever devised. Simple list: #{color:blue} one # two # three Multi-level list: # one ## aye ## bee ## see # two ## x ## y # three Mixed list: * Point one * Point two ## Step 1 ## Step 2 ## Step 3 * Point three ** Sub point 1 ** Sub point 2 Well, that went well. How about we insert an <a href="/" title="watch out">old-fashioned hypertext link</a>? Will the quo...

C++ of the Day #43 - SQLite3 C++ wrapper #1

The Definitive Guide to SQLite 를 읽다가 공부 겸 해서 C++ wrapper를 만들어 보았습니다. 최대한 C++ 냄새(?)가 나도록 만들어 보았습니다. :-) ((SQLite는 복잡한 관리가 필요없이 사용가능한, 파일이나 메모리 기반의, 라이브러리로 제공되는, 약 250kb 용량의, 대부분의 SQL92문을 지원하는, open source RDB입니다.)) 이 wrapper를 사용하기 위해서는 (당연하게도!) sqlite3 와 (당연하게도?) boost 라이브러리가 필요합니다. 사용 예들을 살펴보는 것으로 설명을 대신합니다. 이번 글에서는 다음과 같은 contacts 테이블이 test.db에 존재한다고 가정합니다. CREATE TABLE contacts ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, phone TEXT NOT NULL, UNIQUE(name, phone) ); Command 먼저 test.db 파일을 사용하기 위해 다음과 같이 파일 이름을 주어 connection 객체를 생성합니다. 생성과 동시에 test.db와 연결이 이루어집니다. ((생성자외에 open() 함수를 사용할 수도 있습니다.)) sqlite3pp::connection conn("test.db"); 다음은 contacts 테이블에 정보를 추가하는 가장 간단한 방법입니다. connection 클래스에서 제공하는 execute 함수를 사용합니다. ((executef 함수를 사용하면 printf와 같은 문법을 사용하여 query문을 작성할 수 있습니다.)) conn.execute("INSERT INTO contacts (name, phone) VALUES ('user', '1234')"); 위와 동일한 작업을 parameterized query를 사용하여 할 수도 있습니다. ((step()함수가 실제 query문을 수행하는 함수입니다. ...