我需要在我的应用程序中显示包含图像的 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() 本身并没有删除光标,而是将其更改为标准箭头,它是持久的并覆盖父视图的光标。
另一个问题是我不确定我是否正确布置了整个鼠标处理。例如,我不知道是否应该处理视图、场景或图像项的鼠标事件覆盖的平移和缩放。我想,因为它们对于整个视图(将来将包含其他对象)是全局的,所以它应该属于顶部项目 - 视图。此外,我不确定何时应该对事件调用 accept() 和 ignore(),这实际上是做什么的,以及何时应该调用父类的鼠标事件实现。任何见解将不胜感激。