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