Qt Literacy - Graphics View Framework Theory Overview

I. Overview

Graphics View provides a surface for managing and interacting with a large number of custom 2D graphics items, and a view widget for visualizing these items, supporting zooming and rotation.

The framework includes an event propagation architecture that allows precise double-precision interaction between Items in a Scene. Elements can handle key events, mouse down, move, release, and double-click events, and they can track mouse movement.

Graphics View uses a BSP (Binary Space Partitioning) tree to provide very fast Item discovery, so it can visualize large Scenes in real time, even with millions of Items.

Graphics View was introduced in Qt 4.2, replacing its predecessor QCanvas.

2. Graphics View architecture

Graphics View provides an item-based approach to model-view programming, much like InterView's convenience classes QTableView, QTreeView, and QListView. Multiple views can observe the same Scene, which contains items of different geometric shapes.

1. The Scene

QGraphicsScene provides the Scene of Graphics View. Scene responsibilities are as follows:

  • Provides a quick interface for managing a large number of Items
  • Propagate the event to each Item
  • Manage Item state, such as selection and focus handling
  • Provides untransformed rendering functionality; primarily used for Print functionality

Scene acts as a container for QGraphicsItem objects. Items are added to the Scene by calling QGraphicsScene::addItem(), and then retrieved by calling one of the many item discovery functions. QGraphicsScene::items() and its overloaded functions return all elements contained by or intersected by a point, rectangle, polygon or general vector path . QGraphicsScene::itemAt() Returns the topmost element at the specified position. All item discovery functions return items stacked in descending order (ie, the first returned item is at the top, and the last returned item is at the bottom).

  QGraphicsScene scene;
  QGraphicsRectItem *rect = scene.addRect(QRectF(0, 0, 100, 100));

  QGraphicsItem *item = scene.itemAt(50, 50);
  // item == rect

QGraphicsScene's event propagation architecture schedules Scene events for delivery to Items, and manages the propagation between Items. If the Scene receives a mouse down event at a certain location, the Scene will pass the event to any elements at that location.

QGraphicsScene also manages certain Item states, such as Item selection and focus. You can select elements in the Scene by calling QGraphicsScene::setSelectionArea(), passing an argument of any shape. This feature is also used as the basis for rubber band selection in QGraphicsView. To get a list of all currently selected items, call QGraphicsScene::selectedItems(). Another state that QGraphicsScene handles is whether the Item has keyboard input focus. We can set the focus on an item by calling QGraphicsScene::setFocusItem() or QGraphicsItem::setFocus(), or get the currently focused item by calling QGraphicsScene::focusItem().

Finally, QGraphicsScene allows us to render parts of the Scene to the drawing device through the QGraphicsScene::render() function.

This provides us with a basis for outputting images.

2. The View

QGraphicsView provides a view control, which can visualize the contents of the Scene. You can add multiple views to the same Scene, thus providing multiple viewports for the same dataset. The view widget is a scrolling area that provides scroll bars for navigating within a large Scene. To enable OpenGL support, a QGLWidget can be set as the view by calling QGraphicsView::setViewport().

  QGraphicsScene scene;
  myPopulateScene(&scene);

  QGraphicsView view(&scene);
  view.show();

The View receives input events from the keyboard and mouse, and converts these events into Scene events (converts the used coordinates to Scene coordinates), and then sends the events to the visual Scene.

Using its transformation matrix QGraphicsView::transform(), the view can transform the coordinate system of the Scene. Views also allow advanced navigation features such as zooming and rotation. For convenience, QGraphicsView also provides functions for converting between view and Scene coordinates: QGraphicsView::mapToScene() and QGraphicsView::mapFromScene().
insert image description here

3. Graphic element Item

QGraphicsItem is the base class of graphic elements in Scene. Graphics view provides some standard items for commonly used shapes, such as rectangle (QGraphicsItem), ellipse (QGraphicsEllipseItem), and text item (QGraphicsTextItem), but when writing custom items, we can use the most powerful QGraphicsItem functions. Just inherit this function.

