Qt Item.rotationの落とし穴:よくあるエラーと解決策
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では、ほとんどのビジュアル要素の基底クラスであるItem
にrotation
プロパティがあります。
- 回転の方向
正の値は時計回りの回転を表します。 - デフォルト値
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.Center
、Item.TopLeft
、Item.TopRight
などの定義済み定数があります。
共通の注意点と考慮事項
- 回転の中心(Transform Origin)
QGraphicsItem
ではsetTransformOriginPoint()
、QMLではtransformOrigin
プロパティで、回転の中心点を指定することが非常に重要です。これを適切に設定しないと、予期せぬ位置でアイテムが回転してしまうことがあります。 - 階層的な回転
親アイテムが回転すると、その子アイテムも親の回転の影響を受けます。子アイテム自身のrotation
プロパティは、親の回転に加えて適用されます。 - パフォーマンス
回転はグラフィック処理であり、複雑なアイテムや多数のアイテムを頻繁に回転させると、パフォーマンスに影響を与える可能性があります。Qtはハードウェアアクセラレーションを最大限に活用しますが、アニメーションなどで滑らかな動きを実現するには注意が必要です。 - 座標系の変換
回転はアイテムのローカル座標系を変換します。したがって、回転後にアイテムのグローバルな位置やサイズを計算する際には、変換行列(transform matrix)を考慮する必要があります。
アイテムが意図しない場所を基準に回転する
これは Item.rotation
で最もよくある問題です。アイテムは回転しますが、その回転の中心が期待する位置と異なるために、見当違いの場所へ移動してしまったり、変なふうに回転してしまったりします。
原因
QGraphicsItem
や QML の Item
の rotation
プロパティは、デフォルトでは**アイテムのローカル座標系の原点(通常は左上隅、すなわち (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.x
やtransformOrigin.y
を直接設定することも可能です。
- C++ (QGraphicsItem)
親アイテムの回転が子アイテムに影響する
親子関係にあるアイテムの場合、親アイテムの回転が子アイテムにも継承され、子アイテムが意図しない回転をしてしまうことがあります。
原因
Qt のグラフィックビューフレームワークや QML では、変換(回転、スケール、平行移動など)は親から子へと累積的に適用されます。したがって、親が回転すると、その子も親の回転によって回転し、さらに子自身の rotation
プロパティが適用されます。
トラブルシューティング
- ItemIgnoresTransformations フラグ (QGraphicsItem) / 変換を無視する (QML)
子アイテムが親の変換(特に回転)の影響を受けないようにしたい場合、C++ではQGraphicsItem::ItemIgnoresTransformations
フラグを設定します。QMLでは直接的な同等のプロパティはありませんが、transform
プロパティを明示的に設定したり、親の階層から切り離したりすることで制御可能です。
QMLでは、// 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); // これは親の回転とは独立して適用される
transform
プロパティを操作して親の変換を打ち消す、またはカスタムの変換行列を使用することで同様の効果を得られる場合がありますが、通常はレイアウトや設計を再考する方がシンプルです。
回転後にアイテムの位置がおかしくなる (特に setPos との併用時)
回転したアイテムに対して setPos()
(C++) や x
/y
(QML) を設定すると、アイテムの位置が期待と異なる場合があります。これは、アイテムのpos
がローカル座標系ではなく、親の座標系(またはシーン座標系)で解釈されるためです。
原因
setPos()
はアイテムの左上隅(あるいはtransformOrigin
やoffset
によっては他の点)を基準に、親の座標系(またはシーン座標系)での位置を設定します。アイテムが回転すると、そのローカル座標系が傾くため、同じ 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では、x
、y
プロパティはtransformOrigin
の設定によって相対的な位置が変わることがあります。回転と移動を同時に制御する場合は、Item
のtransform
プロパティに複数のRotation
やTranslation
を適用することを検討するか、x
とy
を回転の中心に合わせて調整するロジックを記述します。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 } }
- C++ (QGraphicsItem)
アイテムのバウンディングボックス (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();
}
解説
- RotatableRect クラス
QGraphicsRectItem
を継承し、currentAngle
を保持します。- コンストラクタで矩形の中心 (
width / 2, height / 2
) をsetTransformOriginPoint()
で設定しているのが重要です。これにより、矩形はその中心を基準に回転します。 rotateBy()
メソッドは、現在の角度を更新し、setRotation()
を呼び出してアイテムの回転を設定します。
- main 関数
QGraphicsScene
とQGraphicsView
を作成し、ビューにシーンを設定します。RotatableRect
のインスタンスを作成し、scene->addItem()
でシーンに追加します。QPushButton
を作成し、clicked
シグナルをrectItem->rotateBy(15)
スロット(ラムダ関数)に接続しています。ボタンをクリックすると、アイテムが15度回転します。QTimer
を使用して、自動で連続的に回転させるアニメーションの例もコメントアウトで示しています。
Qt Quick (QML) の例
QML では、Item
およびその派生クラス(Rectangle
、Image
など)に 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 // 角度をリセット
}
}
}
}
解説
- Rectangle アイテム
id: rotatingRect
を設定して、他の要素から参照できるようにします。color
,border.color
,border.width
,radius
で外観を定義します。transformOrigin: Item.Center
が非常に重要です。これにより、矩形はその中心を基準に回転します。これを省略すると、デフォルトで左上隅を基準に回転します。rotation: 0
で初期の角度を設定します。- 子要素として
Text
を配置し、親(回転する矩形)と一緒に回転することを示しています。
- NumberAnimation on rotation
- QML の強力な機能であるプロパティアニメーションです。
on rotation
という書き方で、rotation
プロパティが変化することを指定します。from: 0
からto: 360
まで、duration: 3000
(3秒) かけて変化させます。loops: Animation.Infinite
で無限ループのアニメーションになります。running: true
で、アプリケーション起動時にアニメーションが自動的に開始されます。
- 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++):
QGraphicsScene
とQGraphicsView
: シーンはグラフィックアイテムを保持するキャンバス、ビューはシーンを表示するウィジェットです。QGraphicsRectItem
: 描画する四角形アイテムを作成します。QGraphicsRectItem(-50, -25, 100, 50)
は、ローカル座標系で(-50, -25)
を左上隅とし、幅100
、高さ50
の矩形を定義します。この場合、アイテムのローカル座標系の中心は(0,0)
になります。setTransformOriginPoint()
: 最も重要です。 アイテムの回転の中心点を設定します。ここではrotatedRect->boundingRect().center()
を使用して、アイテム自身のローカル座標系の中心を回転の中心に設定しています。これにより、アイテムがその場で回転するように見えます。rotationOriginMarker
: 回転の中心がどこにあるかを視覚的に示すために、小さな赤い円をrotatedRect
の子アイテムとして追加しています。子アイテムは親アイテムの変換(回転、移動など)に従うため、親の回転の中心に固定されます。setRotation(value)
: スライダーの値が変更されるたびに、rotatedRect
のrotation
プロパティを更新し、アイテムを回転させます。
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):
Window
: メインウィンドウを定義します。Rectangle
(container): 回転させるアイテムを配置する背景コンテナです。Rectangle
(rotatedRectQML): 回転させる四角形アイテムです。anchors.centerIn: parent
: 親のコンテナの中央に配置されます。transformOrigin: Item.Center
: 最も重要です。 アイテムの回転の中心点をアイテム自身の中心に設定します。これにより、アイテムがその場で回転するように見えます。デフォルトはItem.TopLeft
(左上隅) です。rotation: rotationSliderQML.value
:Slider
のvalue
プロパティに直接バインドすることで、スライダーの動きにアイテムが連動して回転します。- 回転の中心マーカー: C++と同様に、
rotatedRectQML
の子として小さな赤い円をanchors.centerIn: parent
で配置し、回転の中心を視覚的に示しています。
Slider
: 回転角度を制御するためのスライダーです。QMLでは、プロパティバインディングによって、スライダーのvalue
がrotatedRectQML.rotation
にリアルタイムで反映されます。- 3D的な回転 (コメントアウト部分):
rotation
プロパティは主に2D回転に使われますが、transform
プロパティを使うことで、より高度な3D回転(X軸、Y軸、Z軸周りの回転)も可能です。Rotation
要素内でaxis
プロパティを設定します。
QTransform (C++) / transform プロパティ (QML) を使用する
これは、Item.rotation
の単純な角度設定よりもはるかに強力で柔軟な方法です。回転だけでなく、平行移動 (translation)、スケール (scaling)、せん断 (shearing)、さらには3D変換(透視投影など)を組み合わせることができます。
利点
- カスタム回転の中心
setTransformOriginPoint
やtransformOrigin
とは異なり、行列操作によって任意の点を回転の中心に設定できます。 - 変換順序の制御
変換の適用順序を明示的に制御できるため、複雑なアニメーションやレイアウトを正確に実現できます。 - 複数の変換の組み合わせ
回転、移動、拡大縮小などを単一の行列で表現し、適用できます。
欠点
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
に独立した変換を追加するための抽象基底クラスです。これには QGraphicsRotation
と QGraphicsScale
という派生クラスがあります。これらはアニメーションで特に有用です。
利点
- 3D変換
QMatrix4x4
をベースにしているため、3D空間での変換をより自然に扱うことができます。 - アニメーションとの相性
それぞれの変換プロパティを独立してアニメーションさせることができます。特にQGraphicsRotation
は、補間が容易です。 - 独立した変換
QGraphicsItem
のsetRotation()
や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
を使用してQGraphicsRotation
のangle
プロパティをアニメーションさせます。これにより、複雑な補間やアニメーションカーブを適用できます。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
}
}
}