Skip to content

Commit

Permalink
Port most of the remaining ModelerScene code to c++
Browse files Browse the repository at this point in the history
ModelerScene is now a shell class, implementing the bare minimum
required for older Python API compatibility
  • Loading branch information
nyalldawson committed Mar 5, 2020
1 parent ef8f26e commit 70731d8
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 157 deletions.
Expand Up @@ -76,6 +76,11 @@ Returns the current combination of flags set for the scene.
virtual void mousePressEvent( QGraphicsSceneMouseEvent *event );


void createItems( QgsProcessingModelAlgorithm *model, QgsProcessingContext &context );
%Docstring
Populates the scene by creating items representing the specified ``model``.
%End

signals:

void rebuildRequired();
Expand All @@ -90,9 +95,20 @@ Emitted whenever a component of the model is changed.

protected:

virtual QgsModelComponentGraphicItem *createParameterGraphicItem( QgsProcessingModelParameter *param ) const /Factory/;
virtual QgsModelComponentGraphicItem *createChildAlgGraphicItem( QgsProcessingModelChildAlgorithm *child ) const /Factory/;
virtual QgsModelComponentGraphicItem *createOutputGraphicItem( QgsProcessingModelOutput *output ) const /Factory/;
virtual QgsModelComponentGraphicItem *createParameterGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelParameter *param ) const /Factory/;
%Docstring
Creates a new graphic item for a model parameter.
%End

virtual QgsModelComponentGraphicItem *createChildAlgGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelChildAlgorithm *child ) const /Factory/;
%Docstring
Creates a new graphic item for a model child algorithm.
%End

virtual QgsModelComponentGraphicItem *createOutputGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelOutput *output ) const /Factory/;
%Docstring
Creates a new graphic item for a model output.
%End

};

Expand Down
6 changes: 3 additions & 3 deletions python/plugins/processing/modeler/ModelerDialog.py
Expand Up @@ -23,7 +23,6 @@

import codecs
import sys
import operator
import os
import warnings

Expand Down Expand Up @@ -73,7 +72,6 @@
QgsProject,
QgsSettings,
QgsMessageLog,
QgsProcessingUtils,
QgsProcessingModelAlgorithm,
QgsProcessingModelParameter,
QgsProcessingParameterType,
Expand Down Expand Up @@ -789,7 +787,9 @@ def repaintModel(self, controls=True):

if not controls:
self.scene.setFlag(QgsModelGraphicsScene.FlagHideControls)
self.scene.paintModel(self.model)

context = createContext()
self.scene.createItems(self.model, context)
self.view.setScene(self.scene)

self.scene.rebuildRequired.connect(self.repaintModel)
Expand Down
136 changes: 13 additions & 123 deletions python/plugins/processing/modeler/ModelerScene.py
Expand Up @@ -21,140 +21,30 @@
__date__ = 'August 2012'
__copyright__ = '(C) 2012, Victor Olaya'

from qgis.PyQt.QtCore import QPointF, Qt
from qgis.core import (QgsProcessingParameterDefinition,
QgsProcessingModelChildParameterSource,
QgsExpression)
from qgis.gui import (
QgsModelGraphicsScene,
QgsModelArrowItem
)
from qgis.gui import QgsModelGraphicsScene
from processing.modeler.ModelerGraphicItem import (
ModelerInputGraphicItem,
ModelerOutputGraphicItem,
ModelerChildAlgorithmGraphicItem
)
from processing.tools.dataobjects import createContext


class ModelerScene(QgsModelGraphicsScene):
"""
IMPORTANT! This is intentionally a MINIMAL class, only containing code which HAS TO BE HERE
because it contains Python code for compatibility with deprecated methods ONLY.
Don't add anything here -- edit the c++ base class instead!
"""

def __init__(self, parent=None):
super().__init__(parent)
self.paramItems = {}
self.algItems = {}
self.outputItems = {}

