Qtでビューをリセット!QGraphicsView::resetTransform()の基本と応用

2025-05-27

QGraphicsView::resetTransform() とは

QGraphicsView は、QGraphicsScene の内容を表示するためのウィジェットです。このビューは、表示されているシーンに対して様々な変換(拡大・縮小、回転、平行移動、シアなど)を適用することができます。QGraphicsView::resetTransform() は、このビューに現在適用されている全ての変換を初期状態に戻すための関数です。

変換とは?

QGraphicsView は、内部的に変換行列(QTransform または QMatrix)を持っており、この行列を使ってシーンの内容をビューポート(表示領域)にマッピングします。例えば:

  • 平行移動 (translate): view->translate(10, 20); とすると、シーンがX軸方向に10、Y軸方向に20だけ移動して表示されます。
  • 回転 (rotate): view->rotate(45); とすると、シーンが45度回転して表示されます。
  • 拡大・縮小 (scale): view->scale(2, 2); のようにすると、シーンが2倍に拡大されて表示されます。

これらの変換は積み重ねて適用することができます。例えば、拡大してから回転するといった具合です。

resetTransform() の役割

resetTransform() を呼び出すと、これらの適用された全ての変換がリセットされ、ビューの変換行列が**単位行列(identity matrix)**に戻ります。単位行列とは、何も変換が適用されていない状態を意味します。

これにより、以下のようになります。

  1. 拡大・縮小が1:1に戻る: シーンの1単位がビューの1ピクセルに対応するようになります。
  2. 回転が0度に戻る: シーンが回転していない元の向きに戻ります。
  3. 平行移動が元に戻る: シーンの原点 (0,0) がビューポートの左上に(デフォルト設定の場合)対応するようになります。

つまり、QGraphicsView::resetTransform() は、ビューの表示状態を「購入時の状態」や「初期設定」に戻すような機能です。

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

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

    // シーンの作成
    QGraphicsScene scene;
    scene.setSceneRect(0, 0, 400, 300);

    // シーンにアイテムを追加
    QGraphicsRectItem *rect = new QGraphicsRectItem(50, 50, 100, 100);
    rect->setBrush(Qt::blue);
    scene.addItem(rect);

    // ビューの作成とシーンの設定
    QGraphicsView view(&scene);
    view.setWindowTitle("QGraphicsView::resetTransform() Example");

    // ウィンドウとレイアウトの設定
    QWidget window;
    QVBoxLayout *layout = new QVBoxLayout(&window);
    layout->addWidget(&view);

    // 拡大ボタン
    QPushButton *zoomInButton = new QPushButton("ズームイン (x1.2)");
    QObject::connect(zoomInButton, &QPushButton::clicked, [&]() {
        view.scale(1.2, 1.2);
    });
    layout->addWidget(zoomInButton);

    // 回転ボタン
    QPushButton *rotateButton = new QPushButton("時計回りに15度回転");
    QObject::connect(rotateButton, &QPushButton::clicked, [&]() {
        view.rotate(15);
    });
    layout->addWidget(rotateButton);

    // リセットボタン
    QPushButton *resetButton = new QPushButton("変換をリセット");
    QObject::connect(resetButton, &QPushButton::clicked, [&]() {
        view.resetTransform(); // ここで変換をリセット
    });
    layout->addWidget(resetButton);

    window.show();

    return a.exec();
}

このコードを実行すると、青い四角形が表示されたウィンドウが現れます。「ズームイン」ボタンを押すとビューが拡大され、「時計回りに15度回転」ボタンを押すとビューが回転します。そして「変換をリセット」ボタンを押すと、view.resetTransform() が呼び出され、ビューが拡大も回転もされていない初期状態に戻ります。



QGraphicsView::resetTransform() は、ビューの変換を初期状態に戻すためのシンプルで直接的な関数ですが、それでも意図しない動作や混乱を招く可能性があります。

想定通りの表示にならない(アイテムの位置やサイズがずれる)

問題: resetTransform() を呼び出した後、シーン内のアイテムがビューの左上隅に表示されるはずなのに、なぜかズレたり、大きすぎたり小さすぎたりする。

