QGIS API Documentation 3.41.0-Master (57ec4277f5e)
Loading...
Searching...
No Matches
qgslayoutviewtooladdnodeitem.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayoutviewtooladdnodeitem.cpp
3 ----------------------------
4 Date : July 2017
5 Copyright : (C) 2017 Nyall Dawson
6 Email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17#include "moc_qgslayoutviewtooladdnodeitem.cpp"
18#include "qgsapplication.h"
19#include "qgslayoutview.h"
20#include "qgslayout.h"
23#include "qgslogger.h"
25#include "qgsgui.h"
28#include "qgssettings.h"
29#include "qgslayoututils.h"
31#include <QGraphicsRectItem>
32#include <QPen>
33#include <QBrush>
34#include <QMouseEvent>
35
42
44{
45 mItemMetadataId = metadataId;
46}
47
49{
50 if ( event->button() == Qt::LeftButton )
51 {
52 if ( !mRubberBand )
53 {
54 mPolygon.clear();
55 mRubberBand.reset( QgsGui::layoutItemGuiRegistry()->createNodeItemRubberBand( mItemMetadataId, view() ) );
56 if ( mRubberBand )
57 layout()->addItem( mRubberBand.get() );
58
59 // On first press, deselect the currently selected item, if any
60 view()->deselectAll();
61 }
62
63 if ( mRubberBand )
64 {
65 //add a new node
66 addNode( event->snappedPoint() );
67 }
68 }
69 else if ( event->button() == Qt::RightButton && mRubberBand )
70 {
71 // finish up
72
73 // last (temporary) point is removed
74 mPolygon.remove( mPolygon.count() - 1 );
75
76 std::unique_ptr<QgsLayoutItem> item( QgsGui::layoutItemGuiRegistry()->createItem( mItemMetadataId, layout() ) );
77 if ( !item )
78 return;
79
80 if ( QgsLayoutNodesItem *nodesItem = qobject_cast<QgsLayoutNodesItem *>( item.get() ) )
81 {
82 nodesItem->setNodes( mPolygon );
83 if ( !nodesItem->isValid() )
84 {
85 mRubberBand.reset();
86 return;
87 }
88 }
89 QgsLayoutItem *newItem = item.get();
90 layout()->addLayoutItem( item.release() );
91 layout()->setSelectedItem( newItem );
92 emit createdItem();
93 }
94 else
95 {
96 event->ignore();
97 mRubberBand.reset();
98 }
99}
100
102{
103 if ( mRubberBand )
104 {
105 moveTemporaryNode( event->snappedPoint(), event->modifiers() );
106 }
107 else
108 {
109 event->ignore();
110 }
111}
112
114{
115 if ( !mRubberBand )
116 {
117 event->ignore();
118 return;
119 }
120}
121
123{
124 if ( !mRubberBand || event->isAutoRepeat() )
125 {
126 event->ignore();
127 return;
128 }
129
130 if ( event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace )
131 {
132 if ( mPolygon.size() > 2 )
133 {
134 //remove last added vertex
135 mPolygon.pop_back();
136 setRubberBandNodes();
137 moveTemporaryNode( view()->mapToScene( view()->mapFromGlobal( QCursor::pos() ) ), event->modifiers() );
138 }
139 else
140 {
141 // all deleted, cancel
142 mRubberBand.reset();
143 }
144 }
145 else if ( event->key() == Qt::Key_Escape )
146 {
147 mRubberBand.reset();
148 }
149 else
150 {
151 event->ignore();
152 }
153}
154
156{
157 if ( mRubberBand )
158 {
159 // canceled mid operation
160 mRubberBand.reset();
161 }
163}
164
165void QgsLayoutViewToolAddNodeItem::addNode( QPointF scenePoint )
166{
167 mPolygon.append( scenePoint );
168
169 if ( mPolygon.size() == 1 )
170 mPolygon.append( scenePoint );
171
172 setRubberBandNodes();
173}
174
175void QgsLayoutViewToolAddNodeItem::moveTemporaryNode( QPointF scenePoint, Qt::KeyboardModifiers modifiers )
176{
177 if ( mPolygon.isEmpty() )
178 return;
179
180 if ( mPolygon.size() > 1 && ( modifiers & Qt::ShiftModifier ) )
181 {
182 const QPointF start = mPolygon.at( mPolygon.size() - 2 );
183 QLineF newLine = QLineF( start, scenePoint );
184
185 //movement is constrained to 45 degree angles
186 const double angle = QgsLayoutUtils::snappedAngle( newLine.angle() );
187 newLine.setAngle( angle );
188 scenePoint = newLine.p2();
189 }
190
191 mPolygon.replace( mPolygon.size() - 1, scenePoint );
192 setRubberBandNodes();
193}
194
195void QgsLayoutViewToolAddNodeItem::setRubberBandNodes()
196{
197 QList<QGraphicsItem *> items = mRubberBand->childItems();
198
199 // Rubber band is not a QGraphicsItem with children, but may be
200 // a custom QGraphicsPolygonItem / QGraphicsPathItem returned by a Python plugin
201 // In this case, directly append it to the list.
202 if ( items.isEmpty() )
203 {
204 items << mRubberBand.get();
205 }
206
207 if ( QGraphicsPolygonItem *polygonItem = dynamic_cast<QGraphicsPolygonItem *>( items[0] ) )
208 {
209 // The group contains two polygons
210 if ( items.size() == 2 && dynamic_cast<QGraphicsPolygonItem *>( items[1] ) != nullptr )
211 {
212 if ( mPolygon.size() > 3 )
213 {
214 polygonItem->setPolygon( QPolygonF( mPolygon.mid( 0, mPolygon.size() - 1 ) ) );
215 }
216 else
217 {
218 polygonItem->setPolygon( QPolygonF() );
219 }
220 dynamic_cast<QGraphicsPolygonItem *>( items[1] )->setPolygon( mPolygon );
221 }
222 // The group contains a single QGraphicsPolygonItem as rubberband
223 else
224 {
225 polygonItem->setPolygon( mPolygon );
226 }
227 }
228 else if ( QGraphicsPathItem *polylineItem = dynamic_cast<QGraphicsPathItem *>( items[0] ) )
229 {
230 // The group contains two polylines
231 if ( items.size() == 2 && dynamic_cast<QGraphicsPathItem *>( items[1] ) != nullptr )
232 {
233 if ( mPolygon.size() > 2 )
234 {
235 QPainterPath path;
236 path.addPolygon( QPolygonF( mPolygon.mid( 0, mPolygon.size() - 1 ) ) );
237 polylineItem->setPath( path );
238 }
239 else
240 {
241 polylineItem->setPath( QPainterPath() );
242 }
243 if ( mPolygon.size() > 1 )
244 {
245 QPainterPath path;
246 path.addPolygon( mPolygon.mid( mPolygon.size() - 2 ) );
247 dynamic_cast<QGraphicsPathItem *>( items[1] )->setPath( path );
248 }
249 }
250 // The group contains a single QGraphicsPathItem as rubberband
251 else
252 {
253 // rebuild a new qpainter path
254 QPainterPath path;
255 path.addPolygon( mPolygon );
256 polylineItem->setPath( path );
257 }
258 }
259}
260
262{
263 return mItemMetadataId;
264}
static QgsLayoutItemGuiRegistry * layoutItemGuiRegistry()
Returns the global layout item GUI registry, used for registering the GUI behavior of layout items.
Definition qgsgui.cpp:140
Base class for graphical items within a QgsLayout.
An abstract layout item that provides generic methods for node based shapes such as polygon or polyli...
static double snappedAngle(double angle)
Snaps an angle (in degrees) to its closest 45 degree angle.
A QgsLayoutViewMouseEvent is the result of a user interaction with the mouse on a QgsLayoutView.
QPointF snappedPoint() const
Returns the snapped event point location in layout coordinates.
void layoutMoveEvent(QgsLayoutViewMouseEvent *event) override
Mouse move event for overriding.
int itemMetadataId() const
Returns the item metadata id for items created by the tool.
void createdItem()
Emitted when an item has been created using the tool.
void layoutReleaseEvent(QgsLayoutViewMouseEvent *event) override
Mouse release event for overriding.
QgsLayoutViewToolAddNodeItem(QgsLayoutView *view)
Constructs a QgsLayoutViewToolAddNodeItem for the given layout view.
void setItemMetadataId(int metadataId)
Sets the item metadata metadataId for items created by the tool.
void keyPressEvent(QKeyEvent *event) override
Key press event for overriding.
void layoutPressEvent(QgsLayoutViewMouseEvent *event) override
Mouse press event for overriding.
void deactivate() override
Called when tool is deactivated.
Abstract base class for all layout view tools.
void setCursor(const QCursor &cursor)
Sets a user defined cursor for use when the tool is active.
QgsLayoutView * view() const
Returns the view associated with the tool.
virtual void deactivate()
Called when tool is deactivated.
void setFlags(QgsLayoutViewTool::Flags flags)
Sets the combination of flags that will be used for the tool.
@ FlagSnaps
Tool utilizes snapped coordinates.
QgsLayout * layout() const
Returns the layout associated with the tool.
A graphical widget to display and interact with QgsLayouts.
void deselectAll()
Deselects all items in the view.
void setSelectedItem(QgsLayoutItem *item)
Clears any selected items and sets item as the current selection.
void addLayoutItem(QgsLayoutItem *item)
Adds an item to the layout.
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)