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