QGraphicsView::itemAt()

2025-05-27

QGraphicsView::itemAt() とは?

QGraphicsView::itemAt()は、QtのGraphics View Frameworkにおいて、特定のビュー座標に表示されている最も手前(一番上)のアイテムを取得するための便利な関数です。

QtのGraphics View Frameworkは、2Dグラフィカルアイテムを管理し、表示するための強力な仕組みです。主に以下の3つの主要なクラスで構成されます。

  1. QGraphicsScene: 2Dグラフィカルアイテム(QGraphicsItem)を格納・管理するキャンバスのようなものです。アイテムの追加、削除、位置管理、イベントの伝播、選択状態の管理などを行います。
  2. QGraphicsItem: シーン上に表示される個々のグラフィカルなオブジェクト(図形、画像、テキストなど)の基底クラスです。
  3. QGraphicsView: QGraphicsSceneの内容を表示するためのウィジェットです。シーンの内容をスクロール可能なビューポートで視覚化します。ズームや回転といった変換機能も提供します。

QGraphicsView::itemAt()は、このQGraphicsViewクラスのメンバー関数です。

役割と使い方

QGraphicsView::itemAt()の主な役割は、ビューの座標系で指定された点にあるQGraphicsItemを特定することです。

通常、この関数はマウスイベントなどでユーザーがクリックした場所にあるアイテムを特定するために使われます。

関数のシグネチャはいくつかオーバーロードがありますが、最も一般的なものは以下の通りです。

QGraphicsItem *QGraphicsView::itemAt(const QPoint &pos) const
QGraphicsItem *QGraphicsView::itemAt(int x, int y) const
  • pos または (x, y): これは、QGraphicsViewのローカル座標系(つまり、ビューウィジェット内のピクセル座標)での点を指定します。

この関数は、指定されたビュー座標に「表示されている」最も手前のアイテムへのポインタを返します。もし、その位置にどのアイテムも表示されていない場合は、nullptrを返します。

重要なポイント

  • 変換の考慮: QGraphicsViewはズームや回転などの変換を行うことができます。itemAt()はこれらの変換を内部的に考慮し、指定されたビュー座標に対応するシーン上のアイテムを正確に特定します。
  • 最前面のアイテム: 複数のアイテムが重なっている場合、itemAt()は視覚的に一番手前にあるアイテムを返します。
  • ビュー座標を使用: itemAt()に渡す座標は、QGraphicsView自身の座標系です。シーン座標ではありません。もしQMouseEventから座標を取得する場合は、event->pos()のように直接使うことができます。

使用例

例えば、QGraphicsViewを継承したカスタムビューでマウスプレスイベントを処理し、クリックされたアイテムを検出するコードは以下のようになります。

#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsRectItem>
#include <QMouseEvent>
#include <QDebug>

class MyGraphicsView : public QGraphicsView
{
public:
    MyGraphicsView(QGraphicsScene *scene, QWidget *parent = nullptr)
        : QGraphicsView(scene, parent)
    {
        // デバッグ用にいくつかアイテムを追加
        scene->addRect(0, 0, 100, 50, QPen(Qt::blue), QBrush(Qt::cyan));
        scene->addEllipse(50, 20, 80, 80, QPen(Qt::red), QBrush(Qt::magenta));
    }

protected:
    void mousePressEvent(QMouseEvent *event) override
    {
        // クリックされたビュー座標を取得
        QPoint viewPos = event->pos();

        // そのビュー座標にあるアイテムを取得
        QGraphicsItem *item = itemAt(viewPos);

        if (item) {
            qDebug() << "クリックされたアイテムが見つかりました:";
            qDebug() << "  アイテムのクラス名:" << item->metaObject()->className();
            qDebug() << "  アイテムのシーン座標:" << item->scenePos();
            // 必要に応じてアイテムに対する操作を行う
        } else {
            qDebug() << "アイテムが見つかりませんでした。";
        }

        // 親クラスの処理を呼び出す(ドラッグなどのデフォルト動作のため)
        QGraphicsView::mousePressEvent(event);
    }
};

// main関数での使用例
// int main(int argc, char *argv[]) {
//     QApplication a(argc, argv);
//
//     QGraphicsScene scene;
//     MyGraphicsView view(&scene);
//
//     view.setWindowTitle("QGraphicsView::itemAt() Example");
//     view.resize(400, 300);
//     view.show();
//
//     return a.exec();
// }