原因:

  • QGraphicsItem 自身の変換: QGraphicsView の変換はビュー全体に適用されますが、個々の QGraphicsItem も独自の変換を持つことができます(QGraphicsItem::setTransform(), setPos(), setRotation(), setScale() など)。resetTransform() はビューの変換をリセットするだけであり、アイテム自身の変換はリセットしません。
  • QGraphicsView::fitInView() との併用: resetTransform() を呼び出した後に fitInView() を呼び出す場合、その順序や fitInView() の引数(アスペクト比の保持など)によって最終的な表示が変わります。
  • シーンのサイズ (Scene Rect) の不一致: QGraphicsScene::sceneRect() が適切に設定されていない場合、ビューがシーンのどこを「中心」または「全体」と見なすべきか混乱し、resetTransform() 後に期待通りの表示にならないことがあります。特に、アイテムを追加した後に sceneRect を更新していない場合に起こりやすいです。

トラブルシューティング:

  • アイテム自身の変換の確認: resetTransform() はビューの変換のみに影響すること理解しておく必要があります。もしアイテム自体が動いて欲しい場合は、QGraphicsItem::setPos()setTransform() を使ってアイテムを明示的に動かす必要があります。
  • fitInView() の調整: resetTransform() の後、必要に応じて view.fitInView(scene.sceneRect(), Qt::KeepAspectRatio); のように呼び出し、アスペクト比を保持するかどうか、またはビューのサイズに合わせてシーンをフィットさせるかを確認します。
  • QGraphicsScene::setSceneRect() の確認: シーンに追加されるすべてのアイテムを包含するように sceneRect を設定してください。例えば、scene.setSceneRect(scene.itemsBoundingRect()); のように、アイテムの境界矩形に基づいて設定すると良いでしょう。

アニメーション中のresetTransform()

問題: アニメーション中に resetTransform() を呼び出すと、アニメーションが中断されたり、ビューが突然ジャンプしたりする。

原因: アニメーションはビューの変換行列を継続的に変更しているため、途中で resetTransform() が呼び出されると、アニメーションの目標値が失われ、ビューが即座に初期状態に戻ってしまうためです。

トラブルシューティング:

  • アニメーションの再開: resetTransform() 後に、新しい初期状態からアニメーションを再開するようにロジックを組む必要があります。
  • アニメーションの停止: resetTransform() を呼び出す前に、関連する QPropertyAnimationQVariantAnimation などのアニメーションを停止させることを検討してください。

resetMatrix() との混同 (Qt 4 から Qt 5/6 への移行)

問題: 以前のQtのバージョン(Qt 4)では QGraphicsView::resetMatrix() を使用していたが、Qt 5/6 に移行したら動かなくなった、または警告が出る。

原因: QGraphicsView::resetMatrix() はQt 5以降で非推奨(deprecated)となり、代わりに QGraphicsView::resetTransform() を使用するよう推奨されています。QMatrix クラスが QTransform に置き換えられたためです。

トラブルシューティング:

  • 互換性: 古いコードを移行する場合は、警告を無視せずに resetTransform() に置き換えるべきです。
  • resetTransform() の使用: resetMatrix() の代わりに resetTransform() を使用してください。機能的には同じです。

パフォーマンスの問題(あまり一般的ではないが、可能性として)

問題: resetTransform() を頻繁に呼び出すと、ビューの更新が遅くなったり、ちらつきが発生したりする。

原因: resetTransform() はビュー全体の再描画をトリガーする可能性があります。非常に頻繁に呼び出すと、描画コストが増大し、パフォーマンスが低下することがあります。

トラブルシューティング:

  • キャッシュの使用: QGraphicsView::setCacheMode() を使用してビューポートのキャッシュを有効にすることで、描画パフォーマンスが向上する場合があります。
  • setViewportUpdateMode() の確認: QGraphicsView::setViewportUpdateMode() の設定によっては、更新の頻度や方法が変わります。デフォルトの FullViewportUpdate は常にビュー全体を再描画するため、よりパフォーマンスが要求される場合は他のモード(例: MinimalViewportUpdate)を検討できますが、これらは他の描画の問題を引き起こす可能性もあるため注意が必要です。
  • 不必要な呼び出しの削減: resetTransform() を本当に必要なときにのみ呼び出すように、ロジックを見直してください。例えば、ユーザーが明示的にリセットボタンを押したときなど。

QGraphicsItem::ItemIgnoresTransformations フラグ

問題: resetTransform() を呼び出しても、特定のアイテムだけビューの変換の影響を受けない。

原因: QGraphicsItem::ItemIgnoresTransformations フラグが設定されているアイテムは、ビューの変換(拡大・縮小、回転など)を無視します。このフラグが設定されている場合、そのアイテムは常にビューポートの座標系で描画されます。

