技術文章Qt


 
イベントのお話
Nobuhide Tsuda
30-Jly-2011

Qt はイベント駆動型フレームワークである。
イベントハンドラをリインプリメントすることで QObject 派生クラスの動作を再定義できる。
イベントについて正しく理解していないと、マルチスレッドやシグナル・スロットでハマル場合がある。
正しく理解して、正しく使いましょう。

■ 目次:

  1. イベントの基礎
  2. イベントハンドラのリインプリメント
  3. イベントフィルター
  4. もっとイベント

イベントの基礎

演習問題

  1. 「フロー駆動型」と比較した場合の「イベント駆動型」プログラミングの長短所を述べなさい。

イベントハンドラのリインプリメント

表示イベント

QWidget 等では画面表示処理が必要になった場合、void QWidget::paintEvent ( QPaintEvent * event ) がコールされる。

 1: class MainWindow : public QWidget
 2: {
 3:     .....
 4: protected:
 5:     void    paintEvent(QPaintEvent * event);    //  描画のためのイベントハンドラ宣言
 6:     .....
 7: };
 1: void MainWindow::paintEvent(QPaintEvent * event)
 2: {
 3:     QPainter painter(this);
 4:     painter.setPen(Qt::blue);
 5:     painter.setFont(QFont("Arial", 20));
 6:     painter.drawText(rect(), Qt::AlignCenter, "Hello, world.");
 7: }

paintEvent() では、 QPainter(QPaintDevice *) で描画オブジェクトを生成し、描画メソッドを使って画面を描画する。
QPaintDevice 派生クラス:QGLFramebufferObject, QGLPixelBuffer, QImage, QPicture, QPixmap, QPrinter, QSvgGenerator, QWidget

QPainter には、様々なプリミティブな図形を描くメソッドが用意されている。
QPainter の主な描画メソッド:

円弧drawArc( const QRectF & rectangle, int startAngle, int spanAngle )
drawArc ( const QRect & rectangle, int startAngle, int spanAngle )
drawArc ( int x, int y, int width, int height, int startAngle, int spanAngle )
閉円弧drawChord ( const QRectF & rectangle, int startAngle, int spanAngle )
凸ポリゴンdrawConvexPolygon ( const QPointF * points, int pointCount )
楕円drawEllipse ( const QRectF & rectangle ), ...
イメージdrawImage ( const QRectF & target, const QImage & image, const QRectF & source, ... ), ...
直線drawLine ( const QLineF & line ), ...
直線群drawLines ( const QLineF * lines, int lineCount )
連続直線・曲線drawPath ( const QPainterPath & path ), ...
ピクチャdrawPicture ( const QPointF & point, const QPicture & picture ), ...
パイdrawPie ( const QRectF & rectangle, int startAngle, int spanAngle ), ...
ピックスマップdrawPixmap ( const QRectF & target, const QPixmap & pixmap, const QRectF & source ), ...
drawPoint ( const QPointF & position ), ...
点群drawPoints ( const QPointF * points, int pointCount ), ...
(閉)ポリゴンdrawPolygon ( const QPointF * points, int pointCount, Qt::FillRule fillRule = Qt::OddEvenFill ), ...
連続直線drawPolyline ( const QPointF * points, int pointCount ), ...
矩形drawRect ( const QRectF & rectangle ), ...
角丸矩形drawRoundedRect ( const QRectF & rect, qreal xRadius, qreal yRadius, Qt::SizeMode mode = Qt::AbsoluteSize ), ...
静的テキストdrawStaticText ( const QPointF & topLeftPosition, const QStaticText & staticText ), ...
テキストdrawText ( const QPointF & position, const QString & text ), ...
ピックスマップ敷詰drawTiledPixmap ( const QRectF & rectangle, const QPixmap & pixmap, const QPointF & position = QPointF() ), ...

※ イメージ(QImage):ハードウェア非依存画像、ピックスマップ(QPixmap):スクリーンに描画するために最適化
※ ピクチャ(QPicture):描画処理を保存・再生するためのもの
※ 静的テキスト(QStaticText):文字列やフォントなどの変更が稀なもの。表示情報をキャッシュし処理を高速化。

座標は論理座標(qreal)で指定する。左上が原点で、X軸は右方向、Y軸は下方向である。
※ 描画範囲は ウィンドウ で、描画デバイスの描画範囲は ビューポート で指定可能。 通常は1:1に対応している。

QPainter は、線分色、背景色などの属性を持つ。
QPainter 属性:

テキスト背景setBackground ( const QBrush & brush )
テキスト背景モードsetBackgroundMode ( Qt::BGMode mode ) ※ 透明・非透明
ブラシsetBrush ( const QBrush & brush )
クリッピングパスsetClipPath ( const QPainterPath & path, Qt::ClipOperation operation = Qt::ReplaceClip )
クリッピング矩形setClipRect ( const QRectF & rectangle, Qt::ClipOperation operation = Qt::ReplaceClip )
クリッピング領域setClipRegion ( const QRegion & region, Qt::ClipOperation operation = Qt::ReplaceClip )
クリッピングモードsetClipping ( bool enable )
コンポジションモードsetCompositionMode ( CompositionMode mode ) ※ src, dst, src & dst, ...
フォントsetFont ( const QFont & font )
レイアウト方向setLayoutDirection ( Qt::LayoutDirection direction )
透明度setOpacity ( qreal opacity )
ペンsetPen ( const QPen & pen )
描画ヒントsetRenderHint ( RenderHint hint, bool on = true ) ※ アンチエイリアス、スムース、...
座標変換setTransform ( const QTransform & transform, bool combine = false )
ビューポートsetViewport ( const QRect & rectangle ) ※ 描画デバイス範囲
ウィンドウsetWindow ( const QRect & rectangle ) ※ 描画ソース範囲(論理座標)

■ 演習問題

  1. 画面に色々な図形を描画してみなさい

マウスイベント

マウスボタンを押下・はなした場合、ダブルクリックした場合、ポインタを移動した場合にイベントが発生し、ハンドラがコールされる。

ボタン ダウン(押下) void mousePressEvent ( QMouseEvent * event )
ボタン アップ void mouseReleaseEvent ( QMouseEvent * event )
ボタン ダブルクリック void mouseDoubleClickEvent ( QMouseEvent * event )
ポインタ 移動 void mouseMoveEvent ( QMouseEvent * event )

QMouseEvent の以下のメソッドでマウスの状態を参照することができる。

押されたボタン種別 Qt::MouseButton button () const
マウスポインタ位置(グローバル座標系) const QPoint & globalPos () const
マウスポインタ位置(オブジェクト座標系) const QPoint & pos () const
マウスポインタX座標(オブジェクト座標系) int x () const
マウスポインタY座標(オブジェクト座標系) int y () const

■ 演習問題

  1. マウスクリック位置に(サイズ固定の)色々な図形を描画するようにしなさい
  2. マウス押下〜リリース 位置に直生・矩形・楕円を描画するようにしなさい

キーイベント

キーを押すと keyPressEvent ( QKeyEvent * event ) が、離すと keyReleaseEvent ( QKeyEvent * event ) がコールされる。

QKeyEvent の以下のメソッドで押されたキーの情報を取得できる。

オートリピートか? bool QKeyEvent::isAutoRepeat () const
キーコード int QKeyEvent::key () const
スタンダードキーか? bool QKeyEvent::matches ( QKeySequence::StandardKey key ) const
Shift キー等の状態 Qt::KeyboardModifiers QKeyEvent::modifiers () const
テキスト QString QKeyEvent::text () const

■ 演習問題

  1. キー入力された文字列を画面に表示するようにしなさい

イベントフィルター

イベントフィルターをインストールするこで、ソースの無いウィジットでも、動的に動作をカスタマイズ出来るぞ。

手順:

  1. void QObject::installEventFilter ( QObject * filterObj ) で、filterObj のイベントフィルターをインストール
  2. bool QObject::eventFilter ( QObject * watched, QEvent * event ) を実装

installEventFilter(filterObj) で、this に filterObj のイベントフィルターをインストールする。 イベントフィルターがインストールされたオブジェクトにイベントが発生すると、まず eventFilter() がコールされる。 eventFilter() でイベントが処理されなかった場合は、通常のイベント処理が行われる。

eventFilter ( QObject * watched, QEvent * event ) の第1引数は、どのオブジェクトへのイベントかを識別するためのもの。
第2引数はイベントオブジェクト。event->type() でイベント種別を判定可能。
eventFilter() でイベントを処理した場合は true を返す。false を返した場合は、通常のイベント処理が行われる。

使用例:QPlainTextEdit の左側に行番号表示

ViEditView::ViEditView(QWidget *parent)
    : m_mode(CMD), QPlainTextEdit(parent)
{
    m_lineNumberArea = new QWidget(this);        //  行番号表示のための QWidget を生成
    m_lineNumberArea->installEventFilter(this);  //  イベントフィルターをインストール
}
bool ViEditView::eventFilter(QObject *obj, QEvent *event)
{
    //  ペイントイベントの場合は、自前の描画メソッドをコール
    if( obj == m_lineNumberArea && event->type() == QEvent::Paint ) {
        drawLineNumbers();
        return true;
    }
    return false;
}

void ViEditView::drawLineNumbers()
{
    QPainter painter(m_lineNumberArea);  // 行番号表示エリアに描画するための QPainter 生成
    //  painter を使って描画
    .....
}

もっとイベント

イベントクラス、イベントハンドラ

イベントループ

イベント送信