QGraphicsItem supports the following features:

  • Mouse down, move, release, and double-click events, as well as mouse-over, wheel, and context menu events.
  • Keyboard input focus and key events
  • Drag and drop Drop and Drag
  • Group management, parent-child relationship, and QGraphicsItemGroup grouping method
  • Impact checking

Items exist in a local coordinate system. Like QGraphicsView, it also provides many functions to map coordinates between items and Scene, and coordinates between items and items. Also, like QGraphicsView, it can transform its coordinate system using the matrix QGraphicsItem::transform(). This is useful for rotating and scaling individual elements.

Items can contain other Items (sub-Items). A parent's transform is inherited by all its children. However, all its functions (such as QGraphicsItem::contains(), QGraphicsItem::boundingRect(), QGraphicsItem::collidesWith()) still operate in local coordinates, regardless of the element's cumulative transformation.

QGraphicsItem supports collision detection through the QGraphicsItem::shape() function and QGraphicsItem::collidesWith() function, both of which are virtual functions. By returning the Item's shape from QGraphicsItem::shape() as a local coordinate QPainterPath, QGraphicsItem will handle all collision detection for you. However, if you want to provide your own collision detection, you can reimplement QGraphicsItem::collidesWith().

insert image description here

3. Graphic View Coordinate System

The graphics view is based on a Cartesian coordinate system: the position and geometry of an Item in the Scene is represented by two sets of numbers: the x-coordinate and the y-coordinate. When viewing a Scene with an untransformed view, a unit in the Scene is represented by a pixel on the screen.

Note: Since the graph view uses Qt's coordinate system, a reverse y-axis coordinate system (y grows upwards) is not supported.

There are three coordinate systems available in a graphics view: Item coordinates, Scene coordinates, and View coordinates. To simplify implementation, Graphics View provides convenience functions that allow you to map between the three coordinate systems.

When rendering, the Scene coordinates of the graphics view correspond to the logical coordinates of QPainter, and the view coordinates are the same as the device coordinates. In the coordinate system documentation we can read about the relationship between logical coordinates and device coordinates.

insert image description here

1. The coordinates of the primitive Item

Elements exist in their own local coordinate system. Their coordinates are usually centered around the center point (0,0), which is also the center of all transformations. Geometric primitives in the item coordinate system are often referred to as item points, item lines, or item rectangles.

When creating custom items, the item's coordinates are all you need to worry about; QGraphicsScene and QGraphicsView will perform all the transformations for us. This makes it very easy to implement customizations. For example, if you receive a mousedown or drag-in event, the location of the event is given in the coordinates of the element.

QGraphicsItem::contains() virtual function, if a point is inside the Item, it returns true, otherwise it returns false, it accepts the point parameter in the Item coordinates. Similarly, the Item's bounding rectangle and shape are in the Item's coordinates.

The position of an At item is the coordinate of the item's center point in its parent's coordinate system; sometimes also referred to as the parent's coordinates. In this sense, the Scene is considered the "parent" of all parentless items. The position of the top-level item is in Scene coordinates.

Child element coordinates are relative to parent element coordinates. If the child element is not transformed, the difference between the child element's coordinates and the parent's coordinates is the same as the distance between Items in the parent's coordinates. For example: If an untransformed child is positioned exactly at the parent's center point, then the coordinate systems of both children will be the same. If the position of the child node is (10,0), then the (0,10) point of the child node will correspond to the (10,10) point of the parent node.

Because an element's position and transformation are relative to the parent element, the coordinates of a child element are not affected by the parent's transformation, although the parent's transformation implicitly transforms the child. In the above example, even though the parent element is rotated and scaled, the (0,10) point of the child element still corresponds to the (10,10) point of the parent element. However, relative to Scene, child elements will follow the transformation and position of the parent element. If the parent element is scaled (2x, 2x), the position of the child element will be at Scene coordinates (20,0), and its (10,0) point will correspond to the (40,0) point in the Scene.