トラブルシューティング:

  • フラグの確認: 意図的に ItemIgnoresTransformations フラグを設定しているかどうかを確認してください。もしビューの変換の影響を受けさせたい場合は、このフラグを無効にする必要があります。
    myItem->setFlag(QGraphicsItem::ItemIgnoresTransformations, false);
    


QGraphicsView::resetTransform() は、ビューに適用された全ての変換(拡大・縮小、回転、平行移動など)を初期状態に戻すために使用されます。ここでは、この関数をどのように利用するか、いくつかの具体的な例を示します。

例1: 基本的な変換とリセット

この例では、QGraphicsView にいくつかのアイテムを配置し、ボタン操作によってビューを拡大・縮小、回転、そしてリセットする機能を提供します。

#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QGraphicsEllipseItem>
#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QDebug> // デバッグ出力用

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

    // 1. QGraphicsScene の作成
    QGraphicsScene scene;
    scene.setSceneRect(0, 0, 600, 400); // シーンの論理的なサイズを設定

    // 2. シーンにアイテムを追加
    QGraphicsRectItem *rectItem = new QGraphicsRectItem(100, 100, 200, 150);
    rectItem->setBrush(Qt::blue);
    rectItem->setPen(QPen(Qt::black, 3));
    scene.addItem(rectItem);

    QGraphicsEllipseItem *ellipseItem = new QGraphicsEllipseItem(350, 150, 100, 100);
    ellipseItem->setBrush(Qt::green);
    ellipseItem->setPen(QPen(Qt::darkGreen, 2));
    scene.addItem(ellipseItem);

    // 3. QGraphicsView の作成とシーンの設定
    QGraphicsView *view = new QGraphicsView(&scene);
    view->setWindowTitle("QGraphicsView::resetTransform() Example");
    view->setRenderHint(QPainter::Antialiasing); // アンチエイリアシングを有効にして描画を滑らかに

    // 4. GUI コントロールの作成
    QPushButton *zoomInButton = new QPushButton("ズームイン (+1.2)");
    QPushButton *zoomOutButton = new QPushButton("ズームアウト (-1.2)");
    QPushButton *rotateLeftButton = new QPushButton("左に15度回転");
    QPushButton *rotateRightButton = new QPushButton("右に15度回転");
    QPushButton *resetButton = new QPushButton("変換をリセット");
    QPushButton *centerOnRectButton = new QPushButton("四角形にフィット");

    // 5. レイアウトの設定
    QVBoxLayout *mainLayout = new QVBoxLayout();
    QHBoxLayout *controlLayout1 = new QHBoxLayout();
    QHBoxLayout *controlLayout2 = new QHBoxLayout();
    QHBoxLayout *controlLayout3 = new QHBoxLayout();

    controlLayout1->addWidget(zoomInButton);
    controlLayout1->addWidget(zoomOutButton);
    controlLayout2->addWidget(rotateLeftButton);
    controlLayout2->addWidget(rotateRightButton);
    controlLayout3->addWidget(resetButton);
    controlLayout3->addWidget(centerOnRectButton);

    mainLayout->addWidget(view);
    mainLayout->addLayout(controlLayout1);
    mainLayout->addLayout(controlLayout2);
    mainLayout->addLayout(controlLayout3);

    QWidget window;
    window.setLayout(mainLayout);
    window.resize(800, 600);
    window.show();

    // 6. シグナルとスロットの接続
    QObject::connect(zoomInButton, &QPushButton::clicked, [&]() {
        view->scale(1.2, 1.2);
        qDebug() << "Current Transform:" << view->transform();
    });

    QObject::connect(zoomOutButton, &QPushButton::clicked, [&]() {
        view->scale(1.0 / 1.2, 1.0 / 1.2);
        qDebug() << "Current Transform:" << view->transform();
    });

    QObject::connect(rotateLeftButton, &QPushButton::clicked, [&]() {
        view->rotate(-15);
        qDebug() << "Current Transform:" << view->transform();
    });

    QObject::connect(rotateRightButton, &QPushButton::clicked, [&]() {
        view->rotate(15);
        qDebug() << "Current Transform:" << view->transform();
    });

    QObject::connect(resetButton, &QPushButton::clicked, [&]() {
        view->resetTransform(); // ここでビューの変換をリセット
        qDebug() << "Transform Reset. Current Transform:" << view->transform();
    });

    QObject::connect(centerOnRectButton, &QPushButton::clicked, [&]() {
        // `resetTransform()` 後に特定のアイテムにフィットさせる例
        view->resetTransform(); // まず変換をリセット
        view->fitInView(rectItem, Qt::KeepAspectRatio); // そして四角形アイテムにフィット
        qDebug() << "Fit to Rect Item. Current Transform:" << view->transform();
    });

    return app.exec();
}