def getItemsFromParamValue(self, value, child_id, context):
items = []
if isinstance(value, list):
for v in value:
items.extend(self.getItemsFromParamValue(v, child_id, context))
elif isinstance(value, QgsProcessingModelChildParameterSource):
if value.source() == QgsProcessingModelChildParameterSource.ModelParameter:
items.append((self.paramItems[value.parameterName()], None, 0))
elif value.source() == QgsProcessingModelChildParameterSource.ChildOutput:
outputs = self.model.childAlgorithm(value.outputChildId()).algorithm().outputDefinitions()
for i, out in enumerate(outputs):
if out.name() == value.outputName():
break
if value.outputChildId() in self.algItems:
items.append((self.algItems[value.outputChildId()], Qt.BottomEdge, i))
elif value.source() == QgsProcessingModelChildParameterSource.Expression:
variables = self.model.variablesForChildAlgorithm(child_id, context)
exp = QgsExpression(value.expression())
for v in exp.referencedVariables():
if v in variables:
items.extend(self.getItemsFromParamValue(variables[v].source, child_id, context))
return items

def paintModel(self, model):
self.model = model
context = createContext()
# Inputs
for inp in list(model.parameterComponents().values()):
item = ModelerInputGraphicItem(inp.clone(), model)
self.addItem(item)
item.setPos(inp.position().x(), inp.position().y())
self.paramItems[inp.parameterName()] = item

item.requestModelRepaint.connect(self.rebuildRequired)
item.changed.connect(self.componentChanged)

# Input dependency arrows
for input_name in list(model.parameterComponents().keys()):
parameter_def = model.parameterDefinition(input_name)
for parent_name in parameter_def.dependsOnOtherParameters():
if input_name in self.paramItems and parent_name in self.paramItems:
input_item = self.paramItems[input_name]
parent_item = self.paramItems[parent_name]
arrow = QgsModelArrowItem(parent_item, input_item)
arrow.setPenStyle(Qt.DotLine)
self.addItem(arrow)

# We add the algs
for alg in list(model.childAlgorithms().values()):
item = ModelerChildAlgorithmGraphicItem(alg.clone(), model)
self.addItem(item)
item.setPos(alg.position().x(), alg.position().y())
self.algItems[alg.childId()] = item

item.requestModelRepaint.connect(self.rebuildRequired)
item.changed.connect(self.componentChanged)

# And then the arrows

for alg in list(model.childAlgorithms().values()):
idx = 0
for parameter in alg.algorithm().parameterDefinitions():
if not parameter.isDestination() and not parameter.flags() & QgsProcessingParameterDefinition.FlagHidden:
if parameter.name() in alg.parameterSources():
sources = alg.parameterSources()[parameter.name()]
else:
sources = []
for source in sources:
sourceItems = self.getItemsFromParamValue(source, alg.childId(), context)
for sourceItem, sourceEdge, sourceIdx in sourceItems:
if sourceEdge is None:
arrow = QgsModelArrowItem(sourceItem, self.algItems[alg.childId()], Qt.TopEdge, idx)
else:
arrow = QgsModelArrowItem(sourceItem, sourceEdge, sourceIdx, self.algItems[alg.childId()], Qt.TopEdge, idx)
self.addItem(arrow)
idx += 1
for depend in alg.dependencies():
arrow = QgsModelArrowItem(self.algItems[depend], self.algItems[alg.childId()])
self.addItem(arrow)

# And finally the outputs
for alg in list(model.childAlgorithms().values()):
outputs = alg.modelOutputs()
outputItems = {}

for key, out in outputs.items():
if out is not None:
item = ModelerOutputGraphicItem(out.clone(), model)
item.requestModelRepaint.connect(self.rebuildRequired)
item.changed.connect(self.componentChanged)

self.addItem(item)
pos = out.position()

# find the actual index of the linked output from the child algorithm it comes from
source_child_alg_outputs = alg.algorithm().outputDefinitions()
idx = -1
for i, child_alg_output in enumerate(source_child_alg_outputs):
if child_alg_output.name() == out.childOutputName():
idx = i
break
def createParameterGraphicItem(self, model, param):
return ModelerInputGraphicItem(param.clone(), model)

if pos is None:
pos = (alg.position() + QPointF(alg.size().width(), 0)
+ self.algItems[alg.childId()].linkPoint(Qt.BottomEdge, idx))
item.setPos(pos)
outputItems[key] = item
def createChildAlgGraphicItem(self, model, child):
return ModelerChildAlgorithmGraphicItem(child.clone(), model)

arrow = QgsModelArrowItem(self.algItems[alg.childId()], Qt.BottomEdge, idx, item)
self.addItem(arrow)
else:
outputItems[key] = None
self.outputItems[alg.childId()] = outputItems
def createOutputGraphicItem(self, model, output):
return ModelerOutputGraphicItem(output.clone(), model)
58 changes: 39 additions & 19 deletions src/gui/processing/models/qgsmodelgraphicsscene.cpp
Expand Up @@ -43,45 +43,64 @@ void QgsModelGraphicsScene::mousePressEvent( QGraphicsSceneMouseEvent *event )
QGraphicsScene::mousePressEvent( event );
}

