Compare commits

...

17 Commits

Author SHA1 Message Date
Valentin Buira
958b4c36e3
Merge e5e3b9a44a8bb26da7cdacc3fb545b090d991836 into 74549aad26c3358101e88477d9dfa1caae013d72 2025-07-01 20:06:32 +02:00
Valentin Buira
e5e3b9a44a Adress comments
superfluous centerOn
2025-06-30 11:51:11 +02:00
Valentin Buira
88dd13a0ca Fix view jumping around 2025-06-27 18:35:53 +02:00
Valentin Buira
e67201a2aa
Apply suggestions from code review
Co-authored-by: Stefanos Natsis <uclaros@gmail.com>
2025-05-14 13:56:01 +02:00
Valentin Buira
f2c61f819f Avoid needless extra method 2025-05-14 13:29:40 +02:00
Valentin Buira
55f2cbe674 Simplify restaure the previous viewed rect 2025-05-14 12:36:45 +02:00
Valentin Buira
9853fa2da1 Remove the good superfluous method this time 2025-05-14 12:18:59 +02:00
Valentin Buira
d678daf1d5 Revert "Superfluous method"
This reverts commit 13f020ef86c3f1501e1c1f69792f3b16616d4b4a.
2025-05-14 11:05:53 +02:00
Valentin Buira
13f020ef86 Superfluous method 2025-05-14 10:58:34 +02:00
Valentin Buira
32b0327dbf connect signal to method instead of individual calls 2025-05-14 10:48:28 +02:00
Valentin Buira
7ccd8f0435 Adress more comments 2025-05-14 10:44:45 +02:00
Valentin Buira
021467a178 Adress comments 2025-05-13 22:37:09 +02:00
Valentin Buira
88f59e4c37 Fixing test and add more case 2025-05-13 20:56:25 +02:00
Valentin Buira
a06f6ee8b1 Attempt to debug CI test 2025-05-13 11:44:12 +02:00
Valentin Buira
ee23cf7c44 Tidy up PR 2025-05-13 06:26:36 +02:00
Valentin Buira
86f372948e Fix scrollback to (0,0) on each repaintModel / rebuild of the model 2025-05-13 05:39:34 +02:00
Valentin Buira
ccdb476318 Unlimited modeler canvas size 2025-05-12 17:35:20 +02:00
14 changed files with 147 additions and 18 deletions

View File

@ -173,6 +173,16 @@ full details (``longMessage``).
%Docstring %Docstring
Requests a complete rebuild of a model by emitting the according signal Requests a complete rebuild of a model by emitting the according signal
.. versionadded:: 3.44
%End
void updateBounds();
%Docstring
Updates the scene rect based on the bounds of the model.
The bounding rectangle of the model is calculated off all components of
the model, with an additional margin arounds items.
.. versionadded:: 3.44 .. versionadded:: 3.44
%End %End

View File

@ -57,6 +57,15 @@ widget.
void setModelScene( QgsModelGraphicsScene *scene ); void setModelScene( QgsModelGraphicsScene *scene );
%Docstring %Docstring
Sets the related ``scene``. Sets the related ``scene``.
%End
void friendlySetSceneRect();
%Docstring
Sets the scene rect used for scrollbar without disturbing the user i.e:
- We growth the scene rect as the model growth - We shrink only if the
model scene rect is outside the current viewed viewport
Called each time the view viewport moved or the model scene changed
%End %End
QgsModelGraphicsScene *modelScene() const; QgsModelGraphicsScene *modelScene() const;
@ -134,12 +143,10 @@ Pastes items from clipboard, using the specified ``mode``.
%End %End
public slots: public slots:
void snapSelected(); void snapSelected();
%Docstring %Docstring
Snaps the selected items to the grid. Snaps the selected items to the grid.
%End %End
signals: signals:
void algorithmDropped( const QString &algorithmId, const QPointF &pos ); void algorithmDropped( const QString &algorithmId, const QPointF &pos );

View File

@ -173,6 +173,16 @@ full details (``longMessage``).
%Docstring %Docstring
Requests a complete rebuild of a model by emitting the according signal Requests a complete rebuild of a model by emitting the according signal
.. versionadded:: 3.44
%End
void updateBounds();
%Docstring
Updates the scene rect based on the bounds of the model.
The bounding rectangle of the model is calculated off all components of
the model, with an additional margin arounds items.
.. versionadded:: 3.44 .. versionadded:: 3.44
%End %End

