Qt QGraphicsViewの描画を最適化!ViewportUpdateMode徹底解説

2025-05-27

QGraphicsView::ViewportUpdateMode (enum)

QGraphicsView::ViewportUpdateMode は、QGraphicsView のビューポート(表示領域)を更新する際の動作を定義します。この設定は、特に複雑なシーンや頻繁な更新が必要な場合に、アプリケーションの応答性を最適化するために重要です。

各モードの詳細:

  • QGraphicsView::NoViewportUpdate

    • 説明
      ビューポートは自動的には更新されません。開発者が明示的に viewport()->update()viewport()->repaint() を呼び出す必要があります。
    • 用途
      非常に特殊なケースで、開発者が完全に描画タイミングを制御したい場合にのみ使用されます。通常は推奨されません。このモードでは、手動で更新をトリガーしない限り、シーンの変更がビューポートに反映されません。
  • QGraphicsView::BoundingRectViewportUpdate

    • 説明
      シーンに変更があった場合、変更されたすべてのアイテムの境界矩形(Bounding Rect)を結合した領域が再描画されます。これは MinimalViewportUpdate よりも広範囲を更新する可能性がありますが、FullViewportUpdate よりは狭いことが多いです。
    • 用途
      複数のアイテムが同時に変更されるが、それがビューポート全体には及ばない場合に有効です。
  • QGraphicsView::SmartViewportUpdate

    • 説明
      Qt がヒューリスティックに基づいて、最適な更新モードを決定します。これは、FullViewportUpdateMinimalViewportUpdate の中間のような動作をします。Qt が自動的に最適な更新領域を推測し、それに基づいて再描画を行います。
    • 用途
      一般的にバランスの取れたパフォーマンスを提供します。ほとんどのアプリケーションで良い選択肢となるでしょう。
  • QGraphicsView::MinimalViewportUpdate

    • 説明
      シーンに変更があった場合、その変更が影響する最小限の領域のみが再描画されます。たとえば、アイテムが移動した場合、そのアイテムが占めていた領域と新しい領域のみが更新されます。
    • 用途
      シーンが大きく、変更が局所的である場合にパフォーマンスを向上させます。通常、FullViewportUpdate よりも高速です。ただし、特定のグラフィックカードやドライバーによっては、レンダリングの問題が発生する可能性があります。
    • 説明
      これはデフォルトのモードです。シーンに変更があった場合、ビューポート全体が常に再描画されます。最も単純な更新モードですが、シーンが大きく、変更が頻繁に発生する場合、パフォーマンスが低下する可能性があります。
    • 用途
      シーンが非常に小さく、更新頻度が低い場合や、最も確実な描画品質を求める場合に適しています。

QGraphicsView オブジェクトの setViewportUpdateMode() メソッドを使用して設定します。

QGraphicsView* view = new QGraphicsView();
// シーンを設定...

// 例: スマート更新モードに設定
view->setViewportUpdateMode(QGraphicsView::SmartViewportUpdate);


描画のアーティファクト(ゴミ、残像)が発生する

問題
MinimalViewportUpdateSmartViewportUpdate を使用していると、アイテムが移動したり、シーンが更新されたりする際に、以前の描画内容が残ってしまったり、不完全な描画が表示されたりすることがあります。これは、ビューポートの更新領域が適切に計算されていない場合に発生します。

原因

  • マルチスレッド環境で描画を更新しているが、GUIスレッドとの同期が取れていない。
  • QGraphicsItem::setCacheMode() を使用しているが、キャッシュの無効化が適切に行われていない。
  • アイテムが boundingRect() の外側に描画している。アンチエイリアシングを使用している場合や、描画処理で境界線ぎりぎりのピクセルを扱う場合によく起こります。
  • QGraphicsItem::boundingRect()QGraphicsItem::shape() の実装が不正確である。これらのメソッドは、アイテムの描画領域と形状を正確に報告する必要があります。不正確な場合、Qt は更新が必要な領域を正しく判断できません。