解説:

  • view->fitInView(rectItem, Qt::KeepAspectRatio);: resetTransform() を呼び出した後に、特定のアイテム(この場合は rectItem)がビューポートに収まるようにビューを調整する例です。resetTransform() はビューを初期状態に戻すだけで、必ずしも全てのアイテムがビューに表示されるわけではありません。fitInView() と組み合わせることで、特定の表示状態に強制することができます。
  • qDebug() << view->transform();: QGraphicsView::transform() を呼び出すことで、ビューに現在適用されている変換行列の値を確認できます。resetTransform() の前後で値がどう変わるか見てみましょう。
  • view->resetTransform();: この関数が呼び出されると、view に適用されていたすべての scale()rotate() などの変換がクリアされ、ビューの変換行列が単位行列(Identity Matrix)に戻ります。これにより、シーンの原点 (0,0) がビューポートの左上隅に(デフォルトのアライメントの場合)配置され、拡大率も1:1に戻ります。
  • view->rotate(-15);: 現在の変換に加えて、さらに左に15度回転します。これも累積的な操作です。
  • view->scale(1.2, 1.2);: 現在の変換に加えて、さらに1.2倍に拡大します。これは累積的な操作です。
  • scene.setSceneRect(0, 0, 600, 400);: シーンの論理的な境界を設定します。これは、resetTransform()fitInView() がビューの内容をどのように解釈するかに影響します。

例2: マウス操作によるズームとリセット

この例では、マウスのホイールイベントを使ってズームイン/アウトを行い、右クリックでビューの変換をリセットする機能を追加します。

#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QWheelEvent> // マウスホイールイベント用
#include <QMouseEvent> // マウスイベント用
#include <QDebug>

// QGraphicsView を継承したカスタムクラスで、マウスイベントをオーバーライド
class CustomGraphicsView : public QGraphicsView
{
public:
    CustomGraphicsView(QGraphicsScene *scene, QWidget *parent = nullptr)
        : QGraphicsView(scene, parent)
    {
        setRenderHint(QPainter::Antialiasing);
        setTransformationAnchor(QGraphicsView::AnchorUnderMouse); // マウスカーソルを中心にズーム
    }

protected:
    void wheelEvent(QWheelEvent *event) override
    {
        // ズームファクターの計算
        qreal scaleFactor = 1.15; // ズーム率
        if (event->angleDelta().y() > 0) {
            // ホイールを上にスクロール (ズームイン)
            scale(scaleFactor, scaleFactor);
        } else {
            // ホイールを下にスクロール (ズームアウト)
            scale(1.0 / scaleFactor, 1.0 / scaleFactor);
        }
        qDebug() << "Zoomed. Current Transform:" << transform();
    }

    void mousePressEvent(QMouseEvent *event) override
    {
        if (event->button() == Qt::RightButton) {
            // 右クリックで変換をリセット
            resetTransform();
            qDebug() << "Transform Reset by Right Click. Current Transform:" << transform();
        } else {
            // それ以外のマウスイベントは基底クラスに渡す
            QGraphicsView::mousePressEvent(event);
        }
    }
};

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

    QGraphicsScene scene;
    scene.setSceneRect(-300, -200, 600, 400); // シーンの中心を(0,0)にするために負の座標から開始

    // シーンの中心付近にアイテムを配置
    QGraphicsRectItem *rectItem = new QGraphicsRectItem(-50, -50, 100, 100);
    rectItem->setBrush(Qt::red);
    rectItem->setPen(QPen(Qt::black, 2));
    scene.addItem(rectItem);

    CustomGraphicsView view(&scene);
    view.setWindowTitle("Mouse Zoom & Right Click Reset Example");
    view.resize(800, 600);
    view.show();

    return app.exec();
}

