Skip to content

Commit bf19eb6

Browse files
committedJan 25, 2018
[processing] Non-filed based outputs (e.g. postgis, geopackage)
options should be available for certain model outputs and script algorithm outputs We do this by swapping the test for non-file based output support from checking only the algorithm's provider to instead checking on a parameter-by-parameter basis. This is done in order to support models. For models, depending on what child algorithm a model output is based off, an individual model may or may not have support for non-file based outputs. E.g a model may generate outputs from a native qgis alg (supporting these outputs) AND an output from a GDAL alg (with no support for these outputs). In this case we need to enable or disable the ui controls for non-file based outputs on an individual output basis. For scripts (for now) we blindly just say all outputs support non-file based formats. This is going to be the case most of the time, since scripts will usually be written using PyQGIS API. For the exceptions (e.g. scripts which call other algs like GDAL algs) we probably should add some way for the script to indicate whether an individual output supports this, but for now we just say they all do. Fixes #17949
1 parent 723e0a1 commit bf19eb6

File tree

10 files changed

+84
-27
lines changed

10 files changed

+84
-27
lines changed
 

‎python/core/processing/qgsprocessingparameters.sip.in

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1775,20 +1775,20 @@ Returns a new QgsProcessingOutputDefinition corresponding to the definition of t
17751775
parameter.
17761776
%End
17771777

1778-
bool supportsNonFileBasedOutputs() const;
1778+
bool supportsNonFileBasedOutput() const;
17791779
%Docstring
17801780
Returns true if the destination parameter supports non filed-based outputs,
17811781
such as memory layers or direct database outputs.
17821782

1783-
.. seealso:: :py:func:`setSupportsNonFileBasedOutputs`
1783+
.. seealso:: :py:func:`setSupportsNonFileBasedOutput`
17841784
%End
17851785

1786-
void setSupportsNonFileBasedOutputs( bool supportsNonFileBasedOutputs );
1786+
void setSupportsNonFileBasedOutput( bool supportsNonFileBasedOutput );
17871787
%Docstring
17881788
Sets whether the destination parameter supports non filed-based outputs,
17891789
such as memory layers or direct database outputs.
17901790

1791-
.. seealso:: :py:func:`supportsNonFileBasedOutputs`
1791+
.. seealso:: :py:func:`supportsNonFileBasedOutput`
17921792
%End
17931793

17941794
virtual QString defaultFileExtension() const = 0;

‎python/core/processing/qgsprocessingprovider.sip.in

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,10 @@ Otherwise the first reported supported raster format will be used.
143143
virtual bool supportsNonFileBasedOutput() const;
144144
%Docstring
145145
Returns true if the provider supports non-file based outputs (such as memory layers
146-
or direct database outputs).
146+
or direct database outputs). If a provider returns false for this method than it
147+
indicates that none of the outputs from any of the provider's algorithms have
148+
support for non-file based outputs. Returning true indicates that the algorithm's
149+
parameters will each individually declare their non-file based support.
147150

148151
.. seealso:: :py:func:`supportedOutputVectorLayerExtensions`
149152
%End

‎python/plugins/processing/gui/DestinationSelectionPanel.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ def __init__(self, parameter, alg):
7777
self.leText.setPlaceholderText(self.SKIP_OUTPUT)
7878
self.use_temporary = False
7979
elif isinstance(self.parameter, QgsProcessingParameterFeatureSink) \
80-
and alg.provider().supportsNonFileBasedOutput():
80+
and self.parameter.supportsNonFileBasedOutput():
8181
# use memory layers for temporary files if supported
8282
self.leText.setPlaceholderText(self.SAVE_TO_TEMP_LAYER)
8383
elif not isinstance(self.parameter, QgsProcessingParameterFolderDestination):
@@ -107,7 +107,7 @@ def selectOutput(self):
107107
popupMenu.addAction(actionSkipOutput)
108108

109109
if isinstance(self.parameter, QgsProcessingParameterFeatureSink) \
110-
and self.alg.provider().supportsNonFileBasedOutput():
110+
and self.parameter.supportsNonFileBasedOutput():
111111
# use memory layers for temporary layers if supported
112112
actionSaveToTemp = QAction(
113113
self.tr('Create temporary layer'), self.btnSelect)
@@ -123,7 +123,7 @@ def selectOutput(self):
123123
popupMenu.addAction(actionSaveToFile)
124124

125125
if isinstance(self.parameter, QgsProcessingParameterFeatureSink) \
126-
and self.alg.provider().supportsNonFileBasedOutput():
126+
and self.parameter.supportsNonFileBasedOutput():
127127
actionSaveToGpkg = QAction(
128128
self.tr('Save to GeoPackage...'), self.btnSelect)
129129
actionSaveToGpkg.triggered.connect(self.saveToGeopackage)
@@ -146,7 +146,7 @@ def selectOutput(self):
146146
popupMenu.exec_(QCursor.pos())
147147