トラブルシューティング

    • アイテムの描画領域を正確に返すように、これらのメソッドを再確認してください。特に、アンチエイリアシングや太い線を使用する場合は、boundingRect() をわずかに広げる必要がある場合があります。
    • 例: boundingRect().adjusted(-2, -2, 2, 2) のように、数ピクセル余裕を持たせる。
  1. FullViewportUpdate を試す

    • 問題が解決しない場合、一時的に view->setViewportUpdateMode(QGraphicsView::FullViewportUpdate); に設定してみてください。これで描画が正しく行われるのであれば、MinimalViewportUpdateSmartViewportUpdate の問題であることが確定します。パフォーマンスが許容範囲であれば、そのまま FullViewportUpdate を使用することもできます。
  2. QGraphicsItem::setCacheMode() の確認

    • アイテムのキャッシュを有効にしている場合 (ItemCoordinateCacheDeviceCoordinateCache)、アイテムの内容が変更されたときに必ず update() を呼び出すようにしてください。これにより、キャッシュが無効化され、次回の描画で再生成されます。
    • キャッシュモードを無効にしてみる (NoCache) と、問題が解決するかどうか確認できます。
  3. QGraphicsView::setRenderHints() の確認

    • アンチエイリアシング (QPainter::Antialiasing) を有効にしている場合、QGraphicsView は通常、描画領域を自動的に調整してアーティファクトを防ぎます。しかし、もし手動で描画領域を制限している場合や、DontAdjustForAntialiasing フラグを使用している場合は注意が必要です。
  4. 手動でのビューポート更新 (NoViewportUpdate の場合)

    • NoViewportUpdate を使用している場合、変更があったときに view->viewport()->update() または view->viewport()->repaint() を明示的に呼び出す必要があります。呼び出しを忘れると、シーンの変更が画面に反映されません。

パフォーマンスの低下

問題
アプリケーションが重く、描画がカクカクする。特に、多くのアイテムが同時に移動したり、頻繁に更新されたりする場合に顕著です。

原因

  • OpenGL ビューポートを使用していない
    複雑なシーンでは、ソフトウェアレンダリングよりもOpenGLによるハードウェアアクセラレーションの方がパフォーマンスが良い場合が多い。
  • QGraphicsItem の boundingRect() が大きすぎる
    アイテム自体は小さいが、boundingRect() が非常に大きいため、更新領域が不必要に拡大されている。
  • 過剰な QGraphicsScene::update() 呼び出し
    必要以上にシーン全体を更新している。
  • MinimalViewportUpdate や SmartViewportUpdate が最適に機能していない
    多数の小さな更新が頻繁に発生し、Qt が更新領域を計算するのに時間がかかりすぎている。
  • FullViewportUpdate の使用
    シーンが大きく、変更が局所的であるにもかかわらず、ビューポート全体を再描画しているため、不必要な描画コストが発生している。

トラブルシューティング

  1. 適切な ViewportUpdateMode の選択

    • まずは SmartViewportUpdate を試してみてください。これは多くの場合で良好なバランスを提供します。
    • シーンの更新が非常に局所的であれば、MinimalViewportUpdate が最適化に役立つかもしれません。
    • 逆に、多くの小さな更新が広範囲に及ぶ場合、BoundingRectViewportUpdate や、場合によっては FullViewportUpdate の方が効率的なこともあります。これは試行錯誤が必要です。
  2. QGraphicsItem の boundingRect() と shape() の最適化

    • アイテムの描画領域とインタラクション領域を正確に定義することで、Qt が更新領域をより効率的に計算できます。
  3. QGraphicsItem のキャッシュの活用

    • 静的なアイテムや、内容が頻繁に変わらない複雑なアイテムには QGraphicsItem::setCacheMode(QGraphicsItem::DeviceCoordinateCache) または ItemCoordinateCache を設定することを検討してください。これにより、アイテムが移動しても再描画のコストが削減されます。ただし、内容が変更された場合は、必ず update() を呼び出すことを忘れないでください。
  4. QGraphicsView::setOptimizationFlags() の使用

    • 描画パフォーマンスを向上させるための様々なフラグがあります。例えば、QGraphicsView::DontSavePainterStateQGraphicsView::DontAdjustForAntialiasing など。これらのフラグは注意して使用する必要がありますが、特定の状況でパフォーマンスを向上させる可能性があります。
  5. QGraphicsScene::setItemIndexMethod() の設定

    • シーン内のアイテムが頻繁に移動する場合、QGraphicsScene::NoIndex を設定すると、アイテムの検索にかかるオーバーヘッドを削減できる可能性があります。しかし、静的なシーンや、特定のアイテムの検索が頻繁に行われるシーンでは、インデックス化が有効です。
  6. OpenGL ビューポートの使用

    • view->setViewport(new QOpenGLWidget()); を使用して、OpenGL をバックエンドとするビューポートに切り替えることを検討してください。これにより、GPUによる描画の高速化が期待できます。ただし、QOpenGLWidgetの導入には別途考慮事項(コンテキスト管理など)が必要になる場合があります。
  7. 不要な update() 呼び出しの回避

    • QGraphicsItem::update()QGraphicsScene::update() を必要以上に頻繁に呼び出していないか確認してください。Qt の Graphics View Framework は賢く更新を管理するため、通常はアイテムの変更を通知するだけで十分です。

