QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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 
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