Qt はイベント駆動型フレームワークである。
イベントハンドラをリインプリメントすることで QObject 派生クラスの動作を再定義できる。
イベントについて正しく理解していないと、マルチスレッドやシグナル・スロットでハマル場合がある。
正しく理解して、正しく使いましょう。
■ 目次:
┌──────────┐ │ イベントキュー・ │ ┌─────────┐ │ ディスパッチャ │ │ イベントハンドラ │ └────┬─────┘ └────┬────┘ event-1 │ │ ─────→│────────────→┌┴┐ event-2 │ │ │event-1 の処理 ─────→│ │ │ │ └┬┘ │────────────→┌┴┐ │ │ │event-2 の処理 │ │ │ │ └┬┘ event-3 │ │ ─────→│────────────→┌┴┐ │ │ │event-3 の処理 │ │ │ │ └┬┘ │ │ ↓ ↓※イベントは発生時直ぐに処理されるわけではない。それ以前にキューに積まれたイベントが全て処理されると順番がまわってくる。
演習問題
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 ) ※ 描画ソース範囲(論理座標) |
■ 演習問題
マウスボタンを押下・はなした場合、ダブルクリックした場合、ポインタを移動した場合にイベントが発生し、ハンドラがコールされる。
ボタン ダウン(押下) | 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 |
■ 演習問題
キーを押すと 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 |
■ 演習問題
イベントフィルターをインストールするこで、ソースの無いウィジットでも、動的に動作をカスタマイズ出来るぞ。
手順:
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 を使って描画 ..... }
┌──────────┐ │ イベントキュー・ │┌─────────┐┌─────────┐┌─────────┐ │ ディスパッチャ ││ QObject::event() ││ keyPressEvent() ││ paintEvent() │ └────┬─────┘└────┬────┘└────┬────┘└────┬────┘ key-event │ │ │ │ ─────→│─────────→┌┴┐───────→┌┴┐ │ paint-event │ │ │ │ │ │ ─────→│ │ │ │ │ │ : │ └┬┘ └┬┘ │ : │─────────→┌┴┐─────────│────────→┌┴┐ │ │ │ │ │ │ │ │ │ │ │ │ │ └┬┘ │ └┬┘ │ │ │ │ ↓ ↓ ↓ ↓