Qt Item.rotationの落とし穴:よくあるエラーと解決策

2025-06-06

Qtのグラフィックビューフレームワーク(QGraphicsView framework)やQt Quick(QML)において、「Item.rotation」は、QGraphicsItem(C++)やItem(QML)などのグラフィカルな要素の回転角度を定義するプロパティです。このプロパティを設定することで、アイテムがその中心点または指定された変換原点(transform origin)を基準にしてどれだけ回転するかを制御できます。

QGraphicsView Framework (C++) における QGraphicsItem::rotation()

C++のQGraphicsViewフレームワークでは、QGraphicsItemクラスがrotationプロパティを持っています。

  • 回転の方向
    正の値は時計回りの回転を表します。
  • デフォルト値
    0度
  • 値の範囲
    通常は0度から360度ですが、それ以上の値や負の値も有効です。例えば、360度は0度と同じ回転を示し、-90度は270度と同じ回転を示します。
  • 単位
    度 (degree)

  • qreal (double)


QGraphicsRectItem* rect = new QGraphicsRectItem(0, 0, 100, 50);
rect->setRotation(45); // アイテムを45度時計回りに回転させる

// 回転の中心点の変更(デフォルトはアイテムのローカル座標系の原点(0,0))
// rect->setTransformOriginPoint(50, 25); // アイテムの中心を回転の中心にする

QGraphicsItemの回転は、アイテムのローカル座標系に適用されます。デフォルトでは、回転の中心はアイテムのローカル座標系の原点(通常は左上隅)です。もしアイテムの中心を基準に回転させたい場合は、setTransformOriginPoint()メソッドを使って回転の中心点を指定する必要があります。

Qt Quick (QML) における Item::rotation

Qt Quickでは、ほとんどのビジュアル要素の基底クラスであるItemrotationプロパティがあります。

  • 回転の方向
    正の値は時計回りの回転を表します。
  • デフォルト値
    0度
  • 値の範囲
    QMLでも0度から360度、またはそれ以上の値や負の値も有効です。
  • 単位
    度 (degree)

  • real (double)


Rectangle {
    width: 100
    height: 50
    color: "blue"

    rotation: 45 // 45度時計回りに回転

    // 回転の中心点を変更(デフォルトはアイテムのtransformOriginプロパティ、通常はItemの左上隅)
    // transformOrigin: Item.Center // アイテムの中心を回転の中心にする
}

QMLのItemも、デフォルトではアイテムのローカル座標系の原点(通常は左上隅)を基準に回転します。アイテムの中心を基準に回転させたい場合は、transformOriginプロパティを使用します。Item.CenterItem.TopLeftItem.TopRightなどの定義済み定数があります。

共通の注意点と考慮事項

  1. 回転の中心(Transform Origin)
    QGraphicsItemではsetTransformOriginPoint()、QMLではtransformOriginプロパティで、回転の中心点を指定することが非常に重要です。これを適切に設定しないと、予期せぬ位置でアイテムが回転してしまうことがあります。
  2. 階層的な回転
    親アイテムが回転すると、その子アイテムも親の回転の影響を受けます。子アイテム自身のrotationプロパティは、親の回転に加えて適用されます。
  3. パフォーマンス
    回転はグラフィック処理であり、複雑なアイテムや多数のアイテムを頻繁に回転させると、パフォーマンスに影響を与える可能性があります。Qtはハードウェアアクセラレーションを最大限に活用しますが、アニメーションなどで滑らかな動きを実現するには注意が必要です。
  4. 座標系の変換
    回転はアイテムのローカル座標系を変換します。したがって、回転後にアイテムのグローバルな位置やサイズを計算する際には、変換行列(transform matrix)を考慮する必要があります。


アイテムが意図しない場所を基準に回転する

これは Item.rotation で最もよくある問題です。アイテムは回転しますが、その回転の中心が期待する位置と異なるために、見当違いの場所へ移動してしまったり、変なふうに回転してしまったりします。

原因
QGraphicsItem や QML の Itemrotation プロパティは、デフォルトでは**アイテムのローカル座標系の原点(通常は左上隅、すなわち (0,0))**を基準に回転します。アイテムの中心や特定の点を基準に回転させたい場合、このデフォルトの動作が原因で問題が発生します。

トラブルシューティング

  • 回転の中心点を明示的に設定する
    • C++ (QGraphicsItem)
      setTransformOriginPoint(QPointF point) を使用します。
      QGraphicsRectItem* rect = new QGraphicsRectItem(0, 0, 100, 50);
      // アイテムの中心を回転の中心にする場合
      rect->setTransformOriginPoint(50, 25); // rect.width()/2, rect.height()/2
      rect->setRotation(45);
      
    • QML (Item)
      transformOrigin プロパティを使用します。
      Rectangle {
          width: 100
          height: 50
          color: "red"
          transformOrigin: Item.Center // アイテムの中心を回転の中心にする
          rotation: 45
      }
      
      Item.Center 以外にも Item.TopLeft, Item.TopRight, Item.BottomLeft, Item.BottomRight, Item.Left, Item.Right, Item.Top, Item.Bottom などの定数があります。また、transformOrigin.xtransformOrigin.y を直接設定することも可能です。