この例では、MyGraphicsViewクラス内でmousePressEventをオーバーライドし、クリックされた位置にあるアイテムをitemAt()で取得しています。アイテムが見つかれば、その情報をデバッグ出力しています。

QGraphicsView::itemAt()と似た関数にQGraphicsScene::itemAt()があります。この2つの主な違いは、引数に渡す座標の種類です。

  • QGraphicsScene::itemAt(const QPointF &scenePos, const QTransform &transform = QTransform()):

    • 引数はシーン座標です。
    • ビューの変換(ズーム、回転など)とは独立して、純粋なシーン上の位置でアイテムを検出します。
    • QGraphicsViewのマウスイベントからQGraphicsScene::itemAt()を使う場合は、まずevent->pos()(ビュー座標)をmapToScene()を使ってシーン座標に変換する必要があります。
    // MyGraphicsView::mousePressEvent内で
    QPointF scenePos = mapToScene(event->pos()); // ビュー座標をシーン座標に変換
    QGraphicsItem *item = scene()->itemAt(scenePos); // シーンのitemAtを呼び出す
    
  • QGraphicsView::itemAt(const QPoint &viewPos):

    • 引数はビュー座標です。
    • ビューがズームや回転されている場合でも、表示されている内容に基づいてアイテムを正しく検出します。

ほとんどの場合、ユーザーの操作に応じてアイテムを検出する際には、ビューの変換を考慮してビュー座標で直接操作できるQGraphicsView::itemAt()がより便利です。



itemAt() が常に nullptr を返す、または意図しないアイテムを返す

これは最もよく遭遇する問題です。いくつか原因が考えられます。

考えられる原因と解決策

  • ビューのインタラクティブモード

    • 原因
      QGraphicsViewのインタラクティブモードが無効になっている場合、マウスイベントが適切にアイテムに伝播されない可能性があります。
    • 解決策
      • view->setInteractive(true); が設定されていることを確認してください(デフォルトはtrueですが、明示的にfalseに設定されている場合があります)。
  • シーンにアイテムが追加されていない

    • 原因
      そもそもアイテムがQGraphicsSceneに追加されていない場合、当然itemAt()では見つかりません。
    • 解決策
      • scene->addItem(myGraphicsItem); が正しく呼び出され、アイテムがシーンに登録されていることを確認してください。
  • QGraphicsItem::ItemIsSelectable などのフラグが設定されていない

    • 原因
      特定のインタラクションを有効にするために、アイテムに適切なフラグが設定されている必要があります。itemAt()自体はフラグに直接依存しませんが、アイテムが選択可能であるべきなのに反応しない場合、関連する問題として考えられます。
    • 解決策
      • item->setFlag(QGraphicsItem::ItemIsSelectable, true); などのフラグが適切に設定されているか確認してください。
  • アイテムの描画順序 (Z-Value)

    • 原因
      itemAt()は、指定された位置にある複数のアイテムの中から、最もZ-valueが高い(手前にある)アイテムを返します。もし意図しないアイテムが返される場合、それはその位置で最も手前にあるアイテムである可能性があります。
    • 解決策
      • アイテムのZ-valueを確認してください。item->zValue()で取得でき、item->setZValue(value)で設定できます。期待するアイテムが他のアイテムの上に表示されるようにZ-valueを調整してください。
  • 座標系の変換ミス

    • 原因
      QGraphicsView::itemAt()ビュー座標(ビューウィジェット内のピクセル座標)を受け取ります。もし、誤ってシーン座標や他の座標系を渡している場合、アイテムが検出されません。特にQGraphicsScene::itemAt()と混同している場合によく起こります。
    • 解決策
      • QMouseEventから座標を取得する場合、event->pos()はビュー座標なので、そのままitemAt()に渡して問題ありません。
      • もし何らかの理由でシーン座標を持っている場合、QGraphicsView::mapFromScene()を使用してビュー座標に変換してからitemAt()に渡してください。
      • 例(誤った使い方)
        // QGraphicsView::mousePressEvent内での誤った例
        QPointF scenePos = mapToScene(event->pos());
        QGraphicsItem *item = scene()->itemAt(scenePos); // これはQGraphicsScene::itemAt()の呼び出し
        
        例(正しい使い方):
        // QGraphicsView::mousePressEvent内での正しい例 (QGraphicsView::itemAt()を使う場合)
        QPoint viewPos = event->pos();
        QGraphicsItem *item = itemAt(viewPos);
        
  • カスタムアイテムで shape() または boundingRect() が正しく実装されていない

    • 原因
      itemAt() は、デフォルトではアイテムの shape() 関数が返す QPainterPath を使用してヒットテストを行います。shape() がオーバーライドされていない場合、または正確な形状を返さない場合、アイテムの視覚的な形状とヒットテストの形状が一致しません。例えば、カスタムアイテムが複雑な形状を持っていても、boundingRect() のみを返すと、ヒットできる領域が矩形に限定されてしまいます。
    • 解決策
      • カスタムQGraphicsItemを継承している場合、QPainterPath QGraphicsItem::shape() const override 関数を正しく実装してください。この関数は、アイテムの正確な形状をローカル座標で返すべきです。特に、アイテムが回転したりスケールされたりしても、このshape()が正確なヒット領域を定義するようにします。
      • boundingRect()も正確に設定されていることを確認してください。shape()が定義されていない場合、itemAt()boundingRect()を使用します。
      • 例えば、円形のアイテムなのにboundingRect()しか定義していない場合、クリックは矩形範囲でしか反応しません。QGraphicsEllipseItemのような組み込みのアイテムを使用するか、カスタム実装の場合はshape()を適切に定義する必要があります。
    • 原因
      アイテムが視覚的に非常に小さいか、ビューポートの範囲外にある場合、クリックしても検出されません。また、boundingRect() が適切に設定されていても、paint() 関数で実際に描画される内容が非常に小さかったり、透明であったりすると、クリックヒットが期待通りにいかないことがあります。
    • 解決策
      • アイテムのboundingRect()が正しいサイズと位置を返しているか確認してください。
      • paint()関数で実際に描画されている範囲がboundingRect()と一致しているか、またはクリック検出したい範囲をカバーしているか確認してください。
      • デバッグ用に、paint()関数内でboundingRect()painter->drawRect(boundingRect())などで描画し、視覚的に確認すると良いでしょう。
      • ビューのズームレベルやスクロール位置も確認してください。アイテムがビューポートに表示されている必要があります。