148148
def saveToTemporary(self):
149-
if isinstance(self.parameter, QgsProcessingParameterFeatureSink) and self.alg.provider().supportsNonFileBasedOutput():
149+
if isinstance(self.parameter, QgsProcessingParameterFeatureSink) and self.parameter.supportsNonFileBasedOutput():
150150
self.leText.setPlaceholderText(self.SAVE_TO_TEMP_LAYER)
151151
else:
152152
self.leText.setPlaceholderText(self.SAVE_TO_TEMP_FILE)
@@ -188,7 +188,7 @@ def saveToGeopackage(self):
188188
filename, filter = QFileDialog.getSaveFileName(self, self.tr("Save to GeoPackage"), path,
189189
file_filter, options=QFileDialog.DontConfirmOverwrite)
190190

191-
if filename is None:
191+
if not filename:
192192
return
193193

194194
layer_name, ok = QInputDialog.getText(self, self.tr('Save to GeoPackage'), self.tr('Layer name'), text=self.parameter.name().lower())

‎python/plugins/processing/modeler/ModelerAlgorithmProvider.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ def icon(self):
9292
def svgIconPath(self):
9393
return QgsApplication.iconPath("processingModel.svg")
9494

95+
def supportsNonFileBasedOutput(self):
96+
return True
97+
9598
def loadAlgorithms(self):
9699
self.algs = []
97100
folders = ModelerUtils.modelsFolders()

‎python/plugins/processing/script/ScriptAlgorithmProvider.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,10 @@ def loadAlgorithms(self):
9999

100100
def addAlgorithmsFromFolder(self, folder):
101101
self.folder_algorithms.extend(ScriptUtils.loadFromFolder(folder))
102+
103+
def supportsNonFileBasedOutput(self):
104+
# TODO - this may not be strictly true. We probably need a way for scripts
105+
# to indicate whether individual outputs support non-file based outputs,
106+
# but for now allow it. At best we expose nice features to users, at worst
107+
# they'll get an error if they use them with incompatible outputs...
108+
return True