親アイテムの回転が子アイテムに影響する

親子関係にあるアイテムの場合、親アイテムの回転が子アイテムにも継承され、子アイテムが意図しない回転をしてしまうことがあります。

原因
Qt のグラフィックビューフレームワークや QML では、変換(回転、スケール、平行移動など)は親から子へと累積的に適用されます。したがって、親が回転すると、その子も親の回転によって回転し、さらに子自身の rotation プロパティが適用されます。

トラブルシューティング

  • ItemIgnoresTransformations フラグ (QGraphicsItem) / 変換を無視する (QML)
    子アイテムが親の変換(特に回転)の影響を受けないようにしたい場合、C++では QGraphicsItem::ItemIgnoresTransformations フラグを設定します。QMLでは直接的な同等のプロパティはありませんが、transform プロパティを明示的に設定したり、親の階層から切り離したりすることで制御可能です。
    // C++ (QGraphicsItem)
    QGraphicsRectItem* parentRect = new QGraphicsRectItem(0, 0, 200, 100);
    parentRect->setRotation(30);
    
    QGraphicsRectItem* childRect = new QGraphicsRectItem(20, 20, 50, 50, parentRect);
    // 子アイテムが親の回転を無視するように設定
    childRect->setFlag(QGraphicsItem::ItemIgnoresTransformations);
    childRect->setRotation(45); // これは親の回転とは独立して適用される
    
    QMLでは、transform プロパティを操作して親の変換を打ち消す、またはカスタムの変換行列を使用することで同様の効果を得られる場合がありますが、通常はレイアウトや設計を再考する方がシンプルです。

回転後にアイテムの位置がおかしくなる (特に setPos との併用時)

回転したアイテムに対して setPos() (C++) や x/y (QML) を設定すると、アイテムの位置が期待と異なる場合があります。これは、アイテムのposがローカル座標系ではなく、親の座標系(またはシーン座標系)で解釈されるためです。

原因
setPos() はアイテムの左上隅(あるいはtransformOriginoffsetによっては他の点)を基準に、親の座標系(またはシーン座標系)での位置を設定します。アイテムが回転すると、そのローカル座標系が傾くため、同じ pos の値でも回転前とは異なる見え方になります。

トラブルシューティング

  • QTransform (C++) / transform プロパティ (QML) の活用
    回転と平行移動を組み合わせる場合、QTransform クラス(C++)や transform プロパティ内の Rotation, Translation などの変換要素(QML)を直接使用すると、より意図した通りの動作になりやすいです。これらの変換は、適用順序が重要です。
    // C++ (QGraphicsItem)
    QTransform transform;
    transform.translate(centerX, centerY); // 中心に移動
    transform.rotate(angle);              // 中心で回転
    transform.translate(-centerX, -centerY); // 元の位置に戻す
    item->setTransform(transform);
    
    // QML (Item)
    Rectangle {
        id: myRect
        width: 100
        height: 50
        color: "blue"
    
        transform: [
            Rotation {
                origin.x: myRect.width / 2
                origin.y: myRect.height / 2
                angle: 45
            },
            // RotationとTranslationの順序が重要。
            // 例えば、Rotationの後にTranslationを置くと、回転された座標系で移動する。
            Translation { x: 50; y: 50 }
        ]
    }
    
  • 回転を考慮した位置計算
    アイテムの回転を考慮して、新しい位置を計算する必要があります。
    • C++ (QGraphicsItem)
      アイテムの変換行列(QGraphicsItem::transform())を使用して、変換後の新しいローカル原点の位置を計算し、setPos()に渡します。
      // 例: アイテムの中心を特定のシーン座標に設定しつつ回転を維持
      QPointF sceneCenter(100, 100);
      QPointF itemLocalCenter = rect->boundingRect().center();
      
      QTransform transform;
      transform.translate(itemLocalCenter.x(), itemLocalCenter.y());
      transform.rotate(rect->rotation());
      transform.translate(-itemLocalCenter.x(), -itemLocalCenter.y());
      
      // アイテムのローカル座標系の原点がシーン座標の (0,0) に来るようにするオフセット
      QPointF newPos = sceneCenter - transform.map(itemLocalCenter);
      rect->setPos(newPos);
      
    • QML (Item)
      QMLでは、xyプロパティは transformOrigin の設定によって相対的な位置が変わることがあります。回転と移動を同時に制御する場合は、Itemtransform プロパティに複数の RotationTranslation を適用することを検討するか、xy を回転の中心に合わせて調整するロジックを記述します。
      Rectangle {
          id: myRect
          width: 100
          height: 50
          color: "green"
          transformOrigin: Item.Center // 回転の中心をアイテムの中心に
          rotation: 0
      
          // ドラッグなどで回転させつつ移動する場合の例
          MouseArea {
              anchors.fill: parent
              onPositionChanged: {
                  // 回転と位置調整の複雑なロジックは、場合によってはC++で実装し、QMLに公開する方が良い
                  // 単純なドラッグであれば、parent.x += mouse.xDelta; parent.y += mouse.yDelta; で移動し
                  // rotation を変更すればよい。
              }
          }
      
          // 回転アニメーションの例
          NumberAnimation on rotation {
              from: 0
              to: 360
              duration: 3000
              loops: Animation.Infinite
          }
      }
      

