QGIS API Documentation 3.41.0-Master (3440c17df1d)
Loading...
Searching...
No Matches
qgsmodelgraphicsview.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsmodelgraphicsview.cpp
3 ----------------------------------
4 Date : March 2020
5 Copyright : (C) 2020 Nyall Dawson
6 Email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17#include "moc_qgsmodelgraphicsview.cpp"
18#include "qgssettings.h"
19#include "qgsmodelviewtool.h"
29#include "qgsxmlutils.h"
31#include <QDragEnterEvent>
32#include <QScrollBar>
33#include <QApplication>
34#include <QClipboard>
35#include <QMimeData>
36#include <QTimer>
37
39
40#define MIN_VIEW_SCALE 0.05
41#define MAX_VIEW_SCALE 1000.0
42
43QgsModelGraphicsView::QgsModelGraphicsView( QWidget *parent )
44 : QGraphicsView( parent )
45{
46 setResizeAnchor( QGraphicsView::AnchorViewCenter );
47 setMouseTracking( true );
48 viewport()->setMouseTracking( true );
49 setAcceptDrops( true );
50
51 mSpacePanTool = new QgsModelViewToolTemporaryKeyPan( this );
52 mMidMouseButtonPanTool = new QgsModelViewToolTemporaryMousePan( this );
53 mSpaceZoomTool = new QgsModelViewToolTemporaryKeyZoom( this );
54
55 mSnapper.setSnapToGrid( true );
56}
57
58QgsModelGraphicsView::~QgsModelGraphicsView()
59{
60 emit willBeDeleted();
61}
62
63void QgsModelGraphicsView::dragEnterEvent( QDragEnterEvent *event )
64{
65 if ( event->mimeData()->hasText() || event->mimeData()->hasFormat( QStringLiteral( "application/x-vnd.qgis.qgis.algorithmid" ) ) )
66 event->acceptProposedAction();
67 else
68 event->ignore();
69}
70
71void QgsModelGraphicsView::dropEvent( QDropEvent *event )
72{
73 const QPointF dropPoint = mapToScene( event->pos() );
74 if ( event->mimeData()->hasFormat( QStringLiteral( "application/x-vnd.qgis.qgis.algorithmid" ) ) )
75 {
76 QByteArray data = event->mimeData()->data( QStringLiteral( "application/x-vnd.qgis.qgis.algorithmid" ) );
77 QDataStream stream( &data, QIODevice::ReadOnly );
78 QString algorithmId;
79 stream >> algorithmId;
80
81 QTimer::singleShot( 0, this, [this, dropPoint, algorithmId ]
82 {
83 emit algorithmDropped( algorithmId, dropPoint );
84 } );
85 event->accept();
86 }
87 else if ( event->mimeData()->hasText() )
88 {
89 const QString itemId = event->mimeData()->text();
90 QTimer::singleShot( 0, this, [this, dropPoint, itemId ]
91 {
92 emit inputDropped( itemId, dropPoint );
93 } );
94 event->accept();
95 }
96 else
97 {
98 event->ignore();
99 }
100}
101
102void QgsModelGraphicsView::dragMoveEvent( QDragMoveEvent *event )
103{
104 if ( event->mimeData()->hasText() || event->mimeData()->hasFormat( QStringLiteral( "application/x-vnd.qgis.qgis.algorithmid" ) ) )
105 event->acceptProposedAction();
106 else
107 event->ignore();
108}
109
110void QgsModelGraphicsView::wheelEvent( QWheelEvent *event )
111{
112 if ( !scene() )
113 return;
114
115 if ( mTool )
116 {
117 mTool->wheelEvent( event );
118 }
119
120 if ( !mTool || !event->isAccepted() )
121 {
122 event->accept();
123 wheelZoom( event );
124 }
125}
126
127void QgsModelGraphicsView::wheelZoom( QWheelEvent *event )
128{
129 //get mouse wheel zoom behavior settings
130 QgsSettings settings;
131 double zoomFactor = settings.value( QStringLiteral( "qgis/zoom_factor" ), 2 ).toDouble();
132 bool reverseZoom = settings.value( QStringLiteral( "qgis/reverse_wheel_zoom" ), false ).toBool();
133 bool zoomIn = reverseZoom ? event->angleDelta().y() < 0 : event->angleDelta().y() > 0;
134
135 // "Normal" mouse have an angle delta of 120, precision mouses provide data faster, in smaller steps
136 zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * std::fabs( event->angleDelta().y() );
137
138 if ( event->modifiers() & Qt::ControlModifier )
139 {
140 //holding ctrl while wheel zooming results in a finer zoom
141 zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 20.0;
142 }
143
144 //calculate zoom scale factor
145 double scaleFactor = ( zoomIn ? 1 / zoomFactor : zoomFactor );
146
147 //get current visible part of scene
148 QRect viewportRect( 0, 0, viewport()->width(), viewport()->height() );
149 QgsRectangle visibleRect = QgsRectangle( mapToScene( viewportRect ).boundingRect() );
150
151 //transform the mouse pos to scene coordinates
152 QPointF scenePoint = mapToScene( event->position().x(), event->position().y() );
153
154 //adjust view center
155 QgsPointXY oldCenter( visibleRect.center() );
156 QgsPointXY newCenter( scenePoint.x() + ( ( oldCenter.x() - scenePoint.x() ) * scaleFactor ),
157 scenePoint.y() + ( ( oldCenter.y() - scenePoint.y() ) * scaleFactor ) );
158 centerOn( newCenter.x(), newCenter.y() );
159
160 //zoom layout
161 if ( zoomIn )
162 {
163 scaleSafe( zoomFactor );
164 }
165 else
166 {
167 scaleSafe( 1 / zoomFactor );
168 }
169}
170
171void QgsModelGraphicsView::scaleSafe( double scale )
172{
173 double currentScale = transform().m11();
174 scale *= currentScale;
175 scale = std::clamp( scale, MIN_VIEW_SCALE, MAX_VIEW_SCALE );
176 setTransform( QTransform::fromScale( scale, scale ) );
177}
178
179QPointF QgsModelGraphicsView::deltaForKeyEvent( QKeyEvent *event )
180{
181 // increment used for cursor key item movement
182 double increment = 1.0;
183 if ( event->modifiers() & Qt::ShiftModifier )
184 {
185 //holding shift while pressing cursor keys results in a big step
186 increment = 10.0;
187 }
188 else if ( event->modifiers() & Qt::AltModifier )
189 {
190 //holding alt while pressing cursor keys results in a 1 pixel step
191 double viewScale = transform().m11();
192 if ( viewScale > 0 )
193 {
194 increment = 1 / viewScale;
195 }
196 }
197
198 double deltaX = 0;
199 double deltaY = 0;
200 switch ( event->key() )
201 {
202 case Qt::Key_Left:
203 deltaX = -increment;
204 break;
205 case Qt::Key_Right:
206 deltaX = increment;
207 break;
208 case Qt::Key_Up:
209 deltaY = -increment;
210 break;
211 case Qt::Key_Down:
212 deltaY = increment;
213 break;
214 default:
215 break;
216 }
217
218 return QPointF( deltaX, deltaY );
219}
220
221void QgsModelGraphicsView::mousePressEvent( QMouseEvent *event )
222{
223 if ( !modelScene() )
224 return;
225
226 if ( mTool )
227 {
228 std::unique_ptr<QgsModelViewMouseEvent> me( new QgsModelViewMouseEvent( this, event, mTool->flags() & QgsModelViewTool::FlagSnaps ) );
229 mTool->modelPressEvent( me.get() );
230 event->setAccepted( me->isAccepted() );
231 }
232
233 if ( !mTool || !event->isAccepted() )
234 {
235 if ( event->button() == Qt::MiddleButton )
236 {
237 // Pan layout with middle mouse button
238 setTool( mMidMouseButtonPanTool );
239 event->accept();
240 }
241 else
242 {
243 QGraphicsView::mousePressEvent( event );
244 }
245 }
246}
247
248void QgsModelGraphicsView::mouseReleaseEvent( QMouseEvent *event )
249{
250 if ( !modelScene() )
251 return;
252
253 if ( mTool )
254 {
255 std::unique_ptr<QgsModelViewMouseEvent> me( new QgsModelViewMouseEvent( this, event, mTool->flags() & QgsModelViewTool::FlagSnaps ) );
256 mTool->modelReleaseEvent( me.get() );
257 event->setAccepted( me->isAccepted() );
258 }
259
260 if ( !mTool || !event->isAccepted() )
261 QGraphicsView::mouseReleaseEvent( event );
262}
263
264void QgsModelGraphicsView::mouseMoveEvent( QMouseEvent *event )
265{
266 if ( !modelScene() )
267 return;
268
269 mMouseCurrentXY = event->pos();
270
271 QPointF cursorPos = mapToScene( mMouseCurrentXY );
272 if ( mTool )
273 {
274 std::unique_ptr<QgsModelViewMouseEvent> me( new QgsModelViewMouseEvent( this, event, false ) );
275 if ( mTool->flags() & QgsModelViewTool::FlagSnaps )
276 {
277 me->snapPoint();
278 }
279 if ( mTool->flags() & QgsModelViewTool::FlagSnaps )
280 {
281 //draw snapping point indicator
282 if ( me->isSnapped() )
283 {
284 cursorPos = me->snappedPoint();
285 if ( mSnapMarker )
286 {
287 mSnapMarker->setPos( me->snappedPoint() );
288 mSnapMarker->setVisible( true );
289 }
290 }
291 else if ( mSnapMarker )
292 {
293 mSnapMarker->setVisible( false );
294 }
295 }
296 mTool->modelMoveEvent( me.get() );
297 event->setAccepted( me->isAccepted() );
298 }
299
300 if ( !mTool || !event->isAccepted() )
301 QGraphicsView::mouseMoveEvent( event );
302}
303
304void QgsModelGraphicsView::mouseDoubleClickEvent( QMouseEvent *event )
305{
306 if ( !modelScene() )
307 return;
308
309 if ( mTool )
310 {
311 std::unique_ptr<QgsModelViewMouseEvent> me( new QgsModelViewMouseEvent( this, event, mTool->flags() & QgsModelViewTool::FlagSnaps ) );
312 mTool->modelDoubleClickEvent( me.get() );
313 event->setAccepted( me->isAccepted() );
314 }
315
316 if ( !mTool || !event->isAccepted() )
317 QGraphicsView::mouseDoubleClickEvent( event );
318}
319
320void QgsModelGraphicsView::keyPressEvent( QKeyEvent *event )
321{
322 if ( !modelScene() )
323 return;
324
325 if ( mTool )
326 {
327 mTool->keyPressEvent( event );
328 }
329
330 if ( mTool && event->isAccepted() )
331 return;
332
333 if ( event->key() == Qt::Key_Space && ! event->isAutoRepeat() )
334 {
335 if ( !( event->modifiers() & Qt::ControlModifier ) )
336 {
337 // Pan layout with space bar
338 setTool( mSpacePanTool );
339 }
340 else
341 {
342 //ctrl+space pressed, so switch to temporary keyboard based zoom tool
343 setTool( mSpaceZoomTool );
344 }
345 event->accept();
346 }
347 else if ( event->key() == Qt::Key_Left
348 || event->key() == Qt::Key_Right
349 || event->key() == Qt::Key_Up
350 || event->key() == Qt::Key_Down )
351 {
352 QgsModelGraphicsScene *s = modelScene();
353 const QList<QgsModelComponentGraphicItem *> itemList = s->selectedComponentItems();
354 if ( !itemList.empty() )
355 {
356 QPointF delta = deltaForKeyEvent( event );
357
358 itemList.at( 0 )->aboutToChange( tr( "Move Items" ) );
359 for ( QgsModelComponentGraphicItem *item : itemList )
360 {
361 item->moveComponentBy( delta.x(), delta.y() );
362 }
363 itemList.at( 0 )->changed();
364 }
365 event->accept();
366 }
367}
368
369void QgsModelGraphicsView::keyReleaseEvent( QKeyEvent *event )
370{
371 if ( !modelScene() )
372 return;
373
374 if ( mTool )
375 {
376 mTool->keyReleaseEvent( event );
377 }
378
379 if ( !mTool || !event->isAccepted() )
380 QGraphicsView::keyReleaseEvent( event );
381}
382
383void QgsModelGraphicsView::setModelScene( QgsModelGraphicsScene *scene )
384{
385 setScene( scene );
386
387 // IMPORTANT!
388 // previous snap markers, snap lines are owned by previous layout - so don't delete them here!
389 mSnapMarker = new QgsModelViewSnapMarker();
390 mSnapMarker->hide();
391 scene->addItem( mSnapMarker );
392}
393
394QgsModelGraphicsScene *QgsModelGraphicsView::modelScene() const
395{
396 return qobject_cast< QgsModelGraphicsScene * >( QgsModelGraphicsView::scene() );
397}
398
399QgsModelViewTool *QgsModelGraphicsView::tool()
400{
401 return mTool;
402}
403
404void QgsModelGraphicsView::setTool( QgsModelViewTool *tool )
405{
406 if ( !tool )
407 return;
408
409 if ( mTool )
410 {
411 mTool->deactivate();
412 disconnect( mTool, &QgsModelViewTool::itemFocused, this, &QgsModelGraphicsView::itemFocused );
413 }
414
415 // activate new tool before setting it - gives tools a chance
416 // to respond to whatever the current tool is
417 tool->activate();
418 mTool = tool;
419 connect( mTool, &QgsModelViewTool::itemFocused, this, &QgsModelGraphicsView::itemFocused );
420 emit toolSet( mTool );
421}
422
423void QgsModelGraphicsView::unsetTool( QgsModelViewTool *tool )
424{
425 if ( mTool && mTool == tool )
426 {
427 mTool->deactivate();
428 emit toolSet( nullptr );
429 setCursor( Qt::ArrowCursor );
430 }
431}
432
433QgsModelSnapper *QgsModelGraphicsView::snapper()
434{
435 return &mSnapper;
436}
437
438void QgsModelGraphicsView::startMacroCommand( const QString &text )
439{
440 emit macroCommandStarted( text );
441}
442
443void QgsModelGraphicsView::endMacroCommand()
444{
445 emit macroCommandEnded();
446}
447
448void QgsModelGraphicsView::snapSelected()
449{
450 QgsModelGraphicsScene *s = modelScene();
451 const QList<QgsModelComponentGraphicItem *> itemList = s->selectedComponentItems();
452 startMacroCommand( tr( "Snap Items" ) );
453 if ( !itemList.empty() )
454 {
455 bool prevSetting = mSnapper.snapToGrid();
456 mSnapper.setSnapToGrid( true );
457 for ( QgsModelComponentGraphicItem *item : itemList )
458 {
459 bool wasSnapped = false;
460 QRectF snapped = mSnapper.snapRectWithResize( item->mapRectToScene( item->itemRect( ) ), transform().m11(), wasSnapped );
461 if ( wasSnapped )
462 {
463 item->setItemRect( snapped );
464 }
465 }
466 mSnapper.setSnapToGrid( prevSetting );
467 }
468 endMacroCommand();
469}
470
471void QgsModelGraphicsView::copySelectedItems( QgsModelGraphicsView::ClipboardOperation operation )
472{
473 copyItems( modelScene()->selectedComponentItems(), operation );
474}
475
476void QgsModelGraphicsView::copyItems( const QList<QgsModelComponentGraphicItem *> &items, QgsModelGraphicsView::ClipboardOperation operation )
477{
478 if ( !modelScene() )
479 return;
480
481 QgsReadWriteContext context;
482 QDomDocument doc;
483 QDomElement documentElement = doc.createElement( QStringLiteral( "ModelComponentClipboard" ) );
484 if ( operation == ClipboardCut )
485 {
486 emit macroCommandStarted( tr( "Cut Items" ) );
487 emit beginCommand( QString() );
488 }
489
490 QList< QVariant > paramComponents;
491 QList< QVariant > groupBoxComponents;
492 QList< QVariant > algComponents;
493
494 QList< QgsModelComponentGraphicItem * > selectedCommentParents;
495 QList< QgsProcessingModelOutput > selectedOutputs;
496 QList< QgsProcessingModelOutput > selectedOutputsComments;
497 for ( QgsModelComponentGraphicItem *item : items )
498 {
499 if ( const QgsModelCommentGraphicItem *commentItem = dynamic_cast< QgsModelCommentGraphicItem * >( item ) )
500 {
501 selectedCommentParents << commentItem->parentComponentItem();
502 if ( const QgsModelOutputGraphicItem *outputItem = dynamic_cast< QgsModelOutputGraphicItem * >( commentItem->parentComponentItem() ) )
503 {
504 selectedOutputsComments << *( static_cast< const QgsProcessingModelOutput *>( outputItem->component() ) );
505 }
506 }
507 else if ( const QgsModelOutputGraphicItem *outputItem = dynamic_cast< QgsModelOutputGraphicItem * >( item ) )
508 {
509 selectedOutputs << *( static_cast< const QgsProcessingModelOutput *>( outputItem->component() ) );
510 }
511 }
512
513 for ( QgsModelComponentGraphicItem *item : items )
514 {
515 if ( const QgsProcessingModelParameter *param = dynamic_cast< QgsProcessingModelParameter * >( item->component() ) )
516 {
517 QgsProcessingModelParameter component = *param;
518
519 // was comment selected?
520 if ( !selectedCommentParents.contains( item ) )
521 {
522 // no, so drop comment
523 component.comment()->setDescription( QString() );
524 }
525
526 QVariantMap paramDef;
527 paramDef.insert( QStringLiteral( "component" ), component.toVariant() );
528 const QgsProcessingParameterDefinition *def = modelScene()->model()->parameterDefinition( component.parameterName() );
529 paramDef.insert( QStringLiteral( "definition" ), def->toVariantMap() );
530
531 paramComponents << paramDef;
532 }
533 else if ( QgsProcessingModelGroupBox *groupBox = dynamic_cast< QgsProcessingModelGroupBox * >( item->component() ) )
534 {
535 groupBoxComponents << groupBox->toVariant();
536 }
537 else if ( const QgsProcessingModelChildAlgorithm *alg = dynamic_cast< QgsProcessingModelChildAlgorithm * >( item->component() ) )
538 {
539 QgsProcessingModelChildAlgorithm childAlg = *alg;
540
541 // was comment selected?
542 if ( !selectedCommentParents.contains( item ) )
543 {
544 // no, so drop comment
545 childAlg.comment()->setDescription( QString() );
546 }
547
548 // don't copy outputs which weren't selected either
549 QMap<QString, QgsProcessingModelOutput> clipboardOutputs;
550 const QMap<QString, QgsProcessingModelOutput> existingOutputs = childAlg.modelOutputs();
551 for ( auto it = existingOutputs.constBegin(); it != existingOutputs.constEnd(); ++ it )
552 {
553 bool found = false;
554 for ( const QgsProcessingModelOutput &candidate : selectedOutputs )
555 {
556 if ( candidate.childId() == childAlg.childId() && candidate.name() == it.value().name() && candidate.childOutputName() == it.value().childOutputName() )
557 {
558 found = true;
559 break;
560 }
561 }
562 if ( found )
563 {
564 // should we also copy the comment?
565 bool commentFound = false;
566 for ( const QgsProcessingModelOutput &candidate : selectedOutputsComments )
567 {
568 if ( candidate.childId() == childAlg.childId() && candidate.name() == it.value().name() && candidate.childOutputName() == it.value().childOutputName() )
569 {
570 commentFound = true;
571 break;
572 }
573 }
574
575 QgsProcessingModelOutput output = it.value();
576 if ( !commentFound )
577 output.comment()->setDescription( QString() );
578
579 clipboardOutputs.insert( it.key(), output );
580 }
581 }
582 childAlg.setModelOutputs( clipboardOutputs );
583
584 algComponents << childAlg.toVariant();
585 }
586 }
587 QVariantMap components;
588 components.insert( QStringLiteral( "parameters" ), paramComponents );
589 components.insert( QStringLiteral( "groupboxes" ), groupBoxComponents );
590 components.insert( QStringLiteral( "algs" ), algComponents );
591 doc.appendChild( QgsXmlUtils::writeVariant( components, doc ) );
592 if ( operation == ClipboardCut )
593 {
594 emit deleteSelectedItems();
595 emit endCommand();
596 emit macroCommandEnded();
597 }
598
599 QMimeData *mimeData = new QMimeData;
600 mimeData->setData( QStringLiteral( "text/xml" ), doc.toByteArray() );
601 mimeData->setText( doc.toByteArray() );
602 QClipboard *clipboard = QApplication::clipboard();
603 clipboard->setMimeData( mimeData );
604}
605
606void QgsModelGraphicsView::pasteItems( QgsModelGraphicsView::PasteMode mode )
607{
608 if ( !modelScene() )
609 return;
610
611 QDomDocument doc;
612 QClipboard *clipboard = QApplication::clipboard();
613 if ( doc.setContent( clipboard->mimeData()->data( QStringLiteral( "text/xml" ) ) ) )
614 {
615 QDomElement docElem = doc.documentElement();
616 QVariantMap res = QgsXmlUtils::readVariant( docElem ).toMap();
617
618 if ( res.contains( QStringLiteral( "parameters" ) ) && res.contains( QStringLiteral( "algs" ) ) )
619 {
620 QPointF pt;
621 switch ( mode )
622 {
623 case PasteModeCursor:
624 case PasteModeInPlace:
625 {
626 // place items at cursor position
627 pt = mapToScene( mapFromGlobal( QCursor::pos() ) );
628 break;
629 }
630 case PasteModeCenter:
631 {
632 // place items in center of viewport
633 pt = mapToScene( viewport()->rect().center() );
634 break;
635 }
636 }
637
638 emit beginCommand( tr( "Paste Items" ) );
639
640 QRectF pastedBounds;
641
642 QList< QgsProcessingModelGroupBox > pastedGroups;
643 for ( const QVariant &v : res.value( QStringLiteral( "groupboxes" ) ).toList() )
644 {
645 QgsProcessingModelGroupBox box;
646 // don't restore the uuid -- we need them to be unique in the model
647 box.loadVariant( v.toMap(), true );
648
649 pastedGroups << box;
650
651 modelScene()->model()->addGroupBox( box );
652
653 if ( !pastedBounds.isValid( ) )
654 pastedBounds = QRectF( box.position() - QPointF( box.size().width() / 2.0, box.size().height() / 2.0 ), box.size() );
655 else
656 pastedBounds = pastedBounds.united( QRectF( box.position() - QPointF( box.size().width() / 2.0, box.size().height() / 2.0 ), box.size() ) );
657 }
658
659 QStringList pastedParameters;
660 for ( const QVariant &v : res.value( QStringLiteral( "parameters" ) ).toList() )
661 {
662 QVariantMap param = v.toMap();
663 QVariantMap componentDef = param.value( QStringLiteral( "component" ) ).toMap();
664 QVariantMap paramDef = param.value( QStringLiteral( "definition" ) ).toMap();
665
666 std::unique_ptr< QgsProcessingParameterDefinition > paramDefinition( QgsProcessingParameters::parameterFromVariantMap( paramDef ) );
667
668 QgsProcessingModelParameter p;
669 p.loadVariant( componentDef );
670
671 // we need a unique name for the parameter
672 QString name = p.parameterName();
673 QString description = paramDefinition->description();
674 int next = 1;
675 while ( modelScene()->model()->parameterDefinition( name ) )
676 {
677 next++;
678 name = QStringLiteral( "%1 (%2)" ).arg( p.parameterName() ).arg( next );
679 description = QStringLiteral( "%1 (%2)" ).arg( paramDefinition->description() ).arg( next );
680 }
681 paramDefinition->setName( name );
682 paramDefinition->setDescription( description );
683 p.setParameterName( name );
684
685 modelScene()->model()->addModelParameter( paramDefinition.release(), p );
686 pastedParameters << p.parameterName();
687
688 if ( !pastedBounds.isValid( ) )
689 pastedBounds = QRectF( p.position() - QPointF( p.size().width() / 2.0, p.size().height() / 2.0 ), p.size() );
690 else
691 pastedBounds = pastedBounds.united( QRectF( p.position() - QPointF( p.size().width() / 2.0, p.size().height() / 2.0 ), p.size() ) );
692
693 if ( !p.comment()->description().isEmpty() )
694 pastedBounds = pastedBounds.united( QRectF( p.comment()->position() - QPointF( p.comment()->size().width() / 2.0, p.comment()->size().height() / 2.0 ), p.comment()->size() ) );
695 }
696
697 QStringList pastedAlgorithms;
698 for ( const QVariant &v : res.value( QStringLiteral( "algs" ) ).toList() )
699 {
700 QgsProcessingModelChildAlgorithm alg;
701 alg.loadVariant( v.toMap() );
702
703 // ensure algorithm id is unique
704 if ( modelScene()->model()->childAlgorithms().contains( alg.childId() ) )
705 {
706 alg.generateChildId( *modelScene()->model() );
707 }
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
814QgsModelViewSnapMarker::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
826void 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:60
Base class for the definition of processing parameters.
void setDescription(const QString &description)
Sets the description for the parameter.
virtual QVariantMap toVariantMap() const
Saves this parameter to a QVariantMap.
static QgsProcessingParameterDefinition * parameterFromVariantMap(const QVariantMap &map)
Creates a new QgsProcessingParameterDefinition using the configuration from a supplied variant map.
The class is used as a container of context for various read/write operations on other objects.
A rectangle specified with double values.
QgsPointXY center
This class is a composition of two QSettings instances:
Definition qgssettings.h:64
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