Pynote

Python、機械学習、画像処理について

Qt - ドラッグによる範囲選択を実装する

About

ウィジェット上をマウスでドラックして範囲選択をしたい場合、QRubberBand ウィジェットで実現できる。
例えば、画像を表示しているウィジェット上で範囲選択をし、切り取りを行う処理を実装したい場合などに利用する。

f:id:nekobean:20170816234046p:plain

解説

3つのイベントハンドラ

範囲選択は次の3つの段階に分けられる。
それぞれでマウスイベントが発生するので、イベントハンドラを実装して QRubberBand ウィジェットのジオメトリを設定する。

1. マウスをクリックして、長方形の始点を決める。
2. マウスがクリックされた状態で動かして、選択範囲を変化させる。
3. マウスがクリックされた状態で離して、選択範囲を確定する。

f:id:nekobean:20170816234049p:plain

マウスをクリックしたときに発生するイベントを扱う関数

void mousePressEvent(QMouseEvent *event);

マウスがクリックされた状態で動かしているときに発生するイベントを扱う関数

void mouseMoveEvent(QMouseEvent *event);

マウスがクリックされた状態で離したときに発生するイベントを扱う関数

void mouseReleaseEvent(QMouseEvent *event);
マウスをクリックしたときの処理
void MainWindow::mousePressEvent(QMouseEvent *event)
{
    // QRubberBand をまだ作成していない場合は、作成する。
    if (!rubberBand_) {
        rubberBand_ = new QRubberBand(QRubberBand::Rectangle, this);
    }

    // マウスをクリックしたときの座標を取得し、始点とする。
    startPos_ = event->pos();

    // QRubberBand ウィジェットのジオメトリを
    // 始点がクリックした座標で、大きさが縦横ともに 0 の長方形に変更する。
    rubberBand_->setGeometry(QRect(startPos_, QSize()));

    // QRubberBand ウィジェットを表示する。
    rubberBand_->show();
}
マウスを動かしているときに発生するイベント
void MainWindow::mouseMoveEvent(QMouseEvent *event)
{
    // 現在のマウスの座標を取得し、終点とする。
    QPoint endPos = event->pos();

    // QRubberBand ウィジェットのジオメトリを
    // 始点がクリックした座標で、終点が現在の現在のマウスの座標に変更する。
    rubberBand_->setGeometry(QRect(startPos_, endPos).normalized());
}

QRect::normalized() は右下から左上に向けてドラッグした場合、幅、高さが負の値になるので、それを正の値になるように修正する関数である。

QRect は以下のコンストラクタを使用して、インスタンスを作成する。

QRect::QRect(const QPoint & topLeft, const QPoint & bottomRight)

左上から右下にかけてドラッグした場合、topLeft が右下に、bottomRight が左上になる。

QRect((QPoint(100, 100), QPoint(50, 50)))  // 始点 (100, 100), 幅 -50, 高さ -50

QRect::normalized() は、左上、右下が正しく調整された QRect オブジェクトを返す。

QRect((QPoint(100, 100), QPoint(50, 50))).normalized()  // 始点 (50, 50), 幅 50, 高さ 50
マウスをクリックして離したときの処理
void MainWindow::mouseReleaseEvent(QMouseEvent *event)
{
    Q_UNUSED(event)

    // QRubberBand ウィジェットを非表示にする。
    rubberBand_->hide();

    // 選択した範囲を出力する。
    qDebug() << rubberBand_->geometry();
}

特定のウィジェットで範囲選択

特定のウィジェット内で範囲選択をできるようにしたい場合は、カスタムウィジェットを作成して、3つのイベントハンドラを実装する。
以下は画像を表示している QLabel 上で範囲選択を実装する例である。

class ImageLabel : public QLabel
{
    Q_OBJECT

public:
    explicit ImageLabel(QWidget *parent = 0);
    ~ImageLabel();

protected:
    // マウスをクリックしたときに発生するイベントを扱う関数
    void mousePressEvent(QMouseEvent *event);
    // マウスがクリックされた状態で動かしているときに発生するイベントを扱う関数
    void mouseMoveEvent(QMouseEvent *event);
    // マウスがクリックされた状態で離したときに発生するイベントを扱う関数
    void mouseReleaseEvent(QMouseEvent *event);

private:

    // QRubberBand ウィジェット
    QRubberBand *rubberBand_;

    // 選択範囲の始点
    QPoint startPos_;
};