【Qt入門】QGraphicsView::dragMode徹底解説!マウス操作を自由自在に制御

2025-05-27

このプロパティはQGraphicsView::DragModeという列挙型(enum)で定義されており、以下の3つの主要な値を取ります。

    • このモードでは、マウスドラッグ操作は特別な意味を持ちません。
    • ビューのスクロールやアイテムの選択など、組み込みのドラッグ機能は無効になります。
    • カスタムのドラッグ動作を実装したい場合に、このモードを選択してマウスイベントを自分で処理することが一般的です。
  1. QGraphicsView::ScrollHandDrag

    • このモードでは、マウスカーソルが手の形に変わり、ビューポート内をドラッグすると、ビューがスクロールします。
    • まるで紙を掴んで動かすように、表示されているシーン全体をパン(移動)させることができます。
    • 地図アプリケーションや大きな図面を表示するアプリケーションなどで、ビューの移動によく利用されます。
  2. QGraphicsView::RubberBandDrag

    • このモードでは、マウスをドラッグすると、矩形の「ラバーバンド」(破線で囲まれた領域)が表示されます。
    • このラバーバンドは、通常、その中に含まれるアイテムを選択するために使用されます。
    • Windowsのエクスプローラーなどでファイルを選択するときにマウスをドラッグするのと同様の動作です。
    • QGraphicsViewrubberBandSelectionModeプロパティと組み合わせて、アイテムの選択方法(部分的に重なっているだけでも選択するかなど)を細かく制御できます。

QGraphicsViewsetDragMode()メソッドを使って、これらのモードを設定します。

#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsRectItem>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QGraphicsScene scene;
    scene.setSceneRect(0, 0, 800, 600); // シーンのサイズを設定

    // シーンにいくつかのアイテムを追加
    scene.addRect(100, 100, 50, 50, QPen(Qt::black), QBrush(Qt::red));
    scene.addEllipse(200, 200, 70, 70, QPen(Qt::black), QBrush(Qt::blue));

    QGraphicsView view(&scene);

    // ドラグモードをScrollHandDragに設定
    // これにより、マウスドラッグでビューがパン(移動)可能になります
    view.setDragMode(QGraphicsView::ScrollHandDrag);

    // または、アイテムを選択したい場合はRubberBandDragに設定
    // view.setDragMode(QGraphicsView::RubberBandDrag);

    view.setWindowTitle("QGraphicsView Drag Mode Example");
    view.resize(600, 400);
    view.show();

    return a.exec();
}

上記の例では、view.setDragMode(QGraphicsView::ScrollHandDrag); の行で、ビューのマウスドラッグ動作をスクロールモードに設定しています。これをコメントアウトして view.setDragMode(QGraphicsView::RubberBandDrag); に変更すると、マウスドラッグでアイテム選択用のラバーバンドが表示されるようになります。



dragModeが全く機能しない、または期待通りに動かない

考えられる原因と解決策

  • 間違ったDragMode値の使用
    ScrollHandDragを期待しているのにNoDragが設定されていた、といった単純なミスも考えられます。QGraphicsView::ScrollHandDragQGraphicsView::RubberBandDragのように、QGraphicsView::スコープを付けているか確認してください。

  • マウスイベントをオーバーライドしている場合
    QGraphicsViewを継承し、mousePressEventmouseMoveEventmouseReleaseEventなどのマウスイベントを独自にオーバーライドしている場合、基底クラスのイベントハンドラを呼び出していないと、dragModeの機能が無効になることがあります。 カスタムのドラッグ動作とQGraphicsView::dragModeの機能を両立させたい場合は、基底クラスのメソッドを必ず呼び出すようにしてください。

    void MyGraphicsView::mousePressEvent(QMouseEvent *event)
    {
        // 独自の処理
        // ...
        QGraphicsView::mousePressEvent(event); // 非常に重要!
    }
    
  • QGraphicsView::setInteractive(false) が呼び出されている
    setInteractive(false)を呼び出すと、QGraphicsViewのユーザーインタラクション(ドラッグ、選択、アイテムの移動など)がすべて無効になります。dragModeが機能しない場合は、この設定がされていないか確認してください。デフォルトはtrueです。

    view->setInteractive(true); // 明示的に有効にする
    
  • シーンのサイズ (sceneRect) が適切でない
    ScrollHandDragモードでビューをパンしようとしても、シーンがビューポートよりも小さい場合、スクロールする余地がないため動きません。QGraphicsScene::setSceneRect() を呼び出して、シーンの有効範囲を十分に大きく設定しているか確認してください。

    scene->setSceneRect(0, 0, 1000, 800); // 例えば、ビューポートよりも大きく設定
    

    あるいは、シーン内のアイテムのboundingRect()の合計によってシーンの範囲が自動的に決定されるため、何もアイテムがない場合もスクロールする余地がありません。

  • setScene() の呼び出し忘れ
    QGraphicsViewQGraphicsSceneの内容を表示するものです。setScene()を呼び出してシーンを設定していない場合、dragModeは機能しません。

    QGraphicsScene *scene = new QGraphicsScene(this);
    QGraphicsView *view = new QGraphicsView(scene, this); // あるいは view->setScene(scene);
    view->setDragMode(QGraphicsView::ScrollHandDrag);
    