パフォーマンスの問題

多数のアイテムがあるシーンでitemAt()を頻繁に呼び出すと、パフォーマンスに影響を与える可能性があります。

考えられる原因と解決策

  • アイテムの数が多い
    • 原因
      itemAt()は内部的にヒットテストを行うため、シーン内のアイテム数が多いほど処理に時間がかかる可能性があります。
    • 解決策
      • QGraphicsScene::bspTreeIndex() の利用
        QGraphicsSceneはデフォルトでBSPツリーによるインデックスを使用しており、多数のアイテムからの検索を高速化します。通常は自動的に最適化されますが、非常に動的なシーン(頻繁にアイテムが移動、追加、削除されるなど)では、再構築コストがかかることがあります。
      • カスタムなヒットテストロジック
        特定の状況でパフォーマンスが問題になる場合、例えば、特定のレイヤーのアイテムのみを対象にしたり、アイテムをグループ化してまずグループを特定し、その後にグループ内のアイテムを特定したりするなど、より限定的なヒットテストロジックを実装することを検討してください。
      • QGraphicsView::ViewportUpdateMode の設定
        setViewportUpdateMode()MinimalViewportUpdate(デフォルト)、SmartViewportUpdateなどに設定することで、再描画領域を最小限に抑え、パフォーマンスを改善できる場合があります。
  • シンプルな例でテストする
    • 問題が解決しない場合、最小限のコードでQGraphicsViewQGraphicsItemを作成し、itemAt()が機能するかどうかをテストしてください。これにより、問題がカスタムアイテムの実装にあるのか、ビューの設定にあるのか、それともイベント処理にあるのかを切り分けることができます。
  • QGraphicsScene::items(const QPointF &pos) との比較
    • QGraphicsView::itemAt()が期待通りに動かない場合、一度QGraphicsScene::itemAt(mapToScene(event->pos()))を試して、シーンレベルでは検出できるかどうか確認するのも有効です。これで検出できるがQGraphicsView::itemAt()では検出できない場合、ビューの変換や表示に関する問題の可能性が高まります。
  • qDebug() を活用する
    • itemAt()が返すポインタがnullptrかどうか、もしアイテムが返されたらそのアイテムのscenePos()boundingRect()zValue()などをqDebug()で出力し、期待通りの値になっているか確認してください。
    • マウスイベントのevent->pos()(ビュー座標)と、それをmapToScene()で変換したQPointF(シーン座標)も出力して、座標の整合性を確認してください。


QGraphicsView::itemAt() は、主にQGraphicsViewのサブクラス内で、マウスイベントなど特定のビュー座標におけるQGraphicsItemを検出するために使用されます。