QGraphicsItem::pos() is one of the few exceptions. QGraphicsItem's functions operate in item coordinates, no matter what the item is or what its parent transform is. For example, an Item's bounding rectangle (ie QGraphicsItem::boundingRect()) is always given in Item's coordinates.

2. Scene Scene coordinates

Scene represents the base coordinate system of all Items. The Scene coordinate system describes the position of each top-level Item, and also forms the basis for all Scene events sent from the View to the Scene. Each Item in the Scene has a Scene position and bounding rectangle (QGraphicsItem::scenePos(), QGraphicsItem::sceneBoundingRect()), in addition to its local Item position and bounding rectangle. The Scene position describes the Item's position in Scene coordinates, and its Scene bounding rectangle forms the basis for how QGraphicsScene determines which areas of the Scene have changed. Scene changes are communicated through the QGraphicsScene::changed() signal, and the parameter is a list of Scene rectangles.

3. View view coordinates

View coordinates are the coordinates of the control. Each unit in view coordinates corresponds to a pixel. The special thing about this coordinate system is that it is relative to the widget (or viewport) and is not affected by the observed Scene. The upper left corner of the viewport of QGraphicsView is always (0,0), and the lower right corner is always (viewport width, viewport height). All mouse events and drag and drop events are initially received in the form of view coordinates, and you need to map these coordinates into the Scene to interact with elements.

4. Coordinate mapping

Often when working with items in a scene, it is useful to map coordinates and arbitrary shapes from a scene to an item, from one item to another, or from a view to a scene. For example, when you click the mouse in the viewport of a QGraphicsView, you can ask the Scene what Item is under the cursor by calling QGraphicsView::mapToScene(), followed by QGraphicsScene::itemAt(). If you want to know where the item is in the viewport, you can call QGraphicsItem::mapToScene() on the item, and then call QGraphicsView::mapFromScene() on the view. Finally, if you want to find items inside the view ellipse, you can pass a QPainterPath to mapToScene(), and then pass the mapped path to QGraphicsScene::items().

By calling QGraphicsItem::mapToScene() and QGraphicsItem::mapFromScene(), the coordinates and shapes can be mapped to the Scene of the item. You can also map to an Item's parent Item by calling QGraphicsItem::mapToParent() and QGraphicsItem::mapFromParent(), or map between Items by calling QGraphicsItem::mapToItem() and QGraphicsItem::mapFromItem(). All mapping functions can map points, rectangles, polygons, and paths simultaneously.

There is also the same mapping function in the view for mapping with Scene. QGraphicsView::mapFromScene() and QGraphicsView::mapToScene(). To map from a view to an item, first map to the Scene, and then map from the Scene to the item.

4. Key features

1. Scale and Rotate

QGraphicsView supports the same affine transformation as QPainter through QGraphicsView::setMatrix(). By applying transformations to views, we can easily add support for common navigational features such as zooming and rotation.
Here is an example of how to implement scaling and rotation slots in a subclass of QGraphicsView:

  class View : public QGraphicsView
  {
    
    
  Q_OBJECT
      ...
  public slots:
      void zoomIn() {
    
     scale(1.2, 1.2); }
      void zoomOut() {
    
     scale(1 / 1.2, 1 / 1.2); }
      void rotateLeft() {
    
     rotate(-10); }
      void rotateRight() {
    
     rotate(10); }
      ...
  };

Slots can be connected to QToolButtons with autoRepeat enabled.

QGraphicsView keeps the center of the view aligned when you switch the view.

2. print

Graphics View provides single-line printing through its rendering functions QGraphicsScene::render() and QGraphicsView::render(). These functions provide the same API: you can have a Scene or View render all or part of their content to any drawing device by passing a QPainter to any of the render functions. This example shows how to use QPrinter to print the entire Scene into a complete page.

  QGraphicsScene scene;
  scene.addRect(QRectF(0, 0, 100, 200), QPen(Qt::black), QBrush(Qt::green));

  QPrinter printer;
  if (QPrintDialog(&printer).exec() == QDialog::Accepted) {
    
    
      QPainter painter(&printer);
      painter.setRenderHint(QPainter::Antialiasing);
      scene.render(&painter);
  }