アイテムが表示されない、または更新がされない

問題
シーンにアイテムを追加したが表示されない、またはアイテムのプロパティを変更したが、ビューポートに反映されない。

原因

  • シーンの矩形 (sceneRect()) が、表示したいアイテムの範囲を含んでいない。
  • アイテムの boundingRect() が適切に定義されていないため、ビューポートの可視範囲外にあると認識されている。
  • QGraphicsItemQGraphicsScene に追加されていない。
  • NoViewportUpdate モードが設定されており、手動での更新が忘れられている。

トラブルシューティング

  1. NoViewportUpdate でないか確認

    • デフォルトモードは FullViewportUpdate ですが、もし NoViewportUpdate に設定されている場合は、変更後やアイテム追加後に view->viewport()->update(); を呼び出しているか確認してください。
  2. アイテムがシーンに追加されているか確認

    • scene->addItem(myGraphicsItem); が正しく呼び出されていることを確認してください。
  3. QGraphicsItem::boundingRect() の確認

    • アイテムの boundingRect() がアイテムの実際の描画領域をカバーしていることを確認してください。もし boundingRect() が空であるか、非常に小さい場合、アイテムは正しく描画されない可能性があります。
  4. QGraphicsScene::setSceneRect() の確認

    • シーンの矩形が、表示したいアイテムの範囲を包含しているか確認してください。もしアイテムが sceneRect() の外にある場合、表示されないことがあります。必要であれば、view->fitInView(scene->sceneRect(), Qt::KeepAspectRatio); などを使用して、シーン全体をビューに収めることができます。
  5. デバッグ用 QGraphicsItem の描画

    • QGraphicsItem::paint() メソッド内で painter->drawRect(boundingRect()); を追加して、アイテムの boundingRect() が実際にどこに描画されているかを確認する。これにより、問題の範囲を特定できます。


以下に、それぞれの ViewportUpdateMode を設定する具体的なコード例と、そのモードがどのような状況で役立つかについて説明します。

QGraphicsView::ViewportUpdateMode のプログラミング例

この例では、QGraphicsViewQGraphicsScene、および移動可能な QGraphicsRectItem を含む基本的な Qt アプリケーションを作成します。ボタンをクリックすることで、ViewportUpdateMode を切り替えることができます。

#include <QtWidgets>