RubberBandDragでアイテムが選択されない

考えられる原因と解決策

  • シーンにアイテムがない
    選択するアイテムが存在しない場合、当然ながら何も選択されません。シーンにテスト用のアイテムをいくつか追加して試してみてください。

  • rubberBandSelectionModeが適切でない
    QGraphicsView::rubberBandSelectionModeプロパティは、ラバーバンドとアイテムの重なり方によってアイテムが選択されるかを制御します。デフォルトはQt::IntersectsItemShapeですが、場合によってはQt::ContainsItemShape(アイテムが完全にラバーバンドに含まれる場合のみ選択)などに設定されていると、部分的にしか選択されません。

    view->setRubberBandSelectionMode(Qt::IntersectsItemShape); // デフォルトに戻すか確認
    
  • アイテムが選択可能 (ItemIsSelectable) でない
    RubberBandDragモードは、アイテムを選択するために使用されます。選択対象のQGraphicsItemQGraphicsItem::ItemIsSelectableフラグを持っていることを確認してください。

    myGraphicsItem->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable);
    

ScrollHandDragでスクロールバーが表示されない、またはスクロールできない

考えられる原因と解決策

  • スクロールバーポリシーがAlwaysOffになっている
    QGraphicsView::setHorizontalScrollBarPolicy()QGraphicsView::setVerticalScrollBarPolicy()Qt::ScrollBarAlwaysOffに設定されている場合、スクロールバーは表示されず、ScrollHandDragでのパンも視覚的に分かりにくくなる可能性があります。

    view->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); // 必要に応じて表示
    view->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
    
  • シーンの範囲がビューポートより小さい
    「1. dragModeが全く機能しない」の項目で述べたように、sceneRectがビューポートよりも小さい場合、スクロールバーが表示されなかったり、スクロールできなかったりします。シーンの範囲を適切に設定してください。

考えられる原因と解決策

  • カスタムのドラッグ操作とQtの組み込み機能の重複
    QGraphicsViewをサブクラス化して独自のマウスイベントハンドリングを実装している場合、Qtの組み込みdragModeと競合することがあります。例えば、マウスのドラッグでカスタムの描画を行いたい場合などです。 この場合、setDragMode(QGraphicsView::NoDrag)を設定し、完全に自分でマウスイベントを処理するのが最も安全です。必要に応じて、mousePressEventで特定の条件が満たされたときにのみ、setDragMode(QGraphicsView::ScrollHandDrag)を一時的に設定し、マウスボタンが離されたらsetDragMode(QGraphicsView::NoDrag)に戻すといったテクニックも考えられます。

  • QGraphicsItem::ItemIsMovableフラグとQGraphicsView::dragMode
    QGraphicsItem::ItemIsMovableフラグを持つアイテムは、デフォルトで個別にドラッグして移動できます。 QGraphicsView::ScrollHandDragモードの場合、ビュー全体がパンされますが、ビューに加えてアイテムも同時に動かしたいといった特殊なケースでは、QGraphicsView::dragModeの標準動作とアイテムの個別のドラッグ動作が競合する可能性があります。 通常、これらの機能は排他的に使用されます。ビューをパンしたい場合はScrollHandDrag、個々のアイテムを移動したい場合はItemIsMovableフラグを設定し、dragModeNoDragにするのが一般的です。

QGraphicsView::dragModeのトラブルシューティングのポイントは以下の通りです。

  • 複数のドラッグ機能を同時に期待していないか確認する。 (QGraphicsItemの移動とビューのパンなど)
  • カスタムイベントハンドラを実装している場合は、基底クラスの呼び出し忘れがないか確認する。
  • スクロールバーポリシーを確認する。
  • アイテムの設定を確認する
    ItemIsSelectableItemIsMovable
  • シーンとビューの設定を確認する
    setScene()setSceneRect()setInteractive()


  1. QGraphicsView::NoDrag: ドラッグ操作が特に意味を持ちません。カスタムの動作を実装する場合に利用します。
  2. QGraphicsView::ScrollHandDrag: マウスカーソルが手の形に変わり、ビューポートをドラッグするとシーンがパン(移動)します。
  3. QGraphicsView::RubberBandDrag: マウスをドラッグすると、選択範囲を示すラバーバンドが表示され、その中にあるアイテムが選択されます。