The difference between Scene and View render functions is that one operates in Scene coordinates and the other operates in View coordinates. QGraphicsScene::render() is typically used to print an entire fragment of a Scene untransformed, such as drawing geometric data or printing a text document. On the other hand, QGraphicsView::render() is suitable for taking screenshots; its default behavior is to use the provided painter to render the content outside the control's window.

  QGraphicsScene scene;
  scene.addRect(QRectF(0, 0, 100, 200), QPen(Qt::black), QBrush(Qt::green));

  QPixmap pixmap;
  QPainter painter(&pixmap);
  painter.setRenderHint(QPainter::Antialiasing);
  scene.render(&painter);
  painter.end();

  pixmap.save("scene.png");

When the size of the source area and the target area do not match, the content of the source area is stretched to fit the target area. By passing Qt::AspectRatioMode to the rendering function you are using, you can choose to maintain or ignore the aspect ratio of the Scene while the content is stretched.

3. Drag and drop

Because QGraphicsView inherits QWidget indirectly, it already provides the same drag and drop functionality that QWidget provides. In addition, for convenience, the graphics view framework provides drag-and-drop support for Scene and each Item. When the view receives a drag, it converts the drag drop event to a QGraphicsSceneDragDropEvent, which is then forwarded to the Scene. The Scene takes over the scheduling of this event and sends it to the first Item under the mouse cursor that accepts the drop.

To start dragging from an element, create a QDrag object, passing a pointer to the widget to start dragging. Multiple views can observe the Item at the same time, but only one view can initiate the drag. In most cases, dragging is started by pressing or moving the mouse, so in mousePressEvent() or mouseMoveEvent(), you can get the widget's initial pointer from the event. For example:

  void CustomItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
  {
    
    
      QMimeData *data = new QMimeData;
      data->setColor(Qt::green);

      QDrag *drag = new QDrag(event->widget());
      drag->setMimeData(data);
      drag->start();
  }

In order to intercept the Scene's drag and drop events, we need to reimplement QGraphicsScene::dragEnterEvent() and any event handlers required by the specific Scene in the QGraphicsItem subclass. We can read more about dragging and dropping graphics views in the documentation of each QGraphicsScene's event handlers.

Items can be dragged and dropped by calling QGraphicsItem::setAcceptDrops(). To handle incoming drags, reimplement QGraphicsItem::dragEnterEvent(), QGraphicsItem::dragMoveEvent(), QGraphicsItem::dragLeaveEvent(), and QGraphicsItem::dropEvent().

4. Mouse Pointer and Tips

Similar to QWidget, QGraphicsItem also supports mouse (QGraphicsItem::setCursor()) and ToolTip (QGraphicsItem::setToolTip()). When the mouse cursor enters the item area (detected by calling QGraphicsItem::contains()), QGraphicsView activates the mouse pointer and ToolTip.
You can also set the default mouse pointer directly on the view by calling QGraphicsView::setCursor().

5. Animation

The graph view supports multiple levels of animation. You can easily assemble animations by using animation frameworks. To do this, we need our Items to inherit QGraphicsObject and associate QPropertyAnimation with them. QPropertyAnimation allows to animate any QObject property.
Another option is to create a custom item that inherits from QObject and QGraphicsItem. The Item can set its own timer and control the animation in incremental steps in QObject::timerEvent().

A third option is to advance the Scene by calling QGraphicsScene::advance(), which in turn calls QGraphicsItem::advance(), mainly for compatibility with QCanvas in Qt 3.

6. OpenGL rendering

To enable OpenGL rendering, simply set a new QGLWidget as the QGraphicsView's viewport by calling QGraphicsView::setViewport(). If you want OpenGL antialiasing, you need OpenGL sample buffer support (see QGLFormat::sampleBuffers()).

  QGraphicsView view(&scene);
  view.setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers)));