アイテムのバウンディングボックス (boundingRect) が回転後に大きくなる

アイテムを回転させると、その boundingRect() (C++) や width/height (QML) が回転前よりも大きくなることがあります。

原因
boundingRect() は、常に軸に平行な(Axis-Aligned Bounding Box: AABB)最小の矩形を返します。アイテムが回転すると、その回転した形状を完全に包含するために、AABB は元の形状よりも大きくなる必要があります。これは正しい動作であり、描画やクリック検出の際にアイテム全体が正しく含まれるようにするためです。

トラブルシューティング

  • QMLでの表示サイズの調整
    QMLで回転したアイテムの表示サイズが大きくなりすぎるのが視覚的に気になる場合、親の Clip プロパティを true に設定して、アイテムの境界外の部分を描画しないようにします。ただし、これはクリッピングであり、アイテムの実際のサイズは変わりません。
  • より正確な形状検出
    より厳密なヒットテストが必要な場合は、QGraphicsItem::shape() (C++) や QML の contains() メソッドをオーバーライドして、回転を考慮した正確な形状定義を提供します。
  • 理解と許容
    ほとんどの場合、これはQtの意図した動作であり、問題ではありません。

QMLで transform プロパティの Rotation や Translation が効かない/エラーになる

QMLで Item.rotation ではなく Item.transform プロパティを配列で定義する際に構文エラーや意図しない動作が起こることがあります。

原因
transform プロパティは、Rotation, Translation, Scale, Matrix4x4 などの複数の変換要素のリストを受け取ります。これらの要素のインスタンス化やプロパティの記述方法に誤りがある場合、エラーになります。また、プロパティの割り当てに = ではなく : を使用している場合もエラーになります(特にシグナルハンドラ内など)。

  • プロパティの割り当ては = (代入演算子) を使う
    QMLのJavaScriptコンテキスト(onClicked, onTriggered などのシグナルハンドラ内)でプロパティを設定する際は、= (代入演算子) を使用します。
    // Bad
    // transform: Rotation { angle: 90 } // この書き方はNG (QMLコンポーネント定義内ではOK)
    // Good (シグナルハンドラ内)
    myRect.transform = Rotation { angle: 90 }
    
    これは、transformプロパティがリストを受け取るため、単一のRotation要素だけを代入する場合には [] で囲む必要がない、というより、プロパティ割り当ての構文とJavaScriptの構文の違いに注意が必要です。QMLのプロパティ定義とJavaScriptのコードブロックでの代入は挙動が異なります。
  • 正しい構文の使用
    // OK: transformプロパティへの直接的な割り当て
    transform: [
        Rotation { angle: 90 },
        Translation { x: 10; y: 20 }
    ]
    
    // OK: シグナルハンドラ内でtransform要素を操作する場合
    MouseArea {
        onClicked: {
            // 注意: transformプロパティ全体を置き換えるか、
            // 配列の要素を直接変更する必要があります。
            // transform[0].angle = 180; // これを直接行うのは推奨されないか、動作しない場合がある
            // transformを再構築する方が確実
            myRect.transform = [
                Rotation { origin.x: myRect.width/2; origin.y: myRect.height/2; angle: myRect.rotation + 90 },
                Translation { x: 10; y: 20 }
            ]
        }
    }
    


Qt の Item.rotation プログラミング例

QGraphicsView Framework (C++) の例

QGraphicsView は、カスタムの2Dグラフィックシーンを作成し、それを表示するための強力なフレームワークです。ここでは、QGraphicsRectItem を回転させる基本的な例を示します。

この例では、メインウィンドウが QGraphicsView を含み、その中に QGraphicsScene があり、さらにその中に QGraphicsRectItem が配置されます。ボタンをクリックすると、矩形が回転するようにします。

main.cpp

#include <QApplication>
#include <QMainWindow>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsRectItem>
#include <QPushButton>
#include <QVBoxLayout>
#include <QWidget>
#include <QTimer> // アニメーション用

// 矩形アイテムを回転させるためのカスタムクラス
class RotatableRect : public QGraphicsRectItem
{
public:
    RotatableRect(qreal x, qreal y, qreal width, qreal height)
        : QGraphicsRectItem(x, y, width, height),
          currentAngle(0)
    {
        setPen(QPen(Qt::blue, 2)); // 枠線を青色、太さ2
        setBrush(Qt::lightGray);   // 塗りつぶしを薄い灰色

        // 重要: 回転の中心を設定する
        // ここでは矩形の中心を回転の中心に設定しています
        setTransformOriginPoint(width / 2, height / 2);
    }

