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