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