それぞれのモードの基本的な使用例と、関連する設定、さらに一般的な応用例をコードで示します。

基本的な使用例 (Minimal Example)

この例では、QGraphicsViewQGraphicsScene、およびいくつかの基本的なQGraphicsItemを作成し、異なるdragModeを設定した場合の動作を示します。

main.cpp

#include <QApplication>
#include <QMainWindow>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsRectItem>
#include <QGraphicsEllipseItem>
#include <QGraphicsTextItem>
#include <QVBoxLayout>
#include <QRadioButton>
#include <QGroupBox>

class GraphicsViewExample : public QMainWindow
{
    Q_OBJECT

public:
    GraphicsViewExample(QWidget *parent = nullptr) : QMainWindow(parent)
    {
        setWindowTitle("QGraphicsView Drag Mode Example");
        resize(800, 600);

        // QGraphicsSceneの作成と設定
        scene = new QGraphicsScene(this);
        // シーンの有効範囲を設定。これがないとScrollHandDragが効かない場合がある
        scene->setSceneRect(-400, -300, 800, 600); // センターを(0,0)にするために負の値を設定

        // シーンにアイテムを追加
        QGraphicsRectItem *rect1 = new QGraphicsRectItem(-150, -100, 100, 80);
        rect1->setBrush(Qt::red);
        rect1->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
        scene->addItem(rect1);

        QGraphicsEllipseItem *ellipse1 = new QGraphicsEllipseItem(50, 50, 70, 70);
        ellipse1->setBrush(Qt::blue);
        ellipse1->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
        scene->addItem(ellipse1);

        QGraphicsTextItem *text1 = new QGraphicsTextItem("Hello Qt!");
        text1->setPos(-50, -50);
        text1->setDefaultTextColor(Qt::darkGreen);
        text1->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
        scene->addItem(text1);

        // QGraphicsViewの作成
        view = new QGraphicsView(scene, this);
        // スクロールバーの表示ポリシーを設定
        view->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
        view->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);

        // ドラッグモード選択用のUI
        QGroupBox *dragModeGroup = new QGroupBox("Drag Mode");
        QVBoxLayout *groupLayout = new QVBoxLayout;

        rbNoDrag = new QRadioButton("NoDrag");
        rbScrollHandDrag = new QRadioButton("ScrollHandDrag");
        rbRubberBandDrag = new QRadioButton("RubberBandDrag");

        groupLayout->addWidget(rbNoDrag);
        groupLayout->addWidget(rbScrollHandDrag);
        groupLayout->addWidget(rbRubberBandDrag);
        dragModeGroup->setLayout(groupLayout);

        // シグナル・スロット接続
        connect(rbNoDrag, &QRadioButton::toggled, this, &GraphicsViewExample::setNoDragMode);
        connect(rbScrollHandDrag, &QRadioButton::toggled, this, &GraphicsViewExample::setScrollHandDragMode);
        connect(rbRubberBandDrag, &QRadioButton::toggled, this, &GraphicsViewExample::setRubberBandDragMode);

        // レイアウト設定
        QWidget *centralWidget = new QWidget;
        QHBoxLayout *mainLayout = new QHBoxLayout(centralWidget);
        mainLayout->addWidget(view, 1); // Viewを大きく表示
        mainLayout->addWidget(dragModeGroup);

        setCentralWidget(centralWidget);

        // 初期ドラッグモードを設定
        rbScrollHandDrag->setChecked(true); // デフォルトでScrollHandDragを有効にする
    }

private slots:
    void setNoDragMode(bool checked)
    {
        if (checked) {
            view->setDragMode(QGraphicsView::NoDrag);
            qDebug() << "Drag Mode: NoDrag";
        }
    }

    void setScrollHandDragMode(bool checked)
    {
        if (checked) {
            view->setDragMode(QGraphicsView::ScrollHandDrag);
            qDebug() << "Drag Mode: ScrollHandDrag";
            // ScrollHandDragではアイテムの移動はビューのパンと競合するため、ここではアイテムのMovableフラグは影響しない
        }
    }

    void setRubberBandDragMode(bool checked)
    {
        if (checked) {
            view->setDragMode(QGraphicsView::RubberBandDrag);
            qDebug() << "Drag Mode: RubberBandDrag";
            // RubberBandDragでアイテムを選択するには、ItemIsSelectableフラグが必要
            // また、選択モードを制御する rubberBandSelectionMode も重要
            view->setRubberBandSelectionMode(Qt::IntersectsItemShape); // デフォルト
            // view->setRubberBandSelectionMode(Qt::ContainsItemShape); // アイテムが完全に含まれる場合のみ選択
        }
    }

