Почему у меня возникают проблемы с изменением курсоров мыши в моей реализации интерактивного QGraphicsView в Qt? - PullRequest
1 голос
/ 10 марта 2011

Мне нужно отобразить окна 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 () в представлении (связанном с кнопкой в ​​графическом интерфейсе, которыйпользователь нажимает).До этого панорамирование работает нормально, курсор мыши превращается в открытую ладонь.После того, как я активирую режим AOI, курсор переходит в крест на изображении, а нажатие и перетаскивание правой кнопки перемещает представление, но не меняет курсор, как если бы курсор элемента изображения имел приоритет над курсором представления.После того, как я деактивирую режим AOI, перекрестный курсор исчезает, события наведения больше не запускаются, но на этот раз, пока панорамирование все еще работает, я застреваю с простым курсором стрелки при перетаскивании.

РЕДАКТИРОВАТЬ: По сути, это похоже на QGraphicsItem, unsetCursor () не удаляет курсор как таковой, а заменяет его на стандартную стрелку, которая является постоянной и переопределяет курсор родительского представления.

Другая проблема в том, что я не уверен, что вся обработка мыши выложена правильно.Например, я не знаю, должен ли я обрабатывать панорамирование и масштабирование в событиях мыши, переопределяющих вид, сцену или элемент изображения.Я подумал, поскольку, поскольку они являются глобальными для всего представления (которое будет содержать другие объекты в будущем), это должно принадлежать верхнему элементу - представлению.Кроме того, я не уверен, когда мне следует вызывать accept () и ignore () для событий, что это на самом деле делает, и когда я должен вызывать реализации родительских классов для событий мыши.Любое понимание будет с благодарностью.

Ответы [ 2 ]

1 голос
/ 02 мая 2013

Похоже, вы нажали эту ошибку:

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

Обходной путь - установить курсор на видеа не пункт

1 голос
/ 18 марта 2011

Несколько месяцев назад я создал похожее приложение для работы с изображениями: перетаскивание изображений, увеличение / уменьшение масштаба + различные пользовательские инструменты для рисования (добавления) определенных элементов в QGraphicsView. Я использовал только функцию setCursor (), а не unsetCursor ().

Я создал таймер и установил форму курсора внутри функции события таймера в соответствии с пользовательским флагом «Тип инструмента», хранящимся в моем подклассе GraphicsView, и с учетом текущего состояния клавиатуры и мыши. Я думаю, что это более удобно, как:

a) Позволяет установить форму курсора, даже если пользователь не перемещает мышь или не щелкает по конкретному объекту - например, если выбран «инструмент масштабирования», вы можете установить курсор на (+) [курсор увеличения]. Если пользователь удерживает нажатой клавишу Ctrl, вы можете установить курсор на (-) [курсор уменьшения изображения]

b) Нет необходимости хранить какие-либо состояния курсора: вы просто устанавливаете правильную форму курсора в соответствии с выбранным в данный момент инструментом и состоянием мыши / клавиатуры.

в) Это самый простой способ создать форму курсора, чтобы быстро реагировать на все события, связанные с клавиатурой / мышью / сменой инструмента

Я надеюсь, что эта идея таймера поможет вам преодолеть все проблемы, связанные с формой курсора. С таймером с интервалом 30 мсек все будет в порядке.

...