解説:

  • scene.setSceneRect(-300, -200, 600, 400);: この例では、シーンの原点をビューの中心に近づけるために、負の座標から sceneRect を設定しています。resetTransform() 後にアイテムがビューの中心付近に表示されるようにするためです。
  • mousePressEvent(QMouseEvent *event) override: マウスボタンのクリックを検知します。Qt::RightButton が押された場合、resetTransform() を呼び出してビューの変換をリセットします。それ以外のボタンクリックは基底クラスのハンドラに渡します。
  • wheelEvent(QWheelEvent *event) override: マウスホイールの動きを検知し、scale() を呼び出してズームを行います。
  • setTransformationAnchor(QGraphicsView::AnchorUnderMouse);: この設定により、ビューのズームや回転の際、マウスカーソルの位置が固定されるように動作します。ユーザーエクスペリエンスが向上します。
  • CustomGraphicsView クラス: QGraphicsView を継承し、wheelEventmousePressEvent をオーバーライドしています。


QGraphicsView::resetTransform() はビューの変換を完全に初期状態に戻しますが、以下のような場合には他の方法を検討する価値があります。

  1. 特定の変換だけをリセットしたい場合(例:拡大・縮小だけをリセットし、回転は維持したい)
  2. 特定のアイテムをビューにフィットさせたい場合resetTransform() よりもっと賢い方法)
  3. カスタムな初期変換を設定したい場合
  4. スムーズなアニメーションで変換を初期状態に戻したい場合

それぞれのケースについて見ていきましょう。

特定の変換だけをリセットする

QGraphicsView は内部的に QTransform オブジェクトを使用して変換を管理しています。resetTransform() はこの QTransform を単位行列(Identity Matrix)に設定するのと同等です。特定の変換だけをリセットするには、setTransform() を使用して、目的の変換行列を明示的に設定します。

方法:

  • combinefalse に設定すると、既存の変換行列は指定した matrix で完全に置き換えられます。
  • QGraphicsView::setTransform(const QTransform &matrix, bool combine = false) を使用します。

例: ズームだけをリセットし、回転は維持する

#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QPushButton>
#include <QVBoxLayout>
#include <QDebug>

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

    QGraphicsScene scene;
    scene.setSceneRect(0, 0, 400, 300);
    QGraphicsRectItem *rect = new QGraphicsRectItem(50, 50, 100, 100);
    rect->setBrush(Qt::blue);
    scene.addItem(rect);

    QGraphicsView view(&scene);
    view.setWindowTitle("Partial Reset Example");
    view.setRenderHint(QPainter::Antialiasing);

    QPushButton *zoomButton = new QPushButton("ズームイン");
    QPushButton *rotateButton = new QPushButton("回転");
    QPushButton *resetZoomButton = new QPushButton("ズームのみリセット");
    QPushButton *fullResetButton = new QPushButton("完全リセット");

    QVBoxLayout *layout = new QVBoxLayout();
    layout->addWidget(&view);
    layout->addWidget(zoomButton);
    layout->addWidget(rotateButton);
    layout->addWidget(resetZoomButton);
    layout->addWidget(fullResetButton);

    QWidget window;
    window.setLayout(layout);
    window.resize(600, 450);
    window.show();

    // ズーム
    QObject::connect(zoomButton, &QPushButton::clicked, [&]() {
        view.scale(1.5, 1.5);
        qDebug() << "Zoomed. Transform: " << view.transform();
    });

    // 回転
    QObject::connect(rotateButton, &QPushButton::clicked, [&]() {
        view.rotate(30);
        qDebug() << "Rotated. Transform: " << view.transform();
    });

    // ズームのみリセット(回転は維持)
    QObject::connect(resetZoomButton, &QPushButton::clicked, [&]() {
        QTransform currentTransform = view.transform();
        // 現在の変換から回転成分のみを抽出し、スケール成分を単位行列のそれに置き換える
        // これは簡単な例であり、シアなど複雑な変換の場合はより複雑なロジックが必要です
        QTransform newTransform;
        newTransform.rotate(currentTransform.rotation()); // 現在の回転を維持
        view.setTransform(newTransform); // 新しい変換を設定(スケールは1.0, 1.0 に戻る)
        qDebug() << "Zoom Reset. Transform: " << view.transform();
    });

    // 完全リセット (resetTransform() と同じ)
    QObject::connect(fullResetButton, &QPushButton::clicked, [&]() {
        view.resetTransform();
        qDebug() << "Full Reset. Transform: " << view.transform();
    });

    return app.exec();
}

