Skip to content

Commit 3037f22

Browse files
authored
Non-blocking save as image/PDF dialogs (#4874)
1 parent d70f53c commit 3037f22

File tree

6 files changed

+182
-172
lines changed

6 files changed

+182
-172
lines changed

python/core/qgsmaprenderertask.sip

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ class QgsMapRendererTask : QgsTask
3131

3232
QgsMapRendererTask( const QgsMapSettings &ms,
3333
const QString &fileName,
34-
const QString &fileFormat = QString( "PNG" ) );
34+
const QString &fileFormat = QString( "PNG" ),
35+
const bool forceRaster = false );
3536
%Docstring
3637
Constructor for QgsMapRendererTask to render a map to an image file.
3738
%End

src/app/qgisapp.cpp

Lines changed: 8 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -5740,173 +5740,34 @@ void QgisApp::updateFilterLegend()
57405740

57415741
void QgisApp::saveMapAsImage()
57425742
{
5743-
QList< QgsMapDecoration * > decorations;
5744-
QString activeDecorations;
5743+
QList< QgsDecorationItem * > decorations;
57455744
Q_FOREACH ( QgsDecorationItem *decoration, mDecorationItems )
57465745
{
57475746
if ( decoration->enabled() )
57485747
{
57495748
decorations << decoration;
5750-
if ( activeDecorations.isEmpty() )
5751-
activeDecorations = decoration->name().toLower();
5752-
else
5753-
activeDecorations += QString( ", %1" ).arg( decoration->name().toLower() );
57545749
}
57555750
}
57565751

5757-
QgsMapSaveDialog dlg( this, mMapCanvas, activeDecorations );
5758-
if ( !dlg.exec() )
5759-
return;
5760-
5761-
QPair< QString, QString> fileNameAndFilter = QgsGuiUtils::getSaveAsImageName( this, tr( "Choose a file name to save the map image as" ) );
5762-
if ( fileNameAndFilter.first != QLatin1String( "" ) )
5763-
{
5764-
QgsMapSettings ms = QgsMapSettings();
5765-
dlg.applyMapSettings( ms );
5766-
5767-
QgsMapRendererTask *mapRendererTask = new QgsMapRendererTask( ms, fileNameAndFilter.first, fileNameAndFilter.second );
5768-
5769-
if ( dlg.drawAnnotations() )
5770-
{
5771-
mapRendererTask->addAnnotations( QgsProject::instance()->annotationManager()->annotations() );
5772-
}
5773-
5774-
if ( dlg.drawDecorations() )
5775-
{
5776-
mapRendererTask->addDecorations( decorations );
5777-
}
5778-
5779-
mapRendererTask->setSaveWorldFile( dlg.saveWorldFile() );
5780-
5781-
connect( mapRendererTask, &QgsMapRendererTask::renderingComplete, this, [ = ]
5782-
{
5783-
messageBar()->pushSuccess( tr( "Save as image" ), tr( "Successfully saved map to image" ) );
5784-
} );
5785-
connect( mapRendererTask, &QgsMapRendererTask::errorOccurred, this, [ = ]( int error )
5786-
{
5787-
switch ( error )
5788-
{
5789-
case QgsMapRendererTask::ImageAllocationFail:
5790-
{
5791-
messageBar()->pushWarning( tr( "Save as image" ), tr( "Could not allocate required memory for image" ) );
5792-
break;
5793-
}
5794-
case QgsMapRendererTask::ImageSaveFail:
5795-
{
5796-
messageBar()->pushWarning( tr( "Save as image" ), tr( "Could not save the image to file" ) );
5797-
break;
5798-
}
5799-
}
5800-
} );
5801-
5802-
QgsApplication::taskManager()->addTask( mapRendererTask );
5803-
}
5804-
5752+
QgsMapSaveDialog *dlg = new QgsMapSaveDialog( this, mMapCanvas, decorations, QgsProject::instance()->annotationManager()->annotations() );
5753+
dlg->setAttribute( Qt::WA_DeleteOnClose );
5754+
dlg->show();
58055755
} // saveMapAsImage
58065756

58075757
void QgisApp::saveMapAsPdf()
58085758
{
5809-
QList< QgsMapDecoration * > decorations;
5810-
QString activeDecorations;
5759+
QList< QgsDecorationItem * > decorations;
58115760
Q_FOREACH ( QgsDecorationItem *decoration, mDecorationItems )
58125761
{
58135762
if ( decoration->enabled() )
58145763
{
58155764
decorations << decoration;
5816-
if ( activeDecorations.isEmpty() )
5817-
activeDecorations = decoration->name().toLower();
5818-
else
5819-
activeDecorations += QString( ", %1" ).arg( decoration->name().toLower() );
5820-
}
5821-
}
5822-
5823-
QgsMapSaveDialog dlg( this, mMapCanvas, activeDecorations, QgsMapSaveDialog::Pdf );
5824-
if ( !dlg.exec() )
5825-
return;
5826-
5827-
QgsSettings settings;
5828-
QString lastUsedDir = settings.value( QStringLiteral( "UI/lastSaveAsImageDir" ), QDir::homePath() ).toString();
5829-
QString fileName = QFileDialog::getSaveFileName( this, tr( "Save map as" ), lastUsedDir, tr( "PDF Format" ) + " (*.pdf *.PDF)" );
5830-
if ( !fileName.isEmpty() )
5831-
{
5832-
QgsMapSettings ms = QgsMapSettings();
5833-
dlg.applyMapSettings( ms );
5834-
5835-
QPrinter *printer = new QPrinter();
5836-
printer->setOutputFileName( fileName );
5837-
printer->setOutputFormat( QPrinter::PdfFormat );
5838-
printer->setOrientation( QPrinter::Portrait );
5839-
// paper size needs to be given in millimeters in order to be able to set a resolution to pass onto the map renderer
5840-
printer->setPaperSize( dlg.size() * 25.4 / dlg.dpi(), QPrinter::Millimeter );
5841-
printer->setPageMargins( 0, 0, 0, 0, QPrinter::Millimeter );
5842-
printer->setResolution( dlg.dpi() );
5843-
5844-
QPainter *p = new QPainter();
5845-
QImage *image = nullptr;
5846-
if ( dlg.saveAsRaster() )
5847-
{
5848-
image = new QImage( dlg.size(), QImage::Format_ARGB32 );
5849-
if ( image->isNull() )
5850-
{
5851-
messageBar()->pushWarning( tr( "Save as PDF" ), tr( "Could not allocate required memory for image" ) );
5852-
delete p;
5853-
delete image;
5854-
delete printer;
5855-
5856-
return;
5857-
}
5858-
5859-
image->setDotsPerMeterX( 1000 * dlg.dpi() / 25.4 );
5860-
image->setDotsPerMeterY( 1000 * dlg.dpi() / 25.4 );
5861-
p->begin( image );
58625765
}
5863-
else
5864-
{
5865-
p->begin( printer );
5866-
}
5867-
5868-
QgsMapRendererTask *mapRendererTask = new QgsMapRendererTask( ms, p );
5869-
5870-
if ( dlg.drawAnnotations() )
5871-
{
5872-
mapRendererTask->addAnnotations( QgsProject::instance()->annotationManager()->annotations() );
5873-
}
5874-
5875-
if ( dlg.drawDecorations() )
5876-
{
5877-
mapRendererTask->addDecorations( decorations );
5878-
}
5879-
5880-
mapRendererTask->setSaveWorldFile( dlg.saveWorldFile() );
5881-
5882-
connect( mapRendererTask, &QgsMapRendererTask::renderingComplete, this, [ this, p, image, printer ]
5883-
{
5884-
p->end();
5885-
5886-
if ( image )
5887-
{
5888-
QPainter pp;
5889-
pp.begin( printer );
5890-
QRectF rect( 0, 0, image->width(), image->height() );
5891-
pp.drawImage( rect, *image, rect );
5892-
pp.end();
5893-
}
5894-
5895-
messageBar()->pushSuccess( tr( "Save as PDF" ), tr( "Successfully saved map to PDF" ) );
5896-
delete p;
5897-
delete image;
5898-
delete printer;
5899-
} );
5900-
connect( mapRendererTask, &QgsMapRendererTask::errorOccurred, this, [ this, p, image, printer ]( int )
5901-
{
5902-
delete p;
5903-
delete image;
5904-
delete printer;
5905-
} );
5906-
5907-
QgsApplication::taskManager()->addTask( mapRendererTask );
59085766
}
59095767

5768+
QgsMapSaveDialog *dlg = new QgsMapSaveDialog( this, mMapCanvas, decorations, QgsProject::instance()->annotationManager()->annotations(), QgsMapSaveDialog::Pdf );
5769+
dlg->setAttribute( Qt::WA_DeleteOnClose );
5770+
dlg->show();
59105771
} // saveMapAsPdf
59115772

59125773
//overloaded version of the above function

src/app/qgsmapsavedialog.cpp

Lines changed: 106 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,32 @@
1818
#include "qgsmapsavedialog.h"
1919

2020
#include "qgis.h"
21+
#include "qgisapp.h"
2122
#include "qgsscalecalculator.h"
2223
#include "qgsdecorationitem.h"
2324
#include "qgsexpressioncontext.h"
2425
#include "qgsextentgroupbox.h"
2526
#include "qgsmapsettings.h"
2627
#include "qgsmapsettingsutils.h"
28+
#include "qgsmaprenderertask.h"
2729
#include "qgsproject.h"
2830
#include "qgssettings.h"
2931

3032
#include <QCheckBox>
31-
#include <QSpinBox>
33+
#include <QFileDialog>
34+
#include <QImage>
3235
#include <QList>
36+
#include <QPainter>
37+
#include <QPrinter>
38+
#include <QSpinBox>
3339

3440
Q_GUI_EXPORT extern int qt_defaultDpiX();
3541

36-
QgsMapSaveDialog::QgsMapSaveDialog( QWidget *parent, QgsMapCanvas *mapCanvas, const QString &activeDecorations, DialogType type )
42+
QgsMapSaveDialog::QgsMapSaveDialog( QWidget *parent, QgsMapCanvas *mapCanvas, QList< QgsDecorationItem * > decorations, QList< QgsAnnotation *> annotations, DialogType type )
3743
: QDialog( parent )
3844
, mDialogType( type )
3945
, mMapCanvas( mapCanvas )
46+
, mAnnotations( annotations )
4047
{
4148
setupUi( this );
4249

@@ -57,6 +64,15 @@ QgsMapSaveDialog::QgsMapSaveDialog( QWidget *parent, QgsMapCanvas *mapCanvas, co
5764
mScaleWidget->setMapCanvas( mMapCanvas );
5865
mScaleWidget->setShowCurrentScaleButton( true );
5966

67+
QString activeDecorations;
68+
Q_FOREACH ( QgsDecorationItem *decoration, decorations )
69+
{
70+
mDecorations << decoration;
71+
if ( activeDecorations.isEmpty() )
72+
activeDecorations = decoration->name().toLower();
73+
else
74+
activeDecorations += QString( ", %1" ).arg( decoration->name().toLower() );
75+
}
6076
mDrawDecorations->setText( tr( "Draw active decorations: %1" ).arg( !activeDecorations.isEmpty() ? activeDecorations : tr( "none" ) ) );
6177

6278
connect( mResolutionSpinBox, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsMapSaveDialog::updateDpi );
@@ -92,6 +108,8 @@ QgsMapSaveDialog::QgsMapSaveDialog( QWidget *parent, QgsMapCanvas *mapCanvas, co
92108

93109
this->setWindowTitle( tr( "Save map as PDF" ) );
94110
}
111+
112+
connect( buttonBox, &QDialogButtonBox::accepted, this, &QgsMapSaveDialog::accepted );
95113
}
96114

97115
void QgsMapSaveDialog::updateDpi( int dpi )
@@ -222,3 +240,89 @@ void QgsMapSaveDialog::applyMapSettings( QgsMapSettings &mapSettings )
222240

223241
mapSettings.setExpressionContext( expressionContext );
224242
}
243+
244+
void QgsMapSaveDialog::accepted()
245+
{
246+
if ( mDialogType == Image )
247+
{
248+
QPair< QString, QString> fileNameAndFilter = QgsGuiUtils::getSaveAsImageName( QgisApp::instance(), tr( "Choose a file name to save the map image as" ) );
249+
if ( fileNameAndFilter.first != QLatin1String( "" ) )
250+
{
251+
QgsMapSettings ms = QgsMapSettings();
252+
applyMapSettings( ms );
253+
254+
QgsMapRendererTask *mapRendererTask = new QgsMapRendererTask( ms, fileNameAndFilter.first, fileNameAndFilter.second );
255+
256+
if ( drawAnnotations() )
257+
{
258+
mapRendererTask->addAnnotations( mAnnotations );
259+
}
260+
261+
if ( drawDecorations() )
262+
{
263+
mapRendererTask->addDecorations( mDecorations );
264+
}
265+
266+
mapRendererTask->setSaveWorldFile( saveWorldFile() );
267+
268+
connect( mapRendererTask, &QgsMapRendererTask::renderingComplete, [ = ]
269+
{
270+
QgisApp::instance()->messageBar()->pushSuccess( tr( "Save as image" ), tr( "Successfully saved map to image" ) );
271+
} );
272+
connect( mapRendererTask, &QgsMapRendererTask::errorOccurred, [ = ]( int error )
273+
{
274+
switch ( error )
275+
{
276+
case QgsMapRendererTask::ImageAllocationFail:
277+
{
278+
QgisApp::instance()->messageBar()->pushWarning( tr( "Save as image" ), tr( "Could not allocate required memory for image" ) );
279+
break;
280+
}
281+
case QgsMapRendererTask::ImageSaveFail:
282+
{
283+
QgisApp::instance()->messageBar()->pushWarning( tr( "Save as image" ), tr( "Could not save the map to file" ) );
284+
break;
285+
}
286+
}
287+
} );
288+
289+
QgsApplication::taskManager()->addTask( mapRendererTask );
290+
}
291+
}
292+
else
293+
{
294+
QgsSettings settings;
295+
QString lastUsedDir = settings.value( QStringLiteral( "UI/lastSaveAsImageDir" ), QDir::homePath() ).toString();
296+
QString fileName = QFileDialog::getSaveFileName( QgisApp::instance(), tr( "Save map as" ), lastUsedDir, tr( "PDF Format" ) + " (*.pdf *.PDF)" );
297+
if ( !fileName.isEmpty() )
298+
{
299+
QgsMapSettings ms = QgsMapSettings();
300+
applyMapSettings( ms );
301+
302+
QgsMapRendererTask *mapRendererTask = new QgsMapRendererTask( ms, fileName, QStringLiteral( "PDF" ), saveAsRaster() );
303+
304+
if ( drawAnnotations() )
305+
{
306+
mapRendererTask->addAnnotations( mAnnotations );
307+
}
308+
309+
if ( drawDecorations() )
310+
{
311+
mapRendererTask->addDecorations( mDecorations );
312+
}
313+
314+
mapRendererTask->setSaveWorldFile( saveWorldFile() );
315+
316+
connect( mapRendererTask, &QgsMapRendererTask::renderingComplete, [ = ]
317+
{
318+
QgisApp::instance()->messageBar()->pushSuccess( tr( "Save as PDF" ), tr( "Successfully saved map to PDF" ) );
319+
} );
320+
connect( mapRendererTask, &QgsMapRendererTask::errorOccurred, [ = ]( int )
321+
{
322+
QgisApp::instance()->messageBar()->pushWarning( tr( "Save as PDF" ), tr( "Could not save the map to PDF..." ) );
323+
} );
324+
325+
QgsApplication::taskManager()->addTask( mapRendererTask );
326+
}
327+
}
328+
}

src/app/qgsmapsavedialog.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@
2121
#include "ui_qgsmapsavedialog.h"
2222

2323
#include "qgisapp.h"
24-
#include "qgsrectangle.h"
2524
#include "qgsmapcanvas.h"
25+
#include "qgsmapdecoration.h"
26+
#include "qgsrectangle.h"
2627

2728
#include <QDialog>
2829
#include <QSize>
@@ -45,7 +46,10 @@ class APP_EXPORT QgsMapSaveDialog: public QDialog, private Ui::QgsMapSaveDialog
4546

4647
/** Constructor for QgsMapSaveDialog
4748
*/
48-
QgsMapSaveDialog( QWidget *parent = nullptr, QgsMapCanvas *mapCanvas = nullptr, const QString &activeDecorations = QString(), DialogType type = Image );
49+
QgsMapSaveDialog( QWidget *parent = nullptr, QgsMapCanvas *mapCanvas = nullptr,
50+
QList< QgsDecorationItem * > decorations = QList< QgsDecorationItem * >(),
51+
QList< QgsAnnotation *> annotations = QList< QgsAnnotation * >(),
52+
DialogType type = Image );
4953

5054
//! returns extent rectangle
5155
QgsRectangle extent() const;
@@ -73,6 +77,8 @@ class APP_EXPORT QgsMapSaveDialog: public QDialog, private Ui::QgsMapSaveDialog
7377

7478
private:
7579

80+
void accepted();
81+
7682
void updateDpi( int dpi );
7783
void updateOutputWidth( int width );
7884
void updateOutputHeight( int height );
@@ -82,6 +88,9 @@ class APP_EXPORT QgsMapSaveDialog: public QDialog, private Ui::QgsMapSaveDialog
8288

8389
DialogType mDialogType;
8490
QgsMapCanvas *mMapCanvas;
91+
QList< QgsMapDecoration * > mDecorations;
92+
QList< QgsAnnotation *> mAnnotations;
93+
8594
QgsRectangle mExtent;
8695
int mDpi;
8796
QSize mSize;

0 commit comments

Comments
 (0)