7. Item group

By making an item a child of another item, you can achieve the most basic properties of item grouping: all items move together, and all transformations can be propagated from parent to child.

Additionally, QGraphicsItemGroup is a special item that combines child event handling with a useful interface for adding and removing items to and from the group. Adding an element to a QGraphicsItemGroup will maintain the original position and transformation of the element, while rearranging elements will usually cause child elements to be repositioned relative to the new parent. For convenience, you can create QGraphicsItemGroups by calling QGraphicsScene::createItemGroup() from the Scene.

8. Parts and Layout

Qt 4.4 introduced support for geometry and layout-aware items via qgraphicwidget. This special base item is similar to QWidget, but unlike QWidget, it does not inherit from QPaintDevice; it does not derive from QGraphicsItem. This allows us to write complete widgets with events, signals and slots, size hints and policies, and we can also manage the geometry of widgets in the layout via QGraphicsLinearLayout and QGraphicsGridLayout.

1. QGraphicsWidget

Building on the features and stripped-down capabilities of QGraphicsItem, QGraphicsWidget offers the best of both worlds: the extra features from QWidget such as styles, fonts, palettes, layout directions and its geometry, and the resolution independence and transition support from QGraphicsItem. Because the graphics view uses real coordinates instead of integers, QGraphicsWidget's geometry functions can also operate on QRectF and QPointF. This also applies to frame rects, margins and spacing. For example, when using QGraphicsWidget it is very common to specify content margins as (0.5,0.5,0.5,0.5). Both child window components and "top-level" windows can be created; in some cases we can now use graphical views for advanced MDI applications.

Some properties of QWidget are supported, including window flags and attributes, but not all. We should refer to the QGraphicWidget class documentation for a complete overview of what is and isn't supported. For example, you can create decorative windows by passing the Qt::Window window flag to the qgraphicwidget constructor, but the graphics view currently does not support the common Qt::Sheet and Qt::Drawer flags on macOS.

2. QGraphicsLayout

QGraphicsLayout is part of the second-generation layout framework designed specifically for qgraphicwidgets. Its API is very similar to QLayout. You can manage widgets and sublayouts in QGraphicsLinearLayout and QGraphicsGridLayout. You can also easily write your own layout by inheriting QGraphicsLaytItem yourself, or add your own QGraphicsItem Item to the layout by writing an adapter subclass of QGraphicsLayoutItem.

9. Support for embedding widgets

Graphics View provides seamless support for embedding any control into a Scene. Simple controls like QLineEdit or QPushButton, complex controls like QTabWidget, or even complete main windows can be embedded. To embed a widget into a Scene, just call QGraphicsScene::addWidget(), or create an instance of QGraphicsProxyWidget to embed the widget manually.

Through QGraphicsProxyWidget, Graphics View can deeply integrate client widget functionality, including its cursor, ToolTip, mouse, tablet, and keyboard events, child widgets, animations, popup windows (for example, QComboBox or QCompleter), and widget input focus and activation. QGraphicsProxyWidget even integrates the tab order of embedded widgets so that we can tab in and out of embedded widgets. We can even embed the new QGraphicsView into our Scene to provide complex nested Scenes.

When converting embedded controls, Graphics View ensures that the controls convert resolution independently, allowing fonts and styles to remain legible when zoomed in. (Note that the effect of resolution independence depends on the style.)

5. Performance

In order to apply transformations and special effects to elements accurately and quickly, the graphics view is built assuming that the user's hardware can provide reasonable performance for floating point instructions.
Many workstations and desktop computers are equipped with appropriate hardware to accelerate such calculations, but some embedded devices may only provide libraries to handle math operations or emulate floating-point instructions in software.

Therefore, some types of effects may be slower than expected on some devices. This performance loss can be made up for by optimizing in other ways; for example, by rendering a Scene using OpenGL. However, if this optimization itself also relies on floating-point hardware, performance will suffer.

Guess you like

Origin blog.csdn.net/qq_43680827/article/details/132252105