Russian Qt Forum

Qt => 2D и 3D графика => Тема начата: CriDos от Март 27, 2016, 07:24



Название: [РЕШЕНО] QGraphicsScene - захват событий мыши.
Отправлено: CriDos от Март 27, 2016, 07:24
Здравствуйте.
Имеется сцена QGraphicsScene, на сцену добавляется объект класса Item.
Класс Item наследуется от QGraphicsRectItem и у него переопределён метод hoverEnterEvent.
Также у Item в конструкторе задаётся setAcceptHoverEvents(true), т.к. в дальнейшем предполагается взаимодействие с этим объектом с помощью мыши.
Проблема в том, что если нажать мышью на сцене и не отпуская кнопки, провести курсором над объектом Item, метод hoverEnterEvent у объекта Item не будет вызван, более того, объекту вообще не будут отправляться события (bool QGraphicsItem::sceneEvent(QEvent *event)).
После более детального изучения вопроса, понял что после действия mousePressEvent, происходит захват мыши.
Т.е. получается, события в любом случае получает только объект с grabberMouse.
Есть ли более-менее простой способ, захватить мышь и другими объектами сцены?

Пример в аттаче.


Название: Re: QGraphicsScene - захват событий мыши.
Отправлено: CriDos от Март 27, 2016, 08:42
Нужное поведение сцены и объектов реализовано в проекте Fritzing (https://github.com/fritzing/fritzing-app), который на Qt с открытыми сорцами.
Пример.gif (http://i.imgur.com/x9fu9y9.gif)
Но в Fritzing, если я ничего не путаю, реализовано это с помощью ручного слежения за событиями мыши и своих реализаций hover*Item в наследниках QGraphicsView... :(
Код:
void SketchWidget::hoverEnterItem(QGraphicsSceneHoverEvent * event, ItemBase * item) {
if(m_infoViewOnHover || currentlyInfoviewed(item)) {
InfoGraphicsView::hoverEnterItem(event, item);
}

Wire * wire = dynamic_cast<Wire *>(item);
if (wire != NULL) {
if (canChainWire(wire)) {
bool segment = wire->connector0()->chained() && wire->connector1()->chained();
bool disconnected = wire->connector0()->connectionsCount() == 0 &&  wire->connector1()->connectionsCount() == 0;
statusMessage(QString("%1 to add a bendpoint %2")
.arg(disconnected ? tr("Double-click") : tr("Drag or double-click"))
.arg(segment ? tr("or alt-drag to move the segment") : tr("")));
m_lastHoverEnterItem = item;
}
}
}


Название: Re: QGraphicsScene - захват событий мыши.
Отправлено: CriDos от Март 27, 2016, 11:08
Не всё так просто. :(
QGraphicsItem does not become a mouse grabber (http://lists.qt-project.org/pipermail/interest/2014-March/011533.html)


Название: Re: QGraphicsScene - захват событий мыши.
Отправлено: Igors от Март 27, 2016, 11:56
Демонстрация (стандартное приложение Qt Widgets, mainwindow.cpp):
Желание покопаться быстро исчезает если надо выцапарывать Ваш текст, создавать проект и.т.п. Выкладывайте компилябельный проект в zip
Есть ли более-менее простой способ, захватить мышь и другими объектами сцены?
Неясно какого поведения Вы хотите добиться. Юзер нажал на место в сцене где нет айтема и тащит мышь. Что должно происходить?


Название: Re: QGraphicsScene - захват событий мыши.
Отправлено: CriDos от Март 27, 2016, 12:09
Выкладывайте компилябельный проект в zip
Предполагал что так будет легче, ибо создать проект и вставить текст проще (имхо), чем скачать, распаковать, открыть и настроить проект :)


Название: Re: QGraphicsScene - захват событий мыши.
Отправлено: CriDos от Март 27, 2016, 12:16
Цитировать
Неясно какого поведения Вы хотите добиться. Юзер нажал на место в сцене где нет айтема и тащит мышь. Что должно происходить?
Немного переработал демонстрационный пример.
На сцене 2 объекта.
Задача: при нажатии ЛКМ (mousePressEvent) на одном из объектов/сцене, без последующего отжатия кнопки (mouseReleaseEvent), оба объекта должны продолжать получать события hoverEnterEvent и hoverLeaveEvent при перемещении курсора мыши над ними.


Название: Re: QGraphicsScene - захват событий мыши.
Отправлено: CriDos от Март 28, 2016, 02:46
Вопрос решён.
Судя по исходникам QGraphicsScene: https://github.com/qtproject/qtbase/blob/9c1d3bc253abd4418f3050d19ec5f05bef3ada97/src/widgets/graphicsview/qgraphicsscene.cpp (https://github.com/qtproject/qtbase/blob/9c1d3bc253abd4418f3050d19ec5f05bef3ada97/src/widgets/graphicsview/qgraphicsscene.cpp)
Код:
void QGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
    Q_D(QGraphicsScene);
    if (d->mouseGrabberItems.isEmpty()) {
        if (mouseEvent->buttons())
            return;
        QGraphicsSceneHoverEvent hover;
        _q_hoverFromMouseEvent(&hover, mouseEvent);
        mouseEvent->setAccepted(d->dispatchHoverEvent(&hover));
        return;
    }

    // Forward the event to the mouse grabber
    d->sendMouseEvent(mouseEvent);
    mouseEvent->accept();
}
дело не в граббере а банальной проверке нажатых кнопок мыши во время движения мыши...
Код:
if (mouseEvent->buttons())
    return;
Т.е. для того, чтобы события мыши продолжали отправляться объектам, даже при зажатой кнопке мыши, достаточно в перегруженном методе QGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event) указать event->setButtons(Qt::NoButton).
Или в QGraphicsView::mouseMoveEvent(QMouseEvent *event) передавать предку свой QMouseEvent:
Код:
    QMouseEvent customEvent(event->type(), event->pos(), Qt::NoButton, Qt::NoButton, event->modifiers());
    QGraphicsView::mouseMoveEvent(&customEvent);