// カスタムのQGraphicsRectItem。移動時にQGraphicsScene::update()を呼び出す
class MovableRectItem : public QGraphicsRectItem
{
public:
    MovableRectItem(const QRectF& rect) : QGraphicsRectItem(rect) {
        setFlags(ItemIsMovable | ItemSendsGeometryChanges);
        setBrush(Qt::blue);
    }

protected:
    QVariant itemChange(GraphicsItemChange change, const QVariant& value) override {
        if (change == ItemPositionChange && scene()) {
            // アイテムの位置が変更されたときに、そのアイテムの領域を更新するようにシーンに通知します。
            // これにより、QGraphicsView::MinimalViewportUpdate などが機能します。
            scene()->update(boundingRect()); // この行は、MinimalViewportUpdateで特に重要です。
        }
        return QGraphicsRectItem::itemChange(change, value);
    }
};

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

    // 1. シーンを作成
    QGraphicsScene scene;
    scene.setSceneRect(0, 0, 800, 600); // シーンの論理サイズを設定
    scene.setBackgroundBrush(Qt::lightGray);

    // 2. アイテムをシーンに追加
    MovableRectItem* rect1 = new MovableRectItem(QRectF(50, 50, 100, 100));
    MovableRectItem* rect2 = new MovableRectItem(QRectF(200, 100, 80, 80));
    scene.addItem(rect1);
    scene.addItem(rect2);

    // 3. ビューを作成し、シーンを設定
    QGraphicsView view(&scene);
    view.setRenderHint(QPainter::Antialiasing); // アンチエイリアシングを有効にする

    // 4. ViewportUpdateMode を制御するためのボタンとレイアウト
    QPushButton* fullUpdateBtn = new QPushButton("FullViewportUpdate");
    QPushButton* minimalUpdateBtn = new QPushButton("MinimalViewportUpdate");
    QPushButton* smartUpdateBtn = new QPushButton("SmartViewportUpdate");
    QPushButton* boundingRectUpdateBtn = new QPushButton("BoundingRectViewportUpdate");
    QPushButton* noUpdateBtn = new QPushButton("NoViewportUpdate");

    QLabel* currentModeLabel = new QLabel("現在のモード: FullViewportUpdate (初期)");

    QVBoxLayout* controlLayout = new QVBoxLayout;
    controlLayout->addWidget(fullUpdateBtn);
    controlLayout->addWidget(minimalUpdateBtn);
    controlLayout->addWidget(smartUpdateBtn);
    controlLayout->addWidget(boundingRectUpdateBtn);
    controlLayout->addWidget(noUpdateBtn);
    controlLayout->addWidget(currentModeLabel);
    controlLayout->addStretch();

    QHBoxLayout* mainLayout = new QHBoxLayout;
    mainLayout->addWidget(&view, 1); // view が多くのスペースを占めるようにする
    mainLayout->addLayout(controlLayout);

    QWidget window;
    window.setLayout(mainLayout);
    window.setWindowTitle("QGraphicsView Viewport Update Modes");
    window.resize(1000, 700);

    // 5. 各ボタンにシグナルとスロットを接続し、ViewportUpdateMode を設定
    QObject::connect(fullUpdateBtn, &QPushButton::clicked, [&]() {
        view.setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
        currentModeLabel->setText("現在のモード: FullViewportUpdate");
        view.viewport()->update(); // 必要に応じて即時更新
    });

    QObject::connect(minimalUpdateBtn, &QPushButton::clicked, [&]() {
        view.setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate);
        currentModeLabel->setText("現在のモード: MinimalViewportUpdate");
        view.viewport()->update(); // 必要に応じて即時更新
    });

    QObject::connect(smartUpdateBtn, &QPushButton::clicked, [&]() {
        view.setViewportUpdateMode(QGraphicsView::SmartViewportUpdate);
        currentModeLabel->setText("現在のモード: SmartViewportUpdate");
        view.viewport()->update(); // 必要に応じて即時更新
    });

    QObject::connect(boundingRectUpdateBtn, &QPushButton::clicked, [&]() {
        view.setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
        currentModeLabel->setText("現在のモード: BoundingRectViewportUpdate");
        view.viewport()->update(); // 必要に応じて即時更新
    });

    QObject::connect(noUpdateBtn, &QPushButton::clicked, [&]() {
        view.setViewportUpdateMode(QGraphicsView::NoViewportUpdate);
        currentModeLabel->setText("現在のモード: NoViewportUpdate (手動更新が必要)");
        // NoViewportUpdate の場合、自動更新は行われません。
        // シーンに変更があっても、手動で viewport()->update() を呼ばない限り反映されません。
        // 例: QTimer で定期的に update() を呼ぶか、特定のイベントで呼ぶ
    });

    // 初期モードの設定
    view.setViewportUpdateMode(QGraphicsView::FullViewportUpdate);

    window.show();

    return app.exec();
}