private:
    QGraphicsScene *scene;
    QGraphicsView *view;
    QRadioButton *rbNoDrag;
    QRadioButton *rbScrollHandDrag;
    QRadioButton *rbRubberBandDrag;
};

#include "main.moc" // mocファイルをインクルード

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    GraphicsViewExample w;
    w.show();
    return a.exec();
}

解説

  • NoDragモードでは、ビューに対する特別なドラッグ動作は発生しません。アイテムを個別に移動したい場合は、このモードがデフォルトの動作として機能します。
  • RubberBandDragモードを選択すると、ドラッグで矩形の選択範囲が表示され、その中にあるアイテムが選択されます。ItemIsSelectableフラグが設定されていないアイテムは選択されません。
  • ScrollHandDragモードを選択すると、マウスでドラッグするたびにビュー全体が移動します。この際、個々のアイテムは移動しません。
  • 3つのラジオボタンによって、QGraphicsView::NoDragQGraphicsView::ScrollHandDragQGraphicsView::RubberBandDragのいずれかのドラッグモードを選択できます。
  • これらのアイテムには QGraphicsItem::ItemIsMovableQGraphicsItem::ItemIsSelectable フラグが設定されており、個別に移動したり選択したりする準備ができています。
  • このコードは、QGraphicsScene上に複数の図形とテキストアイテムを配置します。

QGraphicsItem::ItemIsMovable と QGraphicsView::dragMode の相互作用

この例では、QGraphicsItemItemIsMovableフラグとQGraphicsViewdragModeがどのように影響し合うかを示します。

#include <QApplication>
#include <QMainWindow>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsRectItem>
#include <QLabel>
#include <QVBoxLayout>
#include <QCheckBox>

class InteractiveView : public QMainWindow
{
    Q_OBJECT

public:
    InteractiveView(QWidget *parent = nullptr) : QMainWindow(parent)
    {
        setWindowTitle("DragMode & ItemIsMovable Example");
        resize(700, 500);

        scene = new QGraphicsScene(this);
        scene->setSceneRect(0, 0, 600, 400); // シーンサイズ設定

        // 動かせるアイテム
        movableItem = new QGraphicsRectItem(50, 50, 100, 80);
        movableItem->setBrush(Qt::green);
        movableItem->setFlag(QGraphicsItem::ItemIsMovable); // 動かせるように設定
        movableItem->setFlag(QGraphicsItem::ItemIsSelectable); // 選択可能にも設定
        scene->addItem(movableItem);

        // 動かせないアイテム
        QGraphicsRectItem *staticItem = new QGraphicsRectItem(200, 150, 120, 90);
        staticItem->setBrush(Qt::darkYellow);
        scene->addItem(staticItem); // Movableフラグは設定しない

        view = new QGraphicsView(scene, this);
        view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
        view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);

        // UIコントロール
        QGroupBox *controlsGroup = new QGroupBox("Controls");
        QVBoxLayout *controlLayout = new QVBoxLayout;

        QLabel *dragModeLabel = new QLabel("View Drag Mode:");
        cbNoDrag = new QRadioButton("NoDrag (Default Item Drag)");
        cbScrollHandDrag = new QRadioButton("ScrollHandDrag (Pan View)");
        cbRubberBandDrag = new QRadioButton("RubberBandDrag (Select Items)");

        controlLayout->addWidget(dragModeLabel);
        controlLayout->addWidget(cbNoDrag);
        controlLayout->addWidget(cbScrollHandDrag);
        controlLayout->addWidget(cbRubberBandDrag);

        QCheckBox *itemMovableCheckbox = new QCheckBox("ItemIsMovable on Green Rect");
        itemMovableCheckbox->setChecked(true); // 最初はMovable
        controlLayout->addWidget(itemMovableCheckbox);

        controlsGroup->setLayout(controlLayout);

