17#include "moc_qgsmodelgraphicsview.cpp"
31#include <QDragEnterEvent>
33#include <QApplication>
40#define MIN_VIEW_SCALE 0.05
41#define MAX_VIEW_SCALE 1000.0
43QgsModelGraphicsView::QgsModelGraphicsView( QWidget *parent )
44 : QGraphicsView( parent )
46 setResizeAnchor( QGraphicsView::AnchorViewCenter );
47 setMouseTracking(
true );
48 viewport()->setMouseTracking(
true );
49 setAcceptDrops(
true );
55 mSnapper.setSnapToGrid(
true );
58QgsModelGraphicsView::~QgsModelGraphicsView()
63void QgsModelGraphicsView::dragEnterEvent( QDragEnterEvent *event )
65 if ( event->mimeData()->hasText() || event->mimeData()->hasFormat( QStringLiteral(
"application/x-vnd.qgis.qgis.algorithmid" ) ) )
66 event->acceptProposedAction();
71void QgsModelGraphicsView::dropEvent( QDropEvent *event )
73 const QPointF dropPoint = mapToScene( event->pos() );
74 if ( event->mimeData()->hasFormat( QStringLiteral(
"application/x-vnd.qgis.qgis.algorithmid" ) ) )
76 QByteArray data =
event->mimeData()->data( QStringLiteral(
"application/x-vnd.qgis.qgis.algorithmid" ) );
77 QDataStream stream( &data, QIODevice::ReadOnly );
79 stream >> algorithmId;
81 QTimer::singleShot( 0,
this, [
this, dropPoint, algorithmId ]
83 emit algorithmDropped( algorithmId, dropPoint );
87 else if ( event->mimeData()->hasText() )
89 const QString itemId =
event->mimeData()->text();
90 QTimer::singleShot( 0,
this, [
this, dropPoint, itemId ]
92 emit inputDropped( itemId, dropPoint );
102void QgsModelGraphicsView::dragMoveEvent( QDragMoveEvent *event )
104 if ( event->mimeData()->hasText() || event->mimeData()->hasFormat( QStringLiteral(
"application/x-vnd.qgis.qgis.algorithmid" ) ) )
105 event->acceptProposedAction();
110void QgsModelGraphicsView::wheelEvent( QWheelEvent *event )
117 mTool->wheelEvent( event );
120 if ( !mTool || !event->isAccepted() )
127void QgsModelGraphicsView::wheelZoom( QWheelEvent *event )
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;
136 zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * std::fabs( event->angleDelta().y() );
138 if ( event->modifiers() & Qt::ControlModifier )
141 zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 20.0;
145 double scaleFactor = ( zoomIn ? 1 / zoomFactor : zoomFactor );
148 QRect viewportRect( 0, 0, viewport()->width(), viewport()->height() );
152 QPointF scenePoint = mapToScene( event->position().x(), event->position().y() );
156 QgsPointXY newCenter( scenePoint.x() + ( ( oldCenter.x() - scenePoint.x() ) * scaleFactor ),
157 scenePoint.y() + ( ( oldCenter.y() - scenePoint.y() ) * scaleFactor ) );
158 centerOn( newCenter.x(), newCenter.y() );
163 scaleSafe( zoomFactor );
167 scaleSafe( 1 / zoomFactor );
171void QgsModelGraphicsView::scaleSafe(
double scale )
173 double currentScale = transform().m11();
174 scale *= currentScale;
176 setTransform( QTransform::fromScale( scale, scale ) );
179QPointF QgsModelGraphicsView::deltaForKeyEvent( QKeyEvent *event )
182 double increment = 1.0;
183 if ( event->modifiers() & Qt::ShiftModifier )
188 else if ( event->modifiers() & Qt::AltModifier )
191 double viewScale = transform().m11();
194 increment = 1 / viewScale;
200 switch ( event->key() )
218 return QPointF( deltaX, deltaY );
221void QgsModelGraphicsView::mousePressEvent( QMouseEvent *event )
229 mTool->modelPressEvent( me.get() );
230 event->setAccepted( me->isAccepted() );
233 if ( !mTool || !event->isAccepted() )
235 if ( event->button() == Qt::MiddleButton )
238 setTool( mMidMouseButtonPanTool );
243 QGraphicsView::mousePressEvent( event );
248void QgsModelGraphicsView::mouseReleaseEvent( QMouseEvent *event )
256 mTool->modelReleaseEvent( me.get() );
257 event->setAccepted( me->isAccepted() );
260 if ( !mTool || !event->isAccepted() )
261 QGraphicsView::mouseReleaseEvent( event );
264void QgsModelGraphicsView::mouseMoveEvent( QMouseEvent *event )
269 mMouseCurrentXY =
event->pos();
271 QPointF cursorPos = mapToScene( mMouseCurrentXY );
282 if ( me->isSnapped() )
284 cursorPos = me->snappedPoint();
287 mSnapMarker->setPos( me->snappedPoint() );
288 mSnapMarker->setVisible(
true );
291 else if ( mSnapMarker )
293 mSnapMarker->setVisible(
false );
296 mTool->modelMoveEvent( me.get() );
297 event->setAccepted( me->isAccepted() );
300 if ( !mTool || !event->isAccepted() )
301 QGraphicsView::mouseMoveEvent( event );
304void QgsModelGraphicsView::mouseDoubleClickEvent( QMouseEvent *event )
312 mTool->modelDoubleClickEvent( me.get() );
313 event->setAccepted( me->isAccepted() );
316 if ( !mTool || !event->isAccepted() )
317 QGraphicsView::mouseDoubleClickEvent( event );
320void QgsModelGraphicsView::keyPressEvent( QKeyEvent *event )
327 mTool->keyPressEvent( event );
330 if ( mTool && event->isAccepted() )
333 if ( event->key() == Qt::Key_Space && ! event->isAutoRepeat() )
335 if ( !( event->modifiers() & Qt::ControlModifier ) )
338 setTool( mSpacePanTool );
343 setTool( mSpaceZoomTool );
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 )
352 QgsModelGraphicsScene *s = modelScene();
353 const QList<QgsModelComponentGraphicItem *> itemList = s->selectedComponentItems();
354 if ( !itemList.empty() )
356 QPointF delta = deltaForKeyEvent( event );
358 itemList.at( 0 )->aboutToChange( tr(
"Move Items" ) );
359 for ( QgsModelComponentGraphicItem *item : itemList )
361 item->moveComponentBy( delta.x(), delta.y() );
363 itemList.at( 0 )->changed();
369void QgsModelGraphicsView::keyReleaseEvent( QKeyEvent *event )
376 mTool->keyReleaseEvent( event );
379 if ( !mTool || !event->isAccepted() )
380 QGraphicsView::keyReleaseEvent( event );
383void QgsModelGraphicsView::setModelScene( QgsModelGraphicsScene *scene )
389 mSnapMarker =
new QgsModelViewSnapMarker();
391 scene->addItem( mSnapMarker );
394QgsModelGraphicsScene *QgsModelGraphicsView::modelScene()
const
396 return qobject_cast< QgsModelGraphicsScene * >( QgsModelGraphicsView::scene() );
420 emit toolSet( mTool );
425 if ( mTool && mTool == tool )
428 emit toolSet(
nullptr );
429 setCursor( Qt::ArrowCursor );
438void QgsModelGraphicsView::startMacroCommand(
const QString &text )
440 emit macroCommandStarted( text );
443void QgsModelGraphicsView::endMacroCommand()
445 emit macroCommandEnded();
448void QgsModelGraphicsView::snapSelected()
450 QgsModelGraphicsScene *s = modelScene();
451 const QList<QgsModelComponentGraphicItem *> itemList = s->selectedComponentItems();
452 startMacroCommand( tr(
"Snap Items" ) );
453 if ( !itemList.empty() )
455 bool prevSetting = mSnapper.snapToGrid();
456 mSnapper.setSnapToGrid(
true );
457 for ( QgsModelComponentGraphicItem *item : itemList )
459 bool wasSnapped =
false;
460 QRectF snapped = mSnapper.snapRectWithResize( item->mapRectToScene( item->itemRect( ) ), transform().m11(), wasSnapped );
463 item->setItemRect( snapped );
466 mSnapper.setSnapToGrid( prevSetting );
471void QgsModelGraphicsView::copySelectedItems( QgsModelGraphicsView::ClipboardOperation operation )
473 copyItems( modelScene()->selectedComponentItems(), operation );
476void QgsModelGraphicsView::copyItems(
const QList<QgsModelComponentGraphicItem *> &items, QgsModelGraphicsView::ClipboardOperation operation )
483 QDomElement documentElement = doc.createElement( QStringLiteral(
"ModelComponentClipboard" ) );
484 if ( operation == ClipboardCut )
486 emit macroCommandStarted( tr(
"Cut Items" ) );
487 emit beginCommand( QString() );
490 QList< QVariant > paramComponents;
491 QList< QVariant > groupBoxComponents;
492 QList< QVariant > algComponents;
494 QList< QgsModelComponentGraphicItem * > selectedCommentParents;
495 QList< QgsProcessingModelOutput > selectedOutputs;
496 QList< QgsProcessingModelOutput > selectedOutputsComments;
497 for ( QgsModelComponentGraphicItem *item : items )
499 if (
const QgsModelCommentGraphicItem *commentItem =
dynamic_cast< QgsModelCommentGraphicItem *
>( item ) )
501 selectedCommentParents << commentItem->parentComponentItem();
502 if (
const QgsModelOutputGraphicItem *outputItem =
dynamic_cast< QgsModelOutputGraphicItem *
>( commentItem->parentComponentItem() ) )
504 selectedOutputsComments << *( static_cast< const QgsProcessingModelOutput *>( outputItem->component() ) );
507 else if (
const QgsModelOutputGraphicItem *outputItem =
dynamic_cast< QgsModelOutputGraphicItem *
>( item ) )
509 selectedOutputs << *( static_cast< const QgsProcessingModelOutput *>( outputItem->component() ) );
513 for ( QgsModelComponentGraphicItem *item : items )
515 if (
const QgsProcessingModelParameter *param =
dynamic_cast< QgsProcessingModelParameter *
>( item->component() ) )
517 QgsProcessingModelParameter component = *param;
520 if ( !selectedCommentParents.contains( item ) )
523 component.comment()->setDescription( QString() );
526 QVariantMap paramDef;
527 paramDef.insert( QStringLiteral(
"component" ), component.toVariant() );
529 paramDef.insert( QStringLiteral(
"definition" ), def->
toVariantMap() );
531 paramComponents << paramDef;
533 else if ( QgsProcessingModelGroupBox *groupBox =
dynamic_cast< QgsProcessingModelGroupBox *
>( item->component() ) )
535 groupBoxComponents << groupBox->toVariant();
537 else if (
const QgsProcessingModelChildAlgorithm *alg =
dynamic_cast< QgsProcessingModelChildAlgorithm *
>( item->component() ) )
539 QgsProcessingModelChildAlgorithm childAlg = *alg;
542 if ( !selectedCommentParents.contains( item ) )
549 QMap<QString, QgsProcessingModelOutput> clipboardOutputs;
550 const QMap<QString, QgsProcessingModelOutput> existingOutputs = childAlg.modelOutputs();
551 for (
auto it = existingOutputs.constBegin(); it != existingOutputs.constEnd(); ++ it )
554 for (
const QgsProcessingModelOutput &candidate : selectedOutputs )
556 if ( candidate.childId() == childAlg.childId() && candidate.name() == it.value().name() && candidate.childOutputName() == it.value().childOutputName() )
565 bool commentFound =
false;
566 for (
const QgsProcessingModelOutput &candidate : selectedOutputsComments )
568 if ( candidate.childId() == childAlg.childId() && candidate.name() == it.value().name() && candidate.childOutputName() == it.value().childOutputName() )
575 QgsProcessingModelOutput output = it.value();
577 output.comment()->setDescription( QString() );
579 clipboardOutputs.insert( it.key(), output );
582 childAlg.setModelOutputs( clipboardOutputs );
584 algComponents << childAlg.toVariant();
587 QVariantMap components;
588 components.insert( QStringLiteral(
"parameters" ), paramComponents );
589 components.insert( QStringLiteral(
"groupboxes" ), groupBoxComponents );
590 components.insert( QStringLiteral(
"algs" ), algComponents );
592 if ( operation == ClipboardCut )
594 emit deleteSelectedItems();
596 emit macroCommandEnded();
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 );
606void QgsModelGraphicsView::pasteItems( QgsModelGraphicsView::PasteMode mode )
612 QClipboard *clipboard = QApplication::clipboard();
613 if ( doc.setContent( clipboard->mimeData()->data( QStringLiteral(
"text/xml" ) ) ) )
615 QDomElement docElem = doc.documentElement();
618 if ( res.contains( QStringLiteral(
"parameters" ) ) && res.contains( QStringLiteral(
"algs" ) ) )
623 case PasteModeCursor:
624 case PasteModeInPlace:
627 pt = mapToScene( mapFromGlobal( QCursor::pos() ) );
630 case PasteModeCenter:
633 pt = mapToScene( viewport()->rect().center() );
638 emit beginCommand( tr(
"Paste Items" ) );
642 QList< QgsProcessingModelGroupBox > pastedGroups;
643 for (
const QVariant &v : res.value( QStringLiteral(
"groupboxes" ) ).toList() )
645 QgsProcessingModelGroupBox box;
647 box.loadVariant( v.toMap(),
true );
651 modelScene()->model()->addGroupBox( box );
653 if ( !pastedBounds.isValid( ) )
654 pastedBounds = QRectF( box.position() - QPointF( box.size().width() / 2.0, box.size().height() / 2.0 ), box.size() );
656 pastedBounds = pastedBounds.united( QRectF( box.position() - QPointF( box.size().width() / 2.0, box.size().height() / 2.0 ), box.size() ) );
659 QStringList pastedParameters;
660 for (
const QVariant &v : res.value( QStringLiteral(
"parameters" ) ).toList() )
662 QVariantMap param = v.toMap();
663 QVariantMap componentDef = param.value( QStringLiteral(
"component" ) ).toMap();
664 QVariantMap paramDef = param.value( QStringLiteral(
"definition" ) ).toMap();
668 QgsProcessingModelParameter p;
669 p.loadVariant( componentDef );
672 QString name = p.parameterName();
673 QString description = paramDefinition->description();
675 while ( modelScene()->model()->parameterDefinition( name ) )
678 name = QStringLiteral(
"%1 (%2)" ).arg( p.parameterName() ).arg( next );
679 description = QStringLiteral(
"%1 (%2)" ).arg( paramDefinition->description() ).arg( next );
681 paramDefinition->setName( name );
682 paramDefinition->setDescription( description );
683 p.setParameterName( name );
685 modelScene()->model()->addModelParameter( paramDefinition.release(), p );
686 pastedParameters << p.parameterName();
688 if ( !pastedBounds.isValid( ) )
689 pastedBounds = QRectF( p.position() - QPointF( p.size().width() / 2.0, p.size().height() / 2.0 ), p.size() );
691 pastedBounds = pastedBounds.united( QRectF( p.position() - QPointF( p.size().width() / 2.0, p.size().height() / 2.0 ), p.size() ) );
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() ) );
697 QStringList pastedAlgorithms;
698 for (
const QVariant &v : res.value( QStringLiteral(
"algs" ) ).toList() )
700 QgsProcessingModelChildAlgorithm alg;
701 alg.loadVariant( v.toMap() );
704 if ( modelScene()->model()->childAlgorithms().contains( alg.childId() ) )
706 alg.generateChildId( *modelScene()->model() );
710 pastedAlgorithms << alg.childId();
712 if ( !pastedBounds.isValid( ) )
713 pastedBounds = QRectF( alg.position() - QPointF( alg.size().width() / 2.0, alg.size().height() / 2.0 ), alg.size() );
715 pastedBounds = pastedBounds.united( QRectF( alg.position() - QPointF( alg.size().width() / 2.0, alg.size().height() / 2.0 ), alg.size() ) );
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() ) );
720 const QMap<QString, QgsProcessingModelChildAlgorithm> existingAlgs = modelScene()->model()->childAlgorithms();
722 const QMap<QString, QgsProcessingModelOutput> outputs = alg.modelOutputs();
723 QMap<QString, QgsProcessingModelOutput> pastedOutputs;
724 for (
auto it = outputs.constBegin(); it != outputs.constEnd(); ++it )
726 QString name = it.value().name();
732 for (
auto algIt = existingAlgs.constBegin(); algIt != existingAlgs.constEnd(); ++algIt )
734 const QMap<QString, QgsProcessingModelOutput> algOutputs = algIt->modelOutputs();
735 for (
auto outputIt = algOutputs.constBegin(); outputIt != algOutputs.constEnd(); ++outputIt )
737 if ( outputIt.value().name() == name )
749 name = QStringLiteral(
"%1 (%2)" ).arg( it.value().name() ).arg( next );
752 QgsProcessingModelOutput newOutput = it.value();
753 newOutput.setName( name );
754 newOutput.setDescription( name );
755 pastedOutputs.insert( name, newOutput );
757 pastedBounds = pastedBounds.united( QRectF( newOutput.position() - QPointF( newOutput.size().width() / 2.0, newOutput.size().height() / 2.0 ), newOutput.size() ) );
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() ) );
762 alg.setModelOutputs( pastedOutputs );
764 modelScene()->model()->addChildAlgorithm( alg );
767 QPointF offset( 0, 0 );
770 case PasteModeInPlace:
773 case PasteModeCursor:
774 case PasteModeCenter:
776 offset = pt - pastedBounds.topLeft();
781 if ( !offset.isNull() )
783 for ( QgsProcessingModelGroupBox pastedGroup : std::as_const( pastedGroups ) )
785 pastedGroup.setPosition( pastedGroup.position() + offset );
786 modelScene()->model()->addGroupBox( pastedGroup );
788 for (
const QString &pastedParam : std::as_const( pastedParameters ) )
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 );
793 for (
const QString &pastedAlg : std::as_const( pastedAlgorithms ) )
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 );
798 const QMap<QString, QgsProcessingModelOutput> outputs = modelScene()->model()->childAlgorithm( pastedAlg ).modelOutputs();
799 for (
auto it = outputs.begin(); it != outputs.end(); ++it )
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 );
811 modelScene()->rebuildRequired();
814QgsModelViewSnapMarker::QgsModelViewSnapMarker()
815 : QGraphicsRectItem( QRectF( 0, 0, 0, 0 ) )
818 QFontMetrics fm( f );
819 mSize = fm.horizontalAdvance(
'X' );
820 setPen( QPen( Qt::transparent, mSize ) );
822 setFlags( flags() | QGraphicsItem::ItemIgnoresTransformations );
823 setZValue( QgsModelGraphicsScene::ZSnapIndicator );
826void QgsModelViewSnapMarker::paint( QPainter *p,
const QStyleOptionGraphicsItem *, QWidget * )
828 QPen pen( QColor( 255, 0, 0 ) );
831 p->setBrush( Qt::NoBrush );
833 double halfSize = mSize / 2.0;
834 p->drawLine( QLineF( -halfSize, -halfSize, halfSize, halfSize ) );
835 p->drawLine( QLineF( -halfSize, halfSize, halfSize, -halfSize ) );
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...
A class to represent a 2D point.
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() const
Returns the center point of the rectangle.
This class is a composition of two QSettings instances:
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.