上記のコードを実行し、各ボタンをクリックしてアイテムをドラッグして移動させてみてください。それぞれのモードでのビューポートの更新挙動の違いを視覚的に確認できます。

    • コード例
      view.setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
    • 挙動
      アイテムを移動させると、ビューポート全体が常に再描画されます。アイテムの移動中も、ビューポート全体がちらつくように見えるかもしれません。
    • 利点
      最もシンプルで確実な描画。描画のアーティファクトが発生しにくい。
    • 欠点
      シーンが大きく、多くのアイテムがあり、頻繁に更新される場合にパフォーマンスが低下しやすい。
  1. QGraphicsView::MinimalViewportUpdate

    • コード例
      view.setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate);
    • 挙動
      アイテムを移動させると、そのアイテムが占めていた古い位置と新しい位置、および影響を受ける可能性のある最小限の領域のみが再描画されます。MovableRectItem::itemChange() 内で scene()->update(boundingRect()); を呼び出すことで、アイテム自身がその変更をシーンに通知し、このモードが効率的に機能します。
    • 利点
      シーンの変更が局所的である場合に、描画コストを大幅に削減し、パフォーマンスを向上させます。
    • 欠点
      QGraphicsItem::boundingRect() の実装が不正確だと、描画のアーティファクト(残像やゴミ)が発生する可能性があります。
  2. QGraphicsView::SmartViewportUpdate

    • コード例
      view.setViewportUpdateMode(QGraphicsView::SmartViewportUpdate);
    • 挙動
      Qt が内部的に、FullViewportUpdateMinimalViewportUpdate のどちらが効率的かを判断しようとします。多くの場合、MinimalViewportUpdate に近い挙動を示しますが、更新領域の計算が複雑になりすぎると、自動的に FullViewportUpdate に切り替えることがあります。
    • 利点
      ほとんどの状況で良いバランスを提供し、開発者が個別に更新領域を最適化する手間を省きます。
    • 欠点
      予測できない挙動を示す可能性がわずかにあります(ただし稀です)。
  3. QGraphicsView::BoundingRectViewportUpdate

    • コード例
      view.setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
    • 挙動
      シーン内の複数のアイテムが変更された場合、それらすべての変更されたアイテムの境界矩形を結合した単一の領域が再描画されます。MinimalViewportUpdate よりも広い領域が更新されますが、FullViewportUpdate ほどではありません。
    • 利点
      MinimalViewportUpdate でアーティファクトが発生する場合や、複数のアイテムが常に同時に移動するようなケースで、FullViewportUpdate よりも効率的になることがあります。更新領域の計算が MinimalViewportUpdate よりも単純なため、オーバーヘッドが少ない場合があります。
  4. QGraphicsView::NoViewportUpdate

    • コード例
      view.setViewportUpdateMode(QGraphicsView::NoViewportUpdate);
    • 挙動
      アイテムを移動させても、ビューポートは自動的に更新されません。アイテムは動きません。MovableRectItem::itemChange()scene()->update(boundingRect()); を呼び出しても、ビューポートは更新されません。画面を更新するには、明示的に view.viewport()->update(); または view.viewport()->repaint(); を呼び出す必要があります。
    • 利点
      描画のタイミングを完全に開発者が制御できるため、特定の固定フレームレートが必要なゲームのようなアプリケーションで役立ちます。不必要な描画計算を完全にスキップできます。
    • 欠点
      更新の管理がすべて開発者の責任になり、手間がかかります。手動で更新をトリガーしない限り、画面が古いままで表示される可能性があります。


レンダリングバックエンドの変更 (OpenGL / Raster)

