为什么我在Qt中实现交互式QGraphicsView时更改鼠标光标有问题?

时间:2011-03-10 01:23:47

标签: c++ qt qgraphicsview

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

2 个答案:

答案 0 :(得分:1)

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

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

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

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

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

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

答案 1 :(得分:1)

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

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

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