QT(16):Graphics View事件传播机制和qdraw项目框架

一、Graphics View框架事件传播机制

QGraphicsView从键盘和鼠标接收输入事件,并将这些事件转换为场景事件(在适当的情况下将使用的坐标转换为场景坐标),然后再将事件发送到可视化场景。

QGraphicsScene的事件传播体系结构将场景事件传递到项目,并管理项目之间的传播。如果场景在特定位置收到鼠标按下事件,则场景会将事件传递给位于该位置的任何项目。

以鼠标点击事件的传播为例:
1、在qgraphicsview.cpp中的鼠标点击事件中,将鼠标事件的数据赋值给新建的场景鼠标事件,并将该事件传递给场景。

void QGraphicsView::mousePressEvent(QMouseEvent *event)
{
    Q_D(QGraphicsView);
// 存储此事件,用于重播、计算增量和滚动拖动;
// 即使在非交互模式下,也允许滚动拖动,因此在函数的最开始存储事件。
    // Store this event for replaying, finding deltas, and for
    // scroll-dragging; even in non-interactive mode, scroll hand dragging is
    // allowed, so we store the event at the very top of this function.
    d->storeMouseEvent(event);
    d->lastMouseEvent.setAccepted(false);

    if (d->sceneInteractionAllowed) {
        // Store some of the event's button-down data.// 存储一些按钮按下的数据
        d->mousePressViewPoint = event->pos();
        d->mousePressScenePoint = mapToScene(d->mousePressViewPoint);
        d->mousePressScreenPoint = event->globalPos();
        d->lastMouseMoveScenePoint = d->mousePressScenePoint;
        d->lastMouseMoveScreenPoint = d->mousePressScreenPoint;
        d->mousePressButton = event->button();

        if (d->scene) {
            // Convert and deliver the mouse event to the scene.
            // 将鼠标事件转换并传递给场景。
            QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMousePress);
            mouseEvent.setWidget(viewport());
            mouseEvent.setButtonDownScenePos(d->mousePressButton, d->mousePressScenePoint);
            mouseEvent.setButtonDownScreenPos(d->mousePressButton, d->mousePressScreenPoint);
            mouseEvent.setScenePos(d->mousePressScenePoint);
            mouseEvent.setScreenPos(d->mousePressScreenPoint);
            mouseEvent.setLastScenePos(d->lastMouseMoveScenePoint);
            mouseEvent.setLastScreenPos(d->lastMouseMoveScreenPoint);
            mouseEvent.setButtons(event->buttons());
            mouseEvent.setButton(event->button());
            mouseEvent.setModifiers(event->modifiers());
            mouseEvent.setSource(event->source());
            mouseEvent.setFlags(event->flags());
            mouseEvent.setAccepted(false);
            if (event->spontaneous())
                qt_sendSpontaneousEvent(d->scene, &mouseEvent);
            else
                QCoreApplication::sendEvent(d->scene, &mouseEvent);
 			// 更新原始鼠标事件的接受状态。
            // Update the original mouse event accepted state.
            bool isAccepted = mouseEvent.isAccepted();
            event->setAccepted(isAccepted);

            // Update the last mouse event accepted state.
            d->lastMouseEvent.setAccepted(isAccepted);

            if (isAccepted)
                return;
        }
    }

#if QT_CONFIG(rubberband)
    if (d->dragMode == QGraphicsView::RubberBandDrag && !d->rubberBanding) {
        if (d->sceneInteractionAllowed) {
            // Rubberbanding is only allowed in interactive mode.
            event->accept();
            d->rubberBanding = true;
            d->rubberBandRect = QRect();
            if (d->scene) {
                bool extendSelection = (event->modifiers() & Qt::ControlModifier) != 0;

                if (extendSelection) {
                    d->rubberBandSelectionOperation = Qt::AddToSelection;
                } else {
                    d->rubberBandSelectionOperation = Qt::ReplaceSelection;
                    d->scene->clearSelection();
                }
            }
        }
    } else