    // 角度を増やすメソッド
    void rotateBy(qreal deltaAngle)
    {
        currentAngle += deltaAngle;
        setRotation(currentAngle);
    }

private:
    qreal currentAngle;
};

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

    QMainWindow window;
    window.setWindowTitle("QGraphicsView Rotation Example");
    window.resize(600, 400);

    // シーンとビューを作成
    QGraphicsScene *scene = new QGraphicsScene(&window);
    scene->setSceneRect(0, 0, 580, 350); // シーンのサイズを設定

    QGraphicsView *view = new QGraphicsView(scene, &window);
    view->setRenderHint(QPainter::Antialiasing); // アンチエイリアシングを有効にして滑らかな描画に

    // 回転させる矩形アイテムを作成し、シーンに追加
    RotatableRect *rectItem = new RotatableRect(200, 100, 150, 80);
    scene->addItem(rectItem);

    // 回転ボタンを作成
    QPushButton *rotateButton = new QPushButton("Rotate 15 Degrees", &window);
    QObject::connect(rotateButton, &QPushButton::clicked, [rectItem]() {
        rectItem->rotateBy(15); // ボタンが押されるたびに15度回転
    });

    // アニメーション用のタイマーを作成(自動で回転させる場合)
    QTimer *animationTimer = new QTimer(&window);
    QObject::connect(animationTimer, &QTimer::timeout, [rectItem]() {
        rectItem->rotateBy(1); // 1ミリ秒ごとに1度回転(高速なので注意)
    });
    // animationTimer->start(50); // 50ミリ秒ごとに1度回転

    // レイアウトの設定
    QWidget *centralWidget = new QWidget(&window);
    QVBoxLayout *layout = new QVBoxLayout(centralWidget);
    layout->addWidget(view);
    layout->addWidget(rotateButton);
    window.setCentralWidget(centralWidget);

    window.show();

    return a.exec();
}

解説

  1. RotatableRect クラス
    • QGraphicsRectItem を継承し、currentAngle を保持します。
    • コンストラクタで矩形の中心 (width / 2, height / 2) を setTransformOriginPoint() で設定しているのが重要です。これにより、矩形はその中心を基準に回転します。
    • rotateBy() メソッドは、現在の角度を更新し、setRotation() を呼び出してアイテムの回転を設定します。
  2. main 関数
    • QGraphicsSceneQGraphicsView を作成し、ビューにシーンを設定します。
    • RotatableRect のインスタンスを作成し、scene->addItem() でシーンに追加します。
    • QPushButton を作成し、clicked シグナルを rectItem->rotateBy(15) スロット(ラムダ関数)に接続しています。ボタンをクリックすると、アイテムが15度回転します。
    • QTimer を使用して、自動で連続的に回転させるアニメーションの例もコメントアウトで示しています。

Qt Quick (QML) の例

QML では、Item およびその派生クラス(RectangleImage など)に rotation プロパティが直接用意されています。アニメーションも非常に簡単に実装できます。

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15 // ボタンを使用する場合

Window {
    width: 640
    height: 480
    visible: true
    title: "Qt Quick Rotation Example"

    Column {
        anchors.fill: parent
        anchors.margins: 10
        spacing: 10

        Rectangle {
            id: rotatingRect
            width: 200
            height: 100
            color: "lightblue"
            border.color: "blue"
            border.width: 2
            radius: 10 // 角を丸くする

            // 重要: 回転の中心を設定する
            // ここでは矩形の中心を回転の中心に設定しています
            transformOrigin: Item.Center

            // rotation プロパティ
            rotation: 0 // 初期角度

            Text {
                anchors.centerIn: parent
                text: "Hello Rotated!"
                color: "black"
                font.pixelSize: 18
            }

            // 回転アニメーション
            // rotation プロパティの値を0から360までdurationミリ秒かけて変化させる
            NumberAnimation on rotation {
                id: rotateAnimation
                from: 0
                to: 360
                duration: 3000 // 3秒
                loops: Animation.Infinite // 無限に繰り返す
                running: true // アプリ起動時にアニメーションを開始
            }
        }

        Row {
            spacing: 10

            Button {
                text: "Rotate +15"
                onClicked: rotatingRect.rotation += 15 // ボタンクリックで15度追加回転
            }

            Button {
                text: "Rotate -15"
                onClicked: rotatingRect.rotation -= 15 // ボタンクリックで15度逆回転
            }

            Button {
                text: rotateAnimation.running ? "Stop Animation" : "Start Animation"
                onClicked: rotateAnimation.running = !rotateAnimation.running // アニメーションの開始/停止
            }

            Button {
                text: "Reset Rotation"
                onClicked: rotatingRect.rotation = 0 // 角度をリセット
            }
        }
    }
}