        // シグナル・スロット接続
        connect(cbNoDrag, &QRadioButton::toggled, this, [this](bool checked) {
            if (checked) view->setDragMode(QGraphicsView::NoDrag);
            updateStatus();
        });
        connect(cbScrollHandDrag, &QRadioButton::toggled, this, [this](bool checked) {
            if (checked) view->setDragMode(QGraphicsView::ScrollHandDrag);
            updateStatus();
        });
        connect(cbRubberBandDrag, &QRadioButton::toggled, this, [this](bool checked) {
            if (checked) view->setDragMode(QGraphicsView::RubberBandDrag);
            updateStatus();
        });
        connect(itemMovableCheckbox, &QCheckBox::toggled, this, [this](bool checked) {
            movableItem->setFlag(QGraphicsItem::ItemIsMovable, checked);
            updateStatus();
        });

        // 状態表示ラベル
        statusLabel = new QLabel("Current Status:");
        controlLayout->addWidget(statusLabel);

        QWidget *centralWidget = new QWidget;
        QHBoxLayout *mainLayout = new QHBoxLayout(centralWidget);
        mainLayout->addWidget(view, 1);
        mainLayout->addWidget(controlsGroup);
        setCentralWidget(centralWidget);

        // 初期設定
        cbNoDrag->setChecked(true);
        updateStatus(); // 初期状態の表示
    }

private slots:
    void updateStatus()
    {
        QString statusText = "Current View Drag Mode: ";
        switch (view->dragMode()) {
            case QGraphicsView::NoDrag: statusText += "NoDrag"; break;
            case QGraphicsView::ScrollHandDrag: statusText += "ScrollHandDrag"; break;
            case QGraphicsView::RubberBandDrag: statusText += "RubberBandDrag"; break;
        }
        statusText += "\nGreen Rect ItemIsMovable: " + QString(movableItem->flags().testFlag(QGraphicsItem::ItemIsMovable) ? "True" : "False");
        statusLabel->setText(statusText);
    }

private:
    QGraphicsScene *scene;
    QGraphicsView *view;
    QGraphicsRectItem *movableItem;
    QRadioButton *cbNoDrag;
    QRadioButton *cbScrollHandDrag;
    QRadioButton *cbRubberBandDrag;
    QLabel *statusLabel;
};

#include "main.moc" // mocファイルをインクルード

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    InteractiveView w;
    w.show();
    return a.exec();
}

解説

  • QGraphicsView::RubberBandDragモードの場合
    • ラバーバンド選択が行われます。緑色の矩形がItemIsSelectableであるため、ラバーバンドで囲むと選択されます。ItemIsMovableフラグは、このモードでのドラッグには影響しません。
  • QGraphicsView::ScrollHandDragモードの場合
    • ビュー全体がパンされます。緑色の矩形にItemIsMovableが設定されていても、ビューのパンが優先され、アイテムは移動しません。
  • QGraphicsView::NoDragモードの場合
    • 緑色の矩形にItemIsMovableが設定されていれば、直接ドラッグして移動できます。
    • ItemIsMovableが解除されていれば、ビュー上でのドラッグは何も効果がありません(デフォルトのQt動作)。
  • この例では、緑色の矩形アイテムにItemIsMovableフラグを動的に設定/解除できるチェックボックスを追加しました。

この例からわかるように、QGraphicsView::dragModeはビューレベルでのマウスドラッグの振る舞いを制御し、QGraphicsItem::ItemIsMovableはアイテムレベルでのドラッグ移動を制御します。これらは状況に応じて使い分けたり、競合を避けるように設計したりする必要があります。

QGraphicsView::NoDragモードで、独自のドラッグ操作を実装したい場合があります。この例では、マウスドラッグで自由に線を描画するカスタムビューを実装します。

#include <QApplication>
#include <QMainWindow>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QMouseEvent>
#include <QGraphicsLineItem>
#include <QDebug> // for qDebug()

// QGraphicsViewを継承してカスタム動作を実装
class CustomDragView : public QGraphicsView
{
    Q_OBJECT

public:
    CustomDragView(QGraphicsScene *scene, QWidget *parent = nullptr)
        : QGraphicsView(scene, parent),
          currentLine(nullptr),
          isDrawing(false)
    {
        // NoDragモードに設定し、独自のドラッグ動作を実装
        setDragMode(QGraphicsView::NoDrag); // これが重要!
    }

protected:
    void mousePressEvent(QMouseEvent *event) override
    {
        if (event->button() == Qt::LeftButton) {
            isDrawing = true;
            startPoint = mapToScene(event->pos()); // ビュー座標をシーン座標に変換
            currentLine = new QGraphicsLineItem();
            currentLine->setPen(QPen(Qt::blue, 2)); // 青い線
            scene()->addItem(currentLine);
            qDebug() << "Drawing started at:" << startPoint;
        }
        // ベースクラスのイベントハンドラを呼び出すのを忘れないこと
        // ただし、この場合はQGraphicsView::NoDragなので、特にデフォルト動作は期待しない
        QGraphicsView::mousePressEvent(event);
    }

