mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-27 00:03:38 -04:00
Upgrade the save as image function into a background task (#4382)
This commit is contained in:
parent
20197c2fa3
commit
db848a3f1a
@ -95,6 +95,7 @@
|
|||||||
%Include qgsmaprendererjob.sip
|
%Include qgsmaprendererjob.sip
|
||||||
%Include qgsmaprendererparalleljob.sip
|
%Include qgsmaprendererparalleljob.sip
|
||||||
%Include qgsmaprenderersequentialjob.sip
|
%Include qgsmaprenderersequentialjob.sip
|
||||||
|
%Include qgsmaprenderertask.sip
|
||||||
%Include qgsmapsettings.sip
|
%Include qgsmapsettings.sip
|
||||||
%Include qgsmaptopixel.sip
|
%Include qgsmaptopixel.sip
|
||||||
%Include qgsmapunitscale.sip
|
%Include qgsmapunitscale.sip
|
||||||
|
76
python/core/qgsmaprenderertask.sip
Normal file
76
python/core/qgsmaprenderertask.sip
Normal 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 *
|
||||||
|
************************************************************************/
|
||||||
|
|
@ -270,6 +270,7 @@
|
|||||||
#include "qgsvectorlayerutils.h"
|
#include "qgsvectorlayerutils.h"
|
||||||
#include "qgshelp.h"
|
#include "qgshelp.h"
|
||||||
#include "qgsvectorfilewritertask.h"
|
#include "qgsvectorfilewritertask.h"
|
||||||
|
#include "qgsmaprenderertask.h"
|
||||||
#include "qgsnewnamedialog.h"
|
#include "qgsnewnamedialog.h"
|
||||||
|
|
||||||
#include "qgssublayersdialog.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" ) );
|
QPair< QString, QString> myFileNameAndFilter = QgisGui::getSaveAsImageName( this, tr( "Choose a file name to save the map image as" ) );
|
||||||
if ( myFileNameAndFilter.first != QLatin1String( "" ) )
|
if ( myFileNameAndFilter.first != QLatin1String( "" ) )
|
||||||
{
|
{
|
||||||
//save the mapview to the selected file
|
//TODO: GUI
|
||||||
mMapCanvas->saveAsImage( myFileNameAndFilter.first, nullptr, myFileNameAndFilter.second );
|
int dpi = 90;
|
||||||
statusBar()->showMessage( tr( "Saved map image to %1" ).arg( myFileNameAndFilter.first ) );
|
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
|
} // saveMapAsImage
|
||||||
|
@ -173,6 +173,7 @@ SET(QGIS_CORE_SRCS
|
|||||||
qgsmaprendererjob.cpp
|
qgsmaprendererjob.cpp
|
||||||
qgsmaprendererparalleljob.cpp
|
qgsmaprendererparalleljob.cpp
|
||||||
qgsmaprenderersequentialjob.cpp
|
qgsmaprenderersequentialjob.cpp
|
||||||
|
qgsmaprenderertask.cpp
|
||||||
qgsmapsettings.cpp
|
qgsmapsettings.cpp
|
||||||
qgsmaptopixel.cpp
|
qgsmaptopixel.cpp
|
||||||
qgsmaptopixelgeometrysimplifier.cpp
|
qgsmaptopixelgeometrysimplifier.cpp
|
||||||
@ -521,6 +522,7 @@ SET(QGIS_CORE_MOC_HDRS
|
|||||||
qgsmaprendererjob.h
|
qgsmaprendererjob.h
|
||||||
qgsmaprendererparalleljob.h
|
qgsmaprendererparalleljob.h
|
||||||
qgsmaprenderersequentialjob.h
|
qgsmaprenderersequentialjob.h
|
||||||
|
qgsmaprenderertask.h
|
||||||
qgsmessagelog.h
|
qgsmessagelog.h
|
||||||
qgsmessageoutput.h
|
qgsmessageoutput.h
|
||||||
qgsnetworkaccessmanager.h
|
qgsnetworkaccessmanager.h
|
||||||
|
133
src/core/qgsmaprenderertask.cpp
Normal file
133
src/core/qgsmaprenderertask.cpp
Normal 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 );
|
||||||
|
}
|
99
src/core/qgsmaprenderertask.h
Normal file
99
src/core/qgsmaprenderertask.h
Normal 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
|
Loading…
x
Reference in New Issue
Block a user