解説

  1. Rectangle アイテム
    • id: rotatingRect を設定して、他の要素から参照できるようにします。
    • color, border.color, border.width, radius で外観を定義します。
    • transformOrigin: Item.Center が非常に重要です。これにより、矩形はその中心を基準に回転します。これを省略すると、デフォルトで左上隅を基準に回転します。
    • rotation: 0 で初期の角度を設定します。
    • 子要素として Text を配置し、親(回転する矩形)と一緒に回転することを示しています。
  2. NumberAnimation on rotation
    • QML の強力な機能であるプロパティアニメーションです。
    • on rotation という書き方で、rotation プロパティが変化することを指定します。
    • from: 0 から to: 360 まで、duration: 3000 (3秒) かけて変化させます。
    • loops: Animation.Infinite で無限ループのアニメーションになります。
    • running: true で、アプリケーション起動時にアニメーションが自動的に開始されます。
  3. Button コントロール
    • QtQuick.Controls をインポートすることで Button を使用できます。
    • onClicked シグナルハンドラ内で、rotatingRect.rotation プロパティを直接操作しています。
      • rotatingRect.rotation += 15 で15度ずつ増加させます。
      • rotatingRect.rotation -= 15 で15度ずつ減少させます。
      • アニメーションの開始/停止は rotateAnimation.running = !rotateAnimation.running で切り替えます。
      • rotatingRect.rotation = 0 で回転をリセットします。

これらの例は、Qt における Item.rotation の基本的な使用方法と、C++ および QML それぞれでの実装アプローチを示しています。どちらのフレームワークでも、transformOrigin (または setTransformOriginPoint) の設定が回転の動作を理解する上で最も重要なポイントです。 Qtの Item.rotation に関連するプログラミング例をC++ (QGraphicsViewフレームワーク) と QML (Qt Quick) の両方で説明します。

C++ (QGraphicsView Framework)

QGraphicsView は、2Dグラフィック要素を扱うための強力なフレームワークです。QGraphicsItem を継承したカスタムアイテムを作成し、その rotation プロパティを操作することで回転を制御します。

MainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsRectItem>
#include <QSlider>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QWidget>
#include <QLabel>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void onRotationSliderValueChanged(int value);

private:
    QGraphicsView *view;
    QGraphicsScene *scene;
    QGraphicsRectItem *rotatedRect;
    QGraphicsEllipseItem *rotationOriginMarker; // 回転の中心を示すマーカー
    QSlider *rotationSlider;
};

#endif // MAINWINDOW_H

MainWindow.cpp

#include "MainWindow.h"
#include <QDebug> // デバッグ出力用

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
    // シーンとビューのセットアップ
    scene = new QGraphicsScene(this);
    scene->setSceneRect(-200, -200, 400, 400); // シーンの範囲を定義
    view = new QGraphicsView(scene, this);
    view->setRenderHint(QPainter::Antialiasing); // アンチエイリアシング有効化

    // 回転させる四角形アイテムの作成
    rotatedRect = new QGraphicsRectItem(-50, -25, 100, 50); // 幅100、高さ50の四角形
    rotatedRect->setBrush(Qt::blue);
    rotatedRect->setPen(QPen(Qt::black, 2));
    scene->addItem(rotatedRect);

    // 回転の中心点を設定
    // アイテムの中心を回転の中心にする場合 (ローカル座標系でのアイテムの中心)
    QPointF rectCenter = rotatedRect->boundingRect().center();
    rotatedRect->setTransformOriginPoint(rectCenter);
    qDebug() << "Rect Center (Local):" << rectCenter;

    // 回転の中心を示すマーカー(小さな赤い円)
    rotationOriginMarker = new QGraphicsEllipseItem(rectCenter.x() - 3, rectCenter.y() - 3, 6, 6);
    rotationOriginMarker->setBrush(Qt::red);
    rotationOriginMarker->setPen(Qt::NoPen);
    rotatedRect->addToGroup(rotationOriginMarker); // マーカーを四角形の子アイテムとして追加

    // スライダーの作成(回転角度を制御)
    rotationSlider = new QSlider(Qt::Horizontal);
    rotationSlider->setRange(0, 360); // 0度から360度
    rotationSlider->setValue(0);
    connect(rotationSlider, &QSlider::valueChanged, this, &MainWindow::onRotationSliderValueChanged);

    // レイアウトのセットアップ
    QVBoxLayout *mainLayout = new QVBoxLayout;
    mainLayout->addWidget(view);

    QHBoxLayout *controlLayout = new QHBoxLayout;
    controlLayout->addWidget(new QLabel("Rotation:"));
    controlLayout->addWidget(rotationSlider);

    mainLayout->addLayout(controlLayout);

    QWidget *centralWidget = new QWidget;
    centralWidget->setLayout(mainLayout);
    setCentralWidget(centralWidget);

    setWindowTitle("Qt Item.rotation Example (C++)");
    resize(600, 600);
}

MainWindow::~MainWindow()
{
    // シーンとアイテムは親オブジェクトによって自動的に解放される
}

void MainWindow::onRotationSliderValueChanged(int value)
{
    // スライダーの値に基づいてアイテムを回転
    rotatedRect->setRotation(value);
    qDebug() << "Rotation set to:" << value << "degrees";
}

main.cpp

#include "MainWindow.h"
#include <QApplication>

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

解説 (C++):

  1. QGraphicsSceneQGraphicsView: シーンはグラフィックアイテムを保持するキャンバス、ビューはシーンを表示するウィジェットです。
  2. QGraphicsRectItem: 描画する四角形アイテムを作成します。QGraphicsRectItem(-50, -25, 100, 50) は、ローカル座標系で (-50, -25) を左上隅とし、幅 100、高さ 50 の矩形を定義します。この場合、アイテムのローカル座標系の中心は (0,0) になります。
  3. setTransformOriginPoint(): 最も重要です。 アイテムの回転の中心点を設定します。ここでは rotatedRect->boundingRect().center() を使用して、アイテム自身のローカル座標系の中心を回転の中心に設定しています。これにより、アイテムがその場で回転するように見えます。
  4. rotationOriginMarker: 回転の中心がどこにあるかを視覚的に示すために、小さな赤い円を rotatedRect の子アイテムとして追加しています。子アイテムは親アイテムの変換(回転、移動など)に従うため、親の回転の中心に固定されます。
  5. setRotation(value): スライダーの値が変更されるたびに、rotatedRectrotation プロパティを更新し、アイテムを回転させます。

QML (Qt Quick)

QMLでは、より簡潔な構文でアイテムの回転を表現できます。

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15 // Sliderを使うために必要

Window {
    width: 600
    height: 600
    visible: true
    title: "Qt Item.rotation Example (QML)"
    color: "#f0f0f0" // 背景色

    Column {
        anchors.fill: parent
        anchors.margins: 20
        spacing: 20

        Rectangle {
            id: container
            width: parent.width - 40
            height: parent.height - 100 // スライダーのスペースを確保
            color: "lightgray"
            border.color: "darkgray"
            border.width: 1

            // 回転させる四角形
            Rectangle {
                id: rotatedRectQML
                width: 150
                height: 75
                color: "green"
                border.color: "darkgreen"
                border.width: 2

                // 親コンテナの中心に配置
                anchors.centerIn: parent

                // 回転の中心点を設定
                // デフォルトはItem.TopLeft。Item.Centerにすることでその場で回転する
                transformOrigin: Item.Center

                // 回転角度
                rotation: rotationSliderQML.value

                // 回転の中心を示す小さなマーカー(赤い円)
                // rotatedRectQML のローカル座標系における中心 (0,0) に配置される
                // transformOrigin: Item.Center としているため、このマーカーが回転の中心にくる
                Rectangle {
                    width: 10
                    height: 10
                    radius: 5 // 円にする
                    color: "red"
                    anchors.centerIn: parent // 親の中心に配置
                }

                // 3D的な回転の例 (オプション)
                // transform: [
                //     Rotation {
                //         origin.x: rotatedRectQML.width / 2
                //         origin.y: rotatedRectQML.height / 2
                //         axis { x: 0; y: 1; z: 0 } // Y軸を中心に回転
                //         angle: rotationSliderQML.value
                //     }
                // ]
            }
        }

        Row {
            width: parent.width
            spacing: 10
            Label { text: "Rotation:" }
            Slider {
                id: rotationSliderQML
                width: parent.width - childrenRect.width - 20
                from: 0
                to: 360
                value: 0
                stepSize: 1
            }
        }
    }
}

解説 (QML):

  1. Window: メインウィンドウを定義します。
  2. Rectangle (container): 回転させるアイテムを配置する背景コンテナです。
  3. Rectangle (rotatedRectQML): 回転させる四角形アイテムです。
    • anchors.centerIn: parent: 親のコンテナの中央に配置されます。
    • transformOrigin: Item.Center: 最も重要です。 アイテムの回転の中心点をアイテム自身の中心に設定します。これにより、アイテムがその場で回転するように見えます。デフォルトは Item.TopLeft (左上隅) です。
    • rotation: rotationSliderQML.value: Slidervalue プロパティに直接バインドすることで、スライダーの動きにアイテムが連動して回転します。
    • 回転の中心マーカー: C++と同様に、rotatedRectQML の子として小さな赤い円を anchors.centerIn: parent で配置し、回転の中心を視覚的に示しています。
  4. Slider: 回転角度を制御するためのスライダーです。QMLでは、プロパティバインディングによって、スライダーの valuerotatedRectQML.rotation にリアルタイムで反映されます。
  5. 3D的な回転 (コメントアウト部分): rotation プロパティは主に2D回転に使われますが、transform プロパティを使うことで、より高度な3D回転(X軸、Y軸、Z軸周りの回転)も可能です。Rotation 要素内で axis プロパティを設定します。


QTransform (C++) / transform プロパティ (QML) を使用する

これは、Item.rotation の単純な角度設定よりもはるかに強力で柔軟な方法です。回転だけでなく、平行移動 (translation)、スケール (scaling)、せん断 (shearing)、さらには3D変換(透視投影など)を組み合わせることができます。