ポイント:

  • QTransform は3x3の行列なので、各成分を直接操作することも可能ですが、一般的には scale(), rotate(), translate() などのコンビニエンス関数を使う方が簡単です。
  • QTransform クラスのメソッド(rotation() など)を使って現在の変換成分を取得し、新しい QTransform を構築します。

特定のアイテムや領域をビューにフィットさせる

resetTransform() はビューを初期状態に戻しますが、必ずしもシーン内のすべてのアイテムがビューポートに収まるわけではありません。特定のアイテムやシーンの領域をビューに完全にフィットさせたい場合は、QGraphicsView::fitInView() を使用するのが最も適切です。

方法:

  • Qt::AspectRatioMode でアスペクト比を保持するかどうかを指定できます。
  • void QGraphicsView::fitInView(const QGraphicsItem *item, Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio)
  • void QGraphicsView::fitInView(const QRectF &rect, Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio)

例: シーン全体または特定のアイテムをフィットさせる

#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QGraphicsEllipseItem>
#include <QPushButton>
#include <QVBoxLayout>

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

    QGraphicsScene scene;
    scene.setSceneRect(0, 0, 800, 600); // シーンの広さを設定

    QGraphicsRectItem *rect1 = new QGraphicsRectItem(50, 50, 150, 100);
    rect1->setBrush(Qt::blue);
    scene.addItem(rect1);

    QGraphicsEllipseItem *ellipse1 = new QGraphicsEllipseItem(300, 200, 100, 80);
    ellipse1->setBrush(Qt::green);
    scene.addItem(ellipse1);

    QGraphicsRectItem *rect2 = new QGraphicsRectItem(600, 450, 100, 100);
    rect2->setBrush(Qt::red);
    scene.addItem(rect2);

    QGraphicsView view(&scene);
    view.setWindowTitle("FitInView Example");
    view.setRenderHint(QPainter::Antialiasing);

    QPushButton *fitSceneButton = new QPushButton("シーン全体をフィット");
    QPushButton *fitRect1Button = new QPushButton("青い四角形にフィット (アスペクト比保持)");
    QPushButton *resetAndFitButton = new QPushButton("リセットしてからシーンをフィット");

    QVBoxLayout *layout = new QVBoxLayout();
    layout->addWidget(&view);
    layout->addWidget(fitSceneButton);
    layout->addWidget(fitRect1Button);
    layout->addWidget(resetAndFitButton);

    QWidget window;
    window.setLayout(layout);
    window.resize(900, 700);
    window.show();

    // シーン全体をビューにフィット
    QObject::connect(fitSceneButton, &QPushButton::clicked, [&]() {
        // 現在の変換を維持しつつ、シーン全体をビューポートにフィットさせる
        view.fitInView(scene.sceneRect(), Qt::KeepAspectRatio);
    });

    // 特定のアイテムをビューにフィット(アスペクト比保持)
    QObject::connect(fitRect1Button, &QPushButton::clicked, [&]() {
        // 青い四角形アイテムがビューポートに収まるように調整
        view.fitInView(rect1, Qt::KeepAspectRatio);
    });

    // まずリセットし、それからシーン全体をフィット
    QObject::connect(resetAndFitButton, &QPushButton::clicked, [&]() {
        view.resetTransform(); // まずビューの変換を初期化
        view.fitInView(scene.sceneRect(), Qt::KeepAspectRatio); // その後シーン全体をフィット
    });

    return app.exec();
}

ポイント:

  • Qt::KeepAspectRatio を使用すると、ビューの縦横比を維持したまま、シーン全体または指定された領域が最大サイズで表示されます。これにより、アイテムが歪むのを防げます。
  • fitInView() は、内部的に最適な scale()translate() を計算して適用します。

カスタムな初期変換を設定する

resetTransform() はビューを単位行列に戻しますが、アプリケーションの要件によっては、常に特定の初期ズームレベルや初期位置から開始したい場合があります。

方法:

  • QGraphicsView::setTransform(const QTransform &matrix) を使用して、カスタムの変換行列を初期状態として設定します。

