blob: 1a94d53162a8f8c3a239f6b3242865dc5c299459 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Free Documentation License Usage
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file. Please review the following information to ensure
** the GNU Free Documentation License version 1.3 requirements
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\example graphicsview/diagramscene
\title Diagram Scene Example
\ingroup examples-graphicsview
\brief Demonstrate how to use the Graphics View framework.
\image diagramscene.png
The Diagram Scene example is an application in which you can
create a flowchart diagram. It is possible to add flowchart shapes
and text and connect the shapes by arrows as shown in the image
above. The shapes, arrows, and text can be given different
colors, and it is possible to change the font, style, and
underline of the text.
The Qt graphics view framework is designed to manage and display
custom 2D graphics items. The main classes of the framework are
QGraphicsItem, QGraphicsScene and QGraphicsView. The graphics
scene manages the items and provides a surface for them.
QGraphicsView is a widget that is used to render a scene on the
screen. See the \l{Graphics View Framework} for a more detailed
description of the framework.
In this example we show how to create such custom graphics
scenes and items by implementing classes that inherit
QGraphicsScene and QGraphicsItem.
In particular we show how to:
\list
\li Create custom graphics items.
\li Handle mouse events and movement of items.
\li Implement a graphics scene that can manage our custom items.
\li Custom painting of items.
\li Create a movable and editable text item.
\endlist
The example consists of the following classes:
\list
\li \c MainWindow creates the widgets and display
them in a QMainWindow. It also manages the interaction
between the widgets and the graphics scene, view and
items.
\li \c DiagramItem inherits QGraphicsPolygonItem and
represents a flowchart shape.
\li \c TextDiagramItem inherits QGraphicsTextItem and
represents text items in the diagram. The class adds
support for moving the item with the mouse, which is not
supported by QGraphicsTextItem.
\li \c Arrow inherits QGraphicsLineItem and is an arrow
that connect two DiagramItems.
\li \c DiagramScene inherits QGraphicsDiagramScene and
provides support for \c DiagramItem, \c Arrow and
\c DiagramTextItem (In addition to the support already
handled by QGraphicsScene).
\endlist
\section1 MainWindow Class Definition
\snippet graphicsview/diagramscene/mainwindow.h 0
The \c MainWindow class creates and lays out the widgets in a
QMainWindow. The class forwards input from the widgets to the
DiagramScene. It also updates its widgets when the diagram
scene's text item changes, or a diagram item or a diagram text item
is inserted into the scene.
The class also deletes items from the scene and handles the
z-ordering, which decides the order in which items are drawn when
they overlap each other.
\section1 MainWindow Class Implementation
We start with a look at the constructor:
\snippet graphicsview/diagramscene/mainwindow.cpp 0
In the constructor we call methods to create the widgets and
layouts of the example before we create the diagram scene.
The toolbars must be created after the scene as they connect
to its signals. We then lay the widgets out in the window.
We connect to the \c itemInserted() and \c textInserted() slots of
the diagram scenes as we want to uncheck the buttons in the tool
box when an item is inserted. When an item is selected in
the scene we receive the \c itemSelected() signal. We use this to
update the widgets that display font properties if the item
selected is a \c DiagramTextItem.
The \c createToolBox() function creates and lays out the widgets
of the \c toolBox QToolBox. We will not examine it with a
high level of detail as it does not deal with graphics framework
specific functionality. Here is its implementation:
\snippet graphicsview/diagramscene/mainwindow.cpp 21
This part of the function sets up the tabbed widget item that
contains the flowchart shapes. An exclusive QButtonGroup always
keeps one button checked; we want the group to allow all buttons
to be unchecked.
We still use a button group since we can associate user
data, which we use to store the diagram type, with each button.
The \c createCellWidget() function sets up the buttons in the
tabbed widget item and is examined later.
The buttons of the background tabbed widget item is set up in the
same way, so we skip to the creation of the tool box:
\snippet graphicsview/diagramscene/mainwindow.cpp 22
We set the preferred size of the toolbox as its maximum. This
way, more space is given to the graphics view.
Here is the \c createActions() function:
\snippet graphicsview/diagramscene/mainwindow.cpp 23
We show an example of the creation of an action. The
functionality the actions trigger is discussed in the slots we
connect the actions to. You can see the \l{Application
Example}{application example} if you need a high-level
introduction to actions.
The is the \c createMenus() function:
\snippet graphicsview/diagramscene/mainwindow.cpp 24
We create the three menus' of the example.
The \c createToolbars() function sets up the examples tool
bars. The three \l{QToolButton}s in the \c colorToolBar, the \c
fontColorToolButton, \c fillColorToolButton, and \c
lineColorToolButton, are interesting as we create icons for them
by drawing on a QPixmap with a QPainter. We show how the \c
fillColorToolButton is created. This button lets the user select a
color for the diagram items.
\snippet graphicsview/diagramscene/mainwindow.cpp 25
\dots
\snippet graphicsview/diagramscene/mainwindow.cpp 26
We set the menu of the tool button with
\l{QToolButton::}{setMenu()}. We need the \c fillAction QAction
object to always be pointing to the selected action of the menu.
The menu is created with the \c createColorMenu() function and, as
we shall see later, contains one menu item for each color that the
items can have. When the user presses the button, which trigger
the \l{QToolButton::}{clicked()} signal, we can set the color of
the selected item to the color of \c fillAction. It is with \c
createColorToolButtonIcon() we create the icon for the button.
\dots
\snippet graphicsview/diagramscene/mainwindow.cpp 27
Here is the \c createBackgroundCellWidget() function:
\snippet graphicsview/diagramscene/mainwindow.cpp 28
This function creates \l{QWidget}s containing a tool button
and a label. The widgets created with this function are used for
the background tabbed widget item in the tool box.
Here is the \c createCellWidget() function:
\snippet graphicsview/diagramscene/mainwindow.cpp 29
This function returns a QWidget containing a QToolButton with
an image of one of the \c DiagramItems, i.e., flowchart shapes.
The image is created by the \c DiagramItem through the \c image()
function. The QButtonGroup class lets us attach an id (int) with
each button; we store the diagram's type, i.e., the
DiagramItem::DiagramType enum. We use the stored diagram type when
we create new diagram items for the scene. The widgets created
with this function is used in the tool box.
Here is the \c createColorMenu() function:
\snippet graphicsview/diagramscene/mainwindow.cpp 30
This function creates a color menu that is used as the
drop-down menu for the tool buttons in the \c colorToolBar. We
create an action for each color that we add to the menu. We fetch
the actions data when we set the color of items, lines, and text.
Here is the \c createColorToolButtonIcon() function:
\snippet graphicsview/diagramscene/mainwindow.cpp 31
This function is used to create the QIcon of the \c
fillColorToolButton, \c fontColorToolButton, and \c
lineColorToolButton. The \a imageFile string is either the text,
flood-fill, or line symbol that is used for the buttons. Beneath
the image we draw a filled rectangle using \a color.
Here is the \c createColorIcon() function:
\snippet graphicsview/diagramscene/mainwindow.cpp 32
This function creates an icon with a filled rectangle in the
color of \a color. It is used for creating icons for the color
menus in the \c fillColorToolButton, \c fontColorToolButton, and
\c lineColorToolButton.
Here is the \c backgroundButtonGroupClicked() slot:
\snippet graphicsview/diagramscene/mainwindow.cpp 1
In this function we set the QBrush that is used to draw the
background of the diagramscene. The background can be a grid of
squares of blue, gray, or white tiles, or no grid at all. We have
\l{QPixmap}s of the tiles from png files that we create the brush
with.
When one of the buttons in the background tabbed widget item is
clicked we change the brush; we find out which button it is by
checking its text.
Here is the implementation of \c buttonGroupClicked():
\snippet graphicsview/diagramscene/mainwindow.cpp 2
This slot is called when a button in \c buttonGroup is checked.
When a button is checked the user can click on the graphics view
and a \c DiagramItem of the selected type will be inserted into
the \c DiagramScene. We must loop through the buttons in the group
to uncheck other buttons as only one button is allowed to be
checked at a time.
\c QButtonGroup assigns an id to each button. We have set the id
of each button to the diagram type, as given by DiagramItem::DiagramType
that will be inserted into the scene when it is clicked. We can
then use the button id when we set the diagram type with
\c setItemType(). In the case of text we assigned an id that has a
value that is not in the DiagramType enum.
Here is the implementation of \c deleteItem():
\snippet graphicsview/diagramscene/mainwindow.cpp 3
This slot deletes the selected item, if any, from the scene. It
deletes the arrows first in order to avoid to delete them twice. If
the item to be deleted is a \c DiagramItem, we also need to delete
arrows connected to it; we don't want arrows in the scene that
aren't connected to items in both ends.
This is the implementation of pointerGroupClicked():
\snippet graphicsview/diagramscene/mainwindow.cpp 4
The \c pointerTypeGroup decides whether the scene is in ItemMove
or InsertLine mode. This button group is exclusive, i.e., only
one button is checked at any time. As with the \c buttonGroup above
we have assigned an id to the buttons that matches values of the
DiagramScene::Mode enum, so that we can use the id to set the
correct mode.
Here is the \c bringToFront() slot:
\snippet graphicsview/diagramscene/mainwindow.cpp 5
Several items may collide, i.e., overlap, with each other in
the scene. This slot is called when the user requests that an
item should be placed on top of the items it collides with.
\l{QGraphicsItem}{QGrapicsItems} have a z-value that decides the
order in which items are stacked in the scene; you can think of it
as the z-axis in a 3D coordinate system. When items collide the
items with higher z-values will be drawn on top of items with
lower values. When we bring an item to the front we can loop
through the items it collides with and set a z-value that is
higher than all of them.
Here is the \c sendToBack() slot:
\snippet graphicsview/diagramscene/mainwindow.cpp 6
This slot works in the same way as \c bringToFront() described
above, but sets a z-value that is lower than items the item that
should be send to the back collides with.
This is the implementation of \c itemInserted():
\snippet graphicsview/diagramscene/mainwindow.cpp 7
This slot is called from the \c DiagramScene when an item has been
added to the scene. We set the mode of the scene back to the mode
before the item was inserted, which is ItemMove or InsertText
depending on which button is checked in the \c pointerTypeGroup.
We must also uncheck the button in the in the \c buttonGroup.
Here is the implementation of \c textInserted():
\snippet graphicsview/diagramscene/mainwindow.cpp 8
We simply set the mode of the scene back to the mode it had before
the text was inserted.
Here is the \c currentFontChanged() slot:
\snippet graphicsview/diagramscene/mainwindow.cpp 9
When the user requests a font change, by using one of the
widgets in the \c fontToolBar, we create a new QFont object and
set its properties to match the state of the widgets. This is done
in \c handleFontChange(), so we simply call that slot.
Here is the \c fontSizeChanged() slot:
\snippet graphicsview/diagramscene/mainwindow.cpp 10
When the user requests a font change, by using one of the
widgets in the \c fontToolBar, we create a new QFont object and
set its properties to match the state of the widgets. This is done
in \c handleFontChange(), so we simply call that slot.
Here is the implementation of \c sceneScaleChanged():
\snippet graphicsview/diagramscene/mainwindow.cpp 11
The user can increase or decrease the scale, with the \c
sceneScaleCombo, the scene is drawn in.
It is not the scene itself that changes its scale, but only the
view.
Here is the \c textColorChanged() slot:
\snippet graphicsview/diagramscene/mainwindow.cpp 12
This slot is called when an item in the drop-down menu of the \c
fontColorToolButton is pressed. We need to change the icon on
the button to the color of the selected QAction. We keep a pointer
to the selected action in \c textAction. It is in \c
textButtonTriggered() we change the text color to the color of \c
textAction, so we call that slot.
Here is the \c itemColorChanged() implementation:
\snippet graphicsview/diagramscene/mainwindow.cpp 13
This slot handles requests for changing the color of \c
DiagramItems in the same manner as \c textColorChanged() does for
\c DiagramTextItems.
Here is the implementation of \c lineColorChanged():
\snippet graphicsview/diagramscene/mainwindow.cpp 14
This slot handles requests for changing the color of \c Arrows in
the same manner that \c textColorChanged() does it for \c
DiagramTextItems.
Here is the \c textButtonTriggered() slot:
\snippet graphicsview/diagramscene/mainwindow.cpp 15
\c textAction points to the QAction of the currently selected menu item
in the \c fontColorToolButton's color drop-down menu. We have set
the data of the action to the QColor the action represents, so we
can simply fetch this when we set the color of text with \c
setTextColor().
Here is the \c fillButtonTriggered() slot:
\snippet graphicsview/diagramscene/mainwindow.cpp 16
\c fillAction points to the selected menu item in the drop-down
menu of \c fillColorToolButton(). We can therefore use the data of
this action when we set the item color with \c setItemColor().
Here is the \c lineButtonTriggered() slot:
\snippet graphicsview/diagramscene/mainwindow.cpp 17
\c lineAction point to the selected item in the drop-down menu of
\c lineColorToolButton. We use its data when we set the arrow
color with \c setLineColor().
Here is the \c handleFontChange() function:
\snippet graphicsview/diagramscene/mainwindow.cpp 18
\c handleFontChange() is called when any of the widgets that show
font properties changes. We create a new QFont object and set its
properties based on the widgets. We then call the \c setFont()
function of \c DiagramScene; it is the scene that set the font of
the \c DiagramTextItems it manages.
Here is the \c itemSelected() slot:
\snippet graphicsview/diagramscene/mainwindow.cpp 19
This slot is called when an item in the \c DiagramScene is
selected. In the case of this example it is only text items that
emit signals when they are selected, so we do not need to check
what kind of graphics \a item is.
We set the state of the widgets to match the properties of the
font of the selected text item.
This is the \c about() slot:
\snippet graphicsview/diagramscene/mainwindow.cpp 20
This slot displays an about box for the example when the user
selects the about menu item from the help menu.
\section1 DiagramScene Class Definition
The \c DiagramScene class inherits QGraphicsScene and adds
functionality to handle \c DiagramItems, \c Arrows, and \c
DiagramTextItems in addition to the items handled by its super
class.
\snippet graphicsview/diagramscene/diagramscene.h 0
In the \c DiagramScene a mouse click can give three different
actions: the item under the mouse can be moved, an item may be
inserted, or an arrow may be connected between to diagram items.
Which action a mouse click has depends on the mode, given by the
Mode enum, the scene is in. The mode is set with the \c setMode()
function.
The scene also sets the color of its items and the font of its
text items. The colors and font used by the scene can be set with
the \c setLineColor(), \c setTextColor(), \c setItemColor() and \c
setFont() functions. The type of \c DiagramItem, given by the
DiagramItem::DiagramType function, to be created when an item is
inserted is set with the \c setItemType() slot.
The \c MainWindow and \c DiagramScene share responsibility for
the examples functionality. \c MainWindow handles the following
tasks: the deletion of items, text, and arrows; moving diagram
items to the back and front; and setting the scale of the scene.
\section1 DiagramScene Class Implementation
We start with the constructor:
\snippet graphicsview/diagramscene/diagramscene.cpp 0
The scene uses \c myItemMenu to set the context menu when it
creates \c DiagramItems. We set the default mode to \c
DiagramScene::MoveItem as this gives the default behavior of
QGraphicsScene.
Here is the \c setLineColor() function:
\snippet graphicsview/diagramscene/diagramscene.cpp 1
The \c isItemChange function returns true if an \c Arrow item is
selected in the scene in which case we want to change its color.
When the \c DiagramScene creates and adds new arrows to the scene
it will also use the new \a color.
Here is the \c setTextColor() function:
\snippet graphicsview/diagramscene/diagramscene.cpp 2
This function sets the color of \c DiagramTextItems equal to the
way \c setLineColor() sets the color of \c Arrows.
Here is the \c setItemColor() function:
\snippet graphicsview/diagramscene/diagramscene.cpp 3
This function sets the color the scene will use when creating
\c DiagramItems. It also changes the color of a selected \c
DiagramItem.
This is the implementation of \c setFont():
\snippet graphicsview/diagramscene/diagramscene.cpp 4
Set the font to use for new and selected, if a text item is
selected, \c DiagramTextItems.
This is the implementation of \c editorLostFocus() slot:
\snippet graphicsview/diagramscene/diagramscene.cpp 5
\c DiagramTextItems emit a signal when they lose focus, which is
connected to this slot. We remove the item if it has no text.
If not, we would leak memory and confuse the user as the items
will be edited when pressed on by the mouse.
The \c mousePressEvent() function handles mouse press event's
different depending on which mode the \c DiagramScene is in. We
examine its implementation for each mode:
\snippet graphicsview/diagramscene/diagramscene.cpp 6
We simply create a new \c DiagramItem and add it to the scene at
the position the mouse was pressed. Note that the origin of its
local coordinate system will be under the mouse pointer position.
\snippet graphicsview/diagramscene/diagramscene.cpp 7
The user adds \c Arrows to the scene by stretching a line between
the items the arrow should connect. The start of the line is fixed
in the place the user clicked the mouse and the end follows the
mouse pointer as long as the button is held down. When the user
releases the mouse button an \c Arrow will be added to the scene
if there is a \c DiagramItem under the start and end of the line.
We will see how this is implemented later; here we simply add the
line.
\snippet graphicsview/diagramscene/diagramscene.cpp 8
The \c DiagramTextItem is editable when the
Qt::TextEditorInteraction flag is set, else it is movable by the
mouse. We always want the text to be drawn on top of the other
items in the scene, so we set the value to a number higher
than other items in the scene.
\snippet graphicsview/diagramscene/diagramscene.cpp 9
We are in MoveItem mode if we get to the default switch; we
can then call the QGraphicsScene implementation, which
handles movement of items with the mouse. We make this call even
if we are in another mode making it possible to add an item and
then keep the mouse button pressed down and start moving
the item. In the case of text items, this is not possible as they
do not propagate mouse events when they are editable.
This is the \c mouseMoveEvent() function:
\snippet graphicsview/diagramscene/diagramscene.cpp 10
We must draw the line if we are in InsertMode and the mouse button
is pressed down (the line is not 0). As discussed in \c
mousePressEvent() the line is drawn from the position the mouse
was pressed to the current position of the mouse.
If we are in MoveItem mode, we call the QGraphicsScene
implementation, which handles movement of items.
In the \c mouseReleaseEvent() function we need to check if an arrow
should be added to the scene:
\snippet graphicsview/diagramscene/diagramscene.cpp 11
First we need to get the items (if any) under the line's start
and end points. The line itself is the first item at these points,
so we remove it from the lists. As a precaution, we check if the
lists are empty, but this should never happen.
\snippet graphicsview/diagramscene/diagramscene.cpp 12
Now we check if there are two different \c DiagramItems under
the lines start and end points. If there are we can create an \c
Arrow with the two items. The arrow is then added to each item and
finally the scene. The arrow must be updated to adjust its start
and end points to the items. We set the z-value of the arrow to
-1000.0 because we always want it to be drawn under the items.
\snippet graphicsview/diagramscene/diagramscene.cpp 13
Here is the \c isItemChange() function:
\snippet graphicsview/diagramscene/diagramscene.cpp 14
The scene has single selection, i.e., only one item can be
selected at any given time. The for loop will then loop one time
with the selected item or none if no item is selected. \c
isItemChange() is used to check whether a selected item exists
and also is of the specified diagram \a type.
\section1 DiagramItem Class Definition
\snippet graphicsview/diagramscene/diagramitem.h 0
The \c DiagramItem represents a flowchart shape in the \c
DiagramScene. It inherits QGraphicsPolygonItem and has a polygon
for each shape. The enum DiagramType has a value for each of the
flowchart shapes.
The class has a list of the arrows that are connected to it.
This is necessary because only the item knows when it is being
moved (with the \c itemChanged() function) at which time the
arrows must be updated. The item can also draw itself onto a
QPixmap with the \c image() function. This is used for the tool
buttons in \c MainWindow, see \c createColorToolButtonIcon() in
\c MainWindow.
The Type enum is a unique identifier of the class. It is used by
\c qgraphicsitem_cast(), which does dynamic casts of graphics
items. The UserType constant is the minimum value a custom
graphics item type can be.
\section1 DiagramItem Class Implementation
We start with a look at the constructor:
\snippet graphicsview/diagramscene/diagramitem.cpp 0
In the constructor we create the items polygon according to
\a diagramType. \l{QGraphicsItem}s are not movable or selectable
by default, so we must set these properties.
Here is the \c removeArrow() function:
\snippet graphicsview/diagramscene/diagramitem.cpp 1
\c removeArrow() is used to remove \c Arrow items when they
or \c DiagramItems they are connected to are removed from the
scene.
Here is the \c removeArrows() function:
\snippet graphicsview/diagramscene/diagramitem.cpp 2
This function is called when the item is removed from the scene
and removes all arrows that are connected to this item. The arrow
must be removed from the \c arrows list of both its start and end
item. Since either the start or the end item is the object where
this function is currently called, we have to make sure to work on
a copy of arrows since removeArrow() is modifying this container.
Here is the \c addArrow() function:
\snippet graphicsview/diagramscene/diagramitem.cpp 3
This function simply adds the \a arrow to the items \c arrows list.
Here is the \c image() function:
\snippet graphicsview/diagramscene/diagramitem.cpp 4
This function draws the polygon of the item onto a QPixmap. In
this example we use this to create icons for the tool buttons in
the tool box.
Here is the \c contextMenuEvent() function:
\snippet graphicsview/diagramscene/diagramitem.cpp 5
We show the context menu. As right mouse clicks, which shows the
menu, don't select items by default we set the item selected with
\l{QGraphicsItem::}{setSelected()}. This is necessary since an
item must be selected to change its elevation with the
\c bringToFront and \c sendToBack actions.
This is the implementation of \c itemChange():
\snippet graphicsview/diagramscene/diagramitem.cpp 6
If the item has moved, we need to update the positions of the
arrows connected to it. The implementation of QGraphicsItem does
nothing, so we just return \a value.
\section1 DiagramTextItem Class Definition
The \c TextDiagramItem class inherits QGraphicsTextItem and
adds the possibility to move editable text items. Editable
QGraphicsTextItems are designed to be fixed in place and editing
starts when the user single clicks on the item. With \c
DiagramTextItem the editing starts with a double click leaving
single click available to interact with and move it.
\snippet graphicsview/diagramscene/diagramtextitem.h 0
We use \c itemChange() and \c focusOutEvent() to notify the
\c DiagramScene when the text item loses focus and gets selected.
We reimplement the functions that handle mouse events to make it
possible to alter the mouse behavior of QGraphicsTextItem.
\section1 DiagramTextItem Implementation
We start with the constructor:
\snippet graphicsview/diagramscene/diagramtextitem.cpp 0
We simply set the item movable and selectable, as these flags are
off by default.
Here is the \c itemChange() function:
\snippet graphicsview/diagramscene/diagramtextitem.cpp 1
When the item is selected we emit the selectedChanged signal. The
\c MainWindow uses this signal to update the widgets that display
font properties to the font of the selected text item.
Here is the \c focusOutEvent() function:
\snippet graphicsview/diagramscene/diagramtextitem.cpp 2
\c DiagramScene uses the signal emitted when the text item loses
focus to remove the item if it is empty, i.e., it contains no
text.
This is the implementation of \c mouseDoubleClickEvent():
\snippet graphicsview/diagramscene/diagramtextitem.cpp 5
When we receive a double click event, we make the item editable by calling
QGraphicsTextItem::setTextInteractionFlags(). We then forward the
double-click to the item itself.
\section1 Arrow Class Definition
The \c Arrow class is a graphics item that connects two \c
DiagramItems. It draws an arrow head to one of the items. To
achieve this the item needs to paint itself and also re implement
methods used by the graphics scene to check for collisions and
selections. The class inherits QGraphicsLine item, and draws the
arrowhead and moves with the items it connects.
\snippet graphicsview/diagramscene/arrow.h 0
The item's color can be set with \c setColor().
\c boundingRect() and \c shape() are reimplemented
from QGraphicsLineItem and are used by the scene
to check for collisions and selections.
Calling \c updatePosition() causes the arrow to recalculate its
position and arrow head angle. \c paint() is reimplemented so that
we can paint an arrow rather than just a line between items.
\c myStartItem and \c myEndItem are the diagram items that the
arrow connects. The arrow is drawn with its head to the end item.
\c arrowHead is a polygon with three vertices's we use to draw the
arrow head.
\section1 Arrow Class Implementation
The constructor of the \c Arrow class looks like this:
\snippet graphicsview/diagramscene/arrow.cpp 0
We set the start and end diagram items of the arrow. The arrow
head will be drawn where the line intersects the end item.
Here is the \c boundingRect() function:
\snippet graphicsview/diagramscene/arrow.cpp 1
We need to reimplement this function because the arrow is
larger than the bounding rectangle of the QGraphicsLineItem. The
graphics scene uses the bounding rectangle to know which regions
of the scene to update.
Here is the \c shape() function:
\snippet graphicsview/diagramscene/arrow.cpp 2
The shape function returns a QPainterPath that is the exact
shape of the item. The QGraphicsLineItem::shape() returns a path
with a line drawn with the current pen, so we only need to add
the arrow head. This function is used to check for collisions and
selections with the mouse.
Here is the \c updatePosition() slot:
\snippet graphicsview/diagramscene/arrow.cpp 3
This slot updates the arrow by setting the start and end
points of its line to the center of the items it connects.
Here is the \c paint() function:
\snippet graphicsview/diagramscene/arrow.cpp 4
If the start and end items collide we do not draw the arrow; the
algorithm we use to find the point the arrow should be drawn at
may fail if the items collide.
We first set the pen and brush we will use for drawing the arrow.
\snippet graphicsview/diagramscene/arrow.cpp 5
We then need to find the position at which to draw the
arrowhead. The head should be drawn where the line and the end
item intersects. This is done by taking the line between each
point in the polygon and check if it intersects with the line of
the arrow. Since the line start and end points are set to the
center of the items the arrow line should intersect one and only
one of the lines of the polygon. Note that the points in the
polygon are relative to the local coordinate system of the item.
We must therefore add the position of the end item to make the
coordinates relative to the scene.
\snippet graphicsview/diagramscene/arrow.cpp 6
We calculate the angle between the x-axis and the line of the
arrow. We need to turn the arrow head to this angle so that it
follows the direction of the arrow. If the angle is negative we
must turn the direction of the arrow.
We can then calculate the three points of the arrow head polygon.
One of the points is the end of the line, which now is the
intersection between the arrow line and the end polygon. Then we
clear the \c arrowHead polygon from the previous calculated arrow
head and set these new points.
\snippet graphicsview/diagramscene/arrow.cpp 7
If the line is selected, we draw two dotted lines that are
parallel with the line of the arrow. We do not use the default
implementation, which uses \l{QGraphicsItem::}{boundingRect()}
because the QRect bounding rectangle is considerably larger than
the line.
*/