Skip to content

Commit db848a3

Browse files
authoredApr 21, 2017
Upgrade the save as image function into a background task (#4382)
1 parent 20197c2 commit db848a3

File tree

6 files changed

+354
-3
lines changed

6 files changed

+354
-3
lines changed
 

‎python/core/core.sip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
%Include qgsmaprendererjob.sip
9696
%Include qgsmaprendererparalleljob.sip
9797
%Include qgsmaprenderersequentialjob.sip
98+
%Include qgsmaprenderertask.sip
9899
%Include qgsmapsettings.sip
99100
%Include qgsmaptopixel.sip
100101
%Include qgsmapunitscale.sip

‎python/core/qgsmaprenderertask.sip

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/************************************************************************
2+
* This file has been generated automatically from *
3+
* *
4+
* src/core/qgsmaprenderertask.h *
5+
* *
6+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
7+
************************************************************************/
8+
9+
10+
11+
12+
13+
class QgsMapRendererTask : QgsTask
14+
{
15+
%Docstring
16+
QgsTask task which draws a map to an image file or a painter as a background
17+
task. This can be used to draw maps without blocking the QGIS interface.
18+
.. versionadded:: 3.0
19+
%End
20+
21+
%TypeHeaderCode
22+
#include "qgsmaprenderertask.h"
23+
%End
24+
public:
25+
26+
enum ErrorType
27+
{
28+
ImageAllocationFail,
29+
ImageSaveFail
30+
};
31+
32+
QgsMapRendererTask( const QgsMapSettings &ms,
33+
const QString &fileName,
34+
const QString &fileFormat = QString( "PNG" ) );
35+
%Docstring
36+
Constructor for QgsMapRendererTask to render a map to an image file.
37+
%End
38+
39+
QgsMapRendererTask( const QgsMapSettings &ms,
40+
QPainter *p );
41+
%Docstring
42+
Constructor for QgsMapRendererTask to render a map to a painter object.
43+
%End
44+
45+
void addAnnotations( QList< QgsAnnotation * > annotations );
46+
%Docstring
47+
Adds ``annotations`` to be rendered on the map.
48+
%End
49+
50+
signals:
51+
52+
void renderingComplete();
53+
%Docstring
54+
Emitted when the map rendering is successfully completed.
55+
%End
56+
57+
void errorOccurred( int error );
58+
%Docstring
59+
Emitted when map rendering failed.
60+
%End
61+
62+
protected:
63+
64+
virtual bool run();
65+
virtual void finished( bool result );
66+
67+
};
68+
69+
/************************************************************************
70+
* This file has been generated automatically from *
71+
* *
72+
* src/core/qgsmaprenderertask.h *
73+
* *
74+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
75+
************************************************************************/
76+

‎src/app/qgisapp.cpp

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@
270270
#include "qgsvectorlayerutils.h"
271271
#include "qgshelp.h"
272272
#include "qgsvectorfilewritertask.h"
273+
#include "qgsmaprenderertask.h"
273274
#include "qgsnewnamedialog.h"
274275

275276
#include "qgssublayersdialog.h"
@@ -5772,9 +5773,48 @@ void QgisApp::saveMapAsImage()
57725773
QPair< QString, QString> myFileNameAndFilter = QgisGui::getSaveAsImageName( this, tr( "Choose a file name to save the map image as" ) );
57735774
if ( myFileNameAndFilter.first != QLatin1String( "" ) )
57745775
{
5775-
//save the mapview to the selected file
5776-
mMapCanvas->saveAsImage( myFileNameAndFilter.first, nullptr, myFileNameAndFilter.second );
5777-
statusBar()->showMessage( tr( "Saved map image to %1" ).arg( myFileNameAndFilter.first ) );
5776+
//TODO: GUI
5777+
int dpi = 90;
5778+
QSize size = mMapCanvas->size() * ( dpi / 90 );
5779+
5780+
QgsMapSettings ms = QgsMapSettings();
5781+
ms.setDestinationCrs( QgsProject::instance()->crs() );
5782+
ms.setExtent( mMapCanvas->extent() );
5783+
ms.setOutputSize( size );
5784+
ms.setOutputDpi( dpi );
5785+
ms.setBackgroundColor( mMapCanvas->canvasColor() );
5786+
ms.setRotation( mMapCanvas->rotation() );
5787+
ms.setLayers( mMapCanvas->layers() );
5788+
5789+
QgsMapRendererTask *mapRendererTask = new QgsMapRendererTask( ms, myFileNameAndFilter.first, myFileNameAndFilter.second );
5790+
mapRendererTask->addAnnotations( QgsProject::instance()->annotationManager()->annotations() );
5791+
5792+
connect( mapRendererTask, &QgsMapRendererTask::renderingComplete, this, [ = ]
5793+
{
5794+
QgsMessageBarItem *successMsg = new QgsMessageBarItem(
5795+
tr( "Successfully saved canvas to image" ),
5796+
QgsMessageBar::SUCCESS );
5797+
messageBar()->pushItem( successMsg );
5798+
} );
5799+
connect( mapRendererTask, &QgsMapRendererTask::errorOccurred, this, [ = ]( int error )
5800+
{
5801+
if ( error == QgsMapRendererTask::ImageAllocationFail )
5802+
{
5803+
QgsMessageBarItem *errorMsg = new QgsMessageBarItem(
5804+
tr( "Could not allocate required memory for image" ),
5805+
QgsMessageBar::WARNING );
5806+
messageBar()->pushItem( errorMsg );
5807+
}
5808+
else if ( error == QgsMapRendererTask::ImageAllocationFail )
5809+
{
5810+
QgsMessageBarItem *errorMsg = new QgsMessageBarItem(
5811+
tr( "Could not save the image to file" ),
5812+
QgsMessageBar::WARNING );
5813+
messageBar()->pushItem( errorMsg );
5814+
}
5815+
} );
5816+
5817+
QgsApplication::taskManager()->addTask( mapRendererTask );
57785818
}
57795819

57805820
} // saveMapAsImage