QGraphicsView は、デフォルトではソフトウェアレンダリング(Raster)を使用しますが、OpenGL を使用するように設定することで、ハードウェアアクセラレーションを活用し、特に複雑なシーンやアニメーションにおいて大幅なパフォーマンス向上を期待できます。

  • 注意点
    • OpenGL は MinimalViewportUpdateSmartViewportUpdate との相性が悪い場合があります。多くの場合、FullViewportUpdate を使用することが推奨されます。これは、OpenGL の描画パイプラインが部分的な更新よりも全体の再描画に適しているためです。
    • クロスプラットフォームでの互換性やドライバーの問題が発生する可能性があります。
  • 利点
    GPUによる高速な描画、特にスケーリング、回転、透過などの変換が多い場合に効果的。
  • 方法
    QGraphicsView::setViewport() メソッドに QOpenGLWidget のインスタンスを渡します。
    #include <QGraphicsView>
    #include <QOpenGLWidget> // Qt 5.x 以降では QOpenGLWidget を使用
    
    // ...
    QGraphicsView* view = new QGraphicsView();
    view->setViewport(new QOpenGLWidget()); // OpenGL をビューポートとして設定
    view->setViewportUpdateMode(QGraphicsView::FullViewportUpdate); // OpenGL との相性が良い
    // ...
    

QGraphicsItem のキャッシュモード (QGraphicsItem::CacheMode)

個々の QGraphicsItem の描画内容をキャッシュすることで、アイテムが頻繁に移動してもその描画コストを削減できます。

  • 注意点
    • キャッシュの生成と無効化にはオーバーヘッドがあります。頻繁に内容が変更されるアイテムでは、キャッシュを使用しない方が良い場合があります。
    • メモリ消費が増加する可能性があります。
  • 利点
    静的またはほとんど変化しない複雑なアイテムの描画パフォーマンスを大幅に向上させます。
  • 方法
    QGraphicsItem::setCacheMode() メソッドを使用します。
    • QGraphicsItem::NoCache (デフォルト)
      キャッシュなし。常に描画されます。
    • QGraphicsItem::ItemCoordinateCache
      アイテムのローカル座標系で描画内容をキャッシュします。アイテムが移動してもキャッシュは有効ですが、スケールや回転などの変換が適用されるとキャッシュは無効化され、再生成されます。
    • QGraphicsItem::DeviceCoordinateCache
      デバイス座標系(ビューポートのピクセル座標)で描画内容をキャッシュします。アイテムの移動、スケール、回転などの変換が適用されると、キャッシュは無効化され、再生成されます。これは通常、アイテムが静的で、ほとんど移動しない場合に最適です。
    // QGraphicsItem のサブクラス内で
    MyGraphicsItem::MyGraphicsItem(...) : QGraphicsRectItem(...) {
        setCacheMode(QGraphicsItem::ItemCoordinateCache); // または DeviceCoordinateCache
        // ...
    }
    
    // アイテムの内容が変更された場合、手動でキャッシュを無効化し再描画を促す
    void MyGraphicsItem::updateContent() {
        // ... 描画内容を変更するコード ...
        update(); // キャッシュを無効化し、次回の描画で再生成
    }
    

QGraphicsView::setRenderHints() の最適化

QPainter のレンダリングヒントを設定することで、描画品質とパフォーマンスのバランスを調整できます。

  • 注意点
    アンチエイリアシングなどの高品質なヒントは描画コストが高いです。必要な場合にのみ有効にし、不要であれば無効にすることでパフォーマンスを改善できます。
  • 利点
    アンチエイリアシングなどの視覚効果のオン/オフを切り替えることで、パフォーマンスに影響を与えることができます。
  • 方法
    QGraphicsView::setRenderHint() を使用します。
    QGraphicsView* view = new QGraphicsView();
    view->setRenderHint(QPainter::Antialiasing, true); // アンチエイリアシングを有効化
    view->setRenderHint(QPainter::HighQualityAntialiasing, false); // 高品質アンチエイリアシングは無効化 (コストが高い)
    // view->setRenderHint(QPainter::SmoothPixmapTransform, true); // ピクスマップのスムーズな変換
    

QGraphicsItem の boundingRect() と shape() の最適化