#endif
        if (d->dragMode == QGraphicsView::ScrollHandDrag && event->button() == Qt::LeftButton) {
        	//在滚动拖动模式下,左键按下开始手动滚动。
            // Left-button press in scroll hand mode initiates hand scrolling.
            event->accept();
            d->handScrolling = true;
            d->handScrollMotions = 0;
#ifndef QT_NO_CURSOR
            viewport()->setCursor(Qt::ClosedHandCursor);
#endif
        }
}

2、在qgraphicsscene.cpp中的鼠标点击事件中,
如果鼠标抓取项为空,新建鼠标悬浮事件,分发该事件。
然后在mousePressEventHandler内处理该鼠标点击事件。

void QGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
    Q_D(QGraphicsScene);
    if (d->mouseGrabberItems.isEmpty()) {
        // Dispatch hover events
        QGraphicsSceneHoverEvent hover;
        _q_hoverFromMouseEvent(&hover, mouseEvent);
        d->dispatchHoverEvent(&hover);
    }

    d->mousePressEventHandler(mouseEvent);
}

3、场景私有类的鼠标事件处理函数中,
(1)、首先将鼠标事件的状态设置为忽略;
(2)、将事件发送给任何已存在的鼠标抓取项;
(3)、temsAtPosition查找在事件位置的所有项目,存储到cachedItemsUnderMouse;
(4)、更新窗口激活状态;
(5)、setFocusItem依次设置cachedItemsUnderMouse中项目为焦点项;
(6)、检查场景模态性;
(7)、sendMouseEvent将鼠标按下事件逐个发送给所有cachedItemsUnderMouse中项目;
(8)、事件还是被忽略吗?那么鼠标按下事件发送给场景。重置鼠标抓取项,清除选择,清除焦点,并保持事件被忽略以便能够传播到原始视图。

