Qtライブラリについて

Qtライブラリ概要

Qtの素性

C++言語で記述されたGUIアプリケーション作成用フレームワーク。X Window System、Windows、Mac OS X、その他組み込み機器などで動作する。各プラットフォーム固有のLook & Feelをサポートする(Qtスタイル・テーマ・サポート)。1つのアプリケーション・ソースコードで各プラットフォーム用実行ファイルを生成することが目的の一つにある。

開発元は、ノルウェーのTrolltech社。2008年にNokiaに買収され、現在Nokiaの一部門となっている。

主要実績は、LinuxのKDEデスクトップ、Webブラウザ Opera、Google Earthのオープンソースやインターネット系ツールのほか、防衛・運輸・通信業界のソフトウェアに使用されている。

日本では、SRA社がサポート・販売を手がけている。

ライセンスは少々複雑。Qtには、商用、オープンソース、アカデミック、教育の4種類のライセンスがある。Qt 4.5からはLGPLのライセンスも適用され、さらに複雑に。

ライブラリが有する機能は、GUIウィジェット、OpenGL、SVG、および、ネットワーク、データベース(SQL)、XML、単体テストフレームワークなどがある。

スタイルシートによる画面レイアウト定義、レイアウトマネージャによる部品配置、シグナル・スロットによるイベント通知など。

GUIスタイル(GNOMEデスクトップではCleanlooksがデフォルト:Ver.4.5〜)。

X11でのサブピクセルレンダリング(Ver.4.5〜)

GUIデザインツール Qt Designerがある。

ビルドツール qmake が提供され、makefileやIDE(Visual Studio)のプロジェクトファイルを生成する。

国際化対応としてユニコードを使用[これで十分かな?]

XMLは、DOMとSAXに対応。XQuery 1.0およびXPath 2.0対応(Ver.4.4〜)。XSL-T 2.0対応(Ver.4.5〜)。

STL互換なコレクションクラスを保有し、ここにオブジェクト(Qtクラスのインスタンスのことか?)を格納しイテレーション操作可能。Qt 3までのQTLコンテナとは異なりテンプレートベースとなっている。QList, QLinkedList, QVector, QStack, QQueue, QMap, QMultiMap, QHash, QMultiHash, QSet。foreach対応。

ローカル・リモートのファイルを操作可能。リモート操作は、QUrl、QFtp、QHttp。

ネットワークはQTcpSocket、QTcpServer、QUdpSocketによるTCP/UDPに対応。

プラグインおよびDLLによる拡張機能を持つ。プラグインには、追加コーデック、データベースドライバ、画像フォーマット、スタイルやウィジェットなどがある。

Qt Scriptなるスクリプト言語によるアプリケーション拡張機能がある。ECMAScriptベースの言語。

QSettingsクラスは設定情報をディスク上に保持する(Windowsの場合レジストリ、他はテキストファイル)

マルチスレッド対応。QThreadでスレッドの開始・終了。スレッドはイベントループを持てる。connectでシグナルに対応するスロットに処理が移るのは、スロット側のオブジェクトが属するスレッドのイベントループからとなる。また、QMutex、QReadWriteLockの排他機構がある。QtConcurrentフレームワーク(Ver.4.4〜)。

Interviewフレームワークは、MVCデザインパターンに基づくモデル/ビューを提供する。ビューにはコントローラが含まれる。

QtWidgetがGraphics Viewに統合され、グラフィックス上にWidgetを描画できるようになった。

ヘルプシステム:QtAssistant

印刷機能:QPrinterInfo、プレビュー、ページサイズ・マージンのカスタマイズ

DBusプロセス間通信に対応。

Qe Webkitと呼ばれるHTMLレンダラー(Webブラウザ Safari/Adobe AIR/Google Chrome/Androidなどで使用)

ODF文書ファイルを扱える(QTextDocumentでの読み込み機能)。

Qtのパッケージ構成

パッケージ 内容
QtCore 非GUIの中核となる機能
QtGui GUIの中核となる機能
QtNetwork ネットワーク機能
QtOpenGL OpenGL機能
QtSql SQL機能
QtXml XML機能
Qt3Support Qt3対応機能
QAxContainer ActiveQtクライアント拡張
QAxServer ActiveQtサーバ拡張
QtDesigner

GUIウィジェット(部品)

QCalendarWidget
QCheckBox
QComboBox
QDateEdit    (日付編集)
QDateTimEdit  (日時編集)
QDial
QDoubleSpinBox  (倍精度浮動小数点のスピン・ボックス)
QFontComboBox
QFrame
QGroupBox
QIconView
QLabel
QLCDNumber  (LCDの数値様な表示)
QLineEdit (1行テキスト編集)
QListBox
QListView
QListWidget
QMenu
QMenuBar
QMultiLineEdit
QProgressBar
QPushButton
QRadioButton
QScrollArea
QScrollBar
QSlider
QSpinBox
QTableView
QTableWidget
QTabWidget
QTextEdit
QTimeEdit (時刻編集)
QToolBar
QToolBox
QToolButton
QTreeView
QTreeWidget