QgsModelComponentGraphicItem *QgsModelGraphicsScene::createParameterGraphicItem( QgsProcessingModelParameter *param ) const
QgsModelComponentGraphicItem *QgsModelGraphicsScene::createParameterGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelParameter *param ) const
{
return new QgsModelParameterGraphicItem( param, mModel, nullptr );
return new QgsModelParameterGraphicItem( param, model, nullptr );
}

QgsModelComponentGraphicItem *QgsModelGraphicsScene::createChildAlgGraphicItem( QgsProcessingModelChildAlgorithm *child ) const
QgsModelComponentGraphicItem *QgsModelGraphicsScene::createChildAlgGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelChildAlgorithm *child ) const
{
return new QgsModelChildAlgorithmGraphicItem( child, mModel, nullptr );
return new QgsModelChildAlgorithmGraphicItem( child, model, nullptr );
}

QgsModelComponentGraphicItem *QgsModelGraphicsScene::createOutputGraphicItem( QgsProcessingModelOutput *output ) const
QgsModelComponentGraphicItem *QgsModelGraphicsScene::createOutputGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelOutput *output ) const
{
return new QgsModelOutputGraphicItem( output, mModel, nullptr );
return new QgsModelOutputGraphicItem( output, model, nullptr );
}

void QgsModelGraphicsScene::createItems( QgsProcessingContext &context )
void QgsModelGraphicsScene::createItems( QgsProcessingModelAlgorithm *model, QgsProcessingContext &context )
{
const QMap<QString, QgsProcessingModelParameter> params = mModel->parameterComponents();
// model input parameters
const QMap<QString, QgsProcessingModelParameter> params = model->parameterComponents();
for ( auto it = params.constBegin(); it != params.constEnd(); ++it )
{
QgsModelComponentGraphicItem *item = createParameterGraphicItem( it.value().clone() );
QgsModelComponentGraphicItem *item = createParameterGraphicItem( model, it.value().clone() );
addItem( item );
item->setPos( it.value().position().x(), it.value().position().y() );
mParameterItems.insert( it.value().parameterName(), item );
connect( item, &QgsModelComponentGraphicItem::requestModelRepaint, this, &QgsModelGraphicsScene::rebuildRequired );
connect( item, &QgsModelComponentGraphicItem::changed, this, &QgsModelGraphicsScene::componentChanged );
}

const QMap<QString, QgsProcessingModelChildAlgorithm> childAlgs = mModel->childAlgorithms();
// input dependency arrows
for ( auto it = params.constBegin(); it != params.constEnd(); ++it )
{
const QgsProcessingParameterDefinition *parameterDef = model->parameterDefinition( it.key() );
const QStringList parameterLinks = parameterDef->dependsOnOtherParameters();
for ( const QString &otherName : parameterLinks )
{
if ( mParameterItems.contains( it.key() ) && mParameterItems.contains( otherName ) )
{
std::unique_ptr< QgsModelArrowItem > arrow = qgis::make_unique< QgsModelArrowItem >( mParameterItems.value( otherName ), mParameterItems.value( it.key() ) );
arrow->setPenStyle( Qt::DotLine );
addItem( arrow.release() );
}
}
}

// child algorithms
const QMap<QString, QgsProcessingModelChildAlgorithm> childAlgs = model->childAlgorithms();
for ( auto it = childAlgs.constBegin(); it != childAlgs.constEnd(); ++it )
{
QgsModelComponentGraphicItem *item = createChildAlgGraphicItem( it.value().clone() );
QgsModelComponentGraphicItem *item = createChildAlgGraphicItem( model, it.value().clone() );
addItem( item );
item->setPos( it.value().position().x(), it.value().position().y() );
mChildAlgorithmItems.insert( it.value().childId(), item );
connect( item, &QgsModelComponentGraphicItem::requestModelRepaint, this, &QgsModelGraphicsScene::rebuildRequired );
connect( item, &QgsModelComponentGraphicItem::changed, this, &QgsModelGraphicsScene::componentChanged );
}

// arrows linking child algorithms
for ( auto it = childAlgs.constBegin(); it != childAlgs.constEnd(); ++it )
{
int idx = 0;
Expand All @@ -95,7 +114,7 @@ void QgsModelGraphicsScene::createItems( QgsProcessingContext &context )
sources = it.value().parameterSources()[parameter->name()];
for ( const QgsProcessingModelChildParameterSource &source : sources )
{
const QList< LinkSource > sourceItems = linkSourcesForParameterValue( QVariant::fromValue( source ), it.value().childId(), context );
const QList< LinkSource > sourceItems = linkSourcesForParameterValue( model, QVariant::fromValue( source ), it.value().childId(), context );
for ( const LinkSource &link : sourceItems )
{
QgsModelArrowItem *arrow = nullptr;
Expand All @@ -116,14 +135,15 @@ void QgsModelGraphicsScene::createItems( QgsProcessingContext &context )
}
}

// and finally the model outputs
for ( auto it = childAlgs.constBegin(); it != childAlgs.constEnd(); ++it )
{
const QMap<QString, QgsProcessingModelOutput> outputs = it.value().modelOutputs();
QMap< QString, QgsModelComponentGraphicItem * > outputItems;

for ( auto outputIt = outputs.constBegin(); outputIt != outputs.constEnd(); ++outputIt )
{
QgsModelComponentGraphicItem *item = createOutputGraphicItem( outputIt.value().clone() );
QgsModelComponentGraphicItem *item = createOutputGraphicItem( model, outputIt.value().clone() );
addItem( item );
connect( item, &QgsModelComponentGraphicItem::requestModelRepaint, this, &QgsModelGraphicsScene::rebuildRequired );
connect( item, &QgsModelComponentGraphicItem::changed, this, &QgsModelGraphicsScene::componentChanged );
Expand Down Expand Up @@ -157,20 +177,20 @@ void QgsModelGraphicsScene::createItems( QgsProcessingContext &context )
}
}

QList<QgsModelGraphicsScene::LinkSource> QgsModelGraphicsScene::linkSourcesForParameterValue( const QVariant &value, const QString &childId, QgsProcessingContext &context ) const
QList<QgsModelGraphicsScene::LinkSource> QgsModelGraphicsScene::linkSourcesForParameterValue( QgsProcessingModelAlgorithm *model, const QVariant &value, const QString &childId, QgsProcessingContext &context ) const
{
QList<QgsModelGraphicsScene::LinkSource> res;
if ( value.type() == QVariant::List )
{
const QVariantList list = value.toList();
for ( const QVariant &v : list )
res.append( linkSourcesForParameterValue( v, childId, context ) );
res.append( linkSourcesForParameterValue( model, v, childId, context ) );
}
else if ( value.type() == QVariant::StringList )
{
const QStringList list = value.toStringList();
for ( const QString &v : list )
res.append( linkSourcesForParameterValue( v, childId, context ) );
res.append( linkSourcesForParameterValue( model, v, childId, context ) );
}
else if ( value.canConvert< QgsProcessingModelChildParameterSource >() )
{
Expand All @@ -186,7 +206,7 @@ QList<QgsModelGraphicsScene::LinkSource> QgsModelGraphicsScene::linkSourcesForPa
}
case QgsProcessingModelChildParameterSource::ChildOutput:
{
const QgsProcessingOutputDefinitions outputs = mModel->childAlgorithm( source.outputChildId() ).algorithm()->outputDefinitions();
const QgsProcessingOutputDefinitions outputs = model->childAlgorithm( source.outputChildId() ).algorithm()->outputDefinitions();
int i = 0;
for ( const QgsProcessingOutputDefinition *output : outputs )
{
Expand All @@ -208,14 +228,14 @@ QList<QgsModelGraphicsScene::LinkSource> QgsModelGraphicsScene::linkSourcesForPa

case QgsProcessingModelChildParameterSource::Expression:
{
const QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> variables = mModel->variablesForChildAlgorithm( childId, context );
const QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> variables = model->variablesForChildAlgorithm( childId, context );
QgsExpression exp( source.expression() );
const QSet<QString> vars = exp.referencedVariables();
for ( const QString &v : vars )
{
if ( variables.contains( v ) )
{
res.append( linkSourcesForParameterValue( QVariant::fromValue( variables.value( v ).source ), childId, context ) );
res.append( linkSourcesForParameterValue( model, QVariant::fromValue( variables.value( v ).source ), childId, context ) );
}
}
break;
Expand Down

0 comments on commit 70731d8

Please sign in to comment.