‎src/core/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ SET(QGIS_CORE_SRCS
173173
qgsmaprendererjob.cpp
174174
qgsmaprendererparalleljob.cpp
175175
qgsmaprenderersequentialjob.cpp
176+
qgsmaprenderertask.cpp
176177
qgsmapsettings.cpp
177178
qgsmaptopixel.cpp
178179
qgsmaptopixelgeometrysimplifier.cpp
@@ -521,6 +522,7 @@ SET(QGIS_CORE_MOC_HDRS
521522
qgsmaprendererjob.h
522523
qgsmaprendererparalleljob.h
523524
qgsmaprenderersequentialjob.h
525+
qgsmaprenderertask.h
524526
qgsmessagelog.h
525527
qgsmessageoutput.h
526528
qgsnetworkaccessmanager.h

‎src/core/qgsmaprenderertask.cpp

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/***************************************************************************
2+
qgsmaprenderertask.h
3+
-------------------------
4+
begin : Apr 2017
5+
copyright : (C) 2017 by Mathieu Pellerin
6+
email : nirvn dot asia at gmail dot com
7+
***************************************************************************/
8+
9+
/***************************************************************************
10+
* *
11+
* This program is free software; you can redistribute it and/or modify *
12+
* it under the terms of the GNU General Public License as published by *
13+
* the Free Software Foundation; either version 2 of the License, or *
14+
* (at your option) any later version. *
15+
* *
16+
***************************************************************************/
17+
18+
#include "qgsannotation.h"
19+
#include "qgsannotationmanager.h"
20+
#include "qgsmaprenderertask.h"
21+
#include "qgsmaprenderercustompainterjob.h"
22+
23+
24+
QgsMapRendererTask::QgsMapRendererTask( const QgsMapSettings &ms, const QString &fileName, const QString &fileFormat )
25+
: QgsTask( tr( "Saving as image" ) )
26+
, mMapSettings( ms )
27+
, mFileName( fileName )
28+
, mFileFormat( fileFormat )
29+
{
30+
}
31+
32+
QgsMapRendererTask::QgsMapRendererTask( const QgsMapSettings &ms, QPainter *p )
33+
: QgsTask( tr( "Saving as image" ) )
34+
, mMapSettings( ms )
35+
, mPainter( p )
36+
{
37+
}
38+
39+
void QgsMapRendererTask::addAnnotations( QList< QgsAnnotation * > annotations )
40+
{
41+
qDeleteAll( mAnnotations );
42+
mAnnotations.clear();
43+
44+
Q_FOREACH ( const QgsAnnotation *a, annotations )
45+
{
46+
mAnnotations << a->clone();
47+
}
48+
}
49+
50+
bool QgsMapRendererTask::run()
51+
{
52+
QImage img;
53+
std::unique_ptr< QPainter > tempPainter;
54+
QPainter *destPainter = mPainter;
55+
56+
if ( !mPainter )
57+
{
58+
// save rendered map to an image file
59+
img = QImage( mMapSettings.outputSize(), QImage::Format_ARGB32 );
60+
if ( img.isNull() )
61+
{
62+
mError = ImageAllocationFail;
63+
return false;
64+
}
65+
66+
tempPainter.reset( new QPainter( &img ) );
67+
destPainter = tempPainter.get();
68+
}
69+
70+
if ( !destPainter )
71+
return false;
72+
73+
QgsMapRendererCustomPainterJob r( mMapSettings, destPainter );
74+
r.renderSynchronously();
75+
76+
QgsRenderContext context = QgsRenderContext::fromMapSettings( mMapSettings );
77+
context.setPainter( destPainter );
78+
79+
Q_FOREACH ( QgsAnnotation *annotation, mAnnotations )
80+
{
81+
if ( !annotation || !annotation->isVisible() )
82+
{
83+
continue;
84+
}
85+
if ( annotation->mapLayer() && !mMapSettings.layers().contains( annotation->mapLayer() ) )
86+
{
87+
continue;
88+
}
89+
90+
context.painter()->save();
91+
context.painter()->setRenderHint( QPainter::Antialiasing, context.flags() & QgsRenderContext::Antialiasing );
92+
93+
double itemX, itemY;
94+
if ( annotation->hasFixedMapPosition() )
95+
{
96+
itemX = mMapSettings.outputSize().width() * ( annotation->mapPosition().x() - mMapSettings.extent().xMinimum() ) / mMapSettings.extent().width();
97+
itemY = mMapSettings.outputSize().height() * ( 1 - ( annotation->mapPosition().y() - mMapSettings.extent().yMinimum() ) / mMapSettings.extent().height() );
98+
}
99+
else
100+
{
101+
itemX = annotation->relativePosition().x() * mMapSettings.outputSize().width();
102+
itemY = annotation->relativePosition().y() * mMapSettings.outputSize().height();
103+
}
104+
105+
context.painter()->translate( itemX, itemY );
106+
107+
annotation->render( context );
108+
context.painter()->restore();
109+
}
110+
qDeleteAll( mAnnotations );
111+
mAnnotations.clear();
112+
113+
if ( !mFileName.isEmpty() )
114+
{
115+
destPainter->end();
116+
bool success = img.save( mFileName, mFileFormat.toLocal8Bit().data() );
117+
if ( !success )
118+
{
119+
mError = ImageSaveFail;
120+
return false;
121+
}
122+
}
123+
124+
return true;
125+
}
126+
127+
void QgsMapRendererTask::finished( bool result )
128+
{
129+
if ( result )
130+
emit renderingComplete();
131+
else
132+
emit errorOccurred( mError );
133+
}

‎src/core/qgsmaprenderertask.h

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/***************************************************************************
2+
qgsmaprenderertask.h
3+
-------------------------
4+
begin : Apr 2017
5+
copyright : (C) 2017 by Mathieu Pellerin
6+
email : nirvn dot asia at gmail dot com
7+
***************************************************************************/
8+
9+
/***************************************************************************
10+
* *
11+
* This program is free software; you can redistribute it and/or modify *
12+
* it under the terms of the GNU General Public License as published by *
13+
* the Free Software Foundation; either version 2 of the License, or *
14+
* (at your option) any later version. *
15+
* *
16+
***************************************************************************/
17+
18+
#ifndef QGSMAPRENDERERTASK_H
19+
#define QGSMAPRENDERERTASK_H
20+
21+
#include "qgis.h"
22+
#include "qgis_core.h"
23+
#include "qgsannotation.h"
24+
#include "qgsannotationmanager.h"
25+
#include "qgsmapsettings.h"
26+
#include "qgstaskmanager.h"
27+
28+
#include <QPainter>
29+
30+
/**
31+
* \class QgsMapRendererTask
32+
* \ingroup core
33+
* QgsTask task which draws a map to an image file or a painter as a background
34+
* task. This can be used to draw maps without blocking the QGIS interface.
35+
* \since QGIS 3.0
36+
*/
37+
class CORE_EXPORT QgsMapRendererTask : public QgsTask
38+
{
39+
Q_OBJECT
40+
41+
public:
42+
43+
//! \brief Error type
44+
enum ErrorType
45+
{
46+
ImageAllocationFail = 1, // Image allocation failure
47+
ImageSaveFail // Image save failure
48+
};
49+
50+
/**
51+
* Constructor for QgsMapRendererTask to render a map to an image file.
52+
*/
53+
QgsMapRendererTask( const QgsMapSettings &ms,
54+
const QString &fileName,
55+
const QString &fileFormat = QString( "PNG" ) );
56+
57+
/**
58+
* Constructor for QgsMapRendererTask to render a map to a painter object.
59+
*/
60+
QgsMapRendererTask( const QgsMapSettings &ms,
61+
QPainter *p );
62+
63+
/**
64+
* Adds \a annotations to be rendered on the map.
65+
*/
66+
void addAnnotations( QList< QgsAnnotation * > annotations );
67+
68+
signals:
69+
70+
/**
71+
* Emitted when the map rendering is successfully completed.
72+
*/
73+
void renderingComplete();
74+
75+
/**
76+
* Emitted when map rendering failed.
77+
*/
78+
void errorOccurred( int error );
79+
80+
protected:
81+
82+
virtual bool run() override;
83+
virtual void finished( bool result ) override;
84+
85+
private:
86+
87+
QgsMapSettings mMapSettings;
88+
89+
QPainter *mPainter = nullptr;
90+
91+
QString mFileName;
92+
QString mFileFormat;
93+
94+
QList< QgsAnnotation * > mAnnotations;
95+
96+
int mError = 0;
97+
};
98+
99+
#endif

0 commit comments

Comments
 (0)