スタイル

QCDEStyle
QCleanlooksStyle
QMacStyle
QMotifStyle
QPlastiqueStyle
QWindowsStyle
QWindowsVistaStyle
QWindowsXPStyle

カスタム・ウィジェットの作成

QWidgetかその派生クラスのいずれかから派生するクラスを作成

paintEvent()をオーバーライドして描画を記述

シグナル・スロット機構

Qtにおいてオブジェクト同士の連携を行う仕組み。C言語の関数コールバックやMFC等のメッセージ・マップを抽象化・高度化したフレームワーク。型安全ながら柔軟性を持つオブジェクト指向な連携方法である。何かイベントが発生した時点でQtウィジェットは「シグナル」を発行する。このシグナルと接続したスロット(関数)が存在すれば、その時点でスロットが起動される。シグナルを発行するクラスとスロットを持つクラスが直接互いを参照する必要はない。

ただし、この恩恵に浴するオブジェクトは、QObjectを継承している必要がある。

また、Qtのシグナル・スロット機構は、C++言語を逸脱している記法であり、ソースコードをC++コンパイラにかける前にMeta-Object Compiler(MOC)と呼ばれるプリプロセッサにかけてC++コードを生成する必要がある。

以下コードは、Trolltech社のQt Whitepaper からの抜粋

class BankAccount : public QObject {
    Q_OBJECT
public:
    BankAccount() { currentBalance = 0; }
    int balance() const { return currentBalance; }
public slots:
    void setBalance(int newBalance);
signals:
    void balanceChanged(int newBalance);
private:
    int currentBalance;
};

void BankAccount::setBalance(int newBalance) {
    if (newBalance != currentBalance) {
        currentBalance = newBalance;
        emit balanceChanged(currentBalance);
    }
}

このBankAccountは、balanceChanged()シグナルを発行する能力を持ち、setBalanceスロットを持つ。

BankAccount x, y;
connect (&x, SIGNAL(balanceChanged(int)), &y, SLOT(setBalance(int)));
x.setBalance(2450);

xに2450をセットすると、xにおいてシグナルbalanceChanged()が発行し、yのsetBalance()スロットが起動される。

マルチスレッド

スレッド、ミューテックス、セマフォ、スレッドローカルストレージ、各種ロック機構をクラスとして提供。

Qtクラスの大半はリエントラントで、いくつかの関数はスレッドセーフである。

2D/3Dグラフィックス

2Dグラフィックスでは、ラスター/ベクター、各種画像ファイルとのI/O、PDFへの出力、SVGなどをサポート。

3Dグラフィックスでは、OpenGLに対応。

Qt問題解決

例外の扱い方

Qtは、一般的なGUIツールキットと同様イベントループによるイベント駆動なアーキテクチャを取っているようです。ここで、イベントを処理するロジックで例外をスローすると、Qtは以下のメッセージを吐いてプログラムがabortします。

Qt has caught an exception thrown from an event handler. Throwing
exceptions from an event handler is not supported in Qt. You must
reimplement QApplication::notify() and catch all exceptions there.

ここで、ちょっと困ったことが、シグナル・スロット(signal/slot)の呼び出しにおいても適用されてしまうことです。QApplicationなどでイベントループを動かしている中で、シグナル・スロットをイベントで処理する(キュー方式)場合、スロット関数内で例外をスローすると、イベントループが停止してしまいます。main関数でtry-catchすれば例外自体は捕捉でき、プロセスそのものは終了させないことが可能ですが、イベントループが終了してしまうので、GUIが消えてしまいます。

対策1. スロット関数は例外を外へ漏らさない

void BankAccount::setBalance(int newBalance) throw() {
    try {
        if (newBalance != currentBalance) {
            currentBalance = newBalance;
            emit balanceChanged(currentBalance);
        }
    } catch (...)
}

Qtライブラリの方針はこの対策かもしれませんが、一介のライブラリごときにアプリケーション全体の例外使用方法を制約されるのは耐えられませんので、却下。

対策2. QApplicationを継承しnotifyメンバ関数をオーバーライド

Qtからのメッセージにある、QApplication::notifyをオーバーライドする派生クラスを定義します。

#include <QApplication>
class MyApplication : public QApplication {
public:
    MyApplication(int& argc, char** argv);
    bool notify(QObject* receiver, QEvent* event); 
};
#include "MyApplication.h"
#include <exception>

MyApplication::MyApplication(int& argc, char** argv) :
    QApplication(argc, argv) {}

bool MyApplication::notify(QObject* receiver, QEvent* event) {
    bool done = true;
    try {
        done = QApplication::notify(receiver, event);
    } catch (const std::exception& ex) {
        // ログや何らかの回復処理
    } catch (...) {
        // ログや何らかの回復処理
    }
    return done;
} 

例外をcatchしたときに、ログ、画面へのポップアップなど例外発生の記録・通知および対処可能な例外であれば回復処理を行います。画面へのポップアップは、QMessageBox::critical(...)などを使うのが定番でしょう。