View File

@ -57,6 +57,15 @@ widget.
void setModelScene( QgsModelGraphicsScene *scene ); void setModelScene( QgsModelGraphicsScene *scene );
%Docstring %Docstring
Sets the related ``scene``. Sets the related ``scene``.
%End
void friendlySetSceneRect();
%Docstring
Sets the scene rect used for scrollbar without disturbing the user i.e:
- We growth the scene rect as the model growth - We shrink only if the
model scene rect is outside the current viewed viewport
Called each time the view viewport moved or the model scene changed
%End %End
QgsModelGraphicsScene *modelScene() const; QgsModelGraphicsScene *modelScene() const;
@ -134,12 +143,10 @@ Pastes items from clipboard, using the specified ``mode``.
%End %End
public slots: public slots:
void snapSelected(); void snapSelected();
%Docstring %Docstring
Snaps the selected items to the grid. Snaps the selected items to the grid.
%End %End
signals: signals:
void algorithmDropped( const QString &algorithmId, const QPointF &pos ); void algorithmDropped( const QString &algorithmId, const QPointF &pos );

View File

@ -71,7 +71,6 @@ pluginPath = os.path.split(os.path.dirname(__file__))[0]
class ModelerDialog(QgsModelDesignerDialog): class ModelerDialog(QgsModelDesignerDialog):
CANVAS_SIZE = 4000
update_model = pyqtSignal() update_model = pyqtSignal()
@ -97,7 +96,6 @@ class ModelerDialog(QgsModelDesignerDialog):
self.setStyleSheet(iface.mainWindow().styleSheet()) self.setStyleSheet(iface.mainWindow().styleSheet())
scene = ModelerScene(self) scene = ModelerScene(self)
scene.setSceneRect(QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE))
self.setModelScene(scene) self.setModelScene(scene)
self.view().ensureVisible(0, 0, 10, 10) self.view().ensureVisible(0, 0, 10, 10)
@ -244,8 +242,6 @@ class ModelerDialog(QgsModelDesignerDialog):
def repaintModel(self, showControls=True): def repaintModel(self, showControls=True):
scene = ModelerScene(self) scene = ModelerScene(self)
scene.setSceneRect(QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE))
if not showControls: if not showControls:
scene.setFlag(QgsModelGraphicsScene.Flag.FlagHideControls) scene.setFlag(QgsModelGraphicsScene.Flag.FlagHideControls)
@ -259,6 +255,7 @@ class ModelerDialog(QgsModelDesignerDialog):
self.setModelScene(scene) self.setModelScene(scene)
# create items later that setModelScene to setup link to messageBar to the scene # create items later that setModelScene to setup link to messageBar to the scene
scene.createItems(self.model(), context) scene.createItems(self.model(), context)
scene.updateBounds()
def create_widget_context(self): def create_widget_context(self):
""" """
@ -347,7 +344,7 @@ class ModelerDialog(QgsModelDesignerDialog):
for i in list(self.model().parameterComponents().values()) for i in list(self.model().parameterComponents().values())
] ]
) )
newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) newX = MARGIN + BOX_WIDTH + maxX
else: else:
newX = MARGIN + BOX_WIDTH / 2 newX = MARGIN + BOX_WIDTH / 2
return QPointF(newX, MARGIN + BOX_HEIGHT / 2) return QPointF(newX, MARGIN + BOX_HEIGHT / 2)
@ -415,8 +412,8 @@ class ModelerDialog(QgsModelDesignerDialog):
for alg in list(self.model().childAlgorithms().values()) for alg in list(self.model().childAlgorithms().values())
] ]
) )
newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) newX = MARGIN + BOX_WIDTH + maxX
newY = min(MARGIN + BOX_HEIGHT + maxY, self.CANVAS_SIZE - BOX_HEIGHT) newY = MARGIN + BOX_HEIGHT + maxY
else: else:
newX = MARGIN + BOX_WIDTH / 2 newX = MARGIN + BOX_WIDTH / 2
newY = MARGIN * 2 + BOX_HEIGHT + BOX_HEIGHT / 2 newY = MARGIN * 2 + BOX_HEIGHT + BOX_HEIGHT / 2