‎src/core/processing/qgsprocessingalgorithm.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,16 @@ void QgsProcessingAlgorithm::setProvider( QgsProcessingProvider *provider )
107107
{
108108
mProvider = provider;
109109

110-
// need to update all destination parameters to set whether the provider supports non file based outputs
111-
Q_FOREACH ( const QgsProcessingParameterDefinition *definition, mParameters )
110+
if ( !mProvider->supportsNonFileBasedOutput() )
112111
{
113-
if ( definition->isDestination() && mProvider )
112+
// need to update all destination parameters to turn off non file based outputs
113+
Q_FOREACH ( const QgsProcessingParameterDefinition *definition, mParameters )
114114
{
115-
const QgsProcessingDestinationParameter *destParam = static_cast< const QgsProcessingDestinationParameter *>( definition );
116-
const_cast< QgsProcessingDestinationParameter *>( destParam )->setSupportsNonFileBasedOutputs( mProvider->supportsNonFileBasedOutput() );
115+
if ( definition->isDestination() && mProvider )
116+
{
117+
const QgsProcessingDestinationParameter *destParam = static_cast< const QgsProcessingDestinationParameter *>( definition );
118+
const_cast< QgsProcessingDestinationParameter *>( destParam )->setSupportsNonFileBasedOutput( false );
119+
}
117120
}
118121
}
119122
}
@@ -246,7 +249,8 @@ bool QgsProcessingAlgorithm::addParameter( QgsProcessingParameterDefinition *def
246249
if ( definition->isDestination() && mProvider )
247250
{
248251
QgsProcessingDestinationParameter *destParam = static_cast< QgsProcessingDestinationParameter *>( definition );
249-
destParam->setSupportsNonFileBasedOutputs( mProvider->supportsNonFileBasedOutput() );
252+
if ( !mProvider->supportsNonFileBasedOutput() )
253+
destParam->setSupportsNonFileBasedOutput( false );
250254
}
251255

252256
mParameters << definition;

‎src/core/processing/qgsprocessingparameters.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3209,7 +3209,7 @@ bool QgsProcessingParameterFeatureSink::fromVariantMap( const QVariantMap &map )
32093209

32103210
QString QgsProcessingParameterFeatureSink::generateTemporaryDestination() const
32113211
{
3212-
if ( supportsNonFileBasedOutputs() )
3212+
if ( supportsNonFileBasedOutput() )
32133213
return QStringLiteral( "memory:%1" ).arg( description() );
32143214
else
32153215
return QgsProcessingDestinationParameter::generateTemporaryDestination();

‎src/core/processing/qgsprocessingparameters.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1720,16 +1720,16 @@ class CORE_EXPORT QgsProcessingDestinationParameter : public QgsProcessingParame
17201720
/**
17211721
* Returns true if the destination parameter supports non filed-based outputs,
17221722
* such as memory layers or direct database outputs.
1723-
* \see setSupportsNonFileBasedOutputs()
1723+
* \see setSupportsNonFileBasedOutput()
17241724
*/
1725-
bool supportsNonFileBasedOutputs() const { return mSupportsNonFileBasedOutputs; }
1725+
bool supportsNonFileBasedOutput() const { return mSupportsNonFileBasedOutputs; }
17261726

17271727
/**
17281728
* Sets whether the destination parameter supports non filed-based outputs,
17291729
* such as memory layers or direct database outputs.
1730-
* \see supportsNonFileBasedOutputs()
1730+
* \see supportsNonFileBasedOutput()
17311731
*/
1732-
void setSupportsNonFileBasedOutputs( bool supportsNonFileBasedOutputs ) { mSupportsNonFileBasedOutputs = supportsNonFileBasedOutputs; }
1732+
void setSupportsNonFileBasedOutput( bool supportsNonFileBasedOutput ) { mSupportsNonFileBasedOutputs = supportsNonFileBasedOutput; }
17331733

17341734
/**
17351735
* Returns the default file extension for destination file paths

‎src/core/processing/qgsprocessingprovider.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,10 @@ class CORE_EXPORT QgsProcessingProvider : public QObject
146146

147147
/**
148148
* Returns true if the provider supports non-file based outputs (such as memory layers
149-
* or direct database outputs).
149+
* or direct database outputs). If a provider returns false for this method than it
150+
* indicates that none of the outputs from any of the provider's algorithms have
151+
* support for non-file based outputs. Returning true indicates that the algorithm's
152+
* parameters will each individually declare their non-file based support.
150153
* \see supportedOutputVectorLayerExtensions()
151154
*/
152155
virtual bool supportsNonFileBasedOutput() const { return false; }

‎tests/src/analysis/testqgsprocessing.cpp

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,16 @@ class DummyAlgorithm : public QgsProcessingAlgorithm
275275
QCOMPARE( asPythonCommand( params, context ), QStringLiteral( "processing.run(\"test\", {'p1':'a','p2':'b'})" ) );
276276
}
277277

278+
void addDestParams()
279+
{
280+
QgsProcessingParameterFeatureSink *sinkParam1 = new QgsProcessingParameterFeatureSink( "supports" );
281+
sinkParam1->setSupportsNonFileBasedOutput( true );
282+
addParameter( sinkParam1 );
283+
QgsProcessingParameterFeatureSink *sinkParam2 = new QgsProcessingParameterFeatureSink( "non_supports" );
284+
sinkParam2->setSupportsNonFileBasedOutput( false );
285+
addParameter( sinkParam2 );
286+
}
287+
278288
};
279289

280290
//dummy provider for testing
@@ -300,7 +310,13 @@ class DummyProvider : public QgsProcessingProvider
300310
return "pcx"; // next-gen raster storage
301311
}
302312

313+
bool supportsNonFileBasedOutput() const override
314+
{
315+
return supportsNonFileOutputs;
316+
}
317+
303318
bool *unloaded = nullptr;
319+
bool supportsNonFileOutputs = false;
304320

305321
protected:
306322

@@ -426,6 +442,7 @@ class TestQgsProcessing: public QObject
426442
void combineFields();
427443
void stringToPythonLiteral();
428444
void defaultExtensionsForProvider();
445+
void supportsNonFileBasedOutput();
429446

430447
private:
431448

@@ -3973,7 +3990,7 @@ void TestQgsProcessing::parameterFeatureSink()
39733990

39743991
QCOMPARE( def->defaultFileExtension(), QStringLiteral( "shp" ) );
39753992
QCOMPARE( def->generateTemporaryDestination(), QStringLiteral( "memory:" ) );
3976-
def->setSupportsNonFileBasedOutputs( false );
3993+
def->setSupportsNonFileBasedOutput( false );
39773994
QVERIFY( def->generateTemporaryDestination().endsWith( QStringLiteral( ".shp" ) ) );
39783995
QVERIFY( def->generateTemporaryDestination().startsWith( QgsProcessingUtils::tempFolder() ) );
39793996

