技術文章Qt

スケルトン作成

まずは、メインウィンドウを持ったアプリケーションのスケルトン(骨組み)を作成し、ビルド・実行してみる。

  1. QtCreator 起動
  2. ファイル>ファイル/プロジェクトの新規作成 メニュー実行
  3. プロジェクト>Qt C++ プロジェクト>Qt GUI アプリケーション選択

  4. プロジェクト名、パス設定
  5. Qt バージョン選択
  6. 詳細>「フォームを生成する(G) □」のチェックを外す

    今回は Qt のシグナル・スロットの使用方法をC++コードレベルで理解するために、QtDesiner を使用しない

  7. サマリ>【完了】をクリック

    → Qt アプリケーションのスケルトンが作成される。

  8. ビルド(Ctrl + B)して、実行(Ctrl + R)すると、下図の様な空ウィンドウが表示される。

Hello, World

メインウィンドウの paintEvent() をオーバライドし、中央に "hello, world" を表示してみる。

  1. ヘッダファイルに paintEvent() 宣言を追加
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        MainWindow(QWidget *parent = 0);
        ~MainWindow();
    
    protected:
        void paintEvent(QPaintEvent *event);
    
    };
    
  2. 実装(cpp)ファイルに paintEvent() を実装する。
    #include <QtGui>
    #include "mainwindow.h"
    
    .....
    
    void MainWindow::paintEvent(QPaintEvent *)
    {
        QPainter painter(this);
        painter.setPen(Qt::blue);
        painter.setFont(QFont("Arial", 20));
        painter.drawText(rect(), Qt::AlignCenter, "Hello, world.");
    }
    
  3. プロジェクトをビルド・実行すると下図の様に "hello, world" が表示される。

表示メッセージを動的に変更可能に

  1. 表示メッセージ文字列をメンバ変数に変更
  2. paintEvent では、メンバ変数を表示
  3. setMessage(const QString &) スロットを定義し、外部から変更可能にする
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        MainWindow(QWidget *parent = 0);
        ~MainWindow();
    
    protected:
        void paintEvent(QPaintEvent *event);
    
    public slots:
        void    setMessage(const QString &);
    
    private:
        QString     m_message;
    };
    
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
    {
        m_message = "hello, world.";
    }
    void MainWindow::paintEvent(QPaintEvent *)
    {
        QPainter painter(this);
        painter.setPen(Qt::blue);
        painter.setFont(QFont("Arial", 20));
        painter.drawText(rect(), Qt::AlignCenter, m_message);
    }
    void MainWindow::setMessage(const QString &mess)
    {
        if( mess != m_message ) {
            m_message = mess;
            repaint();
        }
    }
    
  4. 変更文字列を指定するために、ステータスバーに(QLineEdit)行エディタを配置し、 textChanged(const QString &) シグナルを MainWindow::setMessage(const QString &) にコネクトする。
    class MainWindow : public QMainWindow
    {
        .....
    private:
        QString     m_message;
        class QLineEdit *m_lineEdit;
    };
    
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
    {
        m_message = "hello, world.";
        m_lineEdit = new QLineEdit;
        statusBar()->addWidget(m_lineEdit);
        connect(m_lineEdit, SIGNAL(textChanged(const QString &)),
                this, SLOT(setMessage(const QString &)));
    }
    
  5. 実行結果を下図に示す。

  6. ※ 余談:QString の文字コード指定はデフォルタト(システム)のままでおk。
    内部コードは常にUnicode の様だ。

シグナル発行

ステータスバーに "quit" と入力し Enter を押すと、プログラムを終了するようにしてみる。

  1. onReturnPressed() スロット、quit() シグナルを宣言する。
    class MainWindow : public QMainWindow
    {
        .....
    public slots:
        void    setMessage(const QString &);
        void    onReturnPressed();
    
    signals:
        void    quit();
        .....
    };
    
  2. QLineEdit の enterPressed() シグナルを onReturnPressed() にコネクトし、
    Enter が押された時点のメッセージが "quit" の場合は quit() シグナルを発行する。
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
    {
    	.....
    	connect(m_lineEdit, SIGNAL(returnPressed()), this, SLOT(onReturnPressed()));
    }
    void MainWindow::onReturnPressed()
    {
    	if( m_message == "quit" )
    		emit quit();
    }
    
  3. MainWindow の quit() シグナルを QApplication の quit() スロットにコネクトする。
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        MainWindow w;
        w.show();
        QObject::connect(&w, SIGNAL(quit()), &a, SLOT(quit()));
        return a.exec();
    }
    
  4. 下図にシグナル・スロットの関係を図示する。

シグナル・スロットの特徴

演習問題

  1. 2つまたはそれ以上の数のUIオブジェクトが保持する値を同期するにはどうしたらよいか?
  2. テキストエディタの場合、カーソル位置をステータスバーに表示するのが普通である。
    この機能を実装するにはどのオブジェクトにどのようなシグナル・スロットがあればいいか答えなさい。
  3. テキストエディタの場合、文字列が選択されると 編集>カット メニューが有効になる。
    この機能を実装するにはどのオブジェクトにどのようなシグナル・スロットがあればいいか答えなさい。
  4. QLineEdit, QLabel, QPushButton, QTextEdit などのクラスにはどのようなシグナル・スロットが定義・実装されているかを調べなさい。
  5. Windows ではコントロールの状態が変化したとき、WM_NOTIFY メッセージを発行し、オブジェクト間通信を行うことができる。
    その機構をシグナル・スロット機構と比較し、双方の長短所を述べなさい。