void QGraphicsScenePrivate::mousePressEventHandler(QGraphicsSceneMouseEvent *mouseEvent)
{
    Q_Q(QGraphicsScene);

    // Ignore by default, unless we find a mouse grabber that accepts it.
    //默认情况下忽略事件,除非我们找到能够接收它的鼠标抓取项。
    mouseEvent->ignore();//忽略该事件,往父组件传播

    // Deliver to any existing mouse grabber.将事件发送给任何已存在的鼠标抓取项。
    if (!mouseGrabberItems.isEmpty()) {
        if (mouseGrabberItems.constLast()->isBlockedByModalPanel())
            return;
        //默认情况下忽略事件,但我们在传递后不考虑事件的接受状态;毕竟鼠标已经被抓取了。
        // The event is ignored by default, but we disregard the event's
        // accepted state after delivery; the mouse is grabbed, after all.
        sendMouseEvent(mouseEvent);
        return;
    }

    // Start by determining the number of items at the current position.首先确定当前位置处的项目数。
    // Reuse value from earlier calculations if possible.如果可能,重用之前的计算结果。
    if (cachedItemsUnderMouse.isEmpty()) {
        cachedItemsUnderMouse = itemsAtPosition(mouseEvent->screenPos(),
                                                mouseEvent->scenePos(),
                                                mouseEvent->widget());
    }

    // Update window activation.更新窗口激活状态。
    QGraphicsItem *topItem = cachedItemsUnderMouse.value(0);
    QGraphicsWidget *newActiveWindow = topItem ? topItem->window() : 0;
    if (newActiveWindow && newActiveWindow->isBlockedByModalPanel(&topItem)) {
        // pass activation to the blocking modal window将激活状态传递给阻塞的模态窗口
        newActiveWindow = topItem ? topItem->window() : 0;
    }

    if (newActiveWindow != q->activeWindow())
        q->setActiveWindow(newActiveWindow);

    // Set focus on the topmost enabled item that can take focus. 设置焦点到最上层的可接收焦点的项。
    bool setFocus = false;

    foreach (QGraphicsItem *item, cachedItemsUnderMouse) {
        if (item->isBlockedByModalPanel()
            || (item->d_ptr->flags & QGraphicsItem::ItemStopsFocusHandling)) {
            // Make sure we don't clear focus.确保不清除焦点。
            setFocus = true;
            break;
        }
        if (item->isEnabled() && ((item->flags() & QGraphicsItem::ItemIsFocusable))) {
            if (!item->isWidget() || ((QGraphicsWidget *)item)->focusPolicy() & Qt::ClickFocus) {
                setFocus = true;
                if (item != q->focusItem() && item->d_ptr->mouseSetsFocus)
                    q->setFocusItem(item, Qt::MouseFocusReason);
                break;
            }
        }
        if (item->isPanel())
            break;
        if (item->d_ptr->flags & QGraphicsItem::ItemStopsClickFocusPropagation)
            break;
    }

    // Check for scene modality. 检查场景模态性。
    bool sceneModality = false;
    for (int i = 0; i < modalPanels.size(); ++i) {
        if (modalPanels.at(i)->panelModality() == QGraphicsItem::SceneModal) {
            sceneModality = true;
            break;
        }
    }

    // If nobody could take focus, clear it.如果没有任何项能够接收焦点,清除焦点。
    if (!stickyFocus && !setFocus && !sceneModality)
        q->setFocusItem(0, Qt::MouseFocusReason);

    // Any item will do.随便找个项即可。
    if (sceneModality && cachedItemsUnderMouse.isEmpty())
        cachedItemsUnderMouse << modalPanels.constFirst();

    // Find a mouse grabber by sending mouse press events to all mouse grabber
    // candidates one at a time, until the event is accepted. It's accepted by
    // default, so the receiver has to explicitly ignore it for it to pass
    // through.
    // 通过将鼠标按下事件逐个发送给所有鼠标抓取项的候选项,找到一个鼠标抓取项。
    // 默认情况下事件被接受,因此接收者必须明确地忽略它才能传递。
    foreach (QGraphicsItem *item, cachedItemsUnderMouse) {
        if (!(item->acceptedMouseButtons() & mouseEvent->button())) {
            // Skip items that don't accept the event's mouse button.跳过不接受该事件鼠标按钮的项。
            continue;
        }
	//检查该项是否被模态面板阻塞,并将鼠标事件发送给阻塞的面板而不是该项。
        // Check if this item is blocked by a modal panel and deliver the mouse event to the
        // blocking panel instead of this item if blocked.
        (void) item->isBlockedByModalPanel(&item);

        grabMouse(item, /* implicit = */ true);
        mouseEvent->accept();
	 	//检查我们要发送事件的项是否被禁用(在发送事件之前)。
        // check if the item we are sending to are disabled (before we send the event)
        bool disabled = !item->isEnabled();
        bool isPanel = item->isPanel();
        if (mouseEvent->type() == QEvent::GraphicsSceneMouseDoubleClick
            && item != lastMouseGrabberItem && lastMouseGrabberItem) {
            // If this item is different from the item that received the last
            // mouse event, and mouseEvent is a doubleclick event, then the
            // event is converted to a press. Known limitation:
            // Triple-clicking will not generate a doubleclick, though.
            // 如果该项与上次接收鼠标事件的项不同,并且鼠标事件是一个双击事件,
        	// 则将事件转换为按下事件。已知限制:无法生成三击事件的双击事件。
            QGraphicsSceneMouseEvent mousePress(QEvent::GraphicsSceneMousePress);
            mousePress.spont = mouseEvent->spont;
            mousePress.accept();
            mousePress.setButton(mouseEvent->button());
            mousePress.setButtons(mouseEvent->buttons());
            mousePress.setScreenPos(mouseEvent->screenPos());
            mousePress.setScenePos(mouseEvent->scenePos());
            mousePress.setModifiers(mouseEvent->modifiers());
            mousePress.setWidget(mouseEvent->widget());
            mousePress.setButtonDownPos(mouseEvent->button(),
                                        mouseEvent->buttonDownPos(mouseEvent->button()));
            mousePress.setButtonDownScenePos(mouseEvent->button(),
                                             mouseEvent->buttonDownScenePos(mouseEvent->button()));
            mousePress.setButtonDownScreenPos(mouseEvent->button(),
                                              mouseEvent->buttonDownScreenPos(mouseEvent->button()));
            sendMouseEvent(&mousePress);
            mouseEvent->setAccepted(mousePress.isAccepted());
        } else {
            sendMouseEvent(mouseEvent);
        }

        bool dontSendUngrabEvents = mouseGrabberItems.isEmpty() || mouseGrabberItems.constLast() != item;
        if (disabled) {
            ungrabMouse(item, /* itemIsDying = */ dontSendUngrabEvents);
            break;
        }
        if (mouseEvent->isAccepted()) {
            if (!mouseGrabberItems.isEmpty())
                storeMouseButtonsForMouseGrabber(mouseEvent);
            lastMouseGrabberItem = item;
            return;
        }
        ungrabMouse(item, /* itemIsDying = */ dontSendUngrabEvents);

        // Don't propagate through panels.不要继续传播到面板
        if (isPanel)
            break;
    }
// 事件还是被忽略吗?那么鼠标按下事件发送给场景。
// 重置鼠标抓取项,清除选择,清除焦点,并保持事件被忽略以便能够传播到原始视图。
    // Is the event still ignored? Then the mouse press goes to the scene.
    // Reset the mouse grabber, clear the selection, clear focus, and leave
    // the event ignored so that it can propagate through the originating
    // view.
    if (!mouseEvent->isAccepted()) {
        clearMouseGrabber();

        QGraphicsView *view = mouseEvent->widget() ? qobject_cast<QGraphicsView *>(mouseEvent->widget()->parentWidget()) : 0;
        bool dontClearSelection = view && view->dragMode() == QGraphicsView::ScrollHandDrag;
        bool extendSelection = (mouseEvent->modifiers() & Qt::ControlModifier) != 0;
        dontClearSelection |= extendSelection;
        if (!dontClearSelection) {
            // Clear the selection if the originating view isn't in scroll
            // hand drag mode. The view will clear the selection if no drag
            // happened.
            // 如果原始视图不处于滚动拖动模式,则清除选择。
        	// 如果没有发生拖动,视图将清除选择。
            q->clearSelection();
        }
    }
}