@@ -3985,7 +4002,7 @@ void TestQgsProcessing::parameterFeatureSink()
39854002
QCOMPARE( fromMap.flags(), def->flags() );
39864003
QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
39874004
QCOMPARE( fromMap.dataType(), def->dataType() );
3988-
QCOMPARE( fromMap.supportsNonFileBasedOutputs(), def->supportsNonFileBasedOutputs() );
4005+
QCOMPARE( fromMap.supportsNonFileBasedOutput(), def->supportsNonFileBasedOutput() );
39894006
def.reset( dynamic_cast< QgsProcessingParameterFeatureSink *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
39904007
QVERIFY( dynamic_cast< QgsProcessingParameterFeatureSink *>( def.get() ) );
39914008

@@ -4203,7 +4220,7 @@ void TestQgsProcessing::parameterRasterOut()
42034220
QCOMPARE( fromMap.description(), def->description() );
42044221
QCOMPARE( fromMap.flags(), def->flags() );
42054222
QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
4206-
QCOMPARE( fromMap.supportsNonFileBasedOutputs(), def->supportsNonFileBasedOutputs() );
4223+
QCOMPARE( fromMap.supportsNonFileBasedOutput(), def->supportsNonFileBasedOutput() );
42074224
def.reset( dynamic_cast< QgsProcessingParameterRasterDestination *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
42084225
QVERIFY( dynamic_cast< QgsProcessingParameterRasterDestination *>( def.get() ) );
42094226

@@ -4323,7 +4340,7 @@ void TestQgsProcessing::parameterFileOut()
43234340
QCOMPARE( fromMap.flags(), def->flags() );
43244341
QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
43254342
QCOMPARE( fromMap.fileFilter(), def->fileFilter() );
4326-
QCOMPARE( fromMap.supportsNonFileBasedOutputs(), def->supportsNonFileBasedOutputs() );
4343+
QCOMPARE( fromMap.supportsNonFileBasedOutput(), def->supportsNonFileBasedOutput() );
43274344
def.reset( dynamic_cast< QgsProcessingParameterFileDestination *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
43284345
QVERIFY( dynamic_cast< QgsProcessingParameterFileDestination *>( def.get() ) );
43294346

@@ -4396,7 +4413,7 @@ void TestQgsProcessing::parameterFolderOut()
43964413
QCOMPARE( fromMap.description(), def->description() );
43974414
QCOMPARE( fromMap.flags(), def->flags() );
43984415
QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
4399-
QCOMPARE( fromMap.supportsNonFileBasedOutputs(), def->supportsNonFileBasedOutputs() );
4416+
QCOMPARE( fromMap.supportsNonFileBasedOutput(), def->supportsNonFileBasedOutput() );
44004417
def.reset( dynamic_cast< QgsProcessingParameterFolderDestination *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
44014418
QVERIFY( dynamic_cast< QgsProcessingParameterFolderDestination *>( def.get() ) );
44024419

@@ -5868,5 +5885,25 @@ void TestQgsProcessing::defaultExtensionsForProvider()
58685885
QCOMPARE( provider.defaultRasterFileExtension(), QStringLiteral( "mig" ) );
58695886
}
58705887

5888+
void TestQgsProcessing::supportsNonFileBasedOutput()
5889+
{
5890+
DummyAlgorithm alg( QStringLiteral( "test" ) );
5891+
DummyProvider p( QStringLiteral( "test_provider" ) );
5892+
alg.addDestParams();
5893+
// provider has no support for file based outputs, so both output parameters should deny support
5894+
alg.setProvider( &p );
5895+
QVERIFY( !static_cast< const QgsProcessingDestinationParameter * >( alg.destinationParameterDefinitions().at( 0 ) )->supportsNonFileBasedOutput() );
5896+
QVERIFY( !static_cast< const QgsProcessingDestinationParameter * >( alg.destinationParameterDefinitions().at( 1 ) )->supportsNonFileBasedOutput() );
5897+
5898+
DummyAlgorithm alg2( QStringLiteral( "test" ) );
5899+
DummyProvider p2( QStringLiteral( "test_provider" ) );
5900+
p2.supportsNonFileOutputs = true;
5901+
alg2.addDestParams();
5902+
// provider has support for file based outputs, but only first output parameter should indicate support (since the second has support explicitly denied)
5903+
alg2.setProvider( &p2 );
5904+
QVERIFY( static_cast< const QgsProcessingDestinationParameter * >( alg2.destinationParameterDefinitions().at( 0 ) )->supportsNonFileBasedOutput() );
5905+
QVERIFY( !static_cast< const QgsProcessingDestinationParameter * >( alg2.destinationParameterDefinitions().at( 1 ) )->supportsNonFileBasedOutput() );
5906+
}
5907+
58715908
QGSTEST_MAIN( TestQgsProcessing )
58725909
#include "testqgsprocessing.moc"

0 commit comments

Comments
 (0)
Please sign in to comment.