mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-25 00:58:06 -05:00
[FEATURE] New "Save as image" settings dialog (#4390)
* Resolution dpi setting * Extent setting * Scale setting * Draw annotations / decorations setting
This commit is contained in:
parent
4a2226af31
commit
268acab5d6
@ -47,6 +47,11 @@ class QgsMapRendererTask : QgsTask
|
||||
Adds ``annotations`` to be rendered on the map.
|
||||
%End
|
||||
|
||||
void addDecorations( QList< QgsMapDecoration * > decorations );
|
||||
%Docstring
|
||||
Adds ``decorations`` to be rendered on the map.
|
||||
%End
|
||||
|
||||
virtual void cancel();
|
||||
|
||||
|
||||
|
@ -50,6 +50,7 @@ SET(QGIS_APP_SRCS
|
||||
qgsloadstylefromdbdialog.cpp
|
||||
qgsmapcanvasdockwidget.cpp
|
||||
qgsmaplayerstyleguiutils.cpp
|
||||
qgsmapsavedialog.cpp
|
||||
qgsrulebasedlabelingwidget.cpp
|
||||
qgssavestyletodbdialog.cpp
|
||||
qgssnappinglayertreemodel.cpp
|
||||
@ -230,6 +231,7 @@ SET (QGIS_APP_MOC_HDRS
|
||||
qgsloadstylefromdbdialog.h
|
||||
qgsmapcanvasdockwidget.h
|
||||
qgsmaplayerstyleguiutils.h
|
||||
qgsmapsavedialog.h
|
||||
qgsrulebasedlabelingwidget.h
|
||||
qgssavestyletodbdialog.h
|
||||
qgssnappinglayertreemodel.h
|
||||
|
@ -272,7 +272,9 @@ Q_GUI_EXPORT extern int qt_defaultDpiX();
|
||||
#include "qgsvectorlayerutils.h"
|
||||
#include "qgshelp.h"
|
||||
#include "qgsvectorfilewritertask.h"
|
||||
#include "qgsmapsavedialog.h"
|
||||
#include "qgsmaprenderertask.h"
|
||||
#include "qgsmapdecoration.h"
|
||||
#include "qgsnewnamedialog.h"
|
||||
|
||||
#include "qgssublayersdialog.h"
|
||||
@ -5781,24 +5783,55 @@ void QgisApp::updateFilterLegend()
|
||||
|
||||
void QgisApp::saveMapAsImage()
|
||||
{
|
||||
QList< QgsMapDecoration * > decorations;
|
||||
QString activeDecorations;
|
||||
Q_FOREACH ( QgsDecorationItem *decoration, mDecorationItems )
|
||||
{
|
||||
if ( decoration->enabled() )
|
||||
{
|
||||
decorations << decoration;
|
||||
if ( activeDecorations.isEmpty() )
|
||||
activeDecorations = decoration->name().toLower();
|
||||
else
|
||||
activeDecorations += QString( ", %1" ).arg( decoration->name().toLower() );
|
||||
}
|
||||
}
|
||||
|
||||
QgsMapSaveDialog dlg( this, mMapCanvas, activeDecorations );
|
||||
if ( !dlg.exec() )
|
||||
return;
|
||||
|
||||
QPair< QString, QString> myFileNameAndFilter = QgisGui::getSaveAsImageName( this, tr( "Choose a file name to save the map image as" ) );
|
||||
if ( myFileNameAndFilter.first != QLatin1String( "" ) )
|
||||
{
|
||||
//TODO: GUI
|
||||
int dpi = qt_defaultDpiX();
|
||||
QSize size = mMapCanvas->size() * ( dpi / qt_defaultDpiX() );
|
||||
QSize size = mMapCanvas->size();
|
||||
if ( dlg.extent() != mMapCanvas->extent() )
|
||||
{
|
||||
size.setWidth( mMapCanvas->size().width() * dlg.extent().width() / mMapCanvas->extent().width() );
|
||||
size.setHeight( mMapCanvas->size().height() * dlg.extent().height() / mMapCanvas->extent().height() );
|
||||
}
|
||||
size *= dlg.dpi() / qt_defaultDpiX();
|
||||
|
||||
QgsMapSettings ms = QgsMapSettings();
|
||||
ms.setDestinationCrs( QgsProject::instance()->crs() );
|
||||
ms.setExtent( mMapCanvas->extent() );
|
||||
ms.setOutputSize( size );
|
||||
ms.setOutputDpi( dpi );
|
||||
ms.setExtent( dlg.extent() );
|
||||
ms.setOutputSize( dlg.size() );
|
||||
ms.setOutputDpi( dlg.dpi() );
|
||||
ms.setBackgroundColor( mMapCanvas->canvasColor() );
|
||||
ms.setRotation( mMapCanvas->rotation() );
|
||||
ms.setLayers( mMapCanvas->layers() );
|
||||
|
||||
QgsMapRendererTask *mapRendererTask = new QgsMapRendererTask( ms, myFileNameAndFilter.first, myFileNameAndFilter.second );
|
||||
|
||||
if ( dlg.drawAnnotations() )
|
||||
{
|
||||
mapRendererTask->addAnnotations( QgsProject::instance()->annotationManager()->annotations() );
|
||||
}
|
||||
|
||||
if ( dlg.drawDecorations() )
|
||||
{
|
||||
mapRendererTask->addDecorations( decorations );
|
||||
}
|
||||
|
||||
connect( mapRendererTask, &QgsMapRendererTask::renderingComplete, this, [ = ]
|
||||
{
|
||||
|
117
src/app/qgsmapsavedialog.cpp
Normal file
117
src/app/qgsmapsavedialog.cpp
Normal file
@ -0,0 +1,117 @@
|
||||
/***************************************************************************
|
||||
qgsmapsavedialog.cpp
|
||||
-------------------------------------
|
||||
begin : April 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 "qgsmapsavedialog.h"
|
||||
|
||||
#include "qgis.h"
|
||||
#include "qgsscalecalculator.h"
|
||||
#include "qgsdecorationitem.h"
|
||||
#include "qgsextentgroupbox.h"
|
||||
#include "qgsmapsettings.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QSpinBox>
|
||||
#include <QList>
|
||||
|
||||
Q_GUI_EXPORT extern int qt_defaultDpiX();
|
||||
|
||||
QgsMapSaveDialog::QgsMapSaveDialog( QWidget *parent, QgsMapCanvas *mapCanvas, const QString &activeDecorations )
|
||||
: QDialog( parent )
|
||||
{
|
||||
setupUi( this );
|
||||
|
||||
mExtent = mapCanvas->mapSettings().visibleExtent();
|
||||
mDpi = mapCanvas->mapSettings().outputDpi();
|
||||
mSize = mapCanvas->mapSettings().outputSize();
|
||||
|
||||
mResolutionSpinBox->setValue( qt_defaultDpiX() );
|
||||
|
||||
mExtentGroupBox->setOutputCrs( mapCanvas->mapSettings().destinationCrs() );
|
||||
mExtentGroupBox->setCurrentExtent( mapCanvas->mapSettings().visibleExtent(), mapCanvas->mapSettings().destinationCrs() );
|
||||
mExtentGroupBox->setOutputExtentFromCurrent();
|
||||
|
||||
mScaleWidget->setScale( 1 / mapCanvas->mapSettings().scale() );
|
||||
mScaleWidget->setMapCanvas( mapCanvas );
|
||||
mScaleWidget->setShowCurrentScaleButton( true );
|
||||
|
||||
mDrawDecorations->setText( QString( "Draw active decorations: %1" ).arg( !activeDecorations.isEmpty() ? activeDecorations : tr( "none" ) ) );
|
||||
|
||||
connect( mResolutionSpinBox, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsMapSaveDialog::updateDpi );
|
||||
connect( mExtentGroupBox, &QgsExtentGroupBox::extentChanged, this, &QgsMapSaveDialog::updateExtent );
|
||||
connect( mScaleWidget, &QgsScaleWidget::scaleChanged, this, &QgsMapSaveDialog::updateScale );
|
||||
|
||||
updateOutputSize();
|
||||
}
|
||||
|
||||
void QgsMapSaveDialog::updateDpi( int dpi )
|
||||
{
|
||||
mSize *= ( double )dpi / mDpi;
|
||||
mDpi = dpi;
|
||||
|
||||
updateOutputSize();
|
||||
}
|
||||
|
||||
void QgsMapSaveDialog::updateExtent( const QgsRectangle &extent )
|
||||
{
|
||||
mSize.setWidth( mSize.width() * extent.width() / mExtent.width() );
|
||||
mSize.setHeight( mSize.height() * extent.height() / mExtent.height() );
|
||||
mExtent = extent;
|
||||
|
||||
updateOutputSize();
|
||||
}
|
||||
|
||||
void QgsMapSaveDialog::updateScale( double scale )
|
||||
{
|
||||
QgsScaleCalculator calculator;
|
||||
calculator.setMapUnits( mExtentGroupBox->currentCrs().mapUnits() );
|
||||
calculator.setDpi( mDpi );
|
||||
|
||||
double oldScale = 1 / ( calculator.calculate( mExtent, mSize.width() ) );
|
||||
double scaleRatio = oldScale / scale;
|
||||
mExtent.scale( scaleRatio );
|
||||
mExtentGroupBox->setOutputExtentFromUser( mExtent, mExtentGroupBox->currentCrs() );
|
||||
}
|
||||
|
||||
void QgsMapSaveDialog::updateOutputSize()
|
||||
{
|
||||
mOutputSize->setText( QString( "Output size: %1 x %2 pixels" ).arg( mSize.width() ).arg( mSize.height() ) );
|
||||
}
|
||||
|
||||
QgsRectangle QgsMapSaveDialog::extent() const
|
||||
{
|
||||
return mExtentGroupBox->outputExtent();
|
||||
}
|
||||
|
||||
int QgsMapSaveDialog::dpi() const
|
||||
{
|
||||
return mResolutionSpinBox->value();
|
||||
}
|
||||
|
||||
QSize QgsMapSaveDialog::size() const
|
||||
{
|
||||
return mSize;
|
||||
}
|
||||
|
||||
bool QgsMapSaveDialog::drawAnnotations() const
|
||||
{
|
||||
return mDrawAnnotations->isChecked();
|
||||
}
|
||||
|
||||
bool QgsMapSaveDialog::drawDecorations() const
|
||||
{
|
||||
return mDrawDecorations->isChecked();
|
||||
}
|
72
src/app/qgsmapsavedialog.h
Normal file
72
src/app/qgsmapsavedialog.h
Normal file
@ -0,0 +1,72 @@
|
||||
/***************************************************************************
|
||||
qgsmapsavedialog.h
|
||||
-------------------------------------
|
||||
begin : April 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 QGSMAPSAVEDIALOG_H
|
||||
#define QGSMAPSAVEDIALOG_H
|
||||
|
||||
#include "ui_qgsmapsavedialog.h"
|
||||
|
||||
#include "qgisapp.h"
|
||||
#include "qgsrectangle.h"
|
||||
#include "qgsmapcanvas.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QSize>
|
||||
|
||||
/** \ingroup app
|
||||
* \brief a dialog for saving a map to an image.
|
||||
* \since QGIS 3.0
|
||||
*/
|
||||
class APP_EXPORT QgsMapSaveDialog: public QDialog, private Ui::QgsMapSaveDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/** Constructor for QgsMapSaveDialog
|
||||
*/
|
||||
QgsMapSaveDialog( QWidget *parent = nullptr, QgsMapCanvas *mapCanvas = nullptr, const QString &activeDecorations = QString() );
|
||||
|
||||
//! returns extent rectangle
|
||||
QgsRectangle extent() const;
|
||||
|
||||
//! returns the numerical value of the dpi spin box
|
||||
int dpi() const;
|
||||
|
||||
//! returns the output size
|
||||
QSize size() const;
|
||||
|
||||
//! returns whether the draw annotations element is checked
|
||||
bool drawAnnotations() const;
|
||||
|
||||
//! returns whether the draw decorations element is checked
|
||||
bool drawDecorations() const;
|
||||
|
||||
private:
|
||||
|
||||
void updateDpi( int dpi );
|
||||
void updateExtent( const QgsRectangle &extent );
|
||||
void updateScale( double scale );
|
||||
void updateOutputSize();
|
||||
|
||||
QgsRectangle mExtent;
|
||||
int mDpi;
|
||||
QSize mSize;
|
||||
|
||||
};
|
||||
|
||||
#endif // QGSMAPSAVEDIALOG_H
|
@ -46,6 +46,12 @@ void QgsMapRendererTask::addAnnotations( QList< QgsAnnotation * > annotations )
|
||||
}
|
||||
}
|
||||
|
||||
void QgsMapRendererTask::addDecorations( QList< QgsMapDecoration * > decorations )
|
||||
{
|
||||
mDecorations = decorations;
|
||||
}
|
||||
|
||||
|
||||
void QgsMapRendererTask::cancel()
|
||||
{
|
||||
mJobMutex.lock();
|
||||
@ -97,6 +103,11 @@ bool QgsMapRendererTask::run()
|
||||
QgsRenderContext context = QgsRenderContext::fromMapSettings( mMapSettings );
|
||||
context.setPainter( destPainter );
|
||||
|
||||
Q_FOREACH ( QgsMapDecoration *decoration, mDecorations )
|
||||
{
|
||||
decoration->render( mMapSettings, context );
|
||||
}
|
||||
|
||||
Q_FOREACH ( QgsAnnotation *annotation, mAnnotations )
|
||||
{
|
||||
if ( isCanceled() )
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "qgsannotation.h"
|
||||
#include "qgsannotationmanager.h"
|
||||
#include "qgsmapsettings.h"
|
||||
#include "qgsmapdecoration.h"
|
||||
#include "qgstaskmanager.h"
|
||||
#include "qgsmaprenderercustompainterjob.h"
|
||||
|
||||
@ -67,6 +68,11 @@ class CORE_EXPORT QgsMapRendererTask : public QgsTask
|
||||
*/
|
||||
void addAnnotations( QList< QgsAnnotation * > annotations );
|
||||
|
||||
/**
|
||||
* Adds \a decorations to be rendered on the map.
|
||||
*/
|
||||
void addDecorations( QList< QgsMapDecoration * > decorations );
|
||||
|
||||
void cancel() override;
|
||||
|
||||
signals:
|
||||
@ -99,6 +105,7 @@ class CORE_EXPORT QgsMapRendererTask : public QgsTask
|
||||
QString mFileFormat;
|
||||
|
||||
QList< QgsAnnotation * > mAnnotations;
|
||||
QList< QgsMapDecoration * > mDecorations;
|
||||
|
||||
int mError = 0;
|
||||
};
|
||||
|
@ -29,6 +29,8 @@ QgsExtentGroupBox::QgsExtentGroupBox( QWidget *parent )
|
||||
mYMinLineEdit->setValidator( new QDoubleValidator( this ) );
|
||||
mYMaxLineEdit->setValidator( new QDoubleValidator( this ) );
|
||||
|
||||
mOriginalExtentButton->setVisible( false );
|
||||
|
||||
connect( mCurrentExtentButton, &QAbstractButton::clicked, this, &QgsExtentGroupBox::setOutputExtentFromCurrent );
|
||||
connect( mOriginalExtentButton, &QAbstractButton::clicked, this, &QgsExtentGroupBox::setOutputExtentFromOriginal );
|
||||
connect( this, &QGroupBox::clicked, this, &QgsExtentGroupBox::groupBoxClicked );
|
||||
@ -39,6 +41,8 @@ void QgsExtentGroupBox::setOriginalExtent( const QgsRectangle &originalExtent, c
|
||||
{
|
||||
mOriginalExtent = originalExtent;
|
||||
mOriginalCrs = originalCrs;
|
||||
|
||||
mOriginalExtentButton->setVisible( true );
|
||||
}
|
||||
|
||||
|
||||
|
181
src/ui/qgsmapsavedialog.ui
Normal file
181
src/ui/qgsmapsavedialog.ui
Normal file
@ -0,0 +1,181 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>QgsMapSaveDialog</class>
|
||||
<widget class="QDialog" name="QgsMapSaveDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>600</width>
|
||||
<height>225</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Save map as image</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="5" column="0" colspan="2">>
|
||||
<widget class="QCheckBox" name="mDrawAnnotations">
|
||||
<property name="text">
|
||||
<string>Draw annotations</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="mDrawDecorations">
|
||||
<property name="text">
|
||||
<string>Draw active decorations</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QLabel" name="mOutputSize">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Resolution</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QgsSpinBox" name="mResolutionSpinBox">
|
||||
<property name="suffix">
|
||||
<string> dpi</string>
|
||||
</property>
|
||||
<property name="prefix">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>3000</number>
|
||||
</property>
|
||||
<property name="showClearButton" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_1">
|
||||
<property name="text">
|
||||
<string>Scale</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QgsScaleWidget" name="mScaleWidget">
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QgsExtentGroupBox" name="mExtentGroupBox">
|
||||
<property name="title">
|
||||
<string>Extent</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Save</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>QgsCollapsibleGroupBox</class>
|
||||
<extends>QGroupBox</extends>
|
||||
<header>qgscollapsiblegroupbox.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QgsExtentGroupBox</class>
|
||||
<extends>QgsCollapsibleGroupBox</extends>
|
||||
<header>qgsextentgroupbox.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QgsSpinBox</class>
|
||||
<extends>QSpinBox</extends>
|
||||
<header>qgsspinbox.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QgsScaleWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>qgsscalewidget.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>mExtentGroupBox</tabstop>
|
||||
<tabstop>mDrawAnnotations</tabstop>
|
||||
<tabstop>mDrawDecorations</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>QgsMapSaveDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>QgsMapSaveDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
Loading…
x
Reference in New Issue
Block a user