Qtでマウスドラッグによる矩形移動を実装する

Qtでドラッグ可能な矩形を実現するには、QWidgetを継承したカスタムクラスを作成し、マウスイベントのハンドリングと描画処理を統合します。以下は、座標変換を正確に扱い、親ウィジェット内での安定したドラッグ動作を保証する改良版実装です。

まず、DraggableBoxという名前のカスタムウィジェットを定義します。このクラスは、ドラッグ中の相対オフセットを保持し、親コンテナに対する絶対位置調整を安全に行います:

#include <QWidget>
#include <QPainter>
#include <QMouseEvent>
#include <QApplication>

class DraggableBox : public QWidget
{
    Q_OBJECT

public:
    explicit DraggableBox(QWidget* parent = nullptr)
        : QWidget(parent), isDragging(false), dragOffset(0, 0)
    {
        setAttribute(Qt::WA_TranslucentBackground);
        setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
        resize(120, 80);
    }

protected:
    void paintEvent(QPaintEvent* e) override
    {
        QPainter p(this);
        p.setRenderHint(QPainter::Antialiasing);
        p.setPen(QPen(Qt::darkCyan, 2));
        p.setBrush(QColor(173, 216, 230, 200)); // light blue with alpha
        p.drawRoundedRect(rect().adjusted(1, 1, -1, -1), 6, 6);
    }

    void mousePressEvent(QMouseEvent* e) override
    {
        if (e->button() == Qt::LeftButton) {
            isDragging = true;
            dragOffset = e->globalPos() - frameGeometry().topLeft();
            e->accept();
        }
    }

    void mouseMoveEvent(QMouseEvent* e) override
    {
        if (isDragging && parentWidget()) {
            const QPoint newPos = e->globalPos() - dragOffset;
            const QRect parentRect = parentWidget()->geometry();
            // 範囲制限:親領域内に収める
            const int x = qBound(parentRect.left(), newPos.x(), parentRect.right() - width());
            const int y = qBound(parentRect.top(), newPos.y(), parentRect.bottom() - height());
            move(x, y);
        }
    }

    void mouseReleaseEvent(QMouseEvent* e) override
    {
        if (e->button() == Qt::LeftButton) {
            isDragging = false;
        }
    }

private:
    bool isDragging;
    QPoint dragOffset;
};

この実装では、globalPos()dragOffsetを用いたグローバル座標ベースの移動処理を採用しており、ネストされたレイアウトやスクロール可能コンテナでも正確な挙動を保証します。また、qBound()による境界制限により、矩形が親ウィジェット外へドラッグされるのを防ぎます。

使用例として、メインウィンドウにDraggableBoxを配置する場合:

#include <QApplication>
#include <QVBoxLayout>
#include <QWidget>

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

    QWidget window;
    window.setWindowTitle("Draggable Box Demo");
    window.resize(640, 480);

    QVBoxLayout* layout = new QVBoxLayout(&window);
    layout->setContentsMargins(20, 20, 20, 20);

    DraggableBox* box = new DraggableBox(&window);
    layout->addWidget(box, 0, Qt::AlignCenter);

    window.show();
    return app.exec();
}

このコードでは、中央揃えのレイアウト内に矩形を配置し、視認性向上のため背景透過と角丸デザインを適用しています。ドラッグ中はマウスカーソルの位置に追従し、離すと即座に位置が固定されます。

タグ: Qt C++ QWidget mouse-event gui-programming

7月8日 20:17 投稿