例1: マウスクリックでアイテムを選択する(基本的な使い方)

この例では、QGraphicsViewを継承したカスタムビューを作成し、ユーザーがクリックした位置にあるアイテムを検出して、選択状態を切り替えます。

mygraphicsview.h

#ifndef MYGRAPHICSVIEW_H
#define MYGRAPHICSVIEW_H

#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsItem>
#include <QMouseEvent>
#include <QDebug>

class MyGraphicsView : public QGraphicsView
{
    Q_OBJECT
public:
    explicit MyGraphicsView(QGraphicsScene *scene, QWidget *parent = nullptr);

protected:
    // マウスプレスイベントをオーバーライドしてアイテムを検出
    void mousePressEvent(QMouseEvent *event) override;
};

#endif // MYGRAPHICSVIEW_H

mygraphicsview.cpp

#include "mygraphicsview.h"

MyGraphicsView::MyGraphicsView(QGraphicsScene *scene, QWidget *parent)
    : QGraphicsView(scene, parent)
{
    // ビューのインタラクティブ機能を有効にする (デフォルトでtrueですが、明示的に)
    setInteractive(true);

    // シーンにいくつかのアイテムを追加
    QGraphicsRectItem *rectItem1 = new QGraphicsRectItem(0, 0, 100, 50);
    rectItem1->setPos(50, 50);
    rectItem1->setBrush(Qt::blue);
    rectItem1->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable); // 選択・移動可能にする
    scene->addItem(rectItem1);

    QGraphicsEllipseItem *ellipseItem1 = new QGraphicsEllipseItem(0, 0, 80, 80);
    ellipseItem1->setPos(150, 100);
    ellipseItem1->setBrush(Qt::red);
    ellipseItem1->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable);
    scene->addItem(ellipseItem1);

    QGraphicsTextItem *textItem1 = new QGraphicsTextItem("Hello Qt!");
    textItem1->setPos(80, 150);
    textItem1->setDefaultTextColor(Qt::darkGreen);
    textItem1->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable);
    scene->addItem(textItem1);

    // 重なるアイテムのZ-Valueを設定
    rectItem1->setZValue(0);
    ellipseItem1->setZValue(1); // 楕円を矩形の上に
    textItem1->setZValue(2);    // テキストを一番上に
}

void MyGraphicsView::mousePressEvent(QMouseEvent *event)
{
    // マウスのクリック位置 (ビュー座標) を取得
    QPoint viewPos = event->pos();

    // その位置にある最も手前のアイテムを取得
    QGraphicsItem *item = itemAt(viewPos);

    if (item) {
        qDebug() << "クリックされたアイテム:" << item->metaObject()->className()
                 << ", シーン座標:" << item->scenePos()
                 << ", Z-Value:" << item->zValue();

        // 選択状態を切り替える
        if (event->button() == Qt::LeftButton) {
            item->setSelected(!item->isSelected());
        }
    } else {
        qDebug() << "アイテムがクリックされていません。";
        // アイテム以外の場所をクリックした場合、すべての選択を解除
        scene()->clearSelection();
    }

    // デフォルトのマウスイベント処理も呼び出す(アイテムの移動など)
    QGraphicsView::mousePressEvent(event);
}

main.cpp

#include <QApplication>
#include <QGraphicsScene>
#include "mygraphicsview.h"

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

    QGraphicsScene scene;
    MyGraphicsView view(&scene);

    view.setWindowTitle("QGraphicsView::itemAt() Example");
    view.resize(600, 400);
    view.show();

    return a.exec();
}

解説

  • QGraphicsItem::ItemIsSelectableQGraphicsItem::ItemIsMovableフラグを設定することで、アイテムが選択されたときに破線枠が表示され、ドラッグで移動できるようになります。
  • アイテムが見つからない場合は、シーンの選択をクリアします。
  • itemAt()は、その座標に存在する最も手前のQGraphicsItemを返します。アイテムが見つかれば、そのタイプ、位置、Z-Valueなどをデバッグ出力し、左クリックであれば選択状態を切り替えています。
  • event->pos()でクリックされたビュー座標を取得し、それをitemAt()に渡します。
  • MyGraphicsView::mousePressEventでマウスイベントを捕捉します。

例2: マウスホバーでアイテムの情報を表示する

この例では、マウスがアイテムの上に乗ったときに、そのアイテムの情報をステータスバーに表示します。

mygraphicsview.h (例1と同じファイルに追記または変更)

