QGIS API Documentation 3.29.0-Master (006c3c0232)
qgsmodelgraphicsview.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsmodelgraphicsview.cpp
3 ----------------------------------
4 Date : March 2020
5 Copyright : (C) 2020 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 "qgssettings.h"
18#include "qgsmodelviewtool.h"
28#include "qgsxmlutils.h"
30#include <QDragEnterEvent>
31#include <QScrollBar>
32#include <QApplication>
33#include <QClipboard>
34#include <QMimeData>
35#include <QTimer>
36
38
39#define MIN_VIEW_SCALE 0.05
40#define MAX_VIEW_SCALE 1000.0
41
42QgsModelGraphicsView::QgsModelGraphicsView( QWidget *parent )
43 : QGraphicsView( parent )
44{
45 setResizeAnchor( QGraphicsView::AnchorViewCenter );
46 setMouseTracking( true );
47 viewport()->setMouseTracking( true );
48 setAcceptDrops( true );
49
50 mSpacePanTool = new QgsModelViewToolTemporaryKeyPan( this );
51 mMidMouseButtonPanTool = new QgsModelViewToolTemporaryMousePan( this );
52 mSpaceZoomTool = new QgsModelViewToolTemporaryKeyZoom( this );
53
54 mSnapper.setSnapToGrid( true );
55}
56
57QgsModelGraphicsView::~QgsModelGraphicsView()
58{
59 emit willBeDeleted();
60}
61
62void QgsModelGraphicsView::dragEnterEvent( QDragEnterEvent *event )
63{
64 if ( event->mimeData()->hasText() || event->mimeData()->hasFormat( QStringLiteral( "application/x-vnd.qgis.qgis.algorithmid" ) ) )
65 event->acceptProposedAction();
66 else
67 event->ignore();
68}
69
70void QgsModelGraphicsView::dropEvent( QDropEvent *event )
71{
72 const QPointF dropPoint = mapToScene( event->pos() );
73 if ( event->mimeData()->hasFormat( QStringLiteral( "application/x-vnd.qgis.qgis.algorithmid" ) ) )
74 {
75 QByteArray data = event->mimeData()->data( QStringLiteral( "application/x-vnd.qgis.qgis.algorithmid" ) );
76 QDataStream stream( &data, QIODevice::ReadOnly );
77 QString algorithmId;
78 stream >> algorithmId;
79
80 QTimer::singleShot( 0, this, [this, dropPoint, algorithmId ]
81 {
82 emit algorithmDropped( algorithmId, dropPoint );
83 } );
84 event->accept();
85 }
86 else if ( event->mimeData()->hasText() )
87 {
88 const QString itemId = event->mimeData()->text();
89 QTimer::singleShot( 0, this, [this, dropPoint, itemId ]
90 {
91 emit inputDropped( itemId, dropPoint );
92 } );
93 event->accept();
94 }
95 else
96 {
97 event->ignore();
98 }
99}
100
101void QgsModelGraphicsView::dragMoveEvent( QDragMoveEvent *event )
102{
103 if ( event->mimeData()->hasText() || event->mimeData()->hasFormat( QStringLiteral( "application/x-vnd.qgis.qgis.algorithmid" ) ) )
104 event->acceptProposedAction();
105 else
106 event->ignore();
107}
108
109void QgsModelGraphicsView::wheelEvent( QWheelEvent *event )
110{
111 if ( !scene() )
112 return;
113
114 if ( mTool )
115 {
116 mTool->wheelEvent( event );
117 }
118
119 if ( !mTool || !event->isAccepted() )
120 {
121 event->accept();
122 wheelZoom( event );
123 }
124}
125
126void QgsModelGraphicsView::wheelZoom( QWheelEvent *event )
127{
128 //get mouse wheel zoom behavior settings
129 QgsSettings settings;
130 double zoomFactor = settings.value( QStringLiteral( "qgis/zoom_factor" ), 2 ).toDouble();
131
132 // "Normal" mouse have an angle delta of 120, precision mouses provide data faster, in smaller steps
133 zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * std::fabs( event->angleDelta().y() );
134
135 if ( event->modifiers() & Qt::ControlModifier )
136 {
137 //holding ctrl while wheel zooming results in a finer zoom
138 zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 20.0;
139 }
140
141 //calculate zoom scale factor
142 bool zoomIn = event->angleDelta().y() > 0;
143 double scaleFactor = ( zoomIn ? 1 / zoomFactor : zoomFactor );
144
145 //get current visible part of scene
146 QRect viewportRect( 0, 0, viewport()->width(), viewport()->height() );
147 QgsRectangle visibleRect = QgsRectangle( mapToScene( viewportRect ).boundingRect() );
148
149 //transform the mouse pos to scene coordinates
150 QPointF scenePoint = mapToScene( event->position().x(), event->position().y() );
151
152 //adjust view center
153 QgsPointXY oldCenter( visibleRect.center() );
154 QgsPointXY newCenter( scenePoint.x() + ( ( oldCenter.x() - scenePoint.x() ) * scaleFactor ),
155 scenePoint.y() + ( ( oldCenter.y() - scenePoint.y() ) * scaleFactor ) );
156 centerOn( newCenter.x(), newCenter.y() );
157
158 //zoom layout
159 if ( zoomIn )
160 {
161 scaleSafe( zoomFactor );
162 }
163 else
164 {
165 scaleSafe( 1 / zoomFactor );
166 }
167}
168
169void QgsModelGraphicsView::scaleSafe( double scale )
170{
171 double currentScale = transform().m11();
172 scale *= currentScale;
173 scale = std::clamp( scale, MIN_VIEW_SCALE, MAX_VIEW_SCALE );
174 setTransform( QTransform::fromScale( scale, scale ) );
175}
176
177QPointF QgsModelGraphicsView::deltaForKeyEvent( QKeyEvent *event )
178{
179 // increment used for cursor key item movement
180 double increment = 1.0;
181 if ( event->modifiers() & Qt::ShiftModifier )
182 {
183 //holding shift while pressing cursor keys results in a big step
184 increment = 10.0;
185 }
186 else if ( event->modifiers() & Qt::AltModifier )
187 {
188 //holding alt while pressing cursor keys results in a 1 pixel step
189 double viewScale = transform().m11();
190 if ( viewScale > 0 )
191 {
192 increment = 1 / viewScale;
193 }
194 }
195
196 double deltaX = 0;
197 double deltaY = 0;
198 switch ( event->key() )
199 {
200 case Qt::Key_Left:
201 deltaX = -increment;
202 break;
203 case Qt::Key_Right:
204 deltaX = increment;
205 break;
206 case Qt::Key_Up:
207 deltaY = -increment;
208 break;
209 case Qt::Key_Down:
210 deltaY = increment;
211 break;
212 default:
213 break;
214 }
215
216 return QPointF( deltaX, deltaY );
217}
218
219void QgsModelGraphicsView::mousePressEvent( QMouseEvent *event )
220{
221 if ( !modelScene() )
222 return;
223
224 if ( mTool )
225 {
226 std::unique_ptr<QgsModelViewMouseEvent> me( new QgsModelViewMouseEvent( this, event, mTool->flags() & QgsModelViewTool::FlagSnaps ) );
227 mTool->modelPressEvent( me.get() );
228 event->setAccepted( me->isAccepted() );
229 }
230
231 if ( !mTool || !event->isAccepted() )
232 {
233 if ( event->button() == Qt::MiddleButton )
234 {
235 // Pan layout with middle mouse button
236 setTool( mMidMouseButtonPanTool );
237 event->accept();
238 }
239 else
240 {
241 QGraphicsView::mousePressEvent( event );
242 }
243 }
244}
245
246void QgsModelGraphicsView::mouseReleaseEvent( QMouseEvent *event )
247{
248 if ( !modelScene() )
249 return;
250
251 if ( mTool )
252 {
253 std::unique_ptr<QgsModelViewMouseEvent> me( new QgsModelViewMouseEvent( this, event, mTool->flags() & QgsModelViewTool::FlagSnaps ) );
254 mTool->modelReleaseEvent( me.get() );
255 event->setAccepted( me->isAccepted() );
256 }
257
258 if ( !mTool || !event->isAccepted() )
259 QGraphicsView::mouseReleaseEvent( event );
260}
261
262void QgsModelGraphicsView::mouseMoveEvent( QMouseEvent *event )
263{
264 if ( !modelScene() )
265 return;
266
267 mMouseCurrentXY = event->pos();
268
269 QPointF cursorPos = mapToScene( mMouseCurrentXY );
270 if ( mTool )
271 {
272 std::unique_ptr<QgsModelViewMouseEvent> me( new QgsModelViewMouseEvent( this, event, false ) );
273 if ( mTool->flags() & QgsModelViewTool::FlagSnaps )
274 {
275 me->snapPoint();
276 }
277 if ( mTool->flags() & QgsModelViewTool::FlagSnaps )
278 {
279 //draw snapping point indicator
280 if ( me->isSnapped() )
281 {
282 cursorPos = me->snappedPoint();
283 if ( mSnapMarker )
284 {
285 mSnapMarker->setPos( me->snappedPoint() );
286 mSnapMarker->setVisible( true );
287 }
288 }
289 else if ( mSnapMarker )
290 {
291 mSnapMarker->setVisible( false );
292 }
293 }
294 mTool->modelMoveEvent( me.get() );
295 event->setAccepted( me->isAccepted() );
296 }
297
298 if ( !mTool || !event->isAccepted() )
299 QGraphicsView::mouseMoveEvent( event );
300}
301
302void QgsModelGraphicsView::mouseDoubleClickEvent( QMouseEvent *event )
303{
304 if ( !modelScene() )
305 return;
306
307 if ( mTool )
308 {
309 std::unique_ptr<QgsModelViewMouseEvent> me( new QgsModelViewMouseEvent( this, event, mTool->flags() & QgsModelViewTool::FlagSnaps ) );
310 mTool->modelDoubleClickEvent( me.get() );
311 event->setAccepted( me->isAccepted() );
312 }
313
314 if ( !mTool || !event->isAccepted() )
315 QGraphicsView::mouseDoubleClickEvent( event );
316}
317
318void QgsModelGraphicsView::keyPressEvent( QKeyEvent *event )
319{
320 if ( !modelScene() )
321 return;
322
323 if ( mTool )
324 {
325 mTool->keyPressEvent( event );
326 }
327
328 if ( mTool && event->isAccepted() )
329 return;
330
331 if ( event->key() == Qt::Key_Space && ! event->isAutoRepeat() )
332 {
333 if ( !( event->modifiers() & Qt::ControlModifier ) )
334 {
335 // Pan layout with space bar
336 setTool( mSpacePanTool );
337 }
338 else
339 {
340 //ctrl+space pressed, so switch to temporary keyboard based zoom tool
341 setTool( mSpaceZoomTool );
342 }
343 event->accept();
344 }
345 else if ( event->key() == Qt::Key_Left
346 || event->key() == Qt::Key_Right
347 || event->key() == Qt::Key_Up
348 || event->key() == Qt::Key_Down )
349 {
350 QgsModelGraphicsScene *s = modelScene();
351 const QList<QgsModelComponentGraphicItem *> itemList = s->selectedComponentItems();
352 if ( !itemList.empty() )
353 {
354 QPointF delta = deltaForKeyEvent( event );
355
356 itemList.at( 0 )->aboutToChange( tr( "Move Items" ) );
357 for ( QgsModelComponentGraphicItem *item : itemList )
358 {
359 item->moveComponentBy( delta.x(), delta.y() );
360 }
361 itemList.at( 0 )->changed();
362 }
363 event->accept();
364 }
365}
366
367void QgsModelGraphicsView::keyReleaseEvent( QKeyEvent *event )
368{
369 if ( !modelScene() )
370 return;
371
372 if ( mTool )
373 {
374 mTool->keyReleaseEvent( event );
375 }
376
377 if ( !mTool || !event->isAccepted() )
378 QGraphicsView::keyReleaseEvent( event );
379}
380
381void QgsModelGraphicsView::setModelScene( QgsModelGraphicsScene *scene )
382{
383 setScene( scene );
384
385 // IMPORTANT!
386 // previous snap markers, snap lines are owned by previous layout - so don't delete them here!
387 mSnapMarker = new QgsModelViewSnapMarker();
388 mSnapMarker->hide();
389 scene->addItem( mSnapMarker );
390}
391
392QgsModelGraphicsScene *QgsModelGraphicsView::modelScene() const
393{
394 return qobject_cast< QgsModelGraphicsScene * >( QgsModelGraphicsView::scene() );
395}
396
397QgsModelViewTool *QgsModelGraphicsView::tool()
398{
399 return mTool;
400}
401
402void QgsModelGraphicsView::setTool( QgsModelViewTool *tool )
403{
404 if ( !tool )
405 return;
406
407 if ( mTool )
408 {
409 mTool->deactivate();
410 disconnect( mTool, &QgsModelViewTool::itemFocused, this, &QgsModelGraphicsView::itemFocused );
411 }
412
413 // activate new tool before setting it - gives tools a chance
414 // to respond to whatever the current tool is
415 tool->activate();
416 mTool = tool;
417 connect( mTool, &QgsModelViewTool::itemFocused, this, &QgsModelGraphicsView::itemFocused );
418 emit toolSet( mTool );
419}
420
421void QgsModelGraphicsView::unsetTool( QgsModelViewTool *tool )
422{
423 if ( mTool && mTool == tool )
424 {
425 mTool->deactivate();
426 emit toolSet( nullptr );
427 setCursor( Qt::ArrowCursor );
428 }
429}
430
431QgsModelSnapper *QgsModelGraphicsView::snapper()
432{
433 return &mSnapper;
434}
435
436void QgsModelGraphicsView::startMacroCommand( const QString &text )
437{
438 emit macroCommandStarted( text );
439}
440
441void QgsModelGraphicsView::endMacroCommand()
442{
443 emit macroCommandEnded();
444}
445
446void QgsModelGraphicsView::snapSelected()
447{
448 QgsModelGraphicsScene *s = modelScene();
449 const QList<QgsModelComponentGraphicItem *> itemList = s->selectedComponentItems();
450 startMacroCommand( tr( "Snap Items" ) );
451 if ( !itemList.empty() )
452 {
453 bool prevSetting = mSnapper.snapToGrid();
454 mSnapper.setSnapToGrid( true );
455 for ( QgsModelComponentGraphicItem *item : itemList )
456 {
457 bool wasSnapped = false;
458 QRectF snapped = mSnapper.snapRectWithResize( item->mapRectToScene( item->itemRect( ) ), transform().m11(), wasSnapped );
459 if ( wasSnapped )
460 {
461 item->setItemRect( snapped );
462 }
463 }
464 mSnapper.setSnapToGrid( prevSetting );
465 }
466 endMacroCommand();
467}
468
469void QgsModelGraphicsView::copySelectedItems( QgsModelGraphicsView::ClipboardOperation operation )
470{
471 copyItems( modelScene()->selectedComponentItems(), operation );
472}
473
474void QgsModelGraphicsView::copyItems( const QList<QgsModelComponentGraphicItem *> &items, QgsModelGraphicsView::ClipboardOperation operation )
475{
476 if ( !modelScene() )
477 return;
478
479 QgsReadWriteContext context;
480 QDomDocument doc;
481 QDomElement documentElement = doc.createElement( QStringLiteral( "ModelComponentClipboard" ) );
482 if ( operation == ClipboardCut )
483 {
484 emit macroCommandStarted( tr( "Cut Items" ) );
485 emit beginCommand( QString() );
486 }
487
488 QList< QVariant > paramComponents;
489 QList< QVariant > groupBoxComponents;
490 QList< QVariant > algComponents;
491
492 QList< QgsModelComponentGraphicItem * > selectedCommentParents;
493 QList< QgsProcessingModelOutput > selectedOutputs;
494 QList< QgsProcessingModelOutput > selectedOutputsComments;
495 for ( QgsModelComponentGraphicItem *item : items )
496 {
497 if ( const QgsModelCommentGraphicItem *commentItem = dynamic_cast< QgsModelCommentGraphicItem * >( item ) )
498 {
499 selectedCommentParents << commentItem->parentComponentItem();
500 if ( const QgsModelOutputGraphicItem *outputItem = dynamic_cast< QgsModelOutputGraphicItem * >( commentItem->parentComponentItem() ) )
501 {
502 selectedOutputsComments << *( static_cast< const QgsProcessingModelOutput *>( outputItem->component() ) );
503 }
504 }
505 else if ( const QgsModelOutputGraphicItem *outputItem = dynamic_cast< QgsModelOutputGraphicItem * >( item ) )
506 {
507 selectedOutputs << *( static_cast< const QgsProcessingModelOutput *>( outputItem->component() ) );
508 }
509 }
510
511 for ( QgsModelComponentGraphicItem *item : items )
512 {
513 if ( const QgsProcessingModelParameter *param = dynamic_cast< QgsProcessingModelParameter * >( item->component() ) )
514 {
515 QgsProcessingModelParameter component = *param;
516
517 // was comment selected?
518 if ( !selectedCommentParents.contains( item ) )
519 {
520 // no, so drop comment
521 component.comment()->setDescription( QString() );
522 }
523
524 QVariantMap paramDef;
525 paramDef.insert( QStringLiteral( "component" ), component.toVariant() );
526 const QgsProcessingParameterDefinition *def = modelScene()->model()->parameterDefinition( component.parameterName() );
527 paramDef.insert( QStringLiteral( "definition" ), def->toVariantMap() );
528
529 paramComponents << paramDef;
530 }
531 else if ( QgsProcessingModelGroupBox *groupBox = dynamic_cast< QgsProcessingModelGroupBox * >( item->component() ) )
532 {
533 groupBoxComponents << groupBox->toVariant();
534 }
535 else if ( const QgsProcessingModelChildAlgorithm *alg = dynamic_cast< QgsProcessingModelChildAlgorithm * >( item->component() ) )
536 {
537 QgsProcessingModelChildAlgorithm childAlg = *alg;
538
539 // was comment selected?
540 if ( !selectedCommentParents.contains( item ) )
541 {
542 // no, so drop comment
543 childAlg.comment()->setDescription( QString() );
544 }
545
546 // don't copy outputs which weren't selected either
547 QMap<QString, QgsProcessingModelOutput> clipboardOutputs;
548 const QMap<QString, QgsProcessingModelOutput> existingOutputs = childAlg.modelOutputs();
549 for ( auto it = existingOutputs.constBegin(); it != existingOutputs.constEnd(); ++ it )
550 {
551 bool found = false;
552 for ( const QgsProcessingModelOutput &candidate : selectedOutputs )
553 {
554 if ( candidate.childId() == childAlg.childId() && candidate.name() == it.value().name() && candidate.childOutputName() == it.value().childOutputName() )
555 {
556 found = true;
557 break;
558 }
559 }
560 if ( found )
561 {
562 // should we also copy the comment?
563 bool commentFound = false;
564 for ( const QgsProcessingModelOutput &candidate : selectedOutputsComments )
565 {
566 if ( candidate.childId() == childAlg.childId() && candidate.name() == it.value().name() && candidate.childOutputName() == it.value().childOutputName() )
567 {
568 commentFound = true;
569 break;
570 }
571 }
572
573 QgsProcessingModelOutput output = it.value();
574 if ( !commentFound )
575 output.comment()->setDescription( QString() );
576
577 clipboardOutputs.insert( it.key(), output );
578 }
579 }
580 childAlg.setModelOutputs( clipboardOutputs );
581
582 algComponents << childAlg.toVariant();
583 }
584 }
585 QVariantMap components;
586 components.insert( QStringLiteral( "parameters" ), paramComponents );
587 components.insert( QStringLiteral( "groupboxes" ), groupBoxComponents );
588 components.insert( QStringLiteral( "algs" ), algComponents );
589 doc.appendChild( QgsXmlUtils::writeVariant( components, doc ) );
590 if ( operation == ClipboardCut )
591 {
592 emit deleteSelectedItems();
593 emit endCommand();
594 emit macroCommandEnded();
595 }
596
597 QMimeData *mimeData = new QMimeData;
598 mimeData->setData( QStringLiteral( "text/xml" ), doc.toByteArray() );
599 mimeData->setText( doc.toByteArray() );
600 QClipboard *clipboard = QApplication::clipboard();
601 clipboard->setMimeData( mimeData );
602}
603
604void QgsModelGraphicsView::pasteItems( QgsModelGraphicsView::PasteMode mode )
605{
606 if ( !modelScene() )
607 return;
608
609 QDomDocument doc;
610 QClipboard *clipboard = QApplication::clipboard();
611 if ( doc.setContent( clipboard->mimeData()->data( QStringLiteral( "text/xml" ) ) ) )
612 {
613 QDomElement docElem = doc.documentElement();
614 QVariantMap res = QgsXmlUtils::readVariant( docElem ).toMap();
615
616 if ( res.contains( QStringLiteral( "parameters" ) ) && res.contains( QStringLiteral( "algs" ) ) )
617 {
618 QPointF pt;
619 switch ( mode )
620 {
621 case PasteModeCursor:
622 case PasteModeInPlace:
623 {
624 // place items at cursor position
625 pt = mapToScene( mapFromGlobal( QCursor::pos() ) );
626 break;
627 }
628 case PasteModeCenter:
629 {
630 // place items in center of viewport
631 pt = mapToScene( viewport()->rect().center() );
632 break;
633 }
634 }
635
636 emit beginCommand( tr( "Paste Items" ) );
637
638 QRectF pastedBounds;
639
640 QList< QgsProcessingModelGroupBox > pastedGroups;
641 for ( const QVariant &v : res.value( QStringLiteral( "groupboxes" ) ).toList() )
642 {
643 QgsProcessingModelGroupBox box;
644 // don't restore the uuid -- we need them to be unique in the model
645 box.loadVariant( v.toMap(), true );
646
647 pastedGroups << box;
648
649 modelScene()->model()->addGroupBox( box );
650
651 if ( !pastedBounds.isValid( ) )
652 pastedBounds = QRectF( box.position() - QPointF( box.size().width() / 2.0, box.size().height() / 2.0 ), box.size() );
653 else
654 pastedBounds = pastedBounds.united( QRectF( box.position() - QPointF( box.size().width() / 2.0, box.size().height() / 2.0 ), box.size() ) );
655 }
656
657 QStringList pastedParameters;
658 for ( const QVariant &v : res.value( QStringLiteral( "parameters" ) ).toList() )
659 {
660 QVariantMap param = v.toMap();
661 QVariantMap componentDef = param.value( QStringLiteral( "component" ) ).toMap();
662 QVariantMap paramDef = param.value( QStringLiteral( "definition" ) ).toMap();
663
664 std::unique_ptr< QgsProcessingParameterDefinition > paramDefinition( QgsProcessingParameters::parameterFromVariantMap( paramDef ) );
665
666 QgsProcessingModelParameter p;
667 p.loadVariant( componentDef );
668
669 // we need a unique name for the parameter
670 QString name = p.parameterName();
671 QString description = paramDefinition->description();
672 int next = 1;
673 while ( modelScene()->model()->parameterDefinition( name ) )
674 {
675 next++;
676 name = QStringLiteral( "%1 (%2)" ).arg( p.parameterName() ).arg( next );
677 description = QStringLiteral( "%1 (%2)" ).arg( paramDefinition->description() ).arg( next );
678 }
679 paramDefinition->setName( name );
680 paramDefinition->setDescription( description );
681 p.setParameterName( name );
682
683 modelScene()->model()->addModelParameter( paramDefinition.release(), p );
684 pastedParameters << p.parameterName();
685
686 if ( !pastedBounds.isValid( ) )
687 pastedBounds = QRectF( p.position() - QPointF( p.size().width() / 2.0, p.size().height() / 2.0 ), p.size() );
688 else
689 pastedBounds = pastedBounds.united( QRectF( p.position() - QPointF( p.size().width() / 2.0, p.size().height() / 2.0 ), p.size() ) );
690
691 if ( !p.comment()->description().isEmpty() )
692 pastedBounds = pastedBounds.united( QRectF( p.comment()->position() - QPointF( p.comment()->size().width() / 2.0, p.comment()->size().height() / 2.0 ), p.comment()->size() ) );
693 }
694
695 QStringList pastedAlgorithms;
696 for ( const QVariant &v : res.value( QStringLiteral( "algs" ) ).toList() )
697 {
698 QgsProcessingModelChildAlgorithm alg;
699 alg.loadVariant( v.toMap() );
700
701 // ensure algorithm id is unique
702 if ( modelScene()->model()->childAlgorithms().contains( alg.childId() ) )
703 {
704 alg.generateChildId( *modelScene()->model() );
705 }
706 alg.reattach();
707
708 pastedAlgorithms << alg.childId();
709
710 if ( !pastedBounds.isValid( ) )
711 pastedBounds = QRectF( alg.position() - QPointF( alg.size().width() / 2.0, alg.size().height() / 2.0 ), alg.size() );
712 else
713 pastedBounds = pastedBounds.united( QRectF( alg.position() - QPointF( alg.size().width() / 2.0, alg.size().height() / 2.0 ), alg.size() ) );
714
715 if ( !alg.comment()->description().isEmpty() )
716 pastedBounds = pastedBounds.united( QRectF( alg.comment()->position() - QPointF( alg.comment()->size().width() / 2.0, alg.comment()->size().height() / 2.0 ), alg.comment()->size() ) );
717
718 const QMap<QString, QgsProcessingModelChildAlgorithm> existingAlgs = modelScene()->model()->childAlgorithms();
719
720 const QMap<QString, QgsProcessingModelOutput> outputs = alg.modelOutputs();
721 QMap<QString, QgsProcessingModelOutput> pastedOutputs;
722 for ( auto it = outputs.constBegin(); it != outputs.constEnd(); ++it )
723 {
724 QString name = it.value().name();
725 int next = 1;
726 bool unique = false;
727 while ( !unique )
728 {
729 unique = true;
730 for ( auto algIt = existingAlgs.constBegin(); algIt != existingAlgs.constEnd(); ++algIt )
731 {
732 const QMap<QString, QgsProcessingModelOutput> algOutputs = algIt->modelOutputs();
733 for ( auto outputIt = algOutputs.constBegin(); outputIt != algOutputs.constEnd(); ++outputIt )
734 {
735 if ( outputIt.value().name() == name )
736 {
737 unique = false;
738 break;
739 }
740 }
741 if ( !unique )
742 break;
743 }
744 if ( unique )
745 break;
746 next++;
747 name = QStringLiteral( "%1 (%2)" ).arg( it.value().name() ).arg( next );
748 }
749
750 QgsProcessingModelOutput newOutput = it.value();
751 newOutput.setName( name );
752 newOutput.setDescription( name );
753 pastedOutputs.insert( name, newOutput );
754
755 pastedBounds = pastedBounds.united( QRectF( newOutput.position() - QPointF( newOutput.size().width() / 2.0, newOutput.size().height() / 2.0 ), newOutput.size() ) );
756
757 if ( !alg.comment()->description().isEmpty() )
758 pastedBounds = pastedBounds.united( QRectF( newOutput.comment()->position() - QPointF( newOutput.comment()->size().width() / 2.0, newOutput.comment()->size().height() / 2.0 ), newOutput.comment()->size() ) );
759 }
760 alg.setModelOutputs( pastedOutputs );
761
762 modelScene()->model()->addChildAlgorithm( alg );
763 }
764
765 QPointF offset( 0, 0 );
766 switch ( mode )
767 {
768 case PasteModeInPlace:
769 break;
770
771 case PasteModeCursor:
772 case PasteModeCenter:
773 {
774 offset = pt - pastedBounds.topLeft();
775 break;
776 }
777 }
778
779 if ( !offset.isNull() )
780 {
781 for ( QgsProcessingModelGroupBox pastedGroup : std::as_const( pastedGroups ) )
782 {
783 pastedGroup.setPosition( pastedGroup.position() + offset );
784 modelScene()->model()->addGroupBox( pastedGroup );
785 }
786 for ( const QString &pastedParam : std::as_const( pastedParameters ) )
787 {
788 modelScene()->model()->parameterComponent( pastedParam ).setPosition( modelScene()->model()->parameterComponent( pastedParam ).position() + offset );
789 modelScene()->model()->parameterComponent( pastedParam ).comment()->setPosition( modelScene()->model()->parameterComponent( pastedParam ).comment()->position() + offset );
790 }
791 for ( const QString &pastedAlg : std::as_const( pastedAlgorithms ) )
792 {
793 modelScene()->model()->childAlgorithm( pastedAlg ).setPosition( modelScene()->model()->childAlgorithm( pastedAlg ).position() + offset );
794 modelScene()->model()->childAlgorithm( pastedAlg ).comment()->setPosition( modelScene()->model()->childAlgorithm( pastedAlg ).comment()->position() + offset );
795
796 const QMap<QString, QgsProcessingModelOutput> outputs = modelScene()->model()->childAlgorithm( pastedAlg ).modelOutputs();
797 for ( auto it = outputs.begin(); it != outputs.end(); ++it )
798 {
799 modelScene()->model()->childAlgorithm( pastedAlg ).modelOutput( it.key() ).setPosition( modelScene()->model()->childAlgorithm( pastedAlg ).modelOutput( it.key() ).position() + offset );
800 modelScene()->model()->childAlgorithm( pastedAlg ).modelOutput( it.key() ).comment()->setPosition( modelScene()->model()->childAlgorithm( pastedAlg ).modelOutput( it.key() ).comment()->position() + offset );
801 }
802 }
803 }
804
805 emit endCommand();
806 }
807 }
808
809 modelScene()->rebuildRequired();
810}
811
812QgsModelViewSnapMarker::QgsModelViewSnapMarker()
813 : QGraphicsRectItem( QRectF( 0, 0, 0, 0 ) )
814{
815 QFont f;
816 QFontMetrics fm( f );
817 mSize = fm.horizontalAdvance( 'X' );
818 setPen( QPen( Qt::transparent, mSize ) );
819
820 setFlags( flags() | QGraphicsItem::ItemIgnoresTransformations );
821 setZValue( QgsModelGraphicsScene::ZSnapIndicator );
822}
823
824void QgsModelViewSnapMarker::paint( QPainter *p, const QStyleOptionGraphicsItem *, QWidget * )
825{
826 QPen pen( QColor( 255, 0, 0 ) );
827 pen.setWidth( 0 );
828 p->setPen( pen );
829 p->setBrush( Qt::NoBrush );
830
831 double halfSize = mSize / 2.0;
832 p->drawLine( QLineF( -halfSize, -halfSize, halfSize, halfSize ) );
833 p->drawLine( QLineF( -halfSize, halfSize, halfSize, -halfSize ) );
834}
835
836
838
839
Manages snapping grids and preset snap lines in a layout, and handles snapping points to the nearest ...
A QgsModelViewMouseEvent is the result of a user interaction with the mouse on a QgsModelGraphicsView...
Model designer view tool for temporarily panning a layout while a key is depressed.
Model view tool for temporarily zooming a model while a key is depressed.
Model view tool for temporarily panning a model while a mouse button is depressed.
Abstract base class for all model designer view tools.
@ FlagSnaps
Tool utilizes snapped coordinates.
void itemFocused(QgsModelComponentGraphicItem *item)
Emitted when an item is "focused" by the tool, i.e.
virtual void deactivate()
Called when tool is deactivated.
virtual void activate()
Called when tool is set as the currently active model tool.
A class to represent a 2D point.
Definition: qgspointxy.h:59
Base class for the definition of processing parameters.
void setDescription(const QString &description)
Sets the description for the parameter.
virtual QVariantMap toVariantMap() const
Saves this parameter to a QVariantMap.
static QgsProcessingParameterDefinition * parameterFromVariantMap(const QVariantMap &map)
Creates a new QgsProcessingParameterDefinition using the configuration from a supplied variant map.
The class is used as a container of context for various read/write operations on other objects.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
QgsPointXY center() const SIP_HOLDGIL
Returns the center point of the rectangle.
Definition: qgsrectangle.h:251
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
static QDomElement writeVariant(const QVariant &value, QDomDocument &doc)
Write a QVariant to a QDomElement.
static QVariant readVariant(const QDomElement &element)
Read a QVariant from a QDomElement.
#define MAX_VIEW_SCALE
#define MIN_VIEW_SCALE