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