QGraphicsItemboundingRect() は描画領域を、shape() はインタラクション(クリック、衝突など)領域を定義します。これらの実装が不正確だと、不必要な描画や計算が発生し、パフォーマンスが低下する可能性があります。

  • 注意点
    boundingRect() が実際の描画領域よりも大きすぎると、不必要な描画が発生します。逆に小さすぎると、描画が途切れる可能性があります。
  • 利点
    Qt が更新が必要な領域や衝突検出をより効率的に行えるようになります。MinimalViewportUpdate などで描画のアーティファクトを防ぐ上でも重要です。
  • 方法
    QGraphicsItem のサブクラスでこれらのメソッドを正確に実装します。
    QRectF MyCustomItem::boundingRect() const {
        // 実際の描画領域を正確に返す
        // アンチエイリアシングなどを考慮して、わずかに広げることも検討する
        return QRectF(-50, -50, 100, 100);
    }
    
    QPainterPath MyCustomItem::shape() const {
        // インタラクション領域を定義するパスを返す
        QPainterPath path;
        path.addRect(boundingRect());
        return path;
    }
    

QGraphicsScene::setItemIndexMethod() の調整

QGraphicsScene は、シーン内のアイテムを効率的に検索するためのインデックス構造を持っています。このインデックスの更新オーバーヘッドがパフォーマンスに影響を与えることがあります。

  • 注意点
    インデックスを無効にすると、アイテムの検索操作が非常に遅くなります。
  • 利点
    アイテムの追加や削除、移動が頻繁に行われ、かつ items()itemAt() などのアイテム検索操作がほとんど行われないシーンでは、インデックス化を無効にすることでパフォーマンスが向上する可能性があります。
  • 方法
    QGraphicsScene::setItemIndexMethod() を使用します。
    • QGraphicsScene::BspTreeIndex (デフォルト)
      BSPツリーを使用してアイテムをインデックス化します。アイテムの追加、削除、移動時にインデックスが更新されます。
    • QGraphicsScene::NoIndex
      インデックス化を行いません。
    QGraphicsScene* scene = new QGraphicsScene();
    scene->setItemIndexMethod(QGraphicsScene::NoIndex); // アイテムの検索が不要な場合
    

不要な update() 呼び出しの回避

QGraphicsItem::update()QGraphicsScene::update() を不必要に頻繁に呼び出すと、描画が重複したり、不必要な更新処理がトリガーされたりして、パフォーマンスが低下します。

  • 注意点
    NoViewportUpdate モードを使用している場合は、手動で update() を呼び出す必要があります。
  • 利点
    不要な描画サイクルの削減。
  • 方法
    • アイテムのプロパティを変更する際は、その変更が実際に描画に影響する場合にのみ update() を呼び出します。
    • QGraphicsItem の変更通知メカニズム (ItemSendsGeometryChanges など) を活用し、Qt に更新を任せるようにします。
    • アニメーションなどでは、QPropertyAnimationQTimeLine を使用し、Qt のフレームワークに任せるのが効率的です。

QGraphicsView::setOptimizationFlags()

QGraphicsView には、特定の最適化フラグを設定するメソッドがあります。

  • 注意点
    これらのフラグは注意して使用する必要があります。特に DontAdjustForAntialiasing は描画の問題を引き起こす可能性があります。
  • 利点
    特定の条件下でパフォーマンスを微調整できます。
  • 方法
    QGraphicsView::setOptimizationFlags() を使用します。
    • QGraphicsView::DontSavePainterState: 描画中に QPainter の状態を頻繁に保存・復元するコストを削減します。
    • QGraphicsView::DontAdjustForAntialiasing: アンチエイリアシングの描画領域の調整を無効にします(描画アーティファクトのリスクがあります)。
    QGraphicsView* view = new QGraphicsView();
    view->setOptimizationFlags(QGraphicsView::DontSavePainterState | QGraphicsView::DontAdjustForAntialiasing);
    

カスタムの描画ロジックの最適化

QGraphicsItem::paint() メソッド内の描画ロジック自体を最適化することも非常に重要です。

  • 利点
    描画自体の効率を根本的に改善します。