4、在sendMouseEvent函数中,场景的私有类将事件发送给鼠标抓取项的最后一项

void QGraphicsScenePrivate::sendMouseEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
    if (mouseEvent->button() == 0 && mouseEvent->buttons() == 0 && lastMouseGrabberItemHasImplicitMouseGrab) {
        // ### This is a temporary fix for until we get proper mouse
        // grab events.
        clearMouseGrabber();
        return;
    }

    QGraphicsItem *item = mouseGrabberItems.constLast();
    if (item->isBlockedByModalPanel())
        return;

    const QTransform mapFromScene = item->d_ptr->genericMapFromSceneTransform(mouseEvent->widget());
    const QPointF itemPos = mapFromScene.map(mouseEvent->scenePos());
    for (int i = 0x1; i <= 0x10; i <<= 1) {
        Qt::MouseButton button = Qt::MouseButton(i);
        mouseEvent->setButtonDownPos(button, mouseGrabberButtonDownPos.value(button, itemPos));
        mouseEvent->setButtonDownScenePos(button, mouseGrabberButtonDownScenePos.value(button, mouseEvent->scenePos()));
        mouseEvent->setButtonDownScreenPos(button, mouseGrabberButtonDownScreenPos.value(button, mouseEvent->screenPos()));
    }
    mouseEvent->setPos(itemPos);
    mouseEvent->setLastPos(mapFromScene.map(mouseEvent->lastScenePos()));
    sendEvent(item, mouseEvent);
}

