diff --git a/src/analysis/processing/qgsalgorithmlayoutatlastoimage.cpp b/src/analysis/processing/qgsalgorithmlayoutatlastoimage.cpp index c58c7f7ada1a..bee9fb027432 100644 --- a/src/analysis/processing/qgsalgorithmlayoutatlastoimage.cpp +++ b/src/analysis/processing/qgsalgorithmlayoutatlastoimage.cpp @@ -75,7 +75,7 @@ void QgsLayoutAtlasToImageAlgorithm::initAlgorithm( const QVariantMap & ) addParameter( new QgsProcessingParameterExpression( QStringLiteral( "SORTBY_EXPRESSION" ), QObject::tr( "Sort expression" ), QString(), QStringLiteral( "COVERAGE_LAYER" ), true ) ); addParameter( new QgsProcessingParameterBoolean( QStringLiteral( "SORTBY_REVERSE" ), QObject::tr( "Reverse sort order (used when a sort expression is provided)" ), false ) ); - addParameter( new QgsProcessingParameterExpression( QStringLiteral( "FILENAME_EXPRESSION" ), QObject::tr( "Output filename expression" ), QStringLiteral( "'output_'||@atlas_featurenumber" ), QStringLiteral( "COVERAGE_LAYER" ) ) ); + addParameter( new QgsProcessingParameterExpression( QStringLiteral( "FILENAME_EXPRESSION" ), QObject::tr( "Output filename expression" ), QStringLiteral( "'output_'||@atlas_featurenumber_padded" ), QStringLiteral( "COVERAGE_LAYER" ) ) ); addParameter( new QgsProcessingParameterFile( QStringLiteral( "FOLDER" ), QObject::tr( "Output folder" ), Qgis::ProcessingFileParameterBehavior::Folder ) ); diff --git a/src/analysis/processing/qgsalgorithmlayoutatlastopdf.cpp b/src/analysis/processing/qgsalgorithmlayoutatlastopdf.cpp index f678c6c2bec1..a70bc8e492ca 100644 --- a/src/analysis/processing/qgsalgorithmlayoutatlastopdf.cpp +++ b/src/analysis/processing/qgsalgorithmlayoutatlastopdf.cpp @@ -359,7 +359,7 @@ QVariantMap QgsLayoutAtlasToMultiplePdfAlgorithm::exportAtlas( QgsLayoutAtlas *a QgsLayoutExporter::ExportResult result; if ( atlas->filenameExpression().isEmpty() && filename.isEmpty() ) { - atlas->setFilenameExpression( QStringLiteral( "'output_'||@atlas_featurenumber" ), error ); + atlas->setFilenameExpression( QStringLiteral( "'output_'||@atlas_featurenumber_padded" ), error ); } else if ( !filename.isEmpty() ) { diff --git a/src/app/layout/qgslayoutdesignerdialog.cpp b/src/app/layout/qgslayoutdesignerdialog.cpp index 3596563a9f13..1e6df52ea45b 100644 --- a/src/app/layout/qgslayoutdesignerdialog.cpp +++ b/src/app/layout/qgslayoutdesignerdialog.cpp @@ -2841,7 +2841,7 @@ void QgsLayoutDesignerDialog::exportAtlasToRaster() return; } QString error; - printAtlas->setFilenameExpression( QStringLiteral( "'output_'||@atlas_featurenumber" ), error ); + printAtlas->setFilenameExpression( QStringLiteral( "'output_'||@atlas_featurenumber_padded" ), error ); } else { @@ -3010,7 +3010,7 @@ void QgsLayoutDesignerDialog::exportAtlasToSvg() return; } QString error; - printAtlas->setFilenameExpression( QStringLiteral( "'output_'||@atlas_featurenumber" ), error ); + printAtlas->setFilenameExpression( QStringLiteral( "'output_'||@atlas_featurenumber_padded" ), error ); } QString lastUsedDir = defaultExportPath(); @@ -3202,7 +3202,7 @@ void QgsLayoutDesignerDialog::exportAtlasToPdf() return; } QString error; - printAtlas->setFilenameExpression( QStringLiteral( "'output_'||@atlas_featurenumber" ), error ); + printAtlas->setFilenameExpression( QStringLiteral( "'output_'||@atlas_featurenumber_padded" ), error ); } diff --git a/src/core/expression/qgsexpression.cpp b/src/core/expression/qgsexpression.cpp index e9a5921a6618..7a48a743d85b 100644 --- a/src/core/expression/qgsexpression.cpp +++ b/src/core/expression/qgsexpression.cpp @@ -797,6 +797,7 @@ void QgsExpression::initVariableHelp() sVariableHelpTexts()->insert( QStringLiteral( "atlas_layername" ), QCoreApplication::translate( "variable_help", "Current atlas coverage layer name." ) ); sVariableHelpTexts()->insert( QStringLiteral( "atlas_totalfeatures" ), QCoreApplication::translate( "variable_help", "Total number of features in atlas." ) ); sVariableHelpTexts()->insert( QStringLiteral( "atlas_featurenumber" ), QCoreApplication::translate( "variable_help", "Current atlas feature number." ) ); + sVariableHelpTexts()->insert( QStringLiteral( "atlas_featurenumber_padded" ), QCoreApplication::translate( "variable_help", "Current atlas feature number (zero-padded)." ) ); sVariableHelpTexts()->insert( QStringLiteral( "atlas_filename" ), QCoreApplication::translate( "variable_help", "Current atlas file name." ) ); sVariableHelpTexts()->insert( QStringLiteral( "atlas_pagename" ), QCoreApplication::translate( "variable_help", "Current atlas page name." ) ); sVariableHelpTexts()->insert( QStringLiteral( "atlas_feature" ), QCoreApplication::translate( "variable_help", "Current atlas feature (as feature object)." ) ); diff --git a/src/core/expression/qgsexpressioncontextutils.cpp b/src/core/expression/qgsexpressioncontextutils.cpp index 01304ea82731..3f3692456a6b 100644 --- a/src/core/expression/qgsexpressioncontextutils.cpp +++ b/src/core/expression/qgsexpressioncontextutils.cpp @@ -733,6 +733,8 @@ QgsExpressionContextScope *QgsExpressionContextUtils::atlasScope( const QgsLayou //add known atlas variables scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "atlas_totalfeatures" ), atlas->count(), true, true ) ); scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "atlas_featurenumber" ), atlas->currentFeatureNumber() + 1, true, true ) ); + const QString padded = QString::number( atlas->currentFeatureNumber() + 1 ).rightJustified( QString::number( atlas->count() ).size(), '0' ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "atlas_featurenumber_padded" ), padded, true, true ) ); scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "atlas_filename" ), atlas->currentFilename(), true, true ) ); scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "atlas_pagename" ), atlas->nameForPage( atlas->currentFeatureNumber() ), true, true ) ); diff --git a/src/core/layout/qgslayoutatlas.cpp b/src/core/layout/qgslayoutatlas.cpp index 775c101360d6..4d33a8faa958 100644 --- a/src/core/layout/qgslayoutatlas.cpp +++ b/src/core/layout/qgslayoutatlas.cpp @@ -33,7 +33,7 @@ QgsLayoutAtlas::QgsLayoutAtlas( QgsLayout *layout ) : QObject( layout ) , mLayout( layout ) - , mFilenameExpressionString( QStringLiteral( "'output_'||@atlas_featurenumber" ) ) + , mFilenameExpressionString( QStringLiteral( "'output_'||@atlas_featurenumber_padded" ) ) { //listen out for layer removal diff --git a/src/gui/layout/qgslayoutatlaswidget.cpp b/src/gui/layout/qgslayoutatlaswidget.cpp index 6b5ff64e2365..7cd79ec8502b 100644 --- a/src/gui/layout/qgslayoutatlaswidget.cpp +++ b/src/gui/layout/qgslayoutatlaswidget.cpp @@ -78,18 +78,15 @@ void QgsLayoutAtlasWidget::setMessageBar( QgsMessageBar *bar ) void QgsLayoutAtlasWidget::mUseAtlasCheckBox_stateChanged( int state ) { - if ( state == Qt::Checked ) - { - mAtlas->setEnabled( true ); - mConfigurationGroup->setEnabled( true ); - mOutputGroup->setEnabled( true ); - } - else - { - mAtlas->setEnabled( false ); - mConfigurationGroup->setEnabled( false ); - mOutputGroup->setEnabled( false ); - } + const bool enabled = state == Qt::Checked; + mAtlas->setEnabled( enabled ); + mAtlasCoverageLayerComboBox->setEnabled( enabled ); + mAtlasCoverageLayerLabel->setEnabled( enabled ); + + const bool validLayer = mAtlas->coverageLayer() != nullptr; + + mConfigurationGroup->setEnabled( enabled && validLayer ); + mOutputGroup->setEnabled( enabled && validLayer ); } void QgsLayoutAtlasWidget::changeCoverageLayer( QgsMapLayer *layer ) @@ -112,6 +109,9 @@ void QgsLayoutAtlasWidget::changeCoverageLayer( QgsMapLayer *layer ) updateAtlasFeatures(); } + mConfigurationGroup->setEnabled( mAtlas->enabled() && !vl ); + mOutputGroup->setEnabled( mAtlas->enabled() && !vl ); + // if page name expression is still valid, retain it. Otherwise switch to a nice default. QgsExpression exp( prevPageNameExpression ); QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( vl ) ); @@ -192,18 +192,6 @@ void QgsLayoutAtlasWidget::mAtlasSingleFileCheckBox_stateChanged( int state ) { if ( !mLayout ) return; - - if ( state == Qt::Checked ) - { - mAtlasFilenamePatternEdit->setEnabled( false ); - mAtlasFilenameExpressionButton->setEnabled( false ); - } - else - { - mAtlasFilenamePatternEdit->setEnabled( true ); - mAtlasFilenameExpressionButton->setEnabled( true ); - } - mLayout->setCustomProperty( QStringLiteral( "singleFile" ), state == Qt::Checked ); } @@ -400,8 +388,6 @@ void QgsLayoutAtlasWidget::updateGuiElements() const bool singleFile = mLayout->customProperty( QStringLiteral( "singleFile" ) ).toBool(); mAtlasSingleFileCheckBox->setCheckState( singleFile ? Qt::Checked : Qt::Unchecked ); - mAtlasFilenamePatternEdit->setEnabled( !singleFile ); - mAtlasFilenameExpressionButton->setEnabled( !singleFile ); mAtlasSortFeatureCheckBox->setCheckState( mAtlas->sortFeatures() ? Qt::Checked : Qt::Unchecked ); mAtlasSortFeatureDirectionButton->setEnabled( mAtlas->sortFeatures() ); diff --git a/src/ui/layout/qgslayoutatlaswidgetbase.ui b/src/ui/layout/qgslayoutatlaswidgetbase.ui index 2ad279918ebf..a91907c51332 100644 --- a/src/ui/layout/qgslayoutatlaswidgetbase.ui +++ b/src/ui/layout/qgslayoutatlaswidgetbase.ui @@ -6,8 +6,8 @@ 0 0 - 435 - 359 + 430 + 452 @@ -66,19 +66,6 @@ - - - - Qt::Horizontal - - - - 40 - 20 - - - - @@ -95,7 +82,7 @@ - + Qt::WheelFocus @@ -111,14 +98,35 @@ 0 0 - 417 - 354 + 426 + 420 0 + + + + + + Coverage layer + + + + + + + + 0 + 0 + + + + + + @@ -181,13 +189,6 @@ - - - - Coverage layer - - - @@ -198,16 +199,6 @@ - - - - - 0 - 0 - - - - @@ -319,6 +310,19 @@ + + + + Qt::Horizontal + + + + 40 + 20 + + + + @@ -353,7 +357,6 @@ mUseAtlasCheckBox scrollArea mConfigurationGroup - mAtlasCoverageLayerComboBox mAtlasHideCoverageCheckBox mAtlasFeatureFilterCheckBox mAtlasFeatureFilterEdit diff --git a/tests/src/analysis/testqgsprocessingalgspt2.cpp b/tests/src/analysis/testqgsprocessingalgspt2.cpp index 9ffd8e09732d..359d829c178e 100644 --- a/tests/src/analysis/testqgsprocessingalgspt2.cpp +++ b/tests/src/analysis/testqgsprocessingalgspt2.cpp @@ -413,9 +413,9 @@ void TestQgsProcessingAlgsPt2::exportAtlasLayoutPdfMultiple() results = alg->run( parameters, *context, &feedback, &ok ); QVERIFY( ok ); - QVERIFY( QFile::exists( outputPdfDir + "/output_1.pdf" ) ); - QVERIFY( QFile::exists( outputPdfDir + "/output_2.pdf" ) ); - QVERIFY( QFile::exists( outputPdfDir + "/output_3.pdf" ) ); + QVERIFY( QFile::exists( outputPdfDir + "/output_01.pdf" ) ); + QVERIFY( QFile::exists( outputPdfDir + "/output_02.pdf" ) ); + QVERIFY( QFile::exists( outputPdfDir + "/output_03.pdf" ) ); } void TestQgsProcessingAlgsPt2::exportAtlasLayoutPng()