Qt Graphics View: items()でアイテムを正確に取得するトラブルシューティング
QGraphicsView と QGraphicsScene, QGraphicsItem の関係
まず、QGraphicsView::items()
を理解するために、QtのGraphics View Frameworkの基本的な構造を把握しておくことが重要です。
QGraphicsView
: これは、QGraphicsScene
の内容を表示するためのウィジェットです。ビューはシーンの一部を表示したり、ズームイン/アウトしたり、スクロールしたりすることができます。複数のビューが同じシーンを表示することも可能です。QGraphicsItem
: シーン上に表示される個々のグラフィカルなオブジェクト(例:QGraphicsRectItem
、QGraphicsEllipseItem
、QGraphicsTextItem
など)の基底クラスです。QGraphicsScene
: これは、グラフィカルなアイテム(図形、テキスト、画像など)を管理するキャンバスのようなものです。すべてのグラフィカルなアイテムはまずシーンに追加されます。
QGraphicsView::items()
とは
QGraphicsView::items()
は、特定のビューポート(表示領域)内に表示されている、または特定の領域と交差しているQGraphicsItem
オブジェクトのリストを取得するためのメソッドです。
このメソッドにはいくつかのオーバーロードがあります。
-
QList<QGraphicsItem *> items() const
:- このオーバーロードは、現在の
QGraphicsView
に表示されているすべてのアイテムを、スタッキング順(手前にあるものから奥にあるものへ)でQList<QGraphicsItem *>
として返します。 - ビューの変換(ズームやスクロールなど)に関わらず、ビューポート内に実際に描画されているアイテムが対象になります。
- このオーバーロードは、現在の
-
QList<QGraphicsItem *> items(const QPoint &pos) const
:- ビュー座標系の指定された点
pos
に位置するアイテムのリストを返します。 - 最も手前にあるアイテムがリストの先頭になります。
- ビュー座標系の指定された点
-
QList<QGraphicsItem *> items(int x, int y) const
:items(const QPoint &pos)
と同じく、ビュー座標系のx, y
に位置するアイテムのリストを返します。
-
QList<QGraphicsItem *> items(const QRect &rect, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const
:- ビュー座標系の指定された矩形
rect
と交差または含まれるアイテムのリストを返します。 mode
引数によって、アイテムの選択モード(交差、完全包含など)を指定できます。デフォルトはQt::IntersectsItemShape
です。
- ビュー座標系の指定された矩形
-
QList<QGraphicsItem *> items(int x, int y, int w, int h, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const
:items(const QRect &rect, ...)
と同じく、ビュー座標系のx, y, w, h
で指定された矩形と交差または含まれるアイテムのリストを返します。
-
QList<QGraphicsItem *> items(const QPolygon &polygon, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const
:- ビュー座標系の指定された多角形
polygon
と交差または含まれるアイテムのリストを返します。
- ビュー座標系の指定された多角形
-
QList<QGraphicsItem *> items(const QPainterPath &path, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const
:- ビュー座標系の指定されたパス
path
と交差または含まれるアイテムのリストを返します。
- ビュー座標系の指定されたパス
使用例
例えば、ビューの中心にあるアイテムを取得したい場合:
#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QDebug>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QGraphicsScene scene;
scene.setSceneRect(0, 0, 400, 300); // シーンのサイズを設定
// いくつかのアイテムをシーンに追加
QGraphicsRectItem *rect1 = scene.addRect(0, 0, 50, 50, QPen(Qt::blue), QBrush(Qt::blue));
rect1->setPos(50, 50);
QGraphicsRectItem *rect2 = scene.addRect(0, 0, 70, 70, QPen(Qt::red), QBrush(Qt::red));
rect2->setPos(100, 100);
QGraphicsEllipseItem *ellipse = scene.addEllipse(0, 0, 60, 60, QPen(Qt::green), QBrush(Qt::green));
ellipse->setPos(80, 80); // このアイテムはrect1とrect2の一部に重なるように配置
QGraphicsView view(&scene);
view.setWindowTitle("QGraphicsView::items() Example");
view.resize(500, 400); // ビューのサイズ
view.show();
// ビューの中心にあるアイテムを取得する例
QPoint viewCenter = view.viewport()->rect().center();
QList<QGraphicsItem *> itemsAtCenter = view.items(viewCenter);
qDebug() << "Items at view center (" << viewCenter.x() << ", " << viewCenter.y() << "):";
if (itemsAtCenter.isEmpty()) {
qDebug() << "No items at center.";
} else {
foreach (QGraphicsItem *item, itemsAtCenter) {
qDebug() << " Found item at scene pos:" << item->scenePos() << " Type:" << item->type();
}
}
// ビューポート全体に表示されているすべてのアイテムを取得する例
QList<QGraphicsItem *> allVisibleItems = view.items();
qDebug() << "\nAll visible items in the view:";
foreach (QGraphicsItem *item, allVisibleItems) {
qDebug() << " Found item at scene pos:" << item->scenePos() << " Type:" << item->type();
}
return a.exec();
}
- パフォーマンス: 大量のアイテムがあるシーンで、頻繁に
items()
を呼び出す場合は、パフォーマンスに注意が必要です。QtのGraphics View Frameworkは効率的なアイテム管理のためにインデックスを内部的に使用していますが、広範囲の検索はコストがかかる可能性があります。 - スタッキング順: 返されるアイテムのリストは、Z値(スタッキング順)に基づいてソートされます。リストの最初のアイテムが最も手前にあり、最後にくるアイテムが最も奥にあります。
- 座標系:
QGraphicsView::items()
の引数として渡す座標や矩形、多角形、パスは、ビュー座標系で指定する必要があります。シーン座標系でアイテムを検索したい場合は、QGraphicsScene::items()
を使用します。
QGraphicsView::items() のよくあるエラーとトラブルシューティング
期待したアイテムが返されない、または何も返されない
これは最もよくある問題です。原因はいくつか考えられます。
-
boundingRect()やshape()の実装ミス
- エラー
カスタムアイテムを作成し、boundingRect()
やshape()
をオーバーライドしている場合、これらのメソッドがアイテムの実際の描画範囲を正確に反映していないと、ヒットテスト(items()
もヒットテストを利用します)が正しく機能しないことがあります。例えば、boundingRect()
が小さすぎると、描画されているにも関わらず検出されないことがあります。 - トラブルシューティング
boundingRect()
は、アイテムが占める最小の矩形領域を正確に返すように実装してください。shape()
は、より正確なヒットテストが必要な場合にオーバーライドし、アイテムの正確な形状を返すように実装してください。QPainterPath
を使用して形状を定義します。
- エラー
-
アイテムが不可視になっている (setVisible(false))
- エラー
setVisible(false)
が設定されているアイテムは、QGraphicsView::items()
では通常返されません。 - トラブルシューティング
- 目的のアイテムの可視性フラグを確認してください。
- エラー
-
Z値による重なり
- エラー
QGraphicsView::items()
はZ値(スタッキング順)に基づいてアイテムを返します。もし、目的のアイテムが他の不透明なアイテムの背後に完全に隠れている場合、マウスイベントなどでそのアイテムがヒットしないことがあります。 - トラブルシューティング
QGraphicsItem::setZValue()
を使用して、アイテムのZ値を調整し、目的のアイテムが手前に来るようにしてください。高いZ値を持つアイテムほど手前に表示されます。QGraphicsItem::setAcceptedMouseButtons(Qt::NoButton)
など、アイテムのインタラクション設定が正しくない場合も注意が必要です。
- エラー
-
アイテムがビューポートの範囲外にある
- エラー
QGraphicsView::items()
(引数なし)は、現在ビューポートに表示されているアイテムのみを返します。アイテムがシーン上には存在しても、ビューポートから完全に外れている場合、そのアイテムは返されません。 - トラブルシューティング
- アイテムがビューポート内に確実に表示されているか確認してください。
- シーン全体のアイテムを取得したい場合は、
QGraphicsScene::items()
を使用します。 - アイテムの位置や可視性をデバッグ出力で確認してください(例:
item->scenePos()
、item->isVisible()
)。
- エラー
-
- エラー
QGraphicsView::items()
に渡す座標や矩形は、ビューのローカル座標系で指定する必要があります。しかし、誤ってシーン座標系で指定してしまうことがあります。例えば、シーン上の特定のアイテムの位置(シーン座標)を使ってview->items(item->scenePos())
のように呼び出しても、そのビューポート内の対応するビュー座標でなければ正確な結果は得られません。 - トラブルシューティング
QGraphicsView::mapFromScene()
またはQGraphicsView::mapToScene()
を使用して、座標系を正しく変換してください。- ビューの特定の点をクリックした際のアイテムを取得したい場合は、マウスイベントから取得した
QMouseEvent::pos()
(これはビューポート座標)をそのままview->items(event->pos())
に渡すのが一般的です。 - シーン上の特定の領域に対応するビューポート内のアイテムを取得したい場合は、そのシーン領域を
view->mapFromScene(sceneRect)
でビュー座標系の矩形に変換してからview->items(viewRect)
のように使用します。
- エラー
パフォーマンスの問題(特に多数のアイテムがある場合)
QGraphicsView::items()
は、特に引数なしで呼び出す場合や、広範囲の領域を指定する場合、シーン内のアイテム数が多いとパフォーマンスに影響を与える可能性があります。
-
アイテムの変換(Transformations)
- エラー
大量のアイテムが頻繁に移動、回転、スケールされる場合、items()
が正しい結果を返すために多くの計算が必要となり、パフォーマンスが低下することがあります。 - トラブルシューティング
- アイテムのキャッシュモード(
QGraphicsItem::setCacheMode()
)を適切に設定することで、描画パフォーマンスを改善できます。 QGraphicsView::setViewportUpdateMode()
をFullViewportUpdate
以外に設定することで、描画領域を限定しパフォーマンスを向上させることができます。
- アイテムのキャッシュモード(
- エラー
-
広範囲の検索
- エラー
items()
に非常に大きな矩形やポリゴンを渡し、多数のアイテムと交差する可能性がある場合、検索コストが増大します。 - トラブルシューティング
- 可能な限り、検索範囲を限定するようにしてください。
- もし特定の種類のアイテムのみを検索したい場合は、
QGraphicsScene::items()
のオーバーロード(例えば、QGraphicsScene::items(Qt::ItemFlag)
やカスタムフィルタリング)を検討する方が効率的な場合があります。 - シーンに非常に多くのアイテムがあり、検索パフォーマンスがクリティカルな場合は、
QGraphicsScene::NoIndex
を設定して、Qtの内部インデックス(デフォルトはBSPツリー)を無効にすることも検討できますが、これは通常、アイテムの追加/削除が頻繁で検索が少ないシナリオでのみ有効です。多くの場合、デフォルトのインデックスの方が検索性能が良いです。
- エラー
-
不必要な頻繁な呼び出し
- エラー
イベントループ内でQGraphicsView::items()
を毎フレーム呼び出すなど、不必要に頻繁に呼び出すとパフォーマンスが低下します。 - トラブルシューティング
- 必要なときにのみ
items()
を呼び出すようにロジックを最適化してください。 - 例えば、マウスが移動するたびにすべてのアイテムをチェックするのではなく、特定の条件下でのみチェックするようにします。
- 必要なときにのみ
- エラー
アイテムのメモリ管理とライフサイクル
QGraphicsView::items()
はQList<QGraphicsItem *>
を返しますが、これは生のポインタのリストです。
- 削除されたアイテムへのアクセス
- エラー
items()
で取得したポインタが、その後シーンから削除されたり、メモリが解放されたアイテムを指している場合、ダングリングポインタとなり、アクセスするとクラッシュや未定義動作の原因となります。 - トラブルシューティング
QGraphicsItem
は通常、QGraphicsScene::addItem()
で追加されるとシーンが所有権を持つため、シーンが破棄されるときに自動的に削除されます。手動でアイテムを削除する場合は、ポインタが無効になることを考慮し、そのポインタを使用しているすべての場所で注意が必要です。- 特に、あるイベントハンドラでアイテムを削除し、別の場所でそのアイテムのリストを保持しているような場合に発生しやすいです。リストを使用する前に、ポインタの有効性をチェックするか、アイテムの削除後にリストをクリア/更新するメカニズムを確立してください。
- エラー
- ドキュメントの再確認
- Qtの公式ドキュメントは非常に詳細です。
QGraphicsView::items()
だけでなく、QGraphicsScene
やQGraphicsItem
の関連メソッドやフラグについても再度確認してみましょう。特に、座標変換に関するセクションは重要です。
- Qtの公式ドキュメントは非常に詳細です。
- シンプルな例で再現
- 複雑なコードで問題が発生した場合、最小限のアイテムとビューで同じ現象が再現するか試してみてください。これにより、問題の範囲を絞り込むことができます。
- デバッグ出力の活用
qDebug()
を使って、items()
が返すリストの内容、アイテムの座標、可視性、Z値などをこまめに確認しましょう。- 特に座標系の問題は、ビュー座標とシーン座標の両方でデバッグ出力を行うと、どこでずれが生じているか特定しやすくなります。
// ビュー座標からシーン座標へ変換してデバッグ QPointF scenePoint = view->mapToScene(viewPoint); qDebug() << "View Point:" << viewPoint << "Scene Point:" << scenePoint;
ビューポートに表示されているすべてのアイテムを取得する
最も基本的な使用例です。引数なしで items()
を呼び出すと、現在 QGraphicsView
に描画されているすべてのアイテムが Z 値(スタッキング順)の逆順(手前から奥へ)で返されます。
コード例
#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QGraphicsEllipseItem>
#include <QDebug> // デバッグ出力用
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
// 1. シーンを作成
QGraphicsScene scene;
scene.setSceneRect(0, 0, 400, 300); // シーンの論理的な範囲
// 2. いくつかのアイテムをシーンに追加
QGraphicsRectItem *rect1 = new QGraphicsRectItem(0, 0, 100, 100);
rect1->setPos(50, 50); // シーン座標 (50,50) に配置
rect1->setBrush(Qt::red);
rect1->setZValue(1); // 手前に表示されるようにZ値を設定
QGraphicsEllipseItem *ellipse1 = new QGraphicsEllipseItem(0, 0, 80, 80);
ellipse1->setPos(80, 80); // rect1と重なるように配置
ellipse1->setBrush(Qt::blue);
ellipse1->setZValue(2); // rect1よりも手前
QGraphicsRectItem *rect2 = new QGraphicsRectItem(0, 0, 150, 70);
rect2->setPos(250, 100); // ビューポートの右寄りに配置
rect2->setBrush(Qt::green);
rect2->setZValue(0); // 奥に表示されるように
scene.addItem(rect1);
scene.addItem(ellipse1);
scene.addItem(rect2);
// 3. ビューを作成し、シーンを設定
QGraphicsView view(&scene);
view.setWindowTitle("表示されているすべてのアイテムを取得");
view.resize(600, 400); // ビューのウィンドウサイズ
view.show();
// 4. QGraphicsView::items() を呼び出して、表示されているアイテムを取得
QList<QGraphicsItem *> visibleItems = view.items();
qDebug() << "ビューポートに表示されているすべてのアイテム:";
if (visibleItems.isEmpty()) {
qDebug() << "アイテムは表示されていません。";
} else {
foreach (QGraphicsItem *item, visibleItems) {
// アイテムのタイプ(RectItem, EllipseItemなど)を識別
QString itemType;
if (qgraphicsitem_cast<QGraphicsRectItem*>(item)) {
itemType = "QGraphicsRectItem";
} else if (qgraphicsitem_cast<QGraphicsEllipseItem*>(item)) {
itemType = "QGraphicsEllipseItem";
} else {
itemType = "Unknown Type";
}
qDebug() << "- Scene Pos:" << item->scenePos()
<< ", ZValue:" << item->zValue()
<< ", Type:" << itemType;
}
}
return a.exec();
}
解説
このコードでは、3つのアイテム(2つの矩形と1つの楕円)をシーンに追加しています。view.items()
を呼び出すと、現在ビューに表示されているすべてのアイテムがリストとして返されます。デバッグ出力を見ると、Z値が高い(手前にある)アイテムから順にリストに含まれていることが確認できます。
特定の点に位置するアイテムを取得する(マウスイベントと組み合わせる)
ユーザーがビューをクリックした際に、そのクリック位置にあるアイテムを取得するのに役立ちます。
コード例
#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QGraphicsEllipseItem>
#include <QMouseEvent> // マウスイベント用
#include <QDebug>
// QGraphicsView を継承して、マウスプレスイベントをオーバーライド
class MyGraphicsView : public QGraphicsView
{
public:
MyGraphicsView(QGraphicsScene *scene, QWidget *parent = nullptr)
: QGraphicsView(scene, parent) {}
protected:
void mousePressEvent(QMouseEvent *event) override {
// クリックされたビューポート座標を取得
QPoint viewPos = event->pos();
// QGraphicsView::items(const QPoint &pos) を使用して、その位置にあるアイテムを取得
QList<QGraphicsItem *> itemsAtPos = items(viewPos);
qDebug() << "ビュー座標 (" << viewPos.x() << "," << viewPos.y() << ") でクリックされました。";
if (itemsAtPos.isEmpty()) {
qDebug() << " その位置にアイテムはありません。";
} else {
qDebug() << " 見つかったアイテム:";
foreach (QGraphicsItem *item, itemsAtPos) {
QString itemType;
if (qgraphicsitem_cast<QGraphicsRectItem*>(item)) {
itemType = "QGraphicsRectItem";
} else if (qgraphicsitem_cast<QGraphicsEllipseItem*>(item)) {
itemType = "QGraphicsEllipseItem";
} else {
itemType = "Unknown Type";
}
qDebug() << " - Scene Pos:" << item->scenePos()
<< ", ZValue:" << item->zValue()
<< ", Type:" << itemType;
}
}
// 親クラスのイベントハンドラを呼び出し、デフォルトの動作を維持
QGraphicsView::mousePressEvent(event);
}
};
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QGraphicsScene scene;
scene.setSceneRect(0, 0, 400, 300);
QGraphicsRectItem *rect1 = new QGraphicsRectItem(0, 0, 100, 100);
rect1->setPos(50, 50);
rect1->setBrush(Qt::red);
rect1->setZValue(1);
QGraphicsEllipseItem *ellipse1 = new QGraphicsEllipseItem(0, 0, 80, 80);
ellipse1->setPos(80, 80);
ellipse1->setBrush(Qt::blue);
ellipse1->setZValue(2);
scene.addItem(rect1);
scene.addItem(ellipse1);
MyGraphicsView view(&scene); // カスタムビューを使用
view.setWindowTitle("クリックした位置のアイテムを取得");
view.resize(600, 400);
view.show();
return a.exec();
}
解説
MyGraphicsView
クラスを定義し、mousePressEvent
をオーバーライドしています。ユーザーがビューをクリックすると、event->pos()
でクリックされたビューポート座標を取得し、これを items()
メソッドに渡しています。これにより、クリックされた点に重なっているアイテムのリストが取得されます。ここでも、Z値が高い(手前にある)アイテムからリストに格納されます。
特定の矩形領域と交差するアイテムを取得する
ドラッグ選択のような機能を実現する際に役立ちます。
コード例
#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QGraphicsEllipseItem>
#include <QDebug>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QGraphicsScene scene;
scene.setSceneRect(0, 0, 400, 300);
QGraphicsRectItem *rect1 = new QGraphicsRectItem(0, 0, 100, 100);
rect1->setPos(50, 50);
rect1->setBrush(Qt::red);
scene.addItem(rect1);
QGraphicsEllipseItem *ellipse1 = new QGraphicsEllipseItem(0, 0, 80, 80);
ellipse1->setPos(80, 80);
ellipse1->setBrush(Qt::blue);
scene.addItem(ellipse1);
QGraphicsRectItem *rect2 = new QGraphicsRectItem(0, 0, 120, 60);
rect2->setPos(150, 150);
rect2->setBrush(Qt::green);
scene.addItem(rect2);
QGraphicsView view(&scene);
view.setWindowTitle("矩形領域と交差するアイテムを取得");
view.resize(600, 400);
view.show();
// 検索する矩形領域をビュー座標で定義
// 例えば、ビューの左上から (200, 200) の大きさの領域
QRect searchRect(50, 50, 200, 200); // ビュー座標系の矩形
// QGraphicsView::items(const QRect &rect, Qt::ItemSelectionMode mode) を使用
// Qt::IntersectsItemShape は、アイテムの形状と矩形が交差すれば含めるモード
QList<QGraphicsItem *> intersectedItems = view.items(searchRect, Qt::IntersectsItemShape);
qDebug() << "ビュー座標の矩形 (" << searchRect.x() << "," << searchRect.y() << ","
<< searchRect.width() << "," << searchRect.height() << ") と交差するアイテム:";
if (intersectedItems.isEmpty()) {
qDebug() << " 該当するアイテムはありません。";
} else {
foreach (QGraphicsItem *item, intersectedItems) {
QString itemType;
if (qgraphicsitem_cast<QGraphicsRectItem*>(item)) {
itemType = "QGraphicsRectItem";
} else if (qgraphicsitem_cast<QGraphicsEllipseItem*>(item)) {
itemType = "QGraphicsEllipseItem";
} else {
itemType = "Unknown Type";
}
qDebug() << " - Scene Pos:" << item->scenePos()
<< ", Type:" << itemType;
}
}
// 例えば、矩形がアイテムを完全に含む場合のみを取得するモード
QRect fullyContainedRect(40, 40, 120, 120); // rect1とellipse1を完全に覆うように調整
QList<QGraphicsItem *> containedItems = view.items(fullyContainedRect, Qt::ContainsItemShape);
qDebug() << "\nビュー座標の矩形 (" << fullyContainedRect.x() << "," << fullyContainedRect.y() << ","
<< fullyContainedRect.width() << "," << fullyContainedRect.height() << ") に完全に含まれるアイテム:";
if (containedItems.isEmpty()) {
qDebug() << " 該当するアイテムはありません。";
} else {
foreach (QGraphicsItem *item, containedItems) {
QString itemType;
if (qgraphicsitem_cast<QGraphicsRectItem*>(item)) {
itemType = "QGraphicsRectItem";
} else if (qgraphicsitem_cast<QGraphicsEllipseItem*>(item)) {
itemType = "QGraphicsEllipseItem";
} else {
itemType = "Unknown Type";
}
qDebug() << " - Scene Pos:" << item->scenePos()
<< ", Type:" << itemType;
}
}
return a.exec();
}
解説
この例では、items(const QRect &rect, Qt::ItemSelectionMode mode)
オーバーロードを使用しています。
Qt::ContainsItemShape
モードでは、検索矩形がアイテムの形状を完全に含んでいる場合にのみ、そのアイテムがリストに含まれます。Qt::IntersectsItemShape
モードでは、検索矩形とアイテムの形状が少しでも重なっていれば、そのアイテムがリストに含まれます。searchRect
はビュー座標系で定義された矩形です。
シーン座標からビュー座標への変換の重要性
QGraphicsView::items()
がビュー座標を使用する点を理解することは非常に重要です。もしシーン上の特定の領域のアイテムを検索したい場合は、まずそのシーン領域をビュー座標に変換する必要があります。
コード例
#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QDebug>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QGraphicsScene scene;
scene.setSceneRect(0, 0, 400, 300);
QGraphicsRectItem *rect1 = new QGraphicsRectItem(0, 0, 100, 100);
rect1->setPos(50, 50); // シーン座標
rect1->setBrush(Qt::red);
scene.addItem(rect1);
QGraphicsRectItem *rect2 = new QGraphicsRectItem(0, 0, 80, 80);
rect2->setPos(120, 120); // シーン座標
rect2->setBrush(Qt::blue);
scene.addItem(rect2);
QGraphicsView view(&scene);
view.setWindowTitle("シーン座標からビュー座標への変換");
view.resize(600, 400);
// ズームイン/アウトやスクロールで変換の影響を確認できるように
view.setRenderHint(QPainter::Antialiasing);
view.setDragMode(QGraphicsView::ScrollHandDrag); // ドラッグでスクロールできるように
view.show();
// シーン座標で検索したい矩形を定義
QRectF sceneSearchRect(40, 40, 100, 100); // シーン上の (40,40) からの範囲
// このシーン矩形をビュー座標に変換
// QGraphicsView::mapFromScene() は QRectF を QPolygonF に変換して返す
QPolygonF viewSearchPolygon = view.mapFromScene(sceneSearchRect);
// QGraphicsView::items(const QPolygonF &polygon, ...) を使用
QList<QGraphicsItem *> foundItems = view.items(viewSearchPolygon, Qt::IntersectsItemShape);
qDebug() << "シーン座標の矩形 (" << sceneSearchRect.x() << "," << sceneSearchRect.y() << ","
<< sceneSearchRect.width() << "," << sceneSearchRect.height() << ") に対応するアイテム:";
if (foundItems.isEmpty()) {
qDebug() << " 該当するアイテムはありません。";
} else {
foreach (QGraphicsItem *item, foundItems) {
qDebug() << " - Found item at scene pos:" << item->scenePos();
}
}
return a.exec();
}
解説
この例では、シーン座標系で定義された sceneSearchRect
を使用したい場合、直接 view.items(sceneSearchRect)
を呼び出すことはできません。なぜなら、items()
はビュー座標系の引数を期待するからです。
そこで、view.mapFromScene(sceneSearchRect)
を使用して、シーン座標の矩形をビュー座標の多角形(QPolygonF
)に変換しています。この変換された QPolygonF
を items()
に渡すことで、ズームやスクロールなどのビューの変換が適用された後でも、シーン上の正しい領域のアイテムを正確に取得できます。
QGraphicsView::items()
は「ビューポート座標系」に基づいてアイテムを検索するのに対し、代替メソッドの多くは「シーン座標系」に基づいてアイテムを検索します。この座標系の違いを理解することが使い分けの鍵となります。
QGraphicsScene::items()
QGraphicsView
ではなく、QGraphicsScene
クラス自体が提供する items()
メソッドです。これは、シーン全体のアイテムを検索する際に非常に重要です。
-
- シーン全体のアイテムを操作したい場合
例えば、シーン内のすべてのアイテムをループして特定のプロパティを変更したり、保存したりする場合。 - ビューの表示状態に依存せずアイテムを検索したい場合
アイテムが現在ビューポートに表示されているかどうかに関わらず、シーン上の論理的な位置でアイテムを検索したい場合。 - シーン座標系で直接検索したい場合
アイテムのscenePos()
やsceneBoundingRect()
に基づいて検索を行う場合。
- シーン全体のアイテムを操作したい場合
例 (QGraphicsScene::items())
#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QDebug>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QGraphicsScene scene;
scene.setSceneRect(0, 0, 400, 300);
QGraphicsRectItem *rect1 = scene.addRect(0, 0, 50, 50);
rect1->setPos(10, 10);
QGraphicsRectItem *rect2 = scene.addRect(0, 0, 70, 70);
rect2->setPos(100, 100);
QGraphicsRectItem *rect3 = scene.addRect(0, 0, 60, 60);
rect3->setPos(350, 250); // ビューポート外になる可能性のある位置
QGraphicsView view(&scene);
view.setWindowTitle("QGraphicsScene::items() の例");
view.resize(200, 200); // 小さなビューポート
view.show();
// シーン座標 (50, 50) にあるアイテムを検索
QPointF searchPoint(50, 50);
QList<QGraphicsItem *> itemsAtScenePos = scene.items(searchPoint);
qDebug() << "シーン座標 (" << searchPoint.x() << "," << searchPoint.y() << ") のアイテム:";
foreach (QGraphicsItem *item, itemsAtScenePos) {
qDebug() << " - Found:" << item->scenePos();
}
// シーンの右下にある、ビューポートからは見えないアイテムも取得できる
QList<QGraphicsItem *> allSceneItems = scene.items();
qDebug() << "\nシーン内のすべてのアイテム (可視性に関わらず):";
foreach (QGraphicsItem *item, allSceneItems) {
qDebug() << " - Scene Pos:" << item->scenePos() << ", Is Visible:" << item->isVisible();
}
return a.exec();
}
QGraphicsScene::itemAt() および QGraphicsView::itemAt()
特定の点に存在する最上位のアイテムのみを取得したい場合に便利です。items()
がリストを返すのに対し、itemAt()
は単一のポインタを返します。