利点

  • カスタム回転の中心
    setTransformOriginPointtransformOrigin とは異なり、行列操作によって任意の点を回転の中心に設定できます。
  • 変換順序の制御
    変換の適用順序を明示的に制御できるため、複雑なアニメーションやレイアウトを正確に実現できます。
  • 複数の変換の組み合わせ
    回転、移動、拡大縮小などを単一の行列で表現し、適用できます。

欠点

  • Item.rotation のように単一の角度プロパティを直接操作するよりも、概念的に複雑になる場合があります。

a. C++ (QGraphicsItem) における QTransform

QGraphicsItem::setTransform() メソッドを使用して、カスタムの QTransform オブジェクトをアイテムに適用します。

#include <QGraphicsRectItem>
#include <QTransform>
#include <QDebug>

void applyCustomRotation(QGraphicsRectItem* item, qreal angle, QPointF origin)
{
    QTransform transform;
    // 1. アイテムを回転の中心に移動
    transform.translate(origin.x(), origin.y());
    // 2. 回転
    transform.rotate(angle);
    // 3. 元の位置に戻す
    transform.translate(-origin.x(), -origin.y());

    // アイテムに変換行列を適用
    // true は既存の変換に結合することを意味するが、ここでは毎回新しい変換を作成しているので false でもよい
    item->setTransform(transform, false); 
    qDebug() << "Applied QTransform with rotation:" << angle << "around" << origin;
}

// 使用例
// QGraphicsRectItem* myRect = new QGraphicsRectItem(0, 0, 100, 50);
// myRect->setPos(50, 50); // シーン上の位置
// QPointF customOrigin = myRect->boundingRect().center(); // アイテムのローカル座標系の中心
// applyCustomRotation(myRect, 45, customOrigin);

解説

  • この変換行列を setTransform() でアイテムに設定します。
  • 再度 translate() メソッドで、アイテムを元の位置(または回転中心から相対的な位置)に戻します。
  • rotate() メソッドで回転を適用します。
  • translate() メソッドで、まず回転の中心点を原点に移動させます。
  • QTransform オブジェクトを作成します。

b. QML (Qt Quick) における transform プロパティ

QMLの Item には、transform というリスト型のプロパティがあります。ここに Rotation, Translation, Scale, Matrix4x4 などの変換要素を追加できます。

import QtQuick 2.15

Item {
    width: 200
    height: 200
    // x: 100; y: 100 // 親内の位置

    Rectangle {
        id: myRotatedRect
        width: 100
        height: 50
        color: "orange"
        anchors.centerIn: parent // 親の中央に配置

        // transformプロパティを使用
        transform: [
            Rotation {
                // 回転の中心をアイテムの中心に設定
                origin.x: myRotatedRect.width / 2
                origin.y: myRotatedRect.height / 2
                angle: 45 // 45度回転
            },
            // 他の変換(例:平行移動)もここに追加できる
            // Translation { x: 20; y: 10 } // これは回転後の座標系で移動する
        ]

        // 回転の中心を示すマーカー
        Rectangle {
            width: 10
            height: 10
            radius: 5
            color: "red"
            anchors.centerIn: parent // transformOrigin の位置と一致する
        }
    }

    // アニメーションの例
    PropertyAnimation on myRotatedRect.transform[0].angle {
        from: 0
        to: 360
        duration: 3000
        loops: Animation.Infinite
    }
}

解説

  • リスト内で複数の変換要素を定義した場合、それらは記述された順序で適用されます。これは非常に重要です。例えば、Rotation の後に Translation を置くと、回転された座標系で Translation が適用されます。
  • Rotation 要素は、origin.x, origin.y で回転の中心を設定し、angle で回転角度を設定します。
  • transform プロパティは Transform 型のリストを取ります。

QGraphicsTransform およびその派生クラス (C++ QGraphicsView)

Qt 4.6 以降で導入された QGraphicsTransform は、QGraphicsItem に独立した変換を追加するための抽象基底クラスです。これには QGraphicsRotationQGraphicsScale という派生クラスがあります。これらはアニメーションで特に有用です。

利点

  • 3D変換
    QMatrix4x4 をベースにしているため、3D空間での変換をより自然に扱うことができます。
  • アニメーションとの相性
    それぞれの変換プロパティを独立してアニメーションさせることができます。特に QGraphicsRotation は、補間が容易です。
  • 独立した変換
    QGraphicsItemsetRotation()setTransform() とは別に、複数の変換をアイテムにアタッチできます。

欠点

  • QGraphicsItem::setRotation() よりも設定が少し複雑になります。
#include <QGraphicsRectItem>
#include <QGraphicsRotation>
#include <QPropertyAnimation>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QDebug>

