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
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
%End

View File

@ -57,6 +57,15 @@ widget.
void setModelScene( QgsModelGraphicsScene *scene );
%Docstring
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
QgsModelGraphicsScene *modelScene() const;
@ -134,12 +143,10 @@ Pastes items from clipboard, using the specified ``mode``.
%End
public slots:
void snapSelected();
%Docstring
Snaps the selected items to the grid.
%End
signals:
void algorithmDropped( const QString &algorithmId, const QPointF &pos );

View File

@ -173,6 +173,16 @@ full details (``longMessage``).
%Docstring
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
%End

View File

@ -57,6 +57,15 @@ widget.
void setModelScene( QgsModelGraphicsScene *scene );
%Docstring
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
QgsModelGraphicsScene *modelScene() const;
@ -134,12 +143,10 @@ Pastes items from clipboard, using the specified ``mode``.
%End
public slots:
void snapSelected();
%Docstring
Snaps the selected items to the grid.
%End
signals:
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):
CANVAS_SIZE = 4000
update_model = pyqtSignal()
@ -97,7 +96,6 @@ class ModelerDialog(QgsModelDesignerDialog):
self.setStyleSheet(iface.mainWindow().styleSheet())
scene = ModelerScene(self)
scene.setSceneRect(QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE))
self.setModelScene(scene)
self.view().ensureVisible(0, 0, 10, 10)
@ -244,8 +242,6 @@ class ModelerDialog(QgsModelDesignerDialog):
def repaintModel(self, showControls=True):
scene = ModelerScene(self)
scene.setSceneRect(QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE))
if not showControls:
scene.setFlag(QgsModelGraphicsScene.Flag.FlagHideControls)
@ -259,6 +255,7 @@ class ModelerDialog(QgsModelDesignerDialog):
self.setModelScene(scene)
# create items later that setModelScene to setup link to messageBar to the scene
scene.createItems(self.model(), context)
scene.updateBounds()
def create_widget_context(self):
"""
@ -347,7 +344,7 @@ class ModelerDialog(QgsModelDesignerDialog):
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:
newX = MARGIN + BOX_WIDTH / 2
return QPointF(newX, MARGIN + BOX_HEIGHT / 2)
@ -415,8 +412,8 @@ class ModelerDialog(QgsModelDesignerDialog):
for alg in list(self.model().childAlgorithms().values())
]
)
newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH)
newY = min(MARGIN + BOX_HEIGHT + maxY, self.CANVAS_SIZE - BOX_HEIGHT)
newX = MARGIN + BOX_WIDTH + maxX
newY = MARGIN + BOX_HEIGHT + maxY
else:
newX = MARGIN + BOX_WIDTH / 2
newY = MARGIN * 2 + BOX_HEIGHT + BOX_HEIGHT / 2

View File

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

View File

@ -33,6 +33,8 @@ QgsModelGraphicsScene::QgsModelGraphicsScene( QObject *parent )
: QGraphicsScene( parent )
{
setItemIndexMethod( QGraphicsScene::NoIndex );
connect( this, &QgsModelGraphicsScene::componentChanged, this, &QgsModelGraphicsScene::updateBounds );
}
QgsProcessingModelAlgorithm *QgsModelGraphicsScene::model()
@ -60,6 +62,28 @@ void QgsModelGraphicsScene::mousePressEvent( QGraphicsSceneMouseEvent *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
{
return new QgsModelParameterGraphicItem( param, model, nullptr );

View File

@ -181,6 +181,15 @@ class GUI_EXPORT QgsModelGraphicsScene : public QGraphicsScene
*/
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:
/**
@ -282,6 +291,8 @@ class GUI_EXPORT QgsModelGraphicsScene : public QGraphicsScene
QMap<QString, QgsModelComponentGraphicItem *> mGroupBoxItems;
QgsProcessingModelResult mLastResult;
static constexpr int SCENE_COMPONENT_MARGIN = 50;
QgsMessageBar *mMessageBar = nullptr;
};

View File

@ -52,6 +52,9 @@ QgsModelGraphicsView::QgsModelGraphicsView( QWidget *parent )
mMidMouseButtonPanTool = new QgsModelViewToolTemporaryMousePan( this );
mSpaceZoomTool = new QgsModelViewToolTemporaryKeyZoom( this );
connect( horizontalScrollBar(), &QScrollBar::sliderReleased, this, [=] { friendlySetSceneRect(); } );
connect( verticalScrollBar(), &QScrollBar::sliderReleased, this, [=] { friendlySetSceneRect(); } );
mSnapper.setSnapToGrid( true );
}
@ -397,6 +400,8 @@ void QgsModelGraphicsView::setModelScene( QgsModelGraphicsScene *scene )
{
setScene( scene );
connect( scene, &QgsModelGraphicsScene::sceneRectChanged, this, [=] { friendlySetSceneRect(); } );
// IMPORTANT!
// previous snap markers, snap lines are owned by previous layout - so don't delete them here!
mSnapMarker = new QgsModelViewSnapMarker();
@ -468,7 +473,6 @@ void QgsModelGraphicsView::endCommand()
emit commandEnded();
}
void QgsModelGraphicsView::snapSelected()
{
QgsModelGraphicsScene *s = modelScene();
@ -492,6 +496,21 @@ void QgsModelGraphicsView::snapSelected()
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 )
{
copyItems( modelScene()->selectedComponentItems(), operation );

View File

@ -66,6 +66,16 @@ class GUI_EXPORT QgsModelGraphicsView : public QGraphicsView
*/
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.
* \see view()
@ -158,12 +168,10 @@ class GUI_EXPORT QgsModelGraphicsView : public QGraphicsView
void pasteItems( PasteMode mode );
public slots:
/**
* Snaps the selected items to the grid.
*/
void snapSelected();
signals:
/**

View File

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

View File

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

View File

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

View File

@ -11455,6 +11455,42 @@ void TestProcessingGui::testModelGraphicsView()
// should not exist
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;
QVERIFY( !scene.model() );