アプリケーションで画像を含む MDI ウィンドウを表示する必要があります。マウスの右ボタンを使用して画像をドラッグ スクロールし、マウス ホイールを使用して画像をズームし、それらの上に多角形の関心領域マスクを作成できるようにしたいと考えました。この目的のために、いくつかのマウス イベントを再実装するカスタム QGraphicsView 派生クラス (ImageView) を作成しました。アプリケーションのインターフェイスでカーソルのピクセル位置インジケーターを更新するホバー イベントを実装するための QGraphicsPixmapItem 派生クラス (ImageItem) もあります。概要は次のとおりです (マスク ポリゴンはまだ実装されていません)。
class ImageView : public QGraphicsView
{
Q_OBJECT
public:
ImageView(QWidget *parent) : QGraphicsView(parent), _pan(false), _panStartX(0), _panStartY(0),
_scale(1.2), _scene(NULL), _imgItem(NULL)
{
_scene = new QGraphicsScene(this);
_scene->setBackgroundBrush(Qt::lightGray);
setScene(_scene);
_imgItem = new ImageItem(this);
_scene->addItem(_imgItem);
}
void showImage(const QString &path)
{
_imgItem->loadImage(path);
setSceneRect(0, 0, _imgItem->imageWidth(), _imgItem->imageHeight());
}
void startAoi(AoiType type)
{
_imgItem->setAcceptHoverEvents(true);
_imgItem->setCursor(Qt::CrossCursor);
// explicit mouse tracking enable isn't necessary
}
void stopAoi()
{
_imgItem->unsetCursor();
_imgItem->setAcceptHoverEvents(false);
}
public slots:
void zoomIn() { scale(_scale, _scale); }
void zoomOut() { scale(1/_scale, 1/_scale); }
protected:
void ImageView::mousePressEvent(QMouseEvent *event)
{
// enter pan mode; set flag, change cursor and store start position
if (event->button() == Qt::RightButton)
{
_pan = true;
_panStartX = event->x();
_panStartY = event->y();
setCursor(Qt::OpenHandCursor);
// accept the event and skip the default implementation?
event->accept();
return;
}
// should do event accept here?
event->ignore();
}
void ImageView::mouseReleaseEvent(QMouseEvent *event)
{
// leave pan mode on right button release; clear flag and restore cursor
if (_pan && event->button() == Qt::RightButton)
{
_pan = false;
unsetCursor();
event->accept();
return;
}
// in the future, left clicks will add vertices to a mask polygon
event->ignore() // ?
}
void ImageView::mouseMoveEvent(QMouseEvent *event)
{
// pan-mode move; scroll image by appropriate amount
if (_pan)
{
scrollBy(_panStartX - event->x(), _panStartY - event->y());
_panStartX = event->x();
_panStartY = event->y();
event->accept();
return;
}
// generic mouse move, hover events won't occur otherwise.
QGraphicsView::mouseMoveEvent(event);
// need to accept or ignore afterwards?
}
void ImageView::wheelEvent(QWheelEvent *event)
{
// disallow zooming while panning
if (_pan)
{
event->ignore();
return;
}
// handle mouse wheel zoom
// perform scaling
if (event->delta() > 0) zoomIn();
else zoomOut();
event->accept();
return;
}
bool _pan;
int _panStartX, _panStartY;
const qreal _scale;
QGraphicsScene *_scene;
ImageItem *_imgItem;
};
class ImageItem : public QGraphicsPixmapItem
{
public:
ImageItem(ImageView *view) : QGraphicsPixmapItem()
{
}
void loadImage(const QString &path)
{
_pixmap.load(path);
setPixmap(_pixmap);
}
protected:
virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{
// update label with position in GUI here
}
QPixmap _pixmap;
};
私の最初の問題は、ビューで startAoi() をアクティブにする前にすべてが正常に機能することです (ユーザーが押す GUI のボタンに接続されています)。その前に、パンは正常に機能し、マウス カーソルが開いた手に変わります。AOI モードをアクティブにした後、画像上でカーソルが十字に変わり、右ボタンを押してドラッグするとビューがパンしますが、画像アイテムのカーソルがビューのカーソルよりも優先されるかのように、カーソルは変更されません。AOI モードを無効にすると、クロス カーソルが消え、ホバー イベントがトリガーされなくなります。
編集:基本的に、QGraphicsItem のように見えますが、unsetCursor() はカーソル自体を削除するのではなく、永続的で親ビューのカーソルをオーバーライドする標準の矢印に変更します。
もう 1 つの問題は、マウス処理全体が正しくレイアウトされているかどうかわからないことです。たとえば、ビュー、シーン、またはイメージ アイテムのマウス イベント オーバーライドでパンとズームを処理する必要があるかどうかわかりません。それらはビュー全体(将来的には他のオブジェクトを含む)に対してグローバルであるため、これは一番上の項目であるビューに属しているはずです。また、イベントでいつ accept() と ignore() を呼び出す必要があるのか、これが実際に何をするのか、親クラスのマウス イベントの実装をいつ呼び出す必要があるのか もわかりません。どんな洞察も大歓迎です。