void setupRotatedItemWithQGraphicsTransform(QGraphicsScene* scene)
{
    QGraphicsRectItem* item = new QGraphicsRectItem(-50, -25, 100, 50);
    item->setBrush(Qt::cyan);
    item->setPen(QPen(Qt::black, 2));
    item->setPos(100, 100); // シーン上の位置
    scene->addItem(item);

    // QGraphicsRotation インスタンスを作成
    QGraphicsRotation* rotationTransform = new QGraphicsRotation(item); // 親オブジェクトとしてitemを指定
    
    // 回転の中心を設定 (アイテムのローカル座標系の中心)
    rotationTransform->setOrigin(item->boundingRect().center());
    
    // Y軸を中心に回転させたい場合は軸を設定 (デフォルトはZ軸)
    // rotationTransform->setAxis(Qt::YAxis); 

    // QGraphicsItemに変換を追加
    item->setGraphicsTransformations({rotationTransform});

    // アニメーションを設定 (QGraphicsRotation の angle プロパティをアニメーション)
    QPropertyAnimation* animation = new QPropertyAnimation(rotationTransform, "angle");
    animation->setStartValue(0);
    animation->setEndValue(360);
    animation->setDuration(5000); // 5秒
    animation->setLoopCount(-1); // 無限ループ
    animation->start();

    qDebug() << "QGraphicsItem with QGraphicsRotation set up.";
}

// main関数から呼び出す例
// QGraphicsScene* scene = new QGraphicsScene();
// QGraphicsView* view = new QGraphicsView(scene);
// view->setRenderHint(QPainter::Antialiasing);
// view->resize(400, 400);
// view->show();
// setupRotatedItemWithQGraphicsTransform(scene);

解説

  • QPropertyAnimation を使用して QGraphicsRotationangle プロパティをアニメーションさせます。これにより、複雑な補間やアニメーションカーブを適用できます。
  • QGraphicsItem::setGraphicsTransformations()QGraphicsRotation のリストを渡して適用します。
  • setOrigin() で回転の中心を QGraphicsItem のローカル座標系で指定します。
  • QGraphicsRotation オブジェクトを作成し、親を QGraphicsItem に設定します。

親アイテムを回転させる (QML / QGraphicsView)

利点

  • シンプル
    rotation プロパティを親に設定するだけで済みます。
  • グループ化された回転
    複数のアイテムを一度に回転させることができます。

欠点

  • 子アイテムは親の回転の影響を常に受けます。親の回転を無視させたい場合は、前述の ItemIgnoresTransformations (C++) のような追加の処理が必要です。

a. C++ (QGraphicsItemGroup)

QGraphicsItemGroup を使用してアイテムをグループ化し、そのグループの rotation を設定します。

#include <QGraphicsItemGroup>
#include <QGraphicsRectItem>
#include <QGraphicsEllipseItem>
#include <QGraphicsScene>
#include <QGraphicsView>

void setupGroupRotation(QGraphicsScene* scene)
{
    QGraphicsRectItem* rect1 = new QGraphicsRectItem(0, 0, 50, 50);
    rect1->setBrush(Qt::magenta);
    QGraphicsEllipseItem* ellipse1 = new QGraphicsEllipseItem(60, 0, 50, 50);
    ellipse1->setBrush(Qt::yellow);

    // グループを作成し、アイテムを追加
    QGraphicsItemGroup* group = new QGraphicsItemGroup();
    group->addToGroup(rect1);
    group->addToGroup(ellipse1);
    
    scene->addItem(group); // シーンにグループを追加
    group->setPos(100, 100); // グループ全体のシーン上の位置

    // グループの中心を回転の中心にする(グループのローカル座標系の中心)
    group->setTransformOriginPoint(group->boundingRect().center());
    group->setRotation(30); // グループ全体を30度回転

    // グループの中心を示すマーカー
    QGraphicsEllipseItem* groupOriginMarker = new QGraphicsEllipseItem(group->boundingRect().center().x() - 3, group->boundingRect().center().y() - 3, 6, 6);
    groupOriginMarker->setBrush(Qt::blue);
    groupOriginMarker->setPen(Qt::NoPen);
    group->addToGroup(groupOriginMarker); // マーカーをグループの子アイテムとして追加
}

b. QML (Item をグループとして使用)

QMLでは、単に Item をコンテナとして使用し、その中に子アイテムを配置するだけでグループ化できます。

import QtQuick 2.15

Item {
    width: 300
    height: 300

    Item { // これがグループとして機能する
        id: groupItem
        x: 50; y: 50 // グループ全体の配置

        // グループの中心を回転の中心にする
        transformOrigin: Item.Center

        Rectangle {
            width: 50
            height: 50
            color: "cyan"
            x: -25; y: -25 // グループの中心から相対的な位置
        }
        Circle { // カスタムコンポーネントまたはRectangleのradius使用
            width: 50
            height: 50
            color: "purple"
            x: 25; y: 25 // グループの中心から相対的な位置
        }

        // グループの中心を示すマーカー
        Rectangle {
            width: 10
            height: 10
            radius: 5
            color: "red"
            anchors.centerIn: parent
        }

        // グループ全体を回転
        rotation: 60

        // アニメーション
        NumberAnimation on rotation {
            from: 0
            to: 360
            duration: 4000
            loops: Animation.Infinite
        }
    }
}