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()->hasFormat( QStringLiteral(
"application/x-vnd.qgis.qgis.algorithmid" ) )
66 || event->mimeData()->hasFormat( QStringLiteral(
"application/x-vnd.qgis.qgis.parametertypeid" ) )
67 || event->mimeData()->hasText() )
68 event->acceptProposedAction();
73void QgsModelGraphicsView::dropEvent( QDropEvent *event )
75 const QPointF dropPoint = mapToScene( event->pos() );
76 if ( event->mimeData()->hasFormat( QStringLiteral(
"application/x-vnd.qgis.qgis.algorithmid" ) ) )
78 QByteArray data =
event->mimeData()->data( QStringLiteral(
"application/x-vnd.qgis.qgis.algorithmid" ) );
79 QDataStream stream( &data, QIODevice::ReadOnly );
81 stream >> algorithmId;
83 QTimer::singleShot( 0,
this, [
this, dropPoint, algorithmId] {
84 emit algorithmDropped( algorithmId, dropPoint );
88 else if ( event->mimeData()->hasFormat( QStringLiteral(
"application/x-vnd.qgis.qgis.parametertypeid" ) ) )
90 QByteArray data =
event->mimeData()->data( QStringLiteral(
"application/x-vnd.qgis.qgis.parametertypeid" ) );
91 QDataStream stream( &data, QIODevice::ReadOnly );
93 stream >> paramTypeId;
95 QTimer::singleShot( 0,
this, [
this, dropPoint, paramTypeId] {
96 emit inputDropped( paramTypeId, dropPoint );
100 else if ( event->mimeData()->hasText() )
102 const QString itemId =
event->mimeData()->text();
103 QTimer::singleShot( 0,
this, [
this, dropPoint, itemId] {
104 emit inputDropped( itemId, dropPoint );
114void QgsModelGraphicsView::dragMoveEvent( QDragMoveEvent *event )
116 if ( event->mimeData()->hasFormat( QStringLiteral(
"application/x-vnd.qgis.qgis.algorithmid" ) )
117 || event->mimeData()->hasFormat( QStringLiteral(
"application/x-vnd.qgis.qgis.parametertypeid" ) )
118 || event->mimeData()->hasText() )
119 event->acceptProposedAction();
124void QgsModelGraphicsView::wheelEvent( QWheelEvent *event )
131 mTool->wheelEvent( event );
134 if ( !mTool || !event->isAccepted() )
141void QgsModelGraphicsView::wheelZoom( QWheelEvent *event )
145 double zoomFactor = settings.
value( QStringLiteral(
"qgis/zoom_factor" ), 2 ).toDouble();
146 bool reverseZoom = settings.
value( QStringLiteral(
"qgis/reverse_wheel_zoom" ),
false ).toBool();
147 bool zoomIn = reverseZoom ?
event->angleDelta().y() < 0 :
event->angleDelta().y() > 0;
150 zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * std::fabs( event->angleDelta().y() );
152 if ( event->modifiers() & Qt::ControlModifier )
155 zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 20.0;
159 double scaleFactor = ( zoomIn ? 1 / zoomFactor : zoomFactor );
162 QRect viewportRect( 0, 0, viewport()->width(), viewport()->height() );
166 QPointF scenePoint = mapToScene( event->position().x(), event->position().y() );
170 QgsPointXY newCenter( scenePoint.x() + ( ( oldCenter.x() - scenePoint.x() ) * scaleFactor ), scenePoint.y() + ( ( oldCenter.y() - scenePoint.y() ) * scaleFactor ) );
171 centerOn( newCenter.x(), newCenter.y() );
176 scaleSafe( zoomFactor );
180 scaleSafe( 1 / zoomFactor );
184void QgsModelGraphicsView::scaleSafe(
double scale )
186 double currentScale = transform().m11();
187 scale *= currentScale;
189 setTransform( QTransform::fromScale( scale, scale ) );
192QPointF QgsModelGraphicsView::deltaForKeyEvent( QKeyEvent *event )
195 double increment = 1.0;
196 if ( event->modifiers() & Qt::ShiftModifier )
201 else if ( event->modifiers() & Qt::AltModifier )
204 double viewScale = transform().m11();
207 increment = 1 / viewScale;
213 switch ( event->key() )
231 return QPointF( deltaX, deltaY );
234void QgsModelGraphicsView::mousePressEvent( QMouseEvent *event )
242 mTool->modelPressEvent( me.get() );
243 event->setAccepted( me->isAccepted() );
246 if ( !mTool || !event->isAccepted() )
248 if ( event->button() == Qt::MiddleButton )
251 setTool( mMidMouseButtonPanTool );
256 QGraphicsView::mousePressEvent( event );
261void QgsModelGraphicsView::mouseReleaseEvent( QMouseEvent *event )
269 mTool->modelReleaseEvent( me.get() );
270 event->setAccepted( me->isAccepted() );
273 if ( !mTool || !event->isAccepted() )
274 QGraphicsView::mouseReleaseEvent( event );
277void QgsModelGraphicsView::mouseMoveEvent( QMouseEvent *event )
282 mMouseCurrentXY =
event->pos();
284 QPointF cursorPos = mapToScene( mMouseCurrentXY );
287 auto me = std::make_unique<QgsModelViewMouseEvent>(
this, event,
false );
295 if ( me->isSnapped() )
297 cursorPos = me->snappedPoint();
300 mSnapMarker->setPos( me->snappedPoint() );
301 mSnapMarker->setVisible(
true );
304 else if ( mSnapMarker )
306 mSnapMarker->setVisible(
false );
309 mTool->modelMoveEvent( me.get() );
310 event->setAccepted( me->isAccepted() );
313 if ( !mTool || !event->isAccepted() )
314 QGraphicsView::mouseMoveEvent( event );
317void QgsModelGraphicsView::mouseDoubleClickEvent( QMouseEvent *event )
325 mTool->modelDoubleClickEvent( me.get() );
326 event->setAccepted( me->isAccepted() );
329 if ( !mTool || !event->isAccepted() )
330 QGraphicsView::mouseDoubleClickEvent( event );
333void QgsModelGraphicsView::keyPressEvent( QKeyEvent *event )
340 mTool->keyPressEvent( event );
343 if ( mTool && event->isAccepted() )
346 if ( event->key() == Qt::Key_Space && !event->isAutoRepeat() )
348 if ( !( event->modifiers() & Qt::ControlModifier ) )
351 setTool( mSpacePanTool );
356 setTool( mSpaceZoomTool );
360 else if ( event->key() == Qt::Key_Left
361 || event->key() == Qt::Key_Right
362 || event->key() == Qt::Key_Up
363 || event->key() == Qt::Key_Down )
365 QgsModelGraphicsScene *s = modelScene();
366 const QList<QgsModelComponentGraphicItem *> itemList = s->selectedComponentItems();
367 if ( !itemList.empty() )
369 QPointF delta = deltaForKeyEvent( event );
371 itemList.at( 0 )->aboutToChange( tr(
"Move Items" ) );
372 for ( QgsModelComponentGraphicItem *item : itemList )
374 item->moveComponentBy( delta.x(), delta.y() );
376 itemList.at( 0 )->changed();
382void QgsModelGraphicsView::keyReleaseEvent( QKeyEvent *event )
389 mTool->keyReleaseEvent( event );
392 if ( !mTool || !event->isAccepted() )
393 QGraphicsView::keyReleaseEvent( event );
396void QgsModelGraphicsView::setModelScene( QgsModelGraphicsScene *scene )
402 mSnapMarker =
new QgsModelViewSnapMarker();
404 scene->addItem( mSnapMarker );
407QgsModelGraphicsScene *QgsModelGraphicsView::modelScene()
const
409 return qobject_cast<QgsModelGraphicsScene *>( QgsModelGraphicsView::scene() );
433 emit toolSet( mTool );
438 if ( mTool && mTool == tool )
441 emit toolSet(
nullptr );
442 setCursor( Qt::ArrowCursor );
451void QgsModelGraphicsView::startMacroCommand(
const QString &text )
453 emit macroCommandStarted( text );
456void QgsModelGraphicsView::endMacroCommand()
458 emit macroCommandEnded();
461void QgsModelGraphicsView::beginCommand(
const QString &text )
463 emit commandBegun( text );
466void QgsModelGraphicsView::endCommand()
472void QgsModelGraphicsView::snapSelected()
474 QgsModelGraphicsScene *s = modelScene();
475 const QList<QgsModelComponentGraphicItem *> itemList = s->selectedComponentItems();
476 startMacroCommand( tr(
"Snap Items" ) );
477 if ( !itemList.empty() )
479 bool prevSetting = mSnapper.snapToGrid();
480 mSnapper.setSnapToGrid(
true );
481 for ( QgsModelComponentGraphicItem *item : itemList )
483 bool wasSnapped =
false;
484 QRectF snapped = mSnapper.snapRectWithResize( item->mapRectToScene( item->itemRect() ), transform().m11(), wasSnapped );
487 item->setItemRect( snapped );
490 mSnapper.setSnapToGrid( prevSetting );
495void QgsModelGraphicsView::copySelectedItems( QgsModelGraphicsView::ClipboardOperation operation )
497 copyItems( modelScene()->selectedComponentItems(), operation );
500void QgsModelGraphicsView::copyItems(
const QList<QgsModelComponentGraphicItem *> &items, QgsModelGraphicsView::ClipboardOperation operation )
507 QDomElement documentElement = doc.createElement( QStringLiteral(
"ModelComponentClipboard" ) );
508 if ( operation == ClipboardCut )
510 emit macroCommandStarted( tr(
"Cut Items" ) );
511 emit commandBegun( QString() );
514 QList<QVariant> paramComponents;
515 QList<QVariant> groupBoxComponents;
516 QList<QVariant> algComponents;
518 QList<QgsModelComponentGraphicItem *> selectedCommentParents;
519 QList<QgsProcessingModelOutput> selectedOutputs;
520 QList<QgsProcessingModelOutput> selectedOutputsComments;
521 for ( QgsModelComponentGraphicItem *item : items )
523 if (
const QgsModelCommentGraphicItem *commentItem =
dynamic_cast<QgsModelCommentGraphicItem *
>( item ) )
525 selectedCommentParents << commentItem->parentComponentItem();
526 if (
const QgsModelOutputGraphicItem *outputItem =
dynamic_cast<QgsModelOutputGraphicItem *
>( commentItem->parentComponentItem() ) )
528 selectedOutputsComments << *( static_cast<const QgsProcessingModelOutput *>( outputItem->component() ) );
531 else if (
const QgsModelOutputGraphicItem *outputItem =
dynamic_cast<QgsModelOutputGraphicItem *
>( item ) )
533 selectedOutputs << *( static_cast<const QgsProcessingModelOutput *>( outputItem->component() ) );
537 for ( QgsModelComponentGraphicItem *item : items )
539 if (
const QgsProcessingModelParameter *param =
dynamic_cast<QgsProcessingModelParameter *
>( item->component() ) )
541 QgsProcessingModelParameter component = *param;
544 if ( !selectedCommentParents.contains( item ) )
547 component.comment()->setDescription( QString() );
550 QVariantMap paramDef;
551 paramDef.insert( QStringLiteral(
"component" ), component.toVariant() );
553 paramDef.insert( QStringLiteral(
"definition" ), def->
toVariantMap() );
555 paramComponents << paramDef;
557 else if ( QgsProcessingModelGroupBox *groupBox =
dynamic_cast<QgsProcessingModelGroupBox *
>( item->component() ) )
559 groupBoxComponents << groupBox->toVariant();
561 else if (
const QgsProcessingModelChildAlgorithm *alg =
dynamic_cast<QgsProcessingModelChildAlgorithm *
>( item->component() ) )
563 QgsProcessingModelChildAlgorithm childAlg = *alg;
566 if ( !selectedCommentParents.contains( item ) )
573 QMap<QString, QgsProcessingModelOutput> clipboardOutputs;
574 const QMap<QString, QgsProcessingModelOutput> existingOutputs = childAlg.modelOutputs();
575 for (
auto it = existingOutputs.constBegin(); it != existingOutputs.constEnd(); ++it )
578 for (
const QgsProcessingModelOutput &candidate : selectedOutputs )
580 if ( candidate.childId() == childAlg.childId() && candidate.name() == it.value().name() && candidate.childOutputName() == it.value().childOutputName() )
589 bool commentFound =
false;
590 for (
const QgsProcessingModelOutput &candidate : selectedOutputsComments )
592 if ( candidate.childId() == childAlg.childId() && candidate.name() == it.value().name() && candidate.childOutputName() == it.value().childOutputName() )
599 QgsProcessingModelOutput output = it.value();
601 output.comment()->setDescription( QString() );
603 clipboardOutputs.insert( it.key(), output );
606 childAlg.setModelOutputs( clipboardOutputs );
608 algComponents << childAlg.toVariant();
611 QVariantMap components;
612 components.insert( QStringLiteral(
"parameters" ), paramComponents );
613 components.insert( QStringLiteral(
"groupboxes" ), groupBoxComponents );
614 components.insert( QStringLiteral(
"algs" ), algComponents );
616 if ( operation == ClipboardCut )
618 emit deleteSelectedItems();
620 emit macroCommandEnded();
623 QMimeData *mimeData =
new QMimeData;
624 mimeData->setData( QStringLiteral(
"text/xml" ), doc.toByteArray() );
625 mimeData->setText( doc.toByteArray() );
626 QClipboard *clipboard = QApplication::clipboard();
627 clipboard->setMimeData( mimeData );
630void QgsModelGraphicsView::pasteItems( QgsModelGraphicsView::PasteMode mode )
636 QClipboard *clipboard = QApplication::clipboard();
637 const QMimeData *mimeData = clipboard->mimeData();
640 if ( doc.setContent( mimeData->data( QStringLiteral(
"text/xml" ) ) ) )
642 QDomElement docElem = doc.documentElement();
645 if ( res.contains( QStringLiteral(
"parameters" ) ) && res.contains( QStringLiteral(
"algs" ) ) )
650 case PasteModeCursor:
651 case PasteModeInPlace:
654 pt = mapToScene( mapFromGlobal( QCursor::pos() ) );
657 case PasteModeCenter:
660 pt = mapToScene( viewport()->rect().center() );
665 beginCommand( tr(
"Paste Items" ) );
669 QList<QgsProcessingModelGroupBox> pastedGroups;
670 for (
const QVariant &v : res.value( QStringLiteral(
"groupboxes" ) ).toList() )
672 QgsProcessingModelGroupBox box;
674 box.loadVariant( v.toMap(),
true );
678 modelScene()->model()->addGroupBox( box );
680 if ( !pastedBounds.isValid() )
681 pastedBounds = QRectF( box.position() - QPointF( box.size().width() / 2.0, box.size().height() / 2.0 ), box.size() );
683 pastedBounds = pastedBounds.united( QRectF( box.position() - QPointF( box.size().width() / 2.0, box.size().height() / 2.0 ), box.size() ) );
686 QStringList pastedParameters;
687 for (
const QVariant &v : res.value( QStringLiteral(
"parameters" ) ).toList() )
689 QVariantMap param = v.toMap();
690 QVariantMap componentDef = param.value( QStringLiteral(
"component" ) ).toMap();
691 QVariantMap paramDef = param.value( QStringLiteral(
"definition" ) ).toMap();
695 QgsProcessingModelParameter p;
696 p.loadVariant( componentDef );
699 QString name = p.parameterName();
700 QString description = paramDefinition->description();
702 while ( modelScene()->model()->parameterDefinition( name ) )
705 name = QStringLiteral(
"%1 (%2)" ).arg( p.parameterName() ).arg( next );
706 description = QStringLiteral(
"%1 (%2)" ).arg( paramDefinition->description() ).arg( next );
708 paramDefinition->setName( name );
709 paramDefinition->setDescription( description );
710 p.setParameterName( name );
712 modelScene()->model()->addModelParameter( paramDefinition.release(), p );
713 pastedParameters << p.parameterName();
715 if ( !pastedBounds.isValid() )
716 pastedBounds = QRectF( p.position() - QPointF( p.size().width() / 2.0, p.size().height() / 2.0 ), p.size() );
718 pastedBounds = pastedBounds.united( QRectF( p.position() - QPointF( p.size().width() / 2.0, p.size().height() / 2.0 ), p.size() ) );
720 if ( !p.comment()->description().isEmpty() )
721 pastedBounds = pastedBounds.united( QRectF( p.comment()->position() - QPointF( p.comment()->size().width() / 2.0, p.comment()->size().height() / 2.0 ), p.comment()->size() ) );
724 QStringList pastedAlgorithms;
725 for (
const QVariant &v : res.value( QStringLiteral(
"algs" ) ).toList() )
727 QgsProcessingModelChildAlgorithm alg;
728 alg.loadVariant( v.toMap() );
731 if ( modelScene()->model()->childAlgorithms().contains( alg.childId() ) )
733 alg.generateChildId( *modelScene()->model() );
737 pastedAlgorithms << alg.childId();
739 if ( !pastedBounds.isValid() )
740 pastedBounds = QRectF( alg.position() - QPointF( alg.size().width() / 2.0, alg.size().height() / 2.0 ), alg.size() );
742 pastedBounds = pastedBounds.united( QRectF( alg.position() - QPointF( alg.size().width() / 2.0, alg.size().height() / 2.0 ), alg.size() ) );
744 if ( !alg.comment()->description().isEmpty() )
745 pastedBounds = pastedBounds.united( QRectF( alg.comment()->position() - QPointF( alg.comment()->size().width() / 2.0, alg.comment()->size().height() / 2.0 ), alg.comment()->size() ) );
747 const QMap<QString, QgsProcessingModelChildAlgorithm> existingAlgs = modelScene()->model()->childAlgorithms();
749 const QMap<QString, QgsProcessingModelOutput> outputs = alg.modelOutputs();
750 QMap<QString, QgsProcessingModelOutput> pastedOutputs;
751 for (
auto it = outputs.constBegin(); it != outputs.constEnd(); ++it )
753 QString name = it.value().name();
759 for (
auto algIt = existingAlgs.constBegin(); algIt != existingAlgs.constEnd(); ++algIt )
761 const QMap<QString, QgsProcessingModelOutput> algOutputs = algIt->modelOutputs();
762 for (
auto outputIt = algOutputs.constBegin(); outputIt != algOutputs.constEnd(); ++outputIt )
764 if ( outputIt.value().name() == name )
776 name = QStringLiteral(
"%1 (%2)" ).arg( it.value().name() ).arg( next );
779 QgsProcessingModelOutput newOutput = it.value();
780 newOutput.setName( name );
781 newOutput.setDescription( name );
782 pastedOutputs.insert( name, newOutput );
784 pastedBounds = pastedBounds.united( QRectF( newOutput.position() - QPointF( newOutput.size().width() / 2.0, newOutput.size().height() / 2.0 ), newOutput.size() ) );
786 if ( !alg.comment()->description().isEmpty() )
787 pastedBounds = pastedBounds.united( QRectF( newOutput.comment()->position() - QPointF( newOutput.comment()->size().width() / 2.0, newOutput.comment()->size().height() / 2.0 ), newOutput.comment()->size() ) );
789 alg.setModelOutputs( pastedOutputs );
791 modelScene()->model()->addChildAlgorithm( alg );
794 QPointF offset( 0, 0 );
797 case PasteModeInPlace:
800 case PasteModeCursor:
801 case PasteModeCenter:
803 offset = pt - pastedBounds.topLeft();
808 if ( !offset.isNull() )
810 for ( QgsProcessingModelGroupBox pastedGroup : std::as_const( pastedGroups ) )
812 pastedGroup.setPosition( pastedGroup.position() + offset );
813 modelScene()->model()->addGroupBox( pastedGroup );
815 for (
const QString &pastedParam : std::as_const( pastedParameters ) )
817 modelScene()->model()->parameterComponent( pastedParam ).setPosition( modelScene()->model()->parameterComponent( pastedParam ).position() + offset );
818 modelScene()->model()->parameterComponent( pastedParam ).comment()->setPosition( modelScene()->model()->parameterComponent( pastedParam ).comment()->position() + offset );
820 for (
const QString &pastedAlg : std::as_const( pastedAlgorithms ) )
822 modelScene()->model()->childAlgorithm( pastedAlg ).setPosition( modelScene()->model()->childAlgorithm( pastedAlg ).position() + offset );
823 modelScene()->model()->childAlgorithm( pastedAlg ).comment()->setPosition( modelScene()->model()->childAlgorithm( pastedAlg ).comment()->position() + offset );
825 const QMap<QString, QgsProcessingModelOutput> outputs = modelScene()->model()->childAlgorithm( pastedAlg ).modelOutputs();
826 for (
auto it = outputs.begin(); it != outputs.end(); ++it )
828 modelScene()->model()->childAlgorithm( pastedAlg ).modelOutput( it.key() ).setPosition( modelScene()->model()->childAlgorithm( pastedAlg ).modelOutput( it.key() ).position() + offset );
829 modelScene()->model()->childAlgorithm( pastedAlg ).modelOutput( it.key() ).comment()->setPosition( modelScene()->model()->childAlgorithm( pastedAlg ).modelOutput( it.key() ).comment()->position() + offset );
838 modelScene()->rebuildRequired();
841QgsModelViewSnapMarker::QgsModelViewSnapMarker()
842 : QGraphicsRectItem( QRectF( 0, 0, 0, 0 ) )
845 QFontMetrics fm( f );
846 mSize = fm.horizontalAdvance(
'X' );
847 setPen( QPen( Qt::transparent, mSize ) );
849 setFlags( flags() | QGraphicsItem::ItemIgnoresTransformations );
850 setZValue( QgsModelGraphicsScene::ZSnapIndicator );
853void QgsModelViewSnapMarker::paint( QPainter *p,
const QStyleOptionGraphicsItem *, QWidget * )
855 QPen pen( QColor( 255, 0, 0 ) );
858 p->setBrush( Qt::NoBrush );
860 double halfSize = mSize / 2.0;
861 p->drawLine( QLineF( -halfSize, -halfSize, halfSize, halfSize ) );
862 p->drawLine( QLineF( -halfSize, halfSize, halfSize, -halfSize ) );
Manages snapping grids and preset snap lines in a layout, and handles snapping points to the nearest ...
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.
A container for the context for various read/write operations on objects.
A rectangle specified with double values.
Stores settings for use within QGIS.
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.