    void mouseMoveEvent(QMouseEvent *event) override
    {
        if (isDrawing && currentLine) {
            QPointF endPoint = mapToScene(event->pos());
            currentLine->setLine(QLineF(startPoint, endPoint));
        }
        QGraphicsView::mouseMoveEvent(event);
    }

    void mouseReleaseEvent(QMouseEvent *event) override
    {
        if (event->button() == Qt::LeftButton && isDrawing) {
            isDrawing = false;
            if (currentLine) {
                qDebug() << "Drawing finished. Line from" << startPoint << "to" << mapToScene(event->pos());
                // 線が非常に短い場合は削除するなどの処理も考えられる
                if (QLineF(startPoint, mapToScene(event->pos())).length() < 5.0) {
                    scene()->removeItem(currentLine);
                    delete currentLine;
                    currentLine = nullptr;
                }
            }
        }
        QGraphicsView::mouseReleaseEvent(event);
    }

private:
    QPointF startPoint;
    QGraphicsLineItem *currentLine;
    bool isDrawing;
};

// main.cpp (上記とほぼ同じ)
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QGraphicsScene scene;
    scene.setSceneRect(-200, -200, 400, 400); // シーンサイズを設定

    CustomDragView view(&scene); // カスタムビューを使用
    view.setWindowTitle("Custom Drag Drawing Example");
    view.resize(500, 400);
    view.show();

    return a.exec();
}

#include "main.moc" // mocファイルをインクルード (CustomDragViewクラス定義の後に記述)
  • 重要な注意点
    各イベントハンドラ内で必ず QGraphicsView::mousePressEvent(event); のように基底クラスの同名メソッドを呼び出すようにしてください。これにより、Qtの他のイベント処理メカニズム(例えば、QGraphicsItemへのイベント伝播など)が正常に機能します。この例では線描画のみのため、NoDragモードでは基底クラス呼び出しは必須ではありませんが、他のQGraphicsItemがシーンに存在する場合や、将来的に機能を拡張する場合には非常に重要です。
  • mouseReleaseEventでドラッグ終了を処理します。
  • mouseMoveEventでマウスの現在の位置まで線を更新します。
  • mousePressEventでドラッグ開始点を記録し、QGraphicsLineItemを作成します。
  • コンストラクタでsetDragMode(QGraphicsView::NoDrag)を設定することが重要です。これにより、Qtの組み込みドラッグ機能が抑制され、独自のマウスイベントハンドラが機能するようになります。
  • CustomDragViewクラスはQGraphicsViewを継承しています。


QGraphicsView をサブクラス化し、マウスイベントをオーバーライドする

これが最も一般的で柔軟な方法です。QGraphicsViewmousePressEvent(), mouseMoveEvent(), mouseReleaseEvent()といった仮想関数をオーバーライドして、独自のマウスドラッグロジックを実装します。

特徴

  • カスタムのジェスチャー、マルチタッチドラッグ、特定のキーが押されている間の特殊なドラッグなど、高度なインタラクションを実装できます。
  • マウスの位置をビュー座標からシーン座標に変換するmapToScene()関数が非常に役立ちます。
  • QGraphicsView::NoDragモードを設定し、Qtのデフォルトのドラッグ動作を無効にした上で、完全に自由にマウスイベントを処理できます。

利点

  • ビュー固有のカスタムロジックを直接組み込める。
  • 最も細かく動作を制御できる。

欠点

  • 基底クラスのイベントハンドラを適切に呼び出さないと、予期せぬ副作用が発生する可能性がある(例: QGraphicsItemへのイベント伝播が止まる)。
  • コード量が増える傾向にある。
  • ScrollHandDragRubberBandDragのような既存の便利な機能を自分で再実装する必要がある場合がある。
// CustomDragView.h (抜粋)
class CustomDragView : public QGraphicsView
{
    Q_OBJECT
public:
    CustomDragView(QGraphicsScene *scene, QWidget *parent = nullptr);
protected:
    void mousePressEvent(QMouseEvent *event) override;
    void mouseMoveEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;
private:
    QPointF startPoint;
    QGraphicsLineItem *currentLine;
    bool isDrawing;
};

