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

0

Мне нужно отображать окна MDI, содержащие изображения в моем приложении. Я хотел иметь возможность перетаскивать изображения с помощью правой кнопки мыши, масштабировать их с помощью колеса мыши, а также создавать над ними полигональные маски с интересом. С этой целью я создал собственный класс (ImageView) класса QGraphicsView, который повторяет некоторые события мыши. У меня также есть класс, основанный на 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, перекрестный курсор исчезнет, ​​события наведения больше не срабатывают, но на этот раз, пока кастрюля все еще работает, я застрял с простым стрелочным курсором при перетаскивании.

EDIT: В принципе, похоже, что для QGraphicsItem unsetCursor() не удаляет курсор как таковой, а скорее изменяет его на стандартную стрелку, которая является постоянной и переопределяет родительский вид курсор.

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

Теги:
qt
qgraphicsview

2 ответа

1
Лучший ответ

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

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

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

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

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

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

0

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

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню