Upgrade the save as image function into a background task (#4382)

This commit is contained in:
Mathieu Pellerin 2017-04-21 11:47:37 +07:00 committed by GitHub
parent 20197c2fa3
commit db848a3f1a
6 changed files with 354 additions and 3 deletions

View File

@ -95,6 +95,7 @@
%Include qgsmaprendererjob.sip
%Include qgsmaprendererparalleljob.sip
%Include qgsmaprenderersequentialjob.sip
%Include qgsmaprenderertask.sip
%Include qgsmapsettings.sip
%Include qgsmaptopixel.sip
%Include qgsmapunitscale.sip

View File

@ -0,0 +1,76 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/qgsmaprenderertask.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
class QgsMapRendererTask : QgsTask
{
%Docstring
QgsTask task which draws a map to an image file or a painter as a background
task. This can be used to draw maps without blocking the QGIS interface.
.. versionadded:: 3.0
%End
%TypeHeaderCode
#include "qgsmaprenderertask.h"
%End
public:
enum ErrorType
{
ImageAllocationFail,
ImageSaveFail
};
QgsMapRendererTask( const QgsMapSettings &ms,
const QString &fileName,
const QString &fileFormat = QString( "PNG" ) );
%Docstring
Constructor for QgsMapRendererTask to render a map to an image file.
%End
QgsMapRendererTask( const QgsMapSettings &ms,
QPainter *p );
%Docstring
Constructor for QgsMapRendererTask to render a map to a painter object.
%End
void addAnnotations( QList< QgsAnnotation * > annotations );
%Docstring
Adds ``annotations`` to be rendered on the map.
%End
signals:
void renderingComplete();
%Docstring
Emitted when the map rendering is successfully completed.
%End
void errorOccurred( int error );
%Docstring
Emitted when map rendering failed.
%End
protected:
virtual bool run();
virtual void finished( bool result );
};
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/qgsmaprenderertask.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/

View File

@ -270,6 +270,7 @@
#include "qgsvectorlayerutils.h"
#include "qgshelp.h"
#include "qgsvectorfilewritertask.h"
#include "qgsmaprenderertask.h"
#include "qgsnewnamedialog.h"
#include "qgssublayersdialog.h"
@ -5772,9 +5773,48 @@ void QgisApp::saveMapAsImage()
QPair< QString, QString> myFileNameAndFilter = QgisGui::getSaveAsImageName( this, tr( "Choose a file name to save the map image as" ) );
if ( myFileNameAndFilter.first != QLatin1String( "" ) )
{
//save the mapview to the selected file
mMapCanvas->saveAsImage( myFileNameAndFilter.first, nullptr, myFileNameAndFilter.second );
statusBar()->showMessage( tr( "Saved map image to %1" ).arg( myFileNameAndFilter.first ) );
//TODO: GUI
int dpi = 90;
QSize size = mMapCanvas->size() * ( dpi / 90 );
QgsMapSettings ms = QgsMapSettings();
ms.setDestinationCrs( QgsProject::instance()->crs() );
ms.setExtent( mMapCanvas->extent() );
ms.setOutputSize( size );
ms.setOutputDpi( dpi );
ms.setBackgroundColor( mMapCanvas->canvasColor() );
ms.setRotation( mMapCanvas->rotation() );
ms.setLayers( mMapCanvas->layers() );
QgsMapRendererTask *mapRendererTask = new QgsMapRendererTask( ms, myFileNameAndFilter.first, myFileNameAndFilter.second );
mapRendererTask->addAnnotations( QgsProject::instance()->annotationManager()->annotations() );
connect( mapRendererTask, &QgsMapRendererTask::renderingComplete, this, [ = ]
{
QgsMessageBarItem *successMsg = new QgsMessageBarItem(
tr( "Successfully saved canvas to image" ),
QgsMessageBar::SUCCESS );
messageBar()->pushItem( successMsg );
} );
connect( mapRendererTask, &QgsMapRendererTask::errorOccurred, this, [ = ]( int error )
{
if ( error == QgsMapRendererTask::ImageAllocationFail )
{
QgsMessageBarItem *errorMsg = new QgsMessageBarItem(
tr( "Could not allocate required memory for image" ),
QgsMessageBar::WARNING );
messageBar()->pushItem( errorMsg );
}
else if ( error == QgsMapRendererTask::ImageAllocationFail )
{
QgsMessageBarItem *errorMsg = new QgsMessageBarItem(
tr( "Could not save the image to file" ),
QgsMessageBar::WARNING );
messageBar()->pushItem( errorMsg );
}
} );
QgsApplication::taskManager()->addTask( mapRendererTask );
}
} // saveMapAsImage

View File

@ -173,6 +173,7 @@ SET(QGIS_CORE_SRCS
qgsmaprendererjob.cpp
qgsmaprendererparalleljob.cpp
qgsmaprenderersequentialjob.cpp
qgsmaprenderertask.cpp
qgsmapsettings.cpp
qgsmaptopixel.cpp
qgsmaptopixelgeometrysimplifier.cpp
@ -521,6 +522,7 @@ SET(QGIS_CORE_MOC_HDRS
qgsmaprendererjob.h
qgsmaprendererparalleljob.h
qgsmaprenderersequentialjob.h
qgsmaprenderertask.h
qgsmessagelog.h
qgsmessageoutput.h
qgsnetworkaccessmanager.h

View File

@ -0,0 +1,133 @@
/***************************************************************************
qgsmaprenderertask.h
-------------------------
begin : Apr 2017
copyright : (C) 2017 by Mathieu Pellerin
email : nirvn dot asia at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsannotation.h"
#include "qgsannotationmanager.h"
#include "qgsmaprenderertask.h"
#include "qgsmaprenderercustompainterjob.h"
QgsMapRendererTask::QgsMapRendererTask( const QgsMapSettings &ms, const QString &fileName, const QString &fileFormat )
: QgsTask( tr( "Saving as image" ) )
, mMapSettings( ms )
, mFileName( fileName )
, mFileFormat( fileFormat )
{
}
QgsMapRendererTask::QgsMapRendererTask( const QgsMapSettings &ms, QPainter *p )
: QgsTask( tr( "Saving as image" ) )
, mMapSettings( ms )
, mPainter( p )
{
}
void QgsMapRendererTask::addAnnotations( QList< QgsAnnotation * > annotations )
{
qDeleteAll( mAnnotations );
mAnnotations.clear();
Q_FOREACH ( const QgsAnnotation *a, annotations )
{
mAnnotations << a->clone();
}
}
bool QgsMapRendererTask::run()
{
QImage img;
std::unique_ptr< QPainter > tempPainter;
QPainter *destPainter = mPainter;
if ( !mPainter )
{
// save rendered map to an image file
img = QImage( mMapSettings.outputSize(), QImage::Format_ARGB32 );
if ( img.isNull() )
{
mError = ImageAllocationFail;
return false;
}
tempPainter.reset( new QPainter( &img ) );
destPainter = tempPainter.get();
}
if ( !destPainter )
return false;
QgsMapRendererCustomPainterJob r( mMapSettings, destPainter );
r.renderSynchronously();
QgsRenderContext context = QgsRenderContext::fromMapSettings( mMapSettings );
context.setPainter( destPainter );
Q_FOREACH ( QgsAnnotation *annotation, mAnnotations )
{
if ( !annotation || !annotation->isVisible() )
{
continue;
}
if ( annotation->mapLayer() && !mMapSettings.layers().contains( annotation->mapLayer() ) )
{
continue;
}
context.painter()->save();
context.painter()->setRenderHint( QPainter::Antialiasing, context.flags() & QgsRenderContext::Antialiasing );
double itemX, itemY;
if ( annotation->hasFixedMapPosition() )
{
itemX = mMapSettings.outputSize().width() * ( annotation->mapPosition().x() - mMapSettings.extent().xMinimum() ) / mMapSettings.extent().width();
itemY = mMapSettings.outputSize().height() * ( 1 - ( annotation->mapPosition().y() - mMapSettings.extent().yMinimum() ) / mMapSettings.extent().height() );
}
else
{
itemX = annotation->relativePosition().x() * mMapSettings.outputSize().width();
itemY = annotation->relativePosition().y() * mMapSettings.outputSize().height();
}
context.painter()->translate( itemX, itemY );
annotation->render( context );
context.painter()->restore();
}
qDeleteAll( mAnnotations );
mAnnotations.clear();
if ( !mFileName.isEmpty() )
{
destPainter->end();
bool success = img.save( mFileName, mFileFormat.toLocal8Bit().data() );
if ( !success )
{
mError = ImageSaveFail;
return false;
}
}
return true;
}
void QgsMapRendererTask::finished( bool result )
{
if ( result )
emit renderingComplete();
else
emit errorOccurred( mError );
}

View File

@ -0,0 +1,99 @@
/***************************************************************************
qgsmaprenderertask.h
-------------------------
begin : Apr 2017
copyright : (C) 2017 by Mathieu Pellerin
email : nirvn dot asia at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSMAPRENDERERTASK_H
#define QGSMAPRENDERERTASK_H
#include "qgis.h"
#include "qgis_core.h"
#include "qgsannotation.h"
#include "qgsannotationmanager.h"
#include "qgsmapsettings.h"
#include "qgstaskmanager.h"
#include <QPainter>
/**
* \class QgsMapRendererTask
* \ingroup core
* QgsTask task which draws a map to an image file or a painter as a background
* task. This can be used to draw maps without blocking the QGIS interface.
* \since QGIS 3.0
*/
class CORE_EXPORT QgsMapRendererTask : public QgsTask
{
Q_OBJECT
public:
//! \brief Error type
enum ErrorType
{
ImageAllocationFail = 1, // Image allocation failure
ImageSaveFail // Image save failure
};
/**
* Constructor for QgsMapRendererTask to render a map to an image file.
*/
QgsMapRendererTask( const QgsMapSettings &ms,
const QString &fileName,
const QString &fileFormat = QString( "PNG" ) );
/**
* Constructor for QgsMapRendererTask to render a map to a painter object.
*/
QgsMapRendererTask( const QgsMapSettings &ms,
QPainter *p );
/**
* Adds \a annotations to be rendered on the map.
*/
void addAnnotations( QList< QgsAnnotation * > annotations );
signals:
/**
* Emitted when the map rendering is successfully completed.
*/
void renderingComplete();
/**
* Emitted when map rendering failed.
*/
void errorOccurred( int error );
protected:
virtual bool run() override;
virtual void finished( bool result ) override;
private:
QgsMapSettings mMapSettings;
QPainter *mPainter = nullptr;
QString mFileName;
QString mFileFormat;
QList< QgsAnnotation * > mAnnotations;
int mError = 0;
};
#endif