// CustomDragView.cpp (抜粋)
CustomDragView::CustomDragView(QGraphicsScene *scene, QWidget *parent)
    : QGraphicsView(scene, parent), currentLine(nullptr), isDrawing(false)
{
    setDragMode(QGraphicsView::NoDrag); // カスタムイベント処理のためにデフォルトのドラッグを無効化
}

void CustomDragView::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        isDrawing = true;
        startPoint = mapToScene(event->pos());
        currentLine = new QGraphicsLineItem();
        currentLine->setPen(QPen(Qt::blue, 2));
        scene()->addItem(currentLine);
    }
    // QGraphicsView::mousePressEvent(event); // 必要に応じて呼び出す
}

// ... mouseMoveEvent, mouseReleaseEvent も同様に実装

QGraphicsScene をサブクラス化し、シーンイベントをオーバーライドする

QGraphicsSceneも、QGraphicsSceneMouseEventなどのイベントを処理するための仮想関数を持っています。QGraphicsViewの代わりにQGraphicsSceneをサブクラス化してこれらのイベントをオーバーライドすることで、シーンレベルでのドラッグロジックを実装できます。

特徴

  • シーン内のアイテムの選択、衝突検出など、シーンの状態に基づいたドラッグ動作に適しています。
  • イベントはすでにビュー座標からシーン座標に変換されています。

利点

  • 複数のビューが同じシーンを使用している場合、一貫したドラッグ動作を提供できる。
  • アイテムの移動や操作に焦点を当てたロジックを実装しやすい。

欠点

  • QGraphicsView::setDragMode()の機能とは独立して動作するため、競合しないように注意が必要。
  • ビュー自体のパンやズームといった、ビューポートに直接影響を与える動作を実装するのはより複雑になる。

コード例 (概念)

#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsRectItem>
#include <QDebug>

class MyGraphicsScene : public QGraphicsScene
{
    Q_OBJECT
public:
    MyGraphicsScene(QObject *parent = nullptr) : QGraphicsScene(parent) {}

protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *event) override
    {
        qDebug() << "Scene: Mouse Press at" << event->scenePos();
        // アイテムをクリックした場合のカスタム処理
        QGraphicsItem *item = itemAt(event->scenePos(), QTransform());
        if (item) {
            qDebug() << "Item clicked:" << item;
            // アイテムのドラッグを開始するロジックなど
        }
        QGraphicsScene::mousePressEvent(event); // 基底クラスを呼び出す
    }

    void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override
    {
        qDebug() << "Scene: Mouse Move to" << event->scenePos();
        // ドラッグ中の描画更新やアイテム移動のロジックなど
        QGraphicsScene::mouseMoveEvent(event);
    }

    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override
    {
        qDebug() << "Scene: Mouse Release at" << event->scenePos();
        QGraphicsScene::mouseReleaseEvent(event);
    }
};

// main関数では MyGraphicsScene をインスタンス化して QGraphicsView にセット
// QGraphicsView->setDragMode(QGraphicsView::NoDrag); が推奨される

イベントフィルターを使用する

QObject::installEventFilter()を使用し、QGraphicsView(またはそのviewport())にイベントフィルターをインストールすることで、ビューのイベントをキャッチして処理できます。これは、既存のクラスをサブクラス化せずにカスタム動作を追加したい場合に便利です。

特徴

  • 特定のイベントのみを横取りして処理し、他のイベントはQtのデフォルト処理に任せるといった使い方ができる。
  • 既存のQGraphicsViewインスタンスに動的に動作を追加できる。

利点

  • 既存のクラス階層を変更せずに機能を追加できる。
  • コードの凝集度が高まり、カスタムロジックを独立したクラスにカプセル化できる。

欠点

  • イベントオブジェクトの型キャストが必要になる。
  • 誤ってイベントをreturn trueでフィルタリングしてしまうと、Qtのデフォルト動作が完全に停止することがある。
  • イベント処理の順序や伝播を理解する必要がある。

コード例

#include <QApplication>
#include <QMainWindow>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QMouseEvent>
#include <QEvent>
#include <QDebug>

