QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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  QList< QgsModelComponentGraphicItem * > pastedItems;
614  QDomDocument doc;
615  QClipboard *clipboard = QApplication::clipboard();
616  if ( doc.setContent( clipboard->mimeData()->data( QStringLiteral( "text/xml" ) ) ) )
617  {
618  QDomElement docElem = doc.documentElement();
619  QVariantMap res = QgsXmlUtils::readVariant( docElem ).toMap();
620 
621  if ( res.contains( QStringLiteral( "parameters" ) ) && res.contains( QStringLiteral( "algs" ) ) )
622  {
623  QPointF pt;
624  switch ( mode )
625  {
626  case PasteModeCursor:
627  case PasteModeInPlace:
628  {
629  // place items at cursor position
630  pt = mapToScene( mapFromGlobal( QCursor::pos() ) );
631  break;
632  }
633  case PasteModeCenter:
634  {
635  // place items in center of viewport
636  pt = mapToScene( viewport()->rect().center() );
637  break;
638  }
639  }
640 
641  emit beginCommand( tr( "Paste Items" ) );
642 
643  QRectF pastedBounds;
644 
645  QList< QgsProcessingModelGroupBox > pastedGroups;
646  for ( const QVariant &v : res.value( QStringLiteral( "groupboxes" ) ).toList() )
647  {
648  QgsProcessingModelGroupBox box;
649  // don't restore the uuid -- we need them to be unique in the model
650  box.loadVariant( v.toMap(), true );
651 
652  pastedGroups << box;
653 
654  modelScene()->model()->addGroupBox( box );
655 
656  if ( !pastedBounds.isValid( ) )
657  pastedBounds = QRectF( box.position() - QPointF( box.size().width() / 2.0, box.size().height() / 2.0 ), box.size() );
658  else
659  pastedBounds = pastedBounds.united( QRectF( box.position() - QPointF( box.size().width() / 2.0, box.size().height() / 2.0 ), box.size() ) );
660  }
661 
662  QStringList pastedParameters;
663  for ( const QVariant &v : res.value( QStringLiteral( "parameters" ) ).toList() )
664  {
665  QVariantMap param = v.toMap();
666  QVariantMap componentDef = param.value( QStringLiteral( "component" ) ).toMap();
667  QVariantMap paramDef = param.value( QStringLiteral( "definition" ) ).toMap();
668 
669  std::unique_ptr< QgsProcessingParameterDefinition > paramDefinition( QgsProcessingParameters::parameterFromVariantMap( paramDef ) );
670 
671  QgsProcessingModelParameter p;
672  p.loadVariant( componentDef );
673 
674  // we need a unique name for the parameter
675  QString name = p.parameterName();
676  QString description = paramDefinition->description();
677  int next = 1;
678  while ( modelScene()->model()->parameterDefinition( name ) )
679  {
680  next++;
681  name = QStringLiteral( "%1 (%2)" ).arg( p.parameterName() ).arg( next );
682  description = QStringLiteral( "%1 (%2)" ).arg( paramDefinition->description() ).arg( next );
683  }
684  paramDefinition->setName( name );
685  paramDefinition->setDescription( description );
686  p.setParameterName( name );
687 
688  modelScene()->model()->addModelParameter( paramDefinition.release(), p );
689  pastedParameters << p.parameterName();
690 
691  if ( !pastedBounds.isValid( ) )
692  pastedBounds = QRectF( p.position() - QPointF( p.size().width() / 2.0, p.size().height() / 2.0 ), p.size() );
693  else
694  pastedBounds = pastedBounds.united( QRectF( p.position() - QPointF( p.size().width() / 2.0, p.size().height() / 2.0 ), p.size() ) );
695 
696  if ( !p.comment()->description().isEmpty() )
697  pastedBounds = pastedBounds.united( QRectF( p.comment()->position() - QPointF( p.comment()->size().width() / 2.0, p.comment()->size().height() / 2.0 ), p.comment()->size() ) );
698  }
699 
700  QStringList pastedAlgorithms;
701  for ( const QVariant &v : res.value( QStringLiteral( "algs" ) ).toList() )
702  {
703  QgsProcessingModelChildAlgorithm alg;
704  alg.loadVariant( v.toMap() );
705 
706  // ensure algorithm id is unique
707  alg.generateChildId( *modelScene()->model() );
708  alg.reattach();
709 
710  pastedAlgorithms << alg.childId();
711 
712  if ( !pastedBounds.isValid( ) )
713  pastedBounds = QRectF( alg.position() - QPointF( alg.size().width() / 2.0, alg.size().height() / 2.0 ), alg.size() );
714  else
715  pastedBounds = pastedBounds.united( QRectF( alg.position() - QPointF( alg.size().width() / 2.0, alg.size().height() / 2.0 ), alg.size() ) );
716 
717  if ( !alg.comment()->description().isEmpty() )
718  pastedBounds = pastedBounds.united( QRectF( alg.comment()->position() - QPointF( alg.comment()->size().width() / 2.0, alg.comment()->size().height() / 2.0 ), alg.comment()->size() ) );
719 
720  const QMap<QString, QgsProcessingModelChildAlgorithm> existingAlgs = modelScene()->model()->childAlgorithms();
721 
722  const QMap<QString, QgsProcessingModelOutput> outputs = alg.modelOutputs();
723  QMap<QString, QgsProcessingModelOutput> pastedOutputs;
724  for ( auto it = outputs.constBegin(); it != outputs.constEnd(); ++it )
725  {
726  QString name = it.value().name();
727  int next = 1;
728  bool unique = false;
729  while ( !unique )
730  {
731  unique = true;
732  for ( auto algIt = existingAlgs.constBegin(); algIt != existingAlgs.constEnd(); ++algIt )
733  {
734  const QMap<QString, QgsProcessingModelOutput> algOutputs = algIt->modelOutputs();
735  for ( auto outputIt = algOutputs.constBegin(); outputIt != algOutputs.constEnd(); ++outputIt )
736  {
737  if ( outputIt.value().name() == name )
738  {
739  unique = false;
740  break;
741  }
742  }
743  if ( !unique )
744  break;
745  }
746  if ( unique )
747  break;
748  next++;
749  name = QStringLiteral( "%1 (%2)" ).arg( it.value().name() ).arg( next );
750  }
751 
752  QgsProcessingModelOutput newOutput = it.value();
753  newOutput.setName( name );
754  newOutput.setDescription( name );
755  pastedOutputs.insert( name, newOutput );
756 
757  pastedBounds = pastedBounds.united( QRectF( newOutput.position() - QPointF( newOutput.size().width() / 2.0, newOutput.size().height() / 2.0 ), newOutput.size() ) );
758 
759  if ( !alg.comment()->description().isEmpty() )
760  pastedBounds = pastedBounds.united( QRectF( newOutput.comment()->position() - QPointF( newOutput.comment()->size().width() / 2.0, newOutput.comment()->size().height() / 2.0 ), newOutput.comment()->size() ) );
761  }
762  alg.setModelOutputs( pastedOutputs );
763 
764  modelScene()->model()->addChildAlgorithm( alg );
765  }
766 
767  QPointF offset( 0, 0 );
768  switch ( mode )
769  {
770  case PasteModeInPlace:
771  break;
772 
773  case PasteModeCursor:
774  case PasteModeCenter:
775  {
776  offset = pt - pastedBounds.topLeft();
777  break;
778  }
779  }
780 
781  if ( !offset.isNull() )
782  {
783  for ( QgsProcessingModelGroupBox pastedGroup : std::as_const( pastedGroups ) )
784  {
785  pastedGroup.setPosition( pastedGroup.position() + offset );
786  modelScene()->model()->addGroupBox( pastedGroup );
787  }
788  for ( const QString &pastedParam : std::as_const( pastedParameters ) )
789  {
790  modelScene()->model()->parameterComponent( pastedParam ).setPosition( modelScene()->model()->parameterComponent( pastedParam ).position() + offset );
791  modelScene()->model()->parameterComponent( pastedParam ).comment()->setPosition( modelScene()->model()->parameterComponent( pastedParam ).comment()->position() + offset );
792  }
793  for ( const QString &pastedAlg : std::as_const( pastedAlgorithms ) )
794  {
795  modelScene()->model()->childAlgorithm( pastedAlg ).setPosition( modelScene()->model()->childAlgorithm( pastedAlg ).position() + offset );
796  modelScene()->model()->childAlgorithm( pastedAlg ).comment()->setPosition( modelScene()->model()->childAlgorithm( pastedAlg ).comment()->position() + offset );
797 
798  const QMap<QString, QgsProcessingModelOutput> outputs = modelScene()->model()->childAlgorithm( pastedAlg ).modelOutputs();
799  for ( auto it = outputs.begin(); it != outputs.end(); ++it )
800  {
801  modelScene()->model()->childAlgorithm( pastedAlg ).modelOutput( it.key() ).setPosition( modelScene()->model()->childAlgorithm( pastedAlg ).modelOutput( it.key() ).position() + offset );
802  modelScene()->model()->childAlgorithm( pastedAlg ).modelOutput( it.key() ).comment()->setPosition( modelScene()->model()->childAlgorithm( pastedAlg ).modelOutput( it.key() ).comment()->position() + offset );
803  }
804  }
805  }
806 
807  emit endCommand();
808  }
809  }
810 
811  modelScene()->rebuildRequired();
812 }
813 
814 QgsModelViewSnapMarker::QgsModelViewSnapMarker()
815  : QGraphicsRectItem( QRectF( 0, 0, 0, 0 ) )
816 {
817  QFont f;
818  QFontMetrics fm( f );
819  mSize = fm.horizontalAdvance( 'X' );
820  setPen( QPen( Qt::transparent, mSize ) );
821 
822  setFlags( flags() | QGraphicsItem::ItemIgnoresTransformations );
823  setZValue( QgsModelGraphicsScene::ZSnapIndicator );
824 }
825 
826 void QgsModelViewSnapMarker::paint( QPainter *p, const QStyleOptionGraphicsItem *, QWidget * )
827 {
828  QPen pen( QColor( 255, 0, 0 ) );
829  pen.setWidth( 0 );
830  p->setPen( pen );
831  p->setBrush( Qt::NoBrush );
832 
833  double halfSize = mSize / 2.0;
834  p->drawLine( QLineF( -halfSize, -halfSize, halfSize, halfSize ) );
835  p->drawLine( QLineF( -halfSize, halfSize, halfSize, -halfSize ) );
836 }
837 
838 
840 
841 
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
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
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