1

我需要在我的应用程序中显示包含图像的 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(),这实际上是做什么的,以及何时应该调用父类的鼠标事件实现。任何见解将不胜感激。

4

2 回答 2

1

几个月前,我创建了一个类似的图像处理应用程序:图像拖动、放大/缩小 + 不同的自定义工具在 QGraphicsView 上绘制(添加)特定项目。我只使用了 setCursor() 函数 - 而不是 unsetCursor()。

我创建了一个计时器,并根据存储在我的 GraphicsView 子类中的自定义“工具类型”标志设置了计时器事件函数内的光标形状,并考虑了键盘和鼠标的当前状态。我认为这更方便:

a) 即使用户没有移动鼠标或单击特定对象,它也允许您设置光标形状 - 例如,如果选择了“缩放工具”,您可以将光标设置为 (+) [放大光标]。如果用户按住 Ctrl 键,您可以将光标设置为 (-) [缩小光标]

b) 无需存储任何光标状态:您只需根据当前选择的工具和鼠标/键盘状态设置正确的光标形状。

c) 这是使光标形状快速响应所有键盘/鼠标/工具更改事件的最简单方法

我希望这个计时器的想法可以帮助您克服所有与光标形状相关的问题。一个间隔为 30 毫秒的计时器就可以了。

于 2011-03-18T06:03:06.973 回答
1

看起来你遇到了这个错误:

https://bugreports.qt-project.org/browse/QTBUG-4190

解决方法是将光标设置在视图而不是项目上

于 2013-05-02T11:47:29.497 回答