#ifndef MYGRAPHICSVIEW_H
#define MYGRAPHICSVIEW_H

#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsItem>
#include <QMouseEvent>
#include <QDebug>
#include <QStatusBar> // ステータスバー表示のため

// MyGraphicsViewクラスの定義を更新
class MyGraphicsView : public QGraphicsView
{
    Q_OBJECT
public:
    explicit MyGraphicsView(QGraphicsScene *scene, QWidget *parent = nullptr);
    void setStatusBar(QStatusBar *statusBar) { m_statusBar = statusBar; } // ステータスバーを設定するメソッド

protected:
    void mousePressEvent(QMouseEvent *event) override;
    void mouseMoveEvent(QMouseEvent *event) override; // マウス移動イベントを追加

private:
    QGraphicsItem *m_hoveredItem = nullptr; // 現在ホバーしているアイテム
    QStatusBar *m_statusBar = nullptr; // ステータスバーへのポインタ
};

#endif // MYGRAPHICSVIEW_H

mygraphicsview.cpp (例1のファイルに追記または変更)

#include "mygraphicsview.h"

// コンストラクタは例1と同じでOK

void MyGraphicsView::mousePressEvent(QMouseEvent *event)
{
    // 例1と同じ処理
    // ...
    QGraphicsView::mousePressEvent(event);
}

void MyGraphicsView::mouseMoveEvent(QMouseEvent *event)
{
    QPoint viewPos = event->pos();
    QGraphicsItem *item = itemAt(viewPos); // ホバーしているアイテムを検出

    if (item && item != m_hoveredItem) {
        // 新しいアイテムにホバーした
        if (m_statusBar) {
            m_statusBar->showMessage(QString("ホバー: %1 (Z:%2)").arg(item->metaObject()->className()).arg(item->zValue()));
        }
        m_hoveredItem = item;
    } else if (!item && m_hoveredItem) {
        // アイテムから離れた
        if (m_statusBar) {
            m_statusBar->clearMessage();
        }
        m_hoveredItem = nullptr;
    }

    // 親クラスの処理を呼び出す
    QGraphicsView::mouseMoveEvent(event);
}

main.cpp (ステータスバーを使用するため、QMainWindowを使用)

#include <QApplication>
#include <QMainWindow>
#include <QGraphicsScene>
#include <QStatusBar> // ステータスバー
#include <QVBoxLayout>
#include <QWidget>
#include "mygraphicsview.h" // MyGraphicsViewのインクルード

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

    QMainWindow mainWindow;
    mainWindow.setWindowTitle("QGraphicsView::itemAt() Hover Example");
    mainWindow.resize(800, 600);

    QGraphicsScene *scene = new QGraphicsScene(&mainWindow); // シーンをmainWindowの子オブジェクトに
    MyGraphicsView *view = new MyGraphicsView(scene, &mainWindow); // ビューをmainWindowの子オブジェクトに

    // シーンにアイテムを追加 (MyGraphicsViewのコンストラクタで追加されるのでここでは不要ですが、必要に応じて)
    // QGraphicsRectItem *rect = new QGraphicsRectItem(0, 0, 100, 100);
    // rect->setPos(100, 100);
    // rect->setBrush(Qt::green);
    // scene->addItem(rect);

    // ステータスバーを設定
    QStatusBar *statusBar = new QStatusBar(&mainWindow);
    mainWindow.setStatusBar(statusBar);
    view->setStatusBar(statusBar);

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

    // マウス追跡を有効にする (mouseMoveEventが常に呼ばれるように)
    view->setMouseTracking(true);

    mainWindow.show();

    return a.exec();
}

解説

  • m_hoveredItemというメンバー変数で、前回ホバーしていたアイテムを記憶し、アイテムの出入りを検知してステータスバーのメッセージを更新しています。
  • mouseMoveEvent内でitemAt()を呼び出し、マウスカーソル下のアイテムを検出します。
  • QGraphicsView::setMouseTracking(true)を呼び出すことで、マウスがビューポート内にある限りmouseMoveEventが常に発生するようになります(ボタンが押されていなくても)。

例3: カスタムアイテムと itemAt()

より複雑な形状や独自のヒットテストロジックを持つカスタムアイテムでitemAt()を使用する例です。

mycustomitem.h

#ifndef MYCUSTOMITEM_H
#define MYCUSTOMITEM_H

#include <QGraphicsItem>
#include <QPainter>
#include <QPainterPath>
#include <QDebug>