例: 常に特定のズームレベルから開始する

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

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

    QGraphicsScene scene;
    scene.setSceneRect(0, 0, 400, 300);
    QGraphicsRectItem *rect = new QGraphicsRectItem(50, 50, 100, 100);
    rect->setBrush(Qt::darkCyan);
    scene.addItem(rect);

    QGraphicsView view(&scene);
    view.setWindowTitle("Custom Initial Transform Example");
    view.setRenderHint(QPainter::Antialiasing);

    // 初期変換として、2倍にズームし、少し右下に移動させる
    QTransform initialTransform;
    initialTransform.scale(2.0, 2.0);
    initialTransform.translate(50, 50); // シーンの原点から50,50だけビューを移動
    view.setTransform(initialTransform); // カスタムの初期変換を設定

    QPushButton *resetToCustomButton = new QPushButton("カスタム初期状態に戻す");
    QPushButton *zoomInButton = new QPushButton("ズームイン");

    QVBoxLayout *layout = new QVBoxLayout();
    layout->addWidget(&view);
    layout->addWidget(resetToCustomButton);
    layout->addWidget(zoomInButton);

    QWidget window;
    window.setLayout(layout);
    window.resize(600, 450);
    window.show();

    QObject::connect(resetToCustomButton, &QPushButton::clicked, [&]() {
        view.setTransform(initialTransform); // カスタムの初期変換に戻す
    });

    QObject::connect(zoomInButton, &QPushButton::clicked, [&]() {
        view.scale(1.2, 1.2);
    });

    return app.exec();
}

ポイント:

  • resetTransform() の代わりに、この setTransform(initialTransform) を呼び出すことで、常に特定の初期状態に戻すことができます。
  • initialTransform オブジェクトを作成し、必要な変換を適用してから view.setTransform(initialTransform); で設定します。

スムーズなアニメーションで変換を初期状態に戻す

resetTransform() は瞬時にビューを初期状態に戻しますが、ユーザーエクスペリエンスを向上させるために、スムーズなアニメーションで変換をリセットしたい場合があります。

方法:

  • QPropertyAnimationQGraphicsView::transform プロパティを組み合わせて使用します。

例: アニメーションでビューをリセット

#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QPushButton>
#include <QVBoxLayout>
#include <QPropertyAnimation>
#include <QTransform>
#include <QDebug>

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

    QGraphicsScene scene;
    scene.setSceneRect(0, 0, 400, 300);
    QGraphicsRectItem *rect = new QGraphicsRectItem(50, 50, 100, 100);
    rect->setBrush(Qt::darkMagenta);
    scene.addItem(rect);

    QGraphicsView view(&scene);
    view.setWindowTitle("Animated Reset Example");
    view.setRenderHint(QPainter::Antialiasing);

    QPushButton *zoomRotateButton = new QPushButton("ズーム&回転");
    QPushButton *animateResetButton = new QPushButton("アニメーションでリセット");
    QPushButton *instantResetButton = new QPushButton("瞬時にリセット"); // 比較のため

    QVBoxLayout *layout = new QVBoxLayout();
    layout->addWidget(&view);
    layout->addWidget(zoomRotateButton);
    layout->addWidget(animateResetButton);
    layout->addWidget(instantResetButton);

    QWidget window;
    window.setLayout(layout);
    window.resize(600, 450);
    window.show();

    // アニメーションオブジェクトの作成
    QPropertyAnimation *animation = new QPropertyAnimation(&view, "transform");
    animation->setDuration(500); // 0.5秒のアニメーション
    animation->setEasingCurve(QEasingCurve::OutQuad); // 緩やかに終了するカーブ

    // ズームと回転を適用
    QObject::connect(zoomRotateButton, &QPushButton::clicked, [&]() {
        view.scale(2.0, 2.0);
        view.rotate(45);
        qDebug() << "Applied transform. Current: " << view.transform();
    });

    // アニメーションでリセット
    QObject::connect(animateResetButton, &QPushButton::clicked, [&]() {
        animation->setStartValue(view.transform()); // 現在の変換を開始値とする
        animation->setEndValue(QTransform());       // 単位行列を終了値とする(初期状態)
        animation->start();
        qDebug() << "Animating reset...";
    });

    // 瞬時にリセット (QGraphicsView::resetTransform() と同じ)
    QObject::connect(instantResetButton, &QPushButton::clicked, [&]() {
        view.resetTransform();
        qDebug() << "Instant reset.";
    });

    return app.exec();
}

ポイント:

  • setDuration() でアニメーションの時間を、setEasingCurve() でアニメーションの速度変化を設定できます。
  • setStartValue(view.transform()) で現在のビューの変換行列を取得し、setEndValue(QTransform()) で単位行列(初期状態)を設定します。
  • QPropertyAnimation を使用し、QGraphicsViewtransform プロパティをアニメーションさせます。