1. A Brief Overview of C++
Cross-Platform GUI Libaries:
Qt and Nana
2017 Summer PROG Seminar
KNU School of Computer Science and Engineering
http://cafe.daum.net/knuprog
2. 순서
• 서론
• 개요
• 한계
• GUI 프로그램의 특징
• 라이브러리(소개, 설치 과정, 특징, 레이아웃, 문제점)
• Qt
• Nana
• 종합
• 공통점
• 차이점
3. 개요
• C/C++은 강력한 언어이나, 다양한 플랫폼 위에서
작동하는 GUI 툴킷이 표준 라이브러리에 없음
• 다른 언어로 작성한 GUI 모듈을 C/C++ 핵심 모듈와
연결하는 것(binding)은 까다로움
• WinAPI나 MFC 같은 구형 라이브러리를 대체할 수
있을 만한 GUI 라이브러리가 필요함
• 따라서 open source로 공개된 C++ cross-
platform GUI toolkit 라이브러리 두 가지를
알아보고, 용도에 맞게 활용하고자 함
4. 한계
• 시간 관계상, 많은 라이브러리를 다루지 못함
• 최근까지 commit이 활발하고, 서로 비교하기 쉬운
라이브러리 두 개를 다룸
• Cross-platform이지만, Windows 10과 Visual
Studio 2015 Community 기준으로만 진행함
• 최신 버전인 VS2017은 라이브러리 x86 build 결과물이
compile error를 유발하는 치명적인 버그가 있어서 쓰지
않았음
• VS2010 같은 구 버전은 최신 C++ 표준을 제대로
구현하지 못하므로, 최소 VS2015를 권장함
5. GUI 프로그램의 특징
• 이벤트 주도 프로그래밍(event-driven program-
ming)으로 개발된다.
• 외부와 비동기적인 입출력(asynchronous I/O)이
이루어지는 대화형(interactive) 프로그램에 쓰인다.
• 이벤트 주도 프로그래밍의 구성 요소로는 event,
event loop, event handler가 있다.
6. GUI 프로그램의 특징
• Event: 프로그램에 의해 감지·처리되는 동작이나 사건
• Event loop: 계속 대기하는 루틴으로, event를 수집하고
event handler로 보냄
• Event handler(listener): 특정 event를 받아 처리하는
역할을 하는 함수
GUI
Components,
Keyboard,
Mouse
or etc.
Event
Handler
Event Event
Event Loop
7. Qt에 대한 간략한 소개
• The Qt Company에서 개발하는, C++ 기반의 open
source cross-platform 프레임워크이다.
• 방대한 라이브러리와 통합 개발 환경(IDE)을 포함한
거대한 프레임워크이다.
• 라이브러리에는 GUI뿐만 아니라, network, web view, SQL
DB, serial port, XML/JSON parser 등이 있다.
• 오랜 역사와 많은 사용자 덕분에, 정보를 얻기 쉽다.
8. Qt에 대한 간략한 소개
• 모듈(GUI 포함)의 추상화가 잘 되어 있어, 상당히
많은 플랫폼을 타깃으로 개발할 수 있다.
• Windows 10/8/7
• Linux(X11)
• macOS
• Embedded Linux
• Windows Store App (UWP), Windows RT, etc.
• Android API Level 16
• 이외에도 비공식적으로 지원하는 여러 플랫폼
• PyQt(Python porting) 등 다른 언어와도 붙여 쓸
수 있다.
9. Qt에 대한 간략한 소개
• Qt Designer를 통해, 데스크탑 UI를 WYSIWYG(What You
See Is What You Get) 방식으로 빠르게 만들 수 있다.
• Qt Creator라는 자체 통합 개발 환경(IDE)과 연동된다.
• 참고로 이 IDE에서는 MinGW로 개발하는 게 정신건강에 이롭다.
XML 파일로
저장되어,
나중에 C++
클래스 객체로
변환된다.
10. Qt에 대한 간략한 소개
• 오픈 소스(Open source) 라이선스와
상용(Commerical) 라이선스로 나뉜다.
• GPL v2/v3는 같이 사용된 모든 소스 코드를 공개(또는
그러한 수준의 방식)해야 한다(open source).
• LGPL v3는 동적 링크 라이브러리(DLL) 방식 등의 특정
조건을 따르면 소스 코드를 공개하지 않아도 된다(closed
source).
• Qt의 상용 라이선스는 일정 금액을 지불하고 자유로이 쓸
수 있다. 데스크탑/모바일 개발의 경우 개발자 수(seat)와
기간에 따라서, 임베디드 기기의 경우 기기 running
로열티까지 합쳐 부과된다.
11. Qt의 GUI 관련 모듈
• Qt Core: 모든 Qt 애플리케이션에 기본으로
포함된다.
• 각종 컨테이너와 순회자(iterator) 구현을 포함한다.
• Qt Widgets: C++ 기반의 GUI 클래스 모음이며,
UI 요소들로 데스크탑 프로그램을 만드는 데 쓰인다.
• Native한 GUI를 C++ 코드로 구성할 수 있다.
• 앞으로 비중 있게 다룰 모듈이다.
• Qt WebEngine: Google Chrominum 프로젝트의
일부로서, 웹 코드를 구현한다.
• HTML5/CSS/JavaScript를 이용한 애플리케이션 UI
개발을 지원한다.
12. Qt의 GUI 관련 모듈
• Qt QML: Qt Quick 모듈에서 사용하는 언어
(Qt Quick Markup Language) 모듈이다.
• Qt WebEngine에 포함된 JavaScript 엔진인 V8을
사용하며, OpenGL, Vulkan 등을 renderer로 쓴다.
• QML에 JavaScript를 활용하여, 생산성이 상당히 높고,
미려한 반응형 UI를 구성하기 용이하다.
• JavaScript 기반이므로, 메모리 자원을 많이 소비하고,
로딩 시간을 지체하는 등, 성능이 꽤 떨어진다. 특히
debug할 때 속이 터질 수도 있다.
• C++로 개발되어, C++ 코드와의 binding을 지원한다.
• QML은 많이 쓰이지만, C++와 많이 동떨어져 있다.
13. Qt를 활용하는 기업들
• LG전자: 스마트 TV에 들어가는 리눅스 기반 자체 OS인
webOS에 Qt WebEngine을 활용한다.
• Medec: 40년 이상의 마취학 솔루션 워크스테이션 개발
업체로서, Qt QML을 이용하여 S/W를 개발한다.
https://youtu.be/f7en65HkjeQ
14. Qt를 활용하는 기업들
• AMD: 그래픽카드 설정 및 게임 영상 녹화 S/W인
Radeon Software에 Qt QML로 UI를 제공한다.
• 블리자드 엔터테인먼트: 블리자드의 PC 게임에 대한
설치·실행·업데이트를 수행하는 블리자드 앱(Battle.net
앱)에 Qt QML과 Qt WebEngine을 사용한다.
https://youtu.be/CdvZeQfhWXo
15. VS2015에 Qt 설치하기
1. Qt open source 다운로드 페이지
(www.qt.io/download-open-source)에서
Qt Offline Installers를 Windows로 고른다.
이 작은 곳을 클릭한다!
16. VS2015에 Qt 설치하기
2. 일본(JP)의 대학 사이트 링크에서 다운로드한다.
(원래는 스웨덴의 대학 사이트가 기본으로 잡힌다!)
24. VS2015에 Qt 설치하기
10. Qt GUI Application Wizard에서 원하는 Qt
모듈을 선택한다. 여기서는 그냥 [Next]를 누르면
된다.
25. VS2015에 Qt 설치하기
11. 자동 생성되는 클래스 이름은 물론, 헤더 파일(.h)과
소스 파일(.cpp) 등의 이름까지 설정할 수 있다.
설정 후 [Finish]를 누르면 된다.
26. VS2015에 Qt 설치하기
12. main.cpp만으로, 간단한 예제 코드를 컴파일한다.
(이벤트를 발생시키려면, 좀 더 긴 코드가 필요하다.)
이 코드로 컴파일한 파일은 아직 실행할 수가 없다!
#include <QtWidgets/QApplication>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QLabel>
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
QMainWindow mainWin;
auto lab = new QLabel("Hello, <b>Qt</b>!", &mainWin);
mainWin.resize(160, 30);
mainWin.show();
return app.exec();
}
27. VS2015에 Qt 설치하기
13. Qt의 특정 DLL 파일을 복사하면, release 파일이나
debug 파일을 실행할 수 있다.(dynamic build)
아래 DLL 파일들을 ‘(Qt 폴더)Qt(버전)(버전)
msvc2015(_64)’에서 ‘(솔루션 폴더)Win32(|x64)
Release(|Debug)’로 복사해야 한다.
(‘(d)’가 붙는 것은 debug 전용 DLL 파일)
• pluginsplatformsqwindows(d).dll
→ (실행 파일 폴더)platforms
• binQt5Core(d).dll; binQt5Gui(d).dll;
binQt5Widgets(d).dll → (실행 파일 폴더)
28. VS2015에 Qt 설치하기
아래 release 전용 DLL 파일들을 합치면, 14.5 MB에
육박하지만, 정작 실행 파일은 27 KB밖에 되지 않는다.
29. Qt Widgets의 자세한 특징
• The meta-object system을 통해 widget들을
관리한다.
• Meta-object는 부가적인 데이터(meta data)를 담은
객체이다.
• QObject에 대한 subclass(derived class)의
instance는 신호를 주고 받고, 자동으로 파괴된다.
• RTTI(RunTime Type Indentification: 런타임 타입
식별)가 가능하다. 즉 런타임에도 해당 클래스의 정보를
관리한다.
• MOC(Meta-Object Compiler)를 사용하여, 소스
코드로부터 컴파일 타임에 객체들의 정보를 읽어들인다.
• Signals and slots mechanism이 작동하도록 한다.
30. Qt Widgets의 자세한 특징
• Signals and slots을 활용하여 event와 event
handler를 구현한다.
• The meta-object system에
의해 widget의 signal들과
slot들이 관리된다.
• 특정 signal을 보내면(emit),
slot으로 지정된 함수가 호출된다.
• Signal을 보낸 객체와
slot이 호출된 객체는
단방향 통신을 하게 된다.
(call-back이 아님)
31. Qt Widgets의 자세한 특징
• QObject::connect()를 통해 signal과 slot을 연결한다.
• 예전 방식은 MOC를 통해 signal과 slot의 이름을
문자열로 변환하여 런타임에 비교하는 과정을 거친다.
• 새로운 방식은 slot이 아닌 함수는 물론이고, 각종 함수자
(functor)와 람다 표현식(lambda expression)을
사용할 수 있어, 직관적이고 편리하다.
// 예전 방식(문자열 변환)
connect(but, SIGNAL(clicked()), this, SLOT(close()));
// Qt 5에 도입된 새로운 방식
connect(but, &QPushButton::clicked, this, &QtPractice::close);
connect(but, &QPushButton::clicked, [this] {
this->close();
});
32. Qt Widgets 예제
• MOC의 작동 방식 때문에, 헤더 파일(.h)과 구현
파일(.cpp)을 분리해야 한다.
• The meta-object system이 정상적으로 작동하게
하기 위해, 정해진 매크로를 써야 한다.
// main.cpp
#include "QtPractice.h"
#include <QtWidgets/QApplication>
int main(int argc, char* argv[])
{
QApplication a(argc, argv);
QtPractice w;
w.show();
return a.exec();
}
// QtPractice.h
#pragma once
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QLabel>
#include <QtWidgets/QPushButton>
class QtPractice : public QMainWindow
{
Q_OBJECT // QObject subclass에 필수적인 매크로
public:
explicit QtPractice(QWidget *parent = nullptr);
};
33. Qt Widgets 예제
// QtPractice.cpp
#include "QtPractice.h"
#include <QtWidgets/QVBoxLayout>
QtPractice::QtPractice(QWidget *parent) : QMainWindow(parent)
{
resize(200, 150);
auto place = new QWidget;
auto vLayout = new QVBoxLayout; // 세로 레이아웃
auto lab = new QLabel(QStringLiteral(
uR"(<span style="font-size: 24pt; color: green;">안녕, <b>Qt</b>!</span>)"
)); // HTML 태그를 그대로 사용할 수 있음(rich text)
vLayout->addWidget(lab);
auto but = new QPushButton(QStringLiteral(u"나가기"));
connect(but, &QPushButton::clicked, this, &QtPractice::close);
vLayout->addWidget(but);
place->setLayout(vLayout);
setCentralWidget(place);
}
34. Bonus: Qt QML 예제
• Qt Creator에서 개발 환경 설정을 마친 후, Qt Quick
프로젝트를 생성해야 한다.
• qml.qrc 파일에 QML 파일들의 위치가 저장된다.
• C++ 프로그램의 main() 함수는 아래와 같이 자동으로
생성된다.
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char* argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
36. Qt 참고 서적
• 사실 Qt 프레임워크는
단시간에 설명하는 것조차
힘들 정도로 방대함
• 국내에도 Qt 5의 각종
모듈에 대해 자세히 다룬
서적이 있음(보유 중)
• 중앙도서관 대출 가능
“Qt 5 프로그래밍 가이드”,
김대진 지음, 2014, 성안당
37. Qt의 문제점(QML 제외)
• Qt 고유의 비표준 문법과 구현을 사용한다.
• 표준 C++와 거리가 먼 매크로와 외부 도구들이 쓰여,
가독성과 명확성을 떨어뜨리는 결과를 낳는다.
• The meta-object system을 구현하기 위해 쓰이는
MOC는 C++ 컴파일러와 독립적으로 작동하여, 컴파일
시간을 훨씬 더 길게 만든다.
• 구식 connect()는 signal이나 slot 이름에 오탈자가
있어도, 컴파일 타임에 오류를 잡아내지 못한다.
• 자체 IDE인 Qt Creator가 한국어를 제대로 처리하지
못한다.
38. Qt의 문제점(QML 제외)
• Qt Core 모듈이 자체 구현 컨테이너와 순회자
(iterator)를 정의한다.
• 대부분 C++ STL(Standard Template Library)에 있는
것을 재정의한다.
(QList<T>, QVector<T>, QSet<T>, QMap<Key,T>, etc.)
• STL 스타일과 Java 스타일의 순회자를 따로 제공한다.
• 기존 C++ 프로그래머가 적응하기 힘들게 만들고, 다른
코드나 라이브러리와 어울리지 않는다(특히 Boost).
• Qt Widgets 모듈만 쓰고 싶어도 강제적으로 포함되어,
결과물의 용량을 늘리게 된다.
• 이미 자체 알고리즘 모듈인 Qt Algorithm은 폐기 예정
(deprecated)이다.
39. Qt의 문제점(QML 제외)
• LGPL v3 라이선스의 제한이 크게 존재한다.
• GPL 대신 쓸 수 있는 open source 라이선스이다.
• 동적 링크 라이브러리(DLL: Dynamic Link Library)를
사용할 것이 거의 강제된다. 즉 의존성(dependency)이
생기는 dynamic build만 써야 하는 상황이 된다.
• 사용자의 해킹과 역공학(reverse engineering)을
허용해야 하므로, 임베디드 기기 사용이 거의 불가능하다.
• 무거운 DLL 파일을 계속 함께 배포해야 한다.
• DLL 파일의 사용이 메모리 자원을 많이 소모하는 것은
물론이고, 프로그램의 로딩 시간도 늘어나게 한다.
• 따라서 작은 실행 파일을 만드는 프로젝트에는 부적합하다.
40. Nana에 대한 간략한 소개
• 현재 중국인 프로그래머 Jinhao의 주도로 활발히
진행 중인 open source 프로젝트이다.
• Modern C++ 스타일로 GUI 프로그래밍이 가능한
cross-platform 라이브러리이다.
• Windows와 Linux(X11)을 지원한다.
• Boost Software License로 배포된다.
• MIT 계열의 라이선스로, 제한이 거의 없기 때문에,
상업적인 사유 소프트웨어에서도 비교적 자유롭게 쓸 수
있다.
• 즉 static build를 하기 좋다.
48. VS2015에 Nana 설치하기
8. 속성에서 Nana 라이브러리의 디렉터리에 대한
정보를 입력한다.
• 앞에서 했던 것처럼, ‘런타임 라이브러리’로 ‘다중 스레드(/MT)’를
선택한다.(debug mode에서는 /MTd)
• ‘VC++ 디렉터리’ 탭의 ‘포함 디렉터리’에
‘(Nana 폴더)include’를, ‘라이브러리 디렉터리’에
‘(Nana 폴더)buildbin’을 넣는다. 구분 기호는 콜론(‘;’)이다.
49. VS2015에 Nana 설치하기
• ‘링커’ - ‘입력’ 탭의 ‘추가 종속성’에 ‘nana_(…).lib’를 현재
mode에 맞는 것으로 골라 넣는다. 구분 기호는 콜론(‘;’)이다.
• Debug mode와 release mode에 따라 설정을 반복한다.
필요하다면, 명령어 집합(x86, x64)에 따라서도 반복한다.
50. VS2015에 Nana 설치하기
9. 프로젝트 속성에서 시스템의 진입점 설정을 바꾼다.
• ‘링커’ - ‘고급’의 ‘진입점’(entry)을 ‘mainCRTStartup’으로
설정한다. Win32에서는 WinMain()이 시작 함수이므로,
미설정 시 compile error가 난다.
• main()을 기본으로 사용하게 바꿔야, 소스 코드가 platform
independent하다.
51. VS2015에 Nana 설치하기
10. 간단한 예제를 실행해 본다.
#include <nana/gui.hpp>
#include <nana/gui/widgets/label.hpp>
#include <nana/gui/widgets/button.hpp>
int main()
{
using namespace nana;
form fm;
label lab{ fm, "<bold size=14>Hello, <blue>Nana C++ Library</>!</>" };
lab.format(true);
button but{ fm, "Quit" };
but.events().click([&fm] { fm.close(); });
fm.div("vert <><<><weight=80% text><>><><weight=24 <><button><>><>");
fm["text"] << lab;
fm["button"] << but;
fm.collocate();
fm.show();
exec();
return 0;
}
52. Nana의 자세한 특징
• Modern C++ 스타일로 작성된 라이브러리이다.
• Modern C++ 표준 스타일은 C++ 프로그래머가 적응하기
쉽고, 생산성이 더 높으며, 다른 코드와 잘 어울리고,
표준을 준수하는 모든 컴파일러에서 사용할 수 있다.
• STL 컨테이너를 재사용하기 좋다.
• Renderer가 native하고 필요한 모듈만 들어 있어,
결과물의 크기가 작다. 또한 static build가 쉽다.
• OS의 native API를 사용하므로, overhead가 적다.
• Static build를 할 경우, 배포할 때 DLL 파일이 필요 없고,
프로그램의 startup 시간이 아주 짧다.
53. Nana의 자세한 특징
• 정적 객체를 통해 GUI component를 생성한다.
• C++ 프로그래머가 이해하기 쉽고, 객체가 파괴될 때
자원이 언제 반환되는지도 명확하다.
• 동적 할당 객체에 비해, 현대적 디자인의 C++ 코드이다.
class MyWindow : public nana::form
{
public:
MyWindow();
private:
nana::place place_{ *this };
nana::label lab_hello_{ *this,
u8"<bold size=14>안녕, <blue>Nana C++ Library</>!</>" };
nana::button but_quit_{ *this, u8"나가기" };
};
부모의 window handle을
참조 타입으로 받는다.
54. Nana의 자세한 특징
• Event handling이 깔끔하다.
• C++의 함수자(functor)와 람다 표현식(lambda
expression)을 완벽하게 지원한다.
• Qt의 connect()에 비해, 훨씬 더 간결한 문법을 사용한다.
but_quit_.events().click([this](const arg_click&) {
this->lab_hello_.caption(
u8"<bold red size=14>사실 나가기 버튼이 아니다!</>");
});
this->events().unload([this](const arg_unload& arg) {
msgbox mb{ *this, u8"프로그램 종료", msgbox::yes_no };
mb.icon(msgbox::icon_question);
mb << u8"정말로 나가시겠습니까?";
arg.cancel = (mb() == msgbox::pick_no);
});
55. Nana의 레이아웃 개요
• nana::place를 통해, 마치 HTML처럼 구성할 수 있다.
• div() 메서드의 인자로 field들로 된 문자열을 넣는다.
• Field는 한 쌍의 부등호(‘<>’)로 이루어지며, 다른 field를
포함할 수 있다(nested). 빈 field는 허용 공간을 꽉 채운다.
• 특정 field에 식별자(identifier)로 이름을 지정할 수 있다.
nana::form fm;
nana::place plc{ fm };
plc.div(
"vert”
”<>”
“< <> <weight=80% text> <> >”
“<>”
“<weight=24 <> <button> <> >”
”<>“
);
Fields
Names
of fields
56. Nana의 레이아웃 개요
• 매우 직관적인 방식으로 field에 widget을 추가한다.
• operator[]나 field() 메서드를 이용하여,
nana::place::field_reference 객체(대리자)를 얻는다.
• 위 객체에 operator<<를 사용하여, field의 identifier에 맞는
widget을 추가한다.
• collocate() 메서드로 field들을 결합한다.
• div()의 인수 문자열은 Div-Text라는 문법을 사용한다.
plc["text"] << lab;
plc["button"] << but;
plc.collocate();
fm.show();
실제로는 proxy(대리자)를
이용하지만, 그걸 알 필요는 없음
57. Nana의 Div-Text 레이아웃
• 기본적으로 field들은 수평으로(horizontally) 배치된다.
• vert 키워드를 사용하면, field들을 수직으로(vertically)
배치할 수 있다.
std::array<button, 4> buts{ {
{ fm, u8"버튼" },
{ fm, u8"버튼" },
{ fm, u8"버튼" },
{ fm, u8"버튼" }
} };
plc.div("<buts>");
for (const auto& but : buts)
plc["buts"] << but;
plc.div("<vert buts>");
for (const auto& but : buts)
plc["buts"] << but;
58. Nana의 Div-Text 레이아웃
• grid 키워드를 사용하면, 격자 모양의 레이아웃
field를 만들 수 있다.
• grid=[3,2]는 3열 2행의 격자 레이아웃을 뜻한다.
std::array<button, 6> buts{ fm, fm, fm, fm, fm, fm };
plc.div("<grid=[3,2] buts>");
for (int i = 0; i < 6; i++)
{
buts[i].caption(std::to_string(i));
plc["buts"] << buts[i];
}
이 밖의 내용은 Nana 홈페이지에서 확인할 수 있다.
60. Nana의 문제점
• 라이브러리 개발자와 이용자의 수가 적다.
• 정보를 구하기 힘들기 때문에, 오류가 발견되거나 문제가
발생했을 때, 빨리 해결하기가 쉽지 않다.
• 아는 사람이 적어, 다른 사람과 협업으로 사용하기 힘들다.
• 라이브러리 개발에 따른 기능적 안정성이 낮다.
• 아직 핵심 기능만 개발된 상태이므로, 앞으로도 많은
부분들이 바뀌고 추가된다.
• 라이브러리의 버전을 올렸을 때, 일부 기능이 제대로
작동하지 않을 수 있다.
• 따라서 중대형 프로젝트에 적합하지 않다.
• 한국어 입력기가 깔끔하지는 않다.
61. 종합: Qt와 Nana의 공통점
• Native Look and Feel이 가능하다.
• Java의 GUI 같은 경우, 각 platform의 window와
어울리지 않는 모양이 일정하게 나타난다.
• 다국어(Unicode)를 지원한다.
• Qt는 16-bit unicode를 담는 QString 객체를
자체적으로 구현한다. QChar를 담는 컨테이너이다.
• Nana는 UTF-8 문자열을 담는 std::string을 사용한다.
즉 STL 표준 컨테이너와 호환되며, ASCII 영문 문자열과도
호환된다.
• 국제화(Internatinonalization)를 지원한다.
62. 종합: Qt와 Nana의 차이점
구분
라이선스
사용자 수
C++ 표준 준수
코드를 통한 UI 개발
도구를 통한 UI 개발
Widget 활용 가능성
작은 실행 파일 생성
+ 배포 용이성
Qt Widgets
GPL/LGPL/상용
많은 개인/기업
MOC 사용
생산성 낮음
Qt Designer
아주많은오픈소스
LGPL 적용시
매우 까다로움
Nana
Boost S/L
적은 수의 개인
Modern C++
생산성 높음
프로그램 없음
기본적인 것만
기본적으로
지원
63. 종합: Qt와 Nana의 차이점
• 결론
• 대부분의 경우에서 Qt가 여전히 유리하다.
• 소규모 프로젝트나 간단한 form의 프로그램을 구성할
때는 Nana가 더 유리하다.
• 배포에 있어서는 Nana가 Qt보다 훨씬 더 간단하다.