二、qdraw:

在这里插入图片描述

1、DrawView继承于QGraphicsView,主要实现放缩、刻度尺(控件)、文件操作。

2、DrawScene继承于QGraphicsScene,主要实现项目的排列、组合等操作、键盘、鼠标等事件的处理、背景的绘制。
在事件处理函数中调用DrawTool的mouseMoveEvent事件,管理GraphicsItem类对象的新建和管理。

3、DrawTool管理基类,子类SelectTool(选择工具)、RotationTool(旋转工具)、RectTool(矩形工具)、PolygonTool(折线工具)工具实现具体功能。

RectTool类有rectTool、roundRectTool、ellipseTool三个静态对象。
PolygonTool类有lineTool、polygonTool、bezierTool、polylineTool四个静态对象。在静态变量定义时,七个对象添加到DrawTool指针的列表中。
在mainwindow.cpp中,点击不同的新建图形按钮,改变DrawTool的DrawShape类静态变量c_drawShape值。然后在画布上点击鼠标并移动完成绘制的操作。

enum DrawShape
{
    selection ,
    rotation  ,
    line ,
    rectangle ,
    roundrect ,
    ellipse ,
    bezier,
    polygon,
    polyline,
};

在场景类的鼠标事件中,通过查找DrawTool指针列表中当前操作对应的管理工具,调用该工具的同名函数进行处理,实现绘制功能。
选择和旋转功能同理。

在RectTool的鼠标事件中新建矩形、椭圆、圆角矩形。
在PolygonTool的鼠标事件中新建线段、多边形、贝塞尔曲线、折线。
这些图形的共同基类是GraphicsItem。

4、GraphicsItem继承于QObject和模板类AbstractShapeType,处理具体到项目内部的事件。

template < typename BaseType = QGraphicsItem >
class AbstractShapeType : public BaseType

GraphicsRectTool继承于GraphicsItem,GraphicsEllipseItem继承于GraphicsRectTool。
GraphicsPolygonItem继承于GraphicsItem,GraphicsLineItem继承于GraphicsPolygonItem,GraphicsBezier继承于GraphicsPolygonItem。

paint函数绘制item,duplicate函数复制item,control函数改变形状,stretch函数改变大小。

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是关于Qt Graphics View的教程: 1. 官方文档:Qt Graphics View官方文档是使用该框架的最佳参考资料,其中详细介绍了该框架的所有功能和API。 2. 基本概念:在学习Qt Graphics View之前,需要先了解一些基本概念,如场景(Scene)、视图(View)、图元(Item)等。 3. 创建场景和视图:使用Qt Graphics View创建场景和视图非常简单。可以通过代码或Qt Designer创建一个新的Graphics View窗口。首先需要创建场景,然后将场景与视图关联。 4. 添加图元:可以使用Qt Graphics View中的图元(Item)来绘制2D图形。可以添加图元到场景中,然后它们就会显示在视图中。Qt Graphics View提供了一些内置的图元类,如矩形、椭圆、文字、线条等。 5. 图元转换和变换:使用Qt Graphics View可以对图元进行多种转换和变换操作,如平移、缩放、旋转、剪切、选择等。这些操作可以通过使用QTransform类来实现。 6. 事件处理和信号机制Qt Graphics View提供了丰富的事件和信号机制,方便用户进行图形交互和动画效果的实现。可以通过重载事件处理函数和连接信号和槽函数来处理这些事件和信号。 7. 动画效果:使用Qt Graphics View可以实现各种动画效果,如平移、旋转、缩放等。可以使用QPropertyAnimation类来实现这些效果。 8. 场景和视图的组合:使用Qt Graphics View可以将多个场景和视图组合在一起,创建复杂的图形场景。可以使用QGraphicsProxyWidget类将QWidget转换为图元,然后将它们添加到场景中。 希望以上教程能帮助你了解Qt Graphics View

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值