class MyCustomItem : public QGraphicsItem
{
public:
    MyCustomItem(qreal x, qreal y, qreal width, qreal height, QGraphicsItem *parent = nullptr);

    // アイテムのバウンディングレクタングル (描画範囲)
    QRectF boundingRect() const override;

    // アイテムの形状 (ヒットテストに使用される)
    QPainterPath shape() const override;

    // アイテムの描画
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;

protected:
    // マウスプレスイベントをオーバーライド (アイテム自体がクリックされたときの反応)
    void mousePressEvent(QGraphicsSceneMouseEvent *event) override;

private:
    QRectF m_rect;
};

#endif // MYCUSTOMITEM_H

mycustomitem.cpp

#include "mycustomitem.h"

MyCustomItem::MyCustomItem(qreal x, qreal y, qreal width, qreal height, QGraphicsItem *parent)
    : QGraphicsItem(parent), m_rect(x, y, width, height)
{
    // 選択可能にする
    setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable);
    setAcceptHoverEvents(true); // ホバーイベントを受け取る
}

QRectF MyCustomItem::boundingRect() const
{
    // 少し大きめのバウンディングレクタングルを返すことで、描画のクリッピングを避ける
    // 特にペンで描画する場合に外側に少しはみ出すことがあるため
    return m_rect.adjusted(-1, -1, 1, 1);
}

QPainterPath MyCustomItem::shape() const
{
    QPainterPath path;
    // 四隅が丸い矩形を形状として定義
    path.addRoundedRect(m_rect, 10, 10);
    // または、もっと複雑な形状を定義することも可能
    // path.addEllipse(m_rect.center(), m_rect.width() / 4, m_rect.height() / 4);
    return path;
}

void MyCustomItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(option);
    Q_UNUSED(widget);

    // 選択されている場合は青色の塗りつぶし、そうでない場合は緑色
    QBrush brush = isSelected() ? Qt::blue : Qt::green;
    painter->setBrush(brush);
    painter->setPen(Qt::black);

    // shape()で定義した形状を描画
    painter->drawPath(shape());

    // デバッグ用にバウンディングレクタングルを描画することもできる
    // painter->setPen(Qt::red);
    // painter->drawRect(boundingRect());
}

void MyCustomItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    qDebug() << "カスタムアイテムがクリックされました!";
    // ここでアイテム固有の処理を行う
    // デフォルトの選択/移動動作のためにも親クラスのイベントを呼び出す
    QGraphicsItem::mousePressEvent(event);
}

mygraphicsview.cpp の変更点

#include "mygraphicsview.h"
#include "mycustomitem.h" // カスタムアイテムをインクルード

MyGraphicsView::MyGraphicsView(QGraphicsScene *scene, QWidget *parent)
    : QGraphicsView(scene, parent)
{
    setInteractive(true);

    // MyCustomItemを追加
    MyCustomItem *customItem = new MyCustomItem(0, 0, 120, 80);
    customItem->setPos(100, 100);
    customItem->setZValue(10); // 他のアイテムより手前に
    scene->addItem(customItem);

    // 他の既存のアイテムも追加...
    QGraphicsRectItem *rectItem1 = new QGraphicsRectItem(0, 0, 100, 50);
    rectItem1->setPos(50, 50);
    rectItem1->setBrush(Qt::blue);
    rectItem1->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable);
    scene->addItem(rectItem1);

    // ... (他のアイテムの追加はそのまま)
}

// mousePressEvent と mouseMoveEvent は例1, 例2 と同じでMyCustomItemも検出されます
  • MyGraphicsView::itemAt()は、このカスタムアイテムのshape()に基づいてヒットテストを行うため、正確なクリック検出が可能です。
  • paint()関数はアイテムの描画ロジックを実装します。ここではshape()で定義したパスを描画しています。
  • shape()関数は、アイテムの正確な形状をQPainterPathで返します。itemAt()や衝突検出などはこのshape()を使用してヒットテストを行います。この例では丸い矩形を定義しており、クリックするとその丸い角の部分でも検出されるようになります。
  • boundingRect()は描画と更新領域を定義します。
  • MyCustomItemクラスはQGraphicsItemを継承しています。


QGraphicsView::itemAt()がビュー座標を受け取るのに対し、以下の方法はシーン座標やアイテムの内部ロジックを使用します。

QGraphicsScene::itemAt(const QPointF &pos, const QTransform &deviceTransform = QTransform())

これはQGraphicsView::itemAt()と非常によく似ていますが、引数がシーン座標である点が異なります。