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