class CustomDragEventFilter : public QObject
{
    Q_OBJECT
public:
    CustomDragEventFilter(QObject *parent = nullptr) : QObject(parent), isDragging(false) {}

protected:
    bool eventFilter(QObject *watched, QEvent *event) override
    {
        QGraphicsView *view = qobject_cast<QGraphicsView*>(watched);
        if (!view) return false; // QGraphicsViewでなければ処理しない

        if (event->type() == QEvent::MouseButtonPress) {
            QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
            if (mouseEvent->button() == Qt::LeftButton) {
                isDragging = true;
                lastMousePos = mouseEvent->pos();
                qDebug() << "Filter: Mouse Press at" << lastMousePos;
                // ここでカスタムドラッグ処理の開始ロジック
                // 例: view->setDragMode(QGraphicsView::NoDrag); が設定されている前提
                return true; // イベントを消費し、ビューのデフォルト処理を停止
            }
        } else if (event->type() == QEvent::MouseMove) {
            QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
            if (isDragging) {
                QPoint delta = mouseEvent->pos() - lastMousePos;
                // ビューを移動させる例(ScrollHandDrag相当の処理)
                view->horizontalScrollBar()->setValue(view->horizontalScrollBar()->value() - delta.x());
                view->verticalScrollBar()->setValue(view->verticalScrollBar()->value() - delta.y());
                lastMousePos = mouseEvent->pos();
                qDebug() << "Filter: Mouse Move. Delta:" << delta;
                return true; // イベントを消費
            }
        } else if (event->type() == QEvent::MouseButtonRelease) {
            QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
            if (mouseEvent->button() == Qt::LeftButton && isDragging) {
                isDragging = false;
                qDebug() << "Filter: Mouse Release";
                // カスタムドラッグ処理の終了ロジック
                return true; // イベントを消費
            }
        }
        return QObject::eventFilter(watched, event); // 他のイベントは基底クラスに任せる
    }

private:
    bool isDragging;
    QPoint lastMousePos;
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QGraphicsScene scene;
    scene.setSceneRect(-400, -300, 800, 600);
    scene.addRect(0, 0, 50, 50, QPen(Qt::black), QBrush(Qt::green));

    QGraphicsView view(&scene);
    view.setWindowTitle("Event Filter Drag Example");
    view.resize(600, 400);

    // イベントフィルターをインストール
    CustomDragEventFilter *filter = new CustomDragEventFilter(&view);
    view.installEventFilter(filter); // viewそのものにインストール

    // またはビューポートにインストールすることもできる
    // view.viewport()->installEventFilter(filter);

    // デフォルトのドラッグモードは無効にしておくか、NoDragにする
    view.setDragMode(QGraphicsView::NoDrag);

    view.show();
    return a.exec();
}

#include "main.moc" // mocファイルをインクルード (CustomDragEventFilterクラス定義の後に記述)

解説

  • return trueでイベントを消費すると、それ以上このイベントは伝播しません。return falseだと、イベントは次のイベントフィルターや、最終的にはQGraphicsViewのデフォルトイベントハンドラに渡されます。この例では、カスタムドラッグ処理を行いたいためtrueを返しています。
  • マウスが押されたときにドラッグを開始し、移動中にスクロールバーを操作してビューをパンするロジックを実装しています。
  • eventFilter内で、QEvent::MouseButtonPressQEvent::MouseMoveQEvent::MouseButtonReleaseイベントを監視します。
  • CustomDragEventFilterクラスはQObjectを継承し、eventFilterメソッドをオーバーライドします。

これはQGraphicsView::dragModeとは少し異なりますが、アイテムやデータのドラッグ&ドロップ操作全般に関連する代替手段です。例えば、リストビューからQGraphicsViewに画像をドラッグ&ドロップして配置したい場合などに使用します。

特徴

  • ドロップターゲット側(QGraphicsViewQGraphicsScene)でdragEnterEventdragMoveEventdropEventをオーバーライドしてドロップを受け入れます。
  • QDragオブジェクトを使用してドラッグ操作を開始します。
  • QMimeDataを使用して、ドラッグされるデータを表現します。

利点

  • 標準的なD&Dインタフェースを提供できる。
  • アプリケーション内外でのデータ転送に非常に強力。

欠点

  • QGraphicsItemのカスタムドラッグ&ドロップを実装する場合は、アイテムのイベントハンドラをオーバーライドする必要がある。
  • 純粋なビューのパンや選択には不向き。

QGraphicsView::dragModeはシンプルで一般的なユースケースに最適ですが、より高度な制御が必要な場合は、以下の代替手段を検討してください。

  • アプリケーション間またはUI要素間のデータ転送の場合
    Qtの標準D&Dフレームワークを使用。
  • 既存のクラスに動的に機能を追加する場合
    イベントフィルターを使用。
  • シーン内のアイテム操作に焦点を当てる場合
    QGraphicsSceneをサブクラス化し、シーンイベントをオーバーライド。
  • 最も柔軟性が必要な場合
    QGraphicsViewをサブクラス化し、マウスイベントをオーバーライド。