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