技術文章Qt

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

Qt はイベント駆動型フレームワークである
イベントについて正しく理解していないと、マルチスレッドやシグナル・スロットでハマル場合がある。
正しく理解して、正しく使いましょう。

イベント概要

基本事項

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

イベントループ

イベント送信

sendEvent(), postEvent() によりイベントを QObject 派生クラスのオブジェクトに送信できる。

bool QCoreApplication::sendEvent ( QObject * receiver, QEvent * event ) は、イベントが処理されるまでブロッキングされる。
event オブジェクトはハンドラによりデリートされることはない。

void QCoreApplication::postEvent ( QObject * receiver, QEvent * event ) は、イベントをキューに積み、直ぐに処理を終える。
event オブジェクトは new でヒープ上に確保されなくてはいけない。

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

表示イベント

paintEvent(QPaintEvent *) をリインプリメントして描画をカスタマイズ

 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: }

update() で画面が無効化された場合等、画面再描画が必要になった場合に paintEvent() がコールされる。
QPainter(QPaintDevice *) で描画オブジェクトを生成し、描画メソッドを使って画面を描画する。
QPaintDevice 派生クラス:QGLFramebufferObject, QGLPixelBuffer, QImage, QPicture, QPixmap, QPrinter, QSvgGenerator, QWidget

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 )
凸ポリゴンdrawChord ( const QRectF & rectangle, int startAngle, int spanAngle )
楕円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 ), ...
静的テキストrawStaticText ( 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):文字列やフォントなどの変更が稀なもの。表示情報をキャッシュし処理を高速化。

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. 画面に色々な図形を描画してみなさい

マウスイベント

mousePressEvent(QMouseEvent *), mouseReleaseEvent(QMouseEvent *), mouseDoubleClickEvent(QMouseEvent *) でマウス操作を実装

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

押されたボタン種別 Qt::MouseButton QMouseEvent::button () const
マウスポインタ位置(グローバル座標系) const QPoint & QMouseEvent::globalPos () const
マウスポインタ位置(オブジェクト座標系) const QPoint & QMouseEvent::pos () const
マウスポインタX座標(オブジェクト座標系) int QMouseEvent::x () const
マウスポインタY座標(オブジェクト座標系) int QMouseEvent::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 ) でイベントフィルターをインストール
  2. bool QObject::eventFilter ( QObject * watched, QEvent * event ) を実装

installEventFilter(filterObj) で、filterObj へイベントフィルターをインストールする。 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);
    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);
    .....
}