View File

@ -440,7 +440,7 @@ void QgsModelDesignerDialog::setModel( QgsProcessingModelAlgorithm *model )
mGroupEdit->setText( mModel->group() ); mGroupEdit->setText( mModel->group() );
mNameEdit->setText( mModel->displayName() ); mNameEdit->setText( mModel->displayName() );
repaintModel(); repaintModel( true );
updateVariablesGui(); updateVariablesGui();
mView->centerOn( 0, 0 ); mView->centerOn( 0, 0 );
@ -480,7 +480,6 @@ void QgsModelDesignerDialog::setModelScene( QgsModelGraphicsScene *scene )
mScene->setModel( mModel.get() ); mScene->setModel( mModel.get() );
mScene->setMessageBar( mMessageBar ); mScene->setMessageBar( mMessageBar );
const QPointF center = mView->mapToScene( mView->viewport()->rect().center() );
mView->setModelScene( mScene ); mView->setModelScene( mScene );
mSelectTool->resetCache(); mSelectTool->resetCache();
@ -499,8 +498,6 @@ void QgsModelDesignerDialog::setModelScene( QgsModelGraphicsScene *scene )
connect( mScene, &QgsModelGraphicsScene::showChildAlgorithmOutputs, this, &QgsModelDesignerDialog::showChildAlgorithmOutputs ); connect( mScene, &QgsModelGraphicsScene::showChildAlgorithmOutputs, this, &QgsModelDesignerDialog::showChildAlgorithmOutputs );
connect( mScene, &QgsModelGraphicsScene::showChildAlgorithmLog, this, &QgsModelDesignerDialog::showChildAlgorithmLog ); connect( mScene, &QgsModelGraphicsScene::showChildAlgorithmLog, this, &QgsModelDesignerDialog::showChildAlgorithmLog );
mView->centerOn( center );
if ( oldScene ) if ( oldScene )
oldScene->deleteLater(); oldScene->deleteLater();
} }

View File

@ -33,6 +33,8 @@ QgsModelGraphicsScene::QgsModelGraphicsScene( QObject *parent )
: QGraphicsScene( parent ) : QGraphicsScene( parent )
{ {
setItemIndexMethod( QGraphicsScene::NoIndex ); setItemIndexMethod( QGraphicsScene::NoIndex );
connect( this, &QgsModelGraphicsScene::componentChanged, this, &QgsModelGraphicsScene::updateBounds );
} }
QgsProcessingModelAlgorithm *QgsModelGraphicsScene::model() QgsProcessingModelAlgorithm *QgsModelGraphicsScene::model()
@ -60,6 +62,28 @@ void QgsModelGraphicsScene::mousePressEvent( QGraphicsSceneMouseEvent *event )
QGraphicsScene::mousePressEvent( event ); QGraphicsScene::mousePressEvent( event );
} }
void QgsModelGraphicsScene::updateBounds()
{
//start with an empty rectangle
QRectF bounds;
//add all items
const QList<QGraphicsItem *> constItems = items();
for ( QGraphicsItem *item : constItems )
{
QgsModelComponentGraphicItem *componentItem = dynamic_cast<QgsModelComponentGraphicItem *>( item );
if ( componentItem )
bounds = bounds.united( componentItem->sceneBoundingRect() );
}
if ( bounds.isValid() )
{
bounds.adjust( -SCENE_COMPONENT_MARGIN, -SCENE_COMPONENT_MARGIN, SCENE_COMPONENT_MARGIN, SCENE_COMPONENT_MARGIN );
}
setSceneRect( bounds );
}
QgsModelComponentGraphicItem *QgsModelGraphicsScene::createParameterGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelParameter *param ) const QgsModelComponentGraphicItem *QgsModelGraphicsScene::createParameterGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelParameter *param ) const
{ {
return new QgsModelParameterGraphicItem( param, model, nullptr ); return new QgsModelParameterGraphicItem( param, model, nullptr );

View File

@ -181,6 +181,15 @@ class GUI_EXPORT QgsModelGraphicsScene : public QGraphicsScene
*/ */
void requestRebuildRequired(); void requestRebuildRequired();
/**
* Updates the scene rect based on the bounds of the model.
* The bounding rectangle of the model is calculated off all components of the model, with an additional margin arounds items.
*
* \since QGIS 3.44
*/
void updateBounds();
signals: signals:
/** /**
@ -282,6 +291,8 @@ class GUI_EXPORT QgsModelGraphicsScene : public QGraphicsScene
QMap<QString, QgsModelComponentGraphicItem *> mGroupBoxItems; QMap<QString, QgsModelComponentGraphicItem *> mGroupBoxItems;
QgsProcessingModelResult mLastResult; QgsProcessingModelResult mLastResult;
static constexpr int SCENE_COMPONENT_MARGIN = 50;
QgsMessageBar *mMessageBar = nullptr; QgsMessageBar *mMessageBar = nullptr;
}; };

View File

@ -52,6 +52,9 @@ QgsModelGraphicsView::QgsModelGraphicsView( QWidget *parent )
mMidMouseButtonPanTool = new QgsModelViewToolTemporaryMousePan( this ); mMidMouseButtonPanTool = new QgsModelViewToolTemporaryMousePan( this );
mSpaceZoomTool = new QgsModelViewToolTemporaryKeyZoom( this ); mSpaceZoomTool = new QgsModelViewToolTemporaryKeyZoom( this );
connect( horizontalScrollBar(), &QScrollBar::sliderReleased, this, [=] { friendlySetSceneRect(); } );
connect( verticalScrollBar(), &QScrollBar::sliderReleased, this, [=] { friendlySetSceneRect(); } );
mSnapper.setSnapToGrid( true ); mSnapper.setSnapToGrid( true );
} }
@ -397,6 +400,8 @@ void QgsModelGraphicsView::setModelScene( QgsModelGraphicsScene *scene )
{ {
setScene( scene ); setScene( scene );
connect( scene, &QgsModelGraphicsScene::sceneRectChanged, this, [=] { friendlySetSceneRect(); } );
// IMPORTANT! // IMPORTANT!
// previous snap markers, snap lines are owned by previous layout - so don't delete them here! // previous snap markers, snap lines are owned by previous layout - so don't delete them here!
mSnapMarker = new QgsModelViewSnapMarker(); mSnapMarker = new QgsModelViewSnapMarker();
@ -468,7 +473,6 @@ void QgsModelGraphicsView::endCommand()
emit commandEnded(); emit commandEnded();
} }
void QgsModelGraphicsView::snapSelected() void QgsModelGraphicsView::snapSelected()
{ {
QgsModelGraphicsScene *s = modelScene(); QgsModelGraphicsScene *s = modelScene();
@ -492,6 +496,21 @@ void QgsModelGraphicsView::snapSelected()
endMacroCommand(); endMacroCommand();
} }
void QgsModelGraphicsView::friendlySetSceneRect()
{
QRectF modelSceneRect = modelScene()->sceneRect();
QRectF viewSceneRect = sceneRect();
QRectF visibleRect = mapToScene( viewport()->rect() ).boundingRect();
viewSceneRect.setLeft( std::min( modelSceneRect.left(), visibleRect.left() ) );
viewSceneRect.setRight( std::max( modelSceneRect.right(), visibleRect.right() ) );
viewSceneRect.setTop( std::min( modelSceneRect.top(), visibleRect.top() ) );
viewSceneRect.setBottom( std::max( modelSceneRect.bottom(), visibleRect.bottom() ) );
setSceneRect( viewSceneRect );
}
void QgsModelGraphicsView::copySelectedItems( QgsModelGraphicsView::ClipboardOperation operation ) void QgsModelGraphicsView::copySelectedItems( QgsModelGraphicsView::ClipboardOperation operation )
{ {
copyItems( modelScene()->selectedComponentItems(), operation ); copyItems( modelScene()->selectedComponentItems(), operation );

View File

@ -66,6 +66,16 @@ class GUI_EXPORT QgsModelGraphicsView : public QGraphicsView
*/ */
void setModelScene( QgsModelGraphicsScene *scene ); void setModelScene( QgsModelGraphicsScene *scene );
/**
* Sets the scene rect used for scrollbar without disturbing the user
* i.e:
* - We growth the scene rect as the model growth
* - We shrink only if the model scene rect is outside the current viewed viewport
*
* Called each time the view viewport moved or the model scene changed
*/
void friendlySetSceneRect();
/** /**
* Returns the scene associated with the tool. * Returns the scene associated with the tool.
* \see view() * \see view()
@ -158,12 +168,10 @@ class GUI_EXPORT QgsModelGraphicsView : public QGraphicsView
void pasteItems( PasteMode mode ); void pasteItems( PasteMode mode );
public slots: public slots:
/** /**
* Snaps the selected items to the grid. * Snaps the selected items to the grid.
*/ */
void snapSelected(); void snapSelected();
signals: signals:
/** /**

View File

@ -81,6 +81,7 @@ void QgsModelViewToolPan::modelReleaseEvent( QgsModelViewMouseEvent *event )
mIsPanning = false; mIsPanning = false;
view()->viewport()->setCursor( Qt::OpenHandCursor ); view()->viewport()->setCursor( Qt::OpenHandCursor );
view()->friendlySetSceneRect();
} }
void QgsModelViewToolPan::deactivate() void QgsModelViewToolPan::deactivate()

View File

@ -37,6 +37,7 @@ void QgsModelViewToolTemporaryKeyPan::keyReleaseEvent( QKeyEvent *event )
if ( event->key() == Qt::Key_Space && !event->isAutoRepeat() ) if ( event->key() == Qt::Key_Space && !event->isAutoRepeat() )
{ {
view()->setTool( mPreviousViewTool ); view()->setTool( mPreviousViewTool );
view()->friendlySetSceneRect();
} }
} }

View File

@ -37,6 +37,7 @@ void QgsModelViewToolTemporaryMousePan::modelReleaseEvent( QgsModelViewMouseEven
if ( event->button() == Qt::MiddleButton ) if ( event->button() == Qt::MiddleButton )
{ {
view()->setTool( mPreviousViewTool ); view()->setTool( mPreviousViewTool );
view()->friendlySetSceneRect();
} }
} }

View File

@ -11455,6 +11455,42 @@ void TestProcessingGui::testModelGraphicsView()
// should not exist // should not exist
QVERIFY( !layerCommentItem ); QVERIFY( !layerCommentItem );
//check model bounds
scene2.updateBounds();
QRectF modelRect = scene2.sceneRect();
QGSCOMPARENEAR( modelRect.height(), 624.4, 3 ); // Sligtly higher threeshold because of various font size can marginally change the bounding rect
QGSCOMPARENEAR( modelRect.width(), 655.00, 0.01 );
QGSCOMPARENEAR( modelRect.left(), -252.0, 0.01 );
QGSCOMPARENEAR( modelRect.top(), -232.0, 0.01 );
// test model large modelRect
QgsProcessingModelAlgorithm model2;
QgsProcessingModelChildAlgorithm algc2;
algc2.setChildId( "buffer" );
algc2.setAlgorithmId( "native:buffer" );
algc2.setPosition( QPointF( 4250, 4250 ) );
QgsProcessingModelParameter param1;
param1.setParameterName( QStringLiteral( "LAYER" ) );
param1.setSize( QSizeF( 500, 400 ) );
param1.setPosition( QPointF( -250, -250 ) );
model2.addModelParameter( new QgsProcessingParameterMapLayer( QStringLiteral( "LAYER" ) ), param );
algc2.addParameterSources( QStringLiteral( "INPUT" ), QList<QgsProcessingModelChildParameterSource>() << QgsProcessingModelChildParameterSource::fromModelParameter( QStringLiteral( "LAYER" ) ) );
model2.addChildAlgorithm( algc2 );
QgsModelGraphicsScene scene3;
scene3.setModel( &model2 );
scene3.createItems( &model2, context );
scene3.updateBounds();
QRectF modelRect2 = scene3.sceneRect();
QGSCOMPARENEAR( modelRect2.height(), 4505.4, 3 ); // Sligtly higher threeshold because of various font size can marginally change the bounding rect
QGSCOMPARENEAR( modelRect2.width(), 4603.0, 0.01 );
QGSCOMPARENEAR( modelRect2.left(), -201.0, 0.01 );
QGSCOMPARENEAR( modelRect2.top(), -150.0, 0.01 );
QgsModelGraphicsScene scene; QgsModelGraphicsScene scene;
QVERIFY( !scene.model() ); QVERIFY( !scene.model() );