【初心者向け】Qt Widgetsでアイテム間の座標変換を行う:QGraphicsItem::mapRectFromItem()チュートリアル


QGraphicsItem::mapRectFromItem()は、Qt Widgetsライブラリにおける重要な関数の一つです。これは、あるアイテム内の座標系で定義された矩形を、別のアイテムの座標系にマッピングするために使用されます。

詳細

この関数は、以下の2つの引数を受け取ります。

  • rect: マッピングする矩形
  • item: マッピングする矩形を含むアイテム

mapRectFromItem()は、rectitemの座標系から現在のアイテムの座標系にマッピングし、その結果を新しい矩形として返します。

以下のコード例は、QGraphicsItem::mapRectFromItem()の使い方を示しています。

QGraphicsItem *item1 = new QGraphicsItem();
QGraphicsItem *item2 = new QGraphicsItem();

// item1内の座標系で定義された矩形
QRectF rect(10, 20, 30, 40);

// rectをitem2の座標系にマッピング
QRectF mappedRect = item2->mapRectFromItem(item1, rect);

この例では、rectitem1内の座標系で定義されています。mapRectFromItem()は、rectitem2の座標系にマッピングし、mappedRectに結果を格納します。

  • mapRectFromItem()は、アイテム間の変換行列を使用して矩形をマッピングします。この変換行列は、itemTransform()関数を使用して取得できます。
  • itemnullptrの場合、mapRectFromScene()と同じ動作になります。

応用例

QGraphicsItem::mapRectFromItem()は、さまざまな場面で使用できます。例えば、以下のような用途に使用できます。

  • アイテム間の視覚効果を作成する
  • アイテム間の衝突検出を行う
  • アイテム間のドラッグ操作を実装する


サンプル 1: アイテム間のドラッグ操作

class MyItem : public QGraphicsItem
{
public:
    MyItem(const QRectF &rect) : QGraphicsItem(rect) {}

protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *event) override
    {
        m_dragStartPosition = event->pos();
    }

    void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override
    {
        if (event->buttons() & Qt::LeftButton) {
            QPointF delta = event->pos() - m_dragStartPosition;
            setPos(pos() + delta);

            // アイテム間のドラッグ操作を実装するには、
            // mapRectFromItem()を使用して、ドラッグされたアイテムの位置を
            // 他のアイテムの座標系にマッピングする必要があります。

            QGraphicsItem *otherItem = scene()->itemAt(event->pos(), QGraphicsItem::zOrder);
            if (otherItem) {
                QRectF rect = boundingRect();
                QRectF mappedRect = otherItem->mapRectFromItem(this, rect);

                // ドラッグされたアイテムの位置を他のアイテムの座標系で処理する
                // ...
            }
        }
    }

private:
    QPointF m_dragStartPosition;
};

この例では、MyItemクラスはドラッグ可能なアイテムを表します。mousePressEvent()関数内で、ドラッグ開始位置を記録します。mouseMoveEvent()関数内で、左ボタンが押されている場合、アイテムの位置をドラッグ開始位置からの差分だけ移動します。

また、mapRectFromItem()を使用して、ドラッグされたアイテムの位置を他のアイテムの座標系にマッピングします。この情報を使用して、ドラッグされたアイテムの位置を他のアイテムの座標系で処理することができます。

サンプル 2: アイテム間の衝突検出

以下のコード例は、QGraphicsItem::mapRectFromItem()を使用して、アイテム間の衝突検出を行う方法を示しています。

class MyItem : public QGraphicsItem
{
public:
    MyItem(const QRectF &rect) : QGraphicsItem(rect) {}

protected:
    void shapeChanged() override
    {
        // アイテムの形状が変更されたら、衝突検出を行う
        detectCollisions();
    }

private:
    void detectCollisions()
    {
        QList<QGraphicsItem *> collidingItems = collidingItems();
        for (QGraphicsItem *item : collidingItems) {
            // アイテムが衝突した場合の処理
            // ...

            // 衝突しているアイテムの位置を他のアイテムの座標系で取得するには、
            // mapRectFromItem()を使用する必要があります。

            QRectF rect = item->boundingRect();
            QRectF mappedRect = mapRectFromItem(item, rect);

            // 衝突しているアイテムの位置を他のアイテムの座標系で処理する
            // ...
        }
    }
};

この例では、MyItemクラスは衝突検出を行うアイテムを表します。shapeChanged()関数内で、アイテムの形状が変更されたら、detectCollisions()関数を呼び出して衝突検出を行います。

detectCollisions()関数内で、collidingItems()関数を使用して、衝突しているアイテムを取得します。その後、各衝突しているアイテムに対して、mapRectFromItem()を使用して、衝突しているアイテムの位置を他のアイテムの座標系にマッピングします。この情報を使用して、衝突しているアイテムの位置を他のアイテムの座標系で処理することができます。

以下のコード例は、QGraphicsItem::mapRectFromItem()を使用して、アイテム間の視覚効果を作成する方法を示しています。

class MyItem : public QGraphicsItem
{
public:
    MyItem(const QRectF &rect) : QGraphicsItem(rect) {}

protected:
    void paint(QPainter *painter) override
    {
        painter->setPen(Qt::black);
        painter->drawRect(boundingRect());

        // アイテム間の視覚効果を作成するには、
        // mapRectFromItem()を使用して、他のアイテムの位置を
        // 現在のアイテムの座標系にマッピングする必要があります。

        QGraphicsItem *otherItem = scene()->itemAt(QPointF(100, 100), QGraphicsItem::zOrder);
        if (otherItem) {
            


QTransform::mapRect()

QTransform::mapRect()は、矩形を別の座標系にマッピングするために使用できる汎用的な関数です。QGraphicsItem::mapRectFromItem()と同様に、QTransform::mapRect()は、2つの引数を受け取ります。

  • rect: マッピングする矩形
  • sourceTransform: マッピングする矩形を含む座標系を表す変換行列

QGraphicsItem::mapRectFromItem()と異なり、QTransform::mapRect()は、アイテムに関連付けられていない任意の変換行列を使用できます。これは、アイテム間の変換だけでなく、シーン全体の変換にも使用できることを意味します。

QTransform transform;
transform.translate(100, 100);

QRectF rect(0, 0, 30, 40);
QRectF mappedRect = transform.mapRect(rect);

この例では、transformrectを(100, 100)だけ右下に移動する変換行列を表します。transform.mapRect()は、recttransformによってマッピングし、mappedRectに結果を格納します。

sceneTransform()

sceneTransform()は、現在のアイテムが属するシーンの変換行列を取得するために使用できる関数です。この変換行列は、シーン全体の座標系を表します。

QGraphicsItem::mapRectFromItem()と同様に、sceneTransform()を使用して、アイテム内の矩形を別のアイテムの座標系にマッピングできます。

QGraphicsItem *item1 = new QGraphicsItem();
QGraphicsItem *item2 = new QGraphicsItem();

QRectF rect(10, 20, 30, 40);

QRectF mappedRect = item2->sceneTransform().mapRect(item1->sceneTransform().inverted(), rect);

この例では、item1item2は同じシーンに属しています。item2->sceneTransform()は、item2の座標系を表す変換行列を取得します。item1->sceneTransform().inverted()は、item1の座標系を表す変換行列の逆行列を取得します。

sceneTransform().mapRect()は、rectitem2の座標系にマッピングし、mappedRectに結果を格納します。

localToScene() と sceneToLocal()

localToScene()sceneToLocal()は、アイテム内の座標をシーン座標と相互に変換するために使用できる関数です。これらの関数は、QGraphicsItem::mapRectFromItem()よりもシンプルで効率的な方法で、アイテム間の矩形をマッピングするために使用できます。

QGraphicsItem *item1 = new QGraphicsItem();
QGraphicsItem *item2 = new QGraphicsItem();

QRectF rect(10, 20, 30, 40);

QRectF mappedRect = item2->mapToScene(item1->mapToScene(rect));

この例では、item1item2は同じシーンに属しています。item1->mapToScene()は、rectitem1の座標系からシーン座標系に変換します。item2->mapToScene()は、rectitem2の座標系に変換します。

QGraphicsItem::mapRectFromItem()は、Qt Widgetsライブラリにおける便利な関数ですが、状況によっては代替方法の方が適切な場合があります。上記に紹介した代替方法は、それぞれ異なる利点と欠点があります。

  • localToScene() と sceneToLocal(): シンプルで効率的だが、アイテム間の変換にのみ使用できる。
  • sceneTransform(): シンプルで効率的だが、アイテム間の変換にのみ使用できる。
  • QTransform::mapRect(): 汎用性が高いが、アイテムに関連付けられていない任意の変換行列を使用する必要がある。