Restore direct print actions

This commit is contained in:
Nyall Dawson 2018-01-02 12:47:51 +10:00
parent a730eb7a95
commit 4e45639c45
9 changed files with 758 additions and 5 deletions

View File

@ -290,6 +290,53 @@ to the error description.
.. seealso:: :py:func:`exportToPdf()`
%End
struct PrintExportSettings
{
PrintExportSettings();
%Docstring
Constructor for PrintExportSettings
%End
double dpi;
%Docstring
Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.
%End
bool rasterizeWholeImage;
%Docstring
Set to true to force whole layout to be rasterized while exporting.
This option is mutually exclusive with forceVectorOutput.
%End
QgsLayoutRenderContext::Flags flags;
%Docstring
Layout context flags, which control how the export will be created.
%End
};
ExportResult print( QPrinter &printer, const QgsLayoutExporter::PrintExportSettings &settings );
%Docstring
Prints the layout to a ``printer``, using the specified export ``settings``.
Returns a result code indicating whether the export was successful or an
error was encountered.
%End
static ExportResult print( QgsAbstractLayoutIterator *iterator, QPrinter &printer,
const QgsLayoutExporter::PrintExportSettings &settings,
QString &error /Out/, QgsFeedback *feedback = 0 );
%Docstring
Exports a layout ``iterator`` to a ``printer``, with the specified export ``settings``.
Returns a result code indicating whether the export was successful or an
error was encountered. If an error was obtained then ``error`` will be set
to the error description.
%End
struct SvgExportSettings
{
SvgExportSettings();

View File

@ -73,6 +73,9 @@
#include <QFileDialog>
#include <QMessageBox>
#include <QProgressDialog>
#include <QPrinter>
#include <QPrintDialog>
#include <QPageSetupDialog>
#ifdef Q_OS_MACX
#include <ApplicationServices/ApplicationServices.h>
#endif
@ -195,6 +198,7 @@ QgsLayoutDesignerDialog::QgsLayoutDesignerDialog( QWidget *parent, Qt::WindowFla
connect( mActionLayoutManager, &QAction::triggered, this, &QgsLayoutDesignerDialog::showManager );
connect( mActionRemoveLayout, &QAction::triggered, this, &QgsLayoutDesignerDialog::deleteLayout );
connect( mActionPrint, &QAction::triggered, this, &QgsLayoutDesignerDialog::print );
connect( mActionExportAsImage, &QAction::triggered, this, &QgsLayoutDesignerDialog::exportToRaster );
connect( mActionExportAsPDF, &QAction::triggered, this, &QgsLayoutDesignerDialog::exportToPdf );
connect( mActionExportAsSVG, &QAction::triggered, this, &QgsLayoutDesignerDialog::exportToSvg );
@ -226,6 +230,9 @@ QgsLayoutDesignerDialog::QgsLayoutDesignerDialog( QWidget *parent, Qt::WindowFla
connect( mActionExportReportAsImage, &QAction::triggered, this, &QgsLayoutDesignerDialog::exportReportToRaster );
connect( mActionExportReportAsSVG, &QAction::triggered, this, &QgsLayoutDesignerDialog::exportReportToSvg );
connect( mActionExportReportAsPDF, &QAction::triggered, this, &QgsLayoutDesignerDialog::exportReportToPdf );
connect( mActionPrintReport, &QAction::triggered, this, &QgsLayoutDesignerDialog::printReport );
connect( mActionPageSetup, &QAction::triggered, this, &QgsLayoutDesignerDialog::pageSetup );
mView = new QgsLayoutView();
//mView->setMapCanvas( mQgis->mapCanvas() );
@ -882,6 +889,10 @@ void QgsLayoutDesignerDialog::showItemOptions( QgsLayoutItem *item, bool bringPa
if ( ! widget )
return;
if ( QgsLayoutPagePropertiesWidget *ppWidget = qobject_cast< QgsLayoutPagePropertiesWidget * >( widget.get() ) )
connect( ppWidget, &QgsLayoutPagePropertiesWidget::pageOrientationChanged, this, &QgsLayoutDesignerDialog::pageOrientationChanged );
widget->setDockMode( true );
connect( item, &QgsLayoutItem::destroyed, widget.get(), [this]
{
@ -1573,6 +1584,102 @@ void QgsLayoutDesignerDialog::deleteLayout()
close();
}
void QgsLayoutDesignerDialog::print()
{
if ( containsWmsLayers() )
{
showWmsPrintingWarning();
}
if ( requiresRasterization() )
{
showRasterizationWarning();
}
if ( currentLayout()->pageCollection()->pageCount() == 0 )
return;
// get orientation from first page
QgsLayoutItemPage::Orientation orientation = currentLayout()->pageCollection()->page( 0 )->orientation();
//set printer page orientation
setPrinterPageOrientation( orientation );
QPrintDialog printDialog( printer(), nullptr );
if ( printDialog.exec() != QDialog::Accepted )
{
return;
}
mView->setPaintingEnabled( false );
QApplication::setOverrideCursor( Qt::BusyCursor );
QgsLayoutExporter::PrintExportSettings printSettings;
printSettings.rasterizeWholeImage = mLayout->customProperty( QStringLiteral( "rasterize" ), false ).toBool();
// force a refresh, to e.g. update data defined properties, tables, etc
mLayout->refresh();
QgsLayoutExporter exporter( mLayout );
QString printerName = printer()->printerName();
switch ( exporter.print( *printer(), printSettings ) )
{
case QgsLayoutExporter::Success:
{
QString message;
if ( !printerName.isEmpty() )
{
message = tr( "Successfully printed layout to %1" ).arg( printerName );
}
else
{
message = tr( "Successfully printed layout" );
}
mMessageBar->pushMessage( tr( "Print layout" ),
message,
QgsMessageBar::SUCCESS, 0 );
break;
}
case QgsLayoutExporter::PrintError:
{
QString message;
if ( !printerName.isEmpty() )
{
message = tr( "Could not create print device for %1" ).arg( printerName );
}
else
{
message = tr( "Could not create print device" );
}
QMessageBox::warning( this, tr( "Print layout" ),
message,
QMessageBox::Ok,
QMessageBox::Ok );
break;
}
case QgsLayoutExporter::MemoryError:
QMessageBox::warning( this, tr( "Memory Allocation Error" ),
tr( "Printing the layout "
"resulted in a memory overflow.\n\n"
"Please try a lower resolution or a smaller paper size." ),
QMessageBox::Ok, QMessageBox::Ok );
break;
case QgsLayoutExporter::FileError:
case QgsLayoutExporter::SvgLayerError:
case QgsLayoutExporter::IteratorError:
case QgsLayoutExporter::Canceled:
// no meaning for PDF exports, will not be encountered
break;
}
mView->setPaintingEnabled( true );
QApplication::restoreOverrideCursor();
}
void QgsLayoutDesignerDialog::exportToRaster()
{
if ( containsWmsLayers() )
@ -2032,8 +2139,132 @@ void QgsLayoutDesignerDialog::atlasLast()
void QgsLayoutDesignerDialog::printAtlas()
{
QgsLayoutAtlas *printAtlas = atlas();
if ( !printAtlas || !printAtlas->enabled() )
return;
loadAtlasPredefinedScalesFromProject();
//TODO
if ( containsWmsLayers() )
{
showWmsPrintingWarning();
}
if ( requiresRasterization() )
{
showRasterizationWarning();
}
if ( currentLayout()->pageCollection()->pageCount() == 0 )
return;
// get orientation from first page
QgsLayoutItemPage::Orientation orientation = currentLayout()->pageCollection()->page( 0 )->orientation();
//set printer page orientation
setPrinterPageOrientation( orientation );
QPrintDialog printDialog( printer(), nullptr );
if ( printDialog.exec() != QDialog::Accepted )
{
return;
}
mView->setPaintingEnabled( false );
QApplication::setOverrideCursor( Qt::BusyCursor );
QgsLayoutExporter::PrintExportSettings printSettings;
printSettings.rasterizeWholeImage = mLayout->customProperty( QStringLiteral( "rasterize" ), false ).toBool();
QString error;
std::unique_ptr< QgsFeedback > feedback = qgis::make_unique< QgsFeedback >();
std::unique_ptr< QProgressDialog > progressDialog = qgis::make_unique< QProgressDialog >( tr( "Printing maps..." ), tr( "Abort" ), 0, 100, this );
progressDialog->setWindowTitle( tr( "Printing Atlas" ) );
connect( feedback.get(), &QgsFeedback::progressChanged, this, [ & ]( double progress )
{
progressDialog->setValue( progress );
progressDialog->setLabelText( feedback->property( "progress" ).toString() ) ;
#ifdef Q_OS_LINUX
// For some reason on Windows hasPendingEvents() always return true,
// but one iteration is actually enough on Windows to get good interactivity
// whereas on Linux we must allow for far more iterations.
// For safety limit the number of iterations
int nIters = 0;
while ( QCoreApplication::hasPendingEvents() && ++nIters < 100 )
#endif
{
QCoreApplication::processEvents();
}
} );
connect( progressDialog.get(), &QProgressDialog::canceled, this, [ & ]
{
feedback->cancel();
} );
QString printerName = printer()->printerName();
switch ( QgsLayoutExporter::print( printAtlas, *printer(), printSettings, error, feedback.get() ) )
{
case QgsLayoutExporter::Success:
{
QString message;
if ( !printerName.isEmpty() )
{
message = tr( "Successfully printed atlas to %1" ).arg( printerName );
}
else
{
message = tr( "Successfully printed atlas" );
}
mMessageBar->pushMessage( tr( "Print atlas" ),
message,
QgsMessageBar::SUCCESS, 0 );
break;
}
case QgsLayoutExporter::PrintError:
{
QString message;
if ( !printerName.isEmpty() )
{
message = tr( "Could not create print device for %1" ).arg( printerName );
}
else
{
message = tr( "Could not create print device" );
}
QMessageBox::warning( this, tr( "Print atlas" ),
message,
QMessageBox::Ok,
QMessageBox::Ok );
break;
}
case QgsLayoutExporter::MemoryError:
QMessageBox::warning( this, tr( "Memory Allocation Error" ),
tr( "Printing the layout "
"resulted in a memory overflow.\n\n"
"Please try a lower resolution or a smaller paper size." ),
QMessageBox::Ok, QMessageBox::Ok );
break;
case QgsLayoutExporter::IteratorError:
QMessageBox::warning( this, tr( "Print Atlas" ),
tr( "Error encountered while printing atlas" ),
QMessageBox::Ok,
QMessageBox::Ok );
break;
case QgsLayoutExporter::FileError:
case QgsLayoutExporter::SvgLayerError:
case QgsLayoutExporter::Canceled:
// no meaning for PDF exports, will not be encountered
break;
}
mView->setPaintingEnabled( true );
QApplication::restoreOverrideCursor();
}
void QgsLayoutDesignerDialog::exportAtlasToRaster()
@ -2934,6 +3165,112 @@ void QgsLayoutDesignerDialog::exportReportToPdf()
QApplication::restoreOverrideCursor();
}
void QgsLayoutDesignerDialog::printReport()
{
QPrintDialog printDialog( printer(), nullptr );
if ( printDialog.exec() != QDialog::Accepted )
{
return;
}
mView->setPaintingEnabled( false );
QApplication::setOverrideCursor( Qt::BusyCursor );
QgsLayoutExporter::PrintExportSettings printSettings;
if ( mLayout )
printSettings.rasterizeWholeImage = mLayout->customProperty( QStringLiteral( "rasterize" ), false ).toBool();
QString error;
std::unique_ptr< QgsFeedback > feedback = qgis::make_unique< QgsFeedback >();
std::unique_ptr< QProgressDialog > progressDialog = qgis::make_unique< QProgressDialog >( tr( "Printing maps..." ), tr( "Abort" ), 0, 0, this );
progressDialog->setWindowTitle( tr( "Printing Report" ) );
connect( feedback.get(), &QgsFeedback::progressChanged, this, [ & ]( double )
{
//progressDialog->setValue( progress );
progressDialog->setLabelText( feedback->property( "progress" ).toString() ) ;
#ifdef Q_OS_LINUX
// For some reason on Windows hasPendingEvents() always return true,
// but one iteration is actually enough on Windows to get good interactivity
// whereas on Linux we must allow for far more iterations.
// For safety limit the number of iterations
int nIters = 0;
while ( QCoreApplication::hasPendingEvents() && ++nIters < 100 )
#endif
{
QCoreApplication::processEvents();
}
} );
connect( progressDialog.get(), &QProgressDialog::canceled, this, [ & ]
{
feedback->cancel();
} );
QString printerName = printer()->printerName();
switch ( QgsLayoutExporter::print( dynamic_cast< QgsReport * >( mMasterLayout ), *printer(), printSettings, error, feedback.get() ) )
{
case QgsLayoutExporter::Success:
{
QString message;
if ( !printerName.isEmpty() )
{
message = tr( "Successfully printed report to %1" ).arg( printerName );
}
else
{
message = tr( "Successfully printed report" );
}
mMessageBar->pushMessage( tr( "Print report" ),
message,
QgsMessageBar::SUCCESS, 0 );
break;
}
case QgsLayoutExporter::PrintError:
{
QString message;
if ( !printerName.isEmpty() )
{
message = tr( "Could not create print device for %1" ).arg( printerName );
}
else
{
message = tr( "Could not create print device" );
}
QMessageBox::warning( this, tr( "Print report" ),
message,
QMessageBox::Ok,
QMessageBox::Ok );
break;
}
case QgsLayoutExporter::MemoryError:
QMessageBox::warning( this, tr( "Memory Allocation Error" ),
tr( "Printing the report"
"resulted in a memory overflow.\n\n"
"Please try a lower resolution or a smaller paper size." ),
QMessageBox::Ok, QMessageBox::Ok );
break;
case QgsLayoutExporter::IteratorError:
QMessageBox::warning( this, tr( "Print Report" ),
tr( "Error encountered while printing report" ),
QMessageBox::Ok,
QMessageBox::Ok );
break;
case QgsLayoutExporter::FileError:
case QgsLayoutExporter::SvgLayerError:
case QgsLayoutExporter::Canceled:
// no meaning for PDF exports, will not be encountered
break;
}
mView->setPaintingEnabled( true );
QApplication::restoreOverrideCursor();
}
void QgsLayoutDesignerDialog::showReportSettings()
{
if ( !mReportDock )
@ -2947,6 +3284,25 @@ void QgsLayoutDesignerDialog::showReportSettings()
mReportDock->raise();
}
void QgsLayoutDesignerDialog::pageSetup()
{
if ( currentLayout() && currentLayout()->pageCollection()->pageCount() > 0 )
{
// get orientation from first page
QgsLayoutItemPage::Orientation orientation = currentLayout()->pageCollection()->page( 0 )->orientation();
//set printer page orientation
setPrinterPageOrientation( orientation );
}
QPageSetupDialog pageSetupDialog( printer(), this );
pageSetupDialog.exec();
}
void QgsLayoutDesignerDialog::pageOrientationChanged()
{
mSetPageOrientation = false;
}
void QgsLayoutDesignerDialog::paste()
{
QPointF pt = mView->mapFromGlobal( QCursor::pos() );
@ -3078,6 +3434,8 @@ void QgsLayoutDesignerDialog::createReportWidget()
QgsReportOrganizerWidget *reportWidget = new QgsReportOrganizerWidget( mReportDock, this, report );
reportWidget->setMessageBar( mMessageBar );
mReportDock->setWidget( reportWidget );
mReportDock->show();
mReportDock->raise();
mReportToolbar->show();
@ -3087,9 +3445,10 @@ void QgsLayoutDesignerDialog::createReportWidget()
void QgsLayoutDesignerDialog::initializeRegistry()
{
sInitializedRegistry = true;
auto createPageWidget = ( []( QgsLayoutItem * item )->QgsLayoutItemBaseWidget *
auto createPageWidget = ( [this]( QgsLayoutItem * item )->QgsLayoutItemBaseWidget *
{
return new QgsLayoutPagePropertiesWidget( nullptr, item );
std::unique_ptr< QgsLayoutPagePropertiesWidget > newWidget = qgis::make_unique< QgsLayoutPagePropertiesWidget >( nullptr, item );
return newWidget.release();
} );
QgsGui::layoutItemGuiRegistry()->addLayoutItemGuiMetadata( new QgsLayoutItemGuiMetadata( QgsLayoutItemRegistry::LayoutPage, QObject::tr( "Page" ), QIcon(), createPageWidget, nullptr, QString(), false, QgsLayoutItemAbstractGuiMetadata::FlagNoCreationTools ) );
@ -3573,6 +3932,35 @@ void QgsLayoutDesignerDialog::toggleActions( bool layoutAvailable )
}
}
void QgsLayoutDesignerDialog::setPrinterPageOrientation( QgsLayoutItemPage::Orientation orientation )
{
if ( !mSetPageOrientation )
{
switch ( orientation )
{
case QgsLayoutItemPage::Landscape:
printer()->setOrientation( QPrinter::Landscape );
break;
case QgsLayoutItemPage::Portrait:
printer()->setOrientation( QPrinter::Portrait );
break;
}
mSetPageOrientation = true;
}
}
QPrinter *QgsLayoutDesignerDialog::printer()
{
//only create the printer on demand - creating a printer object can be very slow
//due to QTBUG-3033
if ( !mPrinter )
mPrinter = qgis::make_unique< QPrinter >();
return mPrinter.get();
}
void QgsLayoutDesignerDialog::selectItems( const QList<QgsLayoutItem *> items )
{
for ( QGraphicsItem *item : items )

View File

@ -20,6 +20,7 @@
#include "ui_qgslayoutdesignerbase.h"
#include "qgslayoutdesignerinterface.h"
#include "qgslayoutexporter.h"
#include "qgslayoutpagecollection.h"
#include <QToolButton>
class QgsLayoutDesignerDialog;
@ -304,6 +305,7 @@ class QgsLayoutDesignerDialog: public QMainWindow, private Ui::QgsLayoutDesigner
void showManager();
void renameLayout();
void deleteLayout();
void print();
void exportToRaster();
void exportToPdf();
void exportToSvg();
@ -322,7 +324,14 @@ class QgsLayoutDesignerDialog: public QMainWindow, private Ui::QgsLayoutDesigner
void exportReportToRaster();
void exportReportToSvg();
void exportReportToPdf();
void printReport();
void showReportSettings();
void pageSetup();
//! Sets the printer page orientation when the page orientation changes
void pageOrientationChanged();
private:
static bool sInitializedRegistry;
@ -406,6 +415,10 @@ class QgsLayoutDesignerDialog: public QMainWindow, private Ui::QgsLayoutDesigner
QComboBox *mAtlasPageComboBox = nullptr;
//! Page & Printer Setup
std::unique_ptr< QPrinter > mPrinter;
bool mSetPageOrientation = false;
//! Save window state
void saveWindowState();
@ -462,6 +475,9 @@ class QgsLayoutDesignerDialog: public QMainWindow, private Ui::QgsLayoutDesigner
QgsLayoutAtlas *atlas();
void toggleActions( bool layoutAvailable );
void setPrinterPageOrientation( QgsLayoutItemPage::Orientation orientation );
QPrinter *printer();
};
#endif // QGSLAYOUTDESIGNERDIALOG_H

View File

@ -165,6 +165,8 @@ void QgsLayoutPagePropertiesWidget::updatePageSize()
mPage->layout()->pageCollection()->reflow();
mPage->layout()->pageCollection()->endPageSizeChange();
mPage->layout()->undoStack()->endMacro();
emit pageOrientationChanged();
}
void QgsLayoutPagePropertiesWidget::setToCustomSize()

View File

@ -23,6 +23,7 @@
#include "qgslayoutpoint.h"
#include "qgslayoutitemwidget.h"
#include "qgslayoutmeasurementconverter.h"
#include "qgslayoutpagecollection.h"
class QgsLayoutItem;
class QgsLayoutItemPage;
@ -41,6 +42,11 @@ class QgsLayoutPagePropertiesWidget : public QgsLayoutItemBaseWidget, private Ui
*/
QgsLayoutPagePropertiesWidget( QWidget *parent, QgsLayoutItem *page );
signals:
//! Is emitted when page orientation changes
void pageOrientationChanged();
private slots:
void pageSizeChanged( int index );

View File

@ -620,6 +620,119 @@ QgsLayoutExporter::ExportResult QgsLayoutExporter::exportToPdfs( QgsAbstractLayo
return Success;
}
QgsLayoutExporter::ExportResult QgsLayoutExporter::print( QPrinter &printer, const QgsLayoutExporter::PrintExportSettings &s )
{
if ( !mLayout )
return PrintError;
QgsLayoutExporter::PrintExportSettings settings = s;
if ( settings.dpi <= 0 )
settings.dpi = mLayout->renderContext().dpi();
mErrorFileName.clear();
LayoutContextPreviewSettingRestorer restorer( mLayout );
( void )restorer;
LayoutContextSettingsRestorer contextRestorer( mLayout );
( void )contextRestorer;
mLayout->renderContext().setDpi( settings.dpi );
// If we are not printing as raster, temporarily disable advanced effects
// as QPrinter does not support composition modes and can result
// in items missing from the output
mLayout->renderContext().setFlag( QgsLayoutRenderContext::FlagUseAdvancedEffects, !settings.rasterizeWholeImage );
preparePrint( mLayout, printer, true );
QPainter p;
if ( !p.begin( &printer ) )
{
//error beginning print
return PrintError;
}
ExportResult result = printPrivate( printer, p, false, settings.dpi, settings.rasterizeWholeImage );
p.end();
return result;
}
QgsLayoutExporter::ExportResult QgsLayoutExporter::print( QgsAbstractLayoutIterator *iterator, QPrinter &printer, const QgsLayoutExporter::PrintExportSettings &s, QString &error, QgsFeedback *feedback )
{
error.clear();
if ( !iterator->beginRender() )
return IteratorError;
PrintExportSettings settings = s;
QPainter p;
int total = iterator->count();
double step = total > 0 ? 100.0 / total : 100.0;
int i = 0;
bool first = true;
while ( iterator->next() )
{
if ( feedback )
{
if ( total > 0 )
feedback->setProperty( "progress", QObject::tr( "Printing %1 of %2" ).arg( i + 1 ).arg( total ) );
else
feedback->setProperty( "progress", QObject::tr( "Printing section %1" ).arg( i + 1 ).arg( total ) );
feedback->setProgress( step * i );
}
if ( feedback && feedback->isCanceled() )
{
iterator->endRender();
return Canceled;
}
if ( s.dpi <= 0 )
settings.dpi = iterator->layout()->renderContext().dpi();
LayoutContextPreviewSettingRestorer restorer( iterator->layout() );
( void )restorer;
LayoutContextSettingsRestorer contextRestorer( iterator->layout() );
( void )contextRestorer;
iterator->layout()->renderContext().setDpi( settings.dpi );
// If we are not printing as raster, temporarily disable advanced effects
// as QPrinter does not support composition modes and can result
// in items missing from the output
iterator->layout()->renderContext().setFlag( QgsLayoutRenderContext::FlagUseAdvancedEffects, !settings.rasterizeWholeImage );
if ( first )
{
preparePrint( iterator->layout(), printer, true );
if ( !p.begin( &printer ) )
{
//error beginning print
return PrintError;
}
}
QgsLayoutExporter exporter( iterator->layout() );
ExportResult result = exporter.printPrivate( printer, p, !first, settings.dpi, settings.rasterizeWholeImage );
if ( result != Success )
{
iterator->endRender();
return result;
}
first = false;
i++;
}
if ( feedback )
{
feedback->setProgress( 100 );
}
iterator->endRender();
return Success;
}
QgsLayoutExporter::ExportResult QgsLayoutExporter::exportToSvg( const QString &filePath, const QgsLayoutExporter::SvgExportSettings &s )
{
if ( !mLayout )

View File

@ -299,6 +299,52 @@ class CORE_EXPORT QgsLayoutExporter
const QgsLayoutExporter::PdfExportSettings &settings,
QString &error SIP_OUT, QgsFeedback *feedback = nullptr );
//! Contains settings relating to printing layouts
struct PrintExportSettings
{
//! Constructor for PrintExportSettings
PrintExportSettings()
: flags( QgsLayoutRenderContext::FlagAntialiasing | QgsLayoutRenderContext::FlagUseAdvancedEffects )
{}
//! Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.
double dpi = -1;
/**
* Set to true to force whole layout to be rasterized while exporting.
*
* This option is mutually exclusive with forceVectorOutput.
*/
bool rasterizeWholeImage = false;
/**
* Layout context flags, which control how the export will be created.
*/
QgsLayoutRenderContext::Flags flags = 0;
};
/**
* Prints the layout to a \a printer, using the specified export \a settings.
*
* Returns a result code indicating whether the export was successful or an
* error was encountered.
*/
ExportResult print( QPrinter &printer, const QgsLayoutExporter::PrintExportSettings &settings );
/**
* Exports a layout \a iterator to a \a printer, with the specified export \a settings.
*
* Returns a result code indicating whether the export was successful or an
* error was encountered. If an error was obtained then \a error will be set
* to the error description.
*/
static ExportResult print( QgsAbstractLayoutIterator *iterator, QPrinter &printer,
const QgsLayoutExporter::PrintExportSettings &settings,
QString &error SIP_OUT, QgsFeedback *feedback = nullptr );
//! Contains settings relating to exporting layouts to SVG
struct SvgExportSettings
{

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>1484</width>
<width>2180</width>
<height>609</height>
</rect>
</property>
@ -72,6 +72,7 @@
<addaction name="separator"/>
<addaction name="mActionAddPages"/>
<addaction name="separator"/>
<addaction name="mActionPrint"/>
<addaction name="mActionExportAsImage"/>
<addaction name="mActionExportAsSVG"/>
<addaction name="mActionExportAsPDF"/>
@ -97,7 +98,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>1484</width>
<width>2180</width>
<height>42</height>
</rect>
</property>
@ -123,6 +124,9 @@
<addaction name="mActionExportAsSVG"/>
<addaction name="mActionExportAsPDF"/>
<addaction name="separator"/>
<addaction name="mActionPageSetup"/>
<addaction name="mActionPrint"/>
<addaction name="separator"/>
<addaction name="mActionClose"/>
</widget>
<widget class="QMenu" name="mItemMenu">
@ -346,6 +350,7 @@
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="mActionPrintReport"/>
<addaction name="mActionExportReportAsImage"/>
<addaction name="mActionExportReportAsSVG"/>
<addaction name="mActionExportReportAsPDF"/>
@ -1435,6 +1440,47 @@
<string>Report Settings</string>
</property>
</action>
<action name="mActionPrint">
<property name="checkable">
<bool>false</bool>
</property>
<property name="icon">
<iconset resource="../../../images/images.qrc">
<normaloff>:/images/themes/default/mActionFilePrint.svg</normaloff>:/images/themes/default/mActionFilePrint.svg</iconset>
</property>
<property name="text">
<string>&amp;Print...</string>
</property>
<property name="toolTip">
<string>Print Layout</string>
</property>
<property name="shortcut">
<string>Ctrl+P</string>
</property>
</action>
<action name="mActionPrintReport">
<property name="icon">
<iconset resource="../../../images/images.qrc">
<normaloff>:/images/themes/default/mActionFilePrint.svg</normaloff>:/images/themes/default/mActionFilePrint.svg</iconset>
</property>
<property name="text">
<string>&amp;Print Report...</string>
</property>
<property name="toolTip">
<string>Print Report</string>
</property>
</action>
<action name="mActionPageSetup">
<property name="text">
<string>Pa&amp;ge Setup…</string>
</property>
<property name="toolTip">
<string>Page setup</string>
</property>
<property name="shortcut">
<string>Ctrl+Shift+P</string>
</property>
</action>
</widget>
<resources>
<include location="../../../images/images.qrc"/>

View File

@ -41,6 +41,7 @@ from qgis.core import (QgsMultiRenderChecker,
QgsReport)
from qgis.PyQt.QtCore import QSize, QSizeF, QDir, QRectF, Qt
from qgis.PyQt.QtGui import QImage, QPainter
from qgis.PyQt.QtPrintSupport import QPrinter
from qgis.PyQt.QtSvg import QSvgRenderer, QSvgGenerator
from qgis.testing import start_app, unittest
@ -484,6 +485,61 @@ class TestQgsLayoutExporter(unittest.TestCase):
self.assertTrue(self.checkImage('exporttosvglayered_page1', 'exporttopdfdpi_page1', rendered_page_1, size_tolerance=1))
self.assertTrue(self.checkImage('exporttosvglayered_page2', 'exporttopdfdpi_page2', rendered_page_2, size_tolerance=1))
def testPrint(self):
l = QgsLayout(QgsProject.instance())
l.initializeDefaults()
# add a second page
page2 = QgsLayoutItemPage(l)
page2.setPageSize('A5')
l.pageCollection().addPage(page2)
# add some items
item1 = QgsLayoutItemShape(l)
item1.attemptSetSceneRect(QRectF(10, 20, 100, 150))
fill = QgsSimpleFillSymbolLayer()
fill_symbol = QgsFillSymbol()
fill_symbol.changeSymbolLayer(0, fill)
fill.setColor(Qt.green)
fill.setStrokeStyle(Qt.NoPen)
item1.setSymbol(fill_symbol)
l.addItem(item1)
item2 = QgsLayoutItemShape(l)
item2.attemptSetSceneRect(QRectF(10, 20, 100, 150))
item2.attemptMove(QgsLayoutPoint(10, 20), page=1)
fill = QgsSimpleFillSymbolLayer()
fill_symbol = QgsFillSymbol()
fill_symbol.changeSymbolLayer(0, fill)
fill.setColor(Qt.cyan)
fill.setStrokeStyle(Qt.NoPen)
item2.setSymbol(fill_symbol)
l.addItem(item2)
exporter = QgsLayoutExporter(l)
# setup settings
settings = QgsLayoutExporter.PrintExportSettings()
settings.dpi = 80
settings.rasterizeWholeImage = False
pdf_file_path = os.path.join(self.basetestpath, 'test_printdpi.pdf')
# make a qprinter directed to pdf
printer = QPrinter()
printer.setOutputFileName(pdf_file_path)
printer.setOutputFormat(QPrinter.PdfFormat)
self.assertEqual(exporter.print(printer, settings), QgsLayoutExporter.Success)
self.assertTrue(os.path.exists(pdf_file_path))
rendered_page_1 = os.path.join(self.basetestpath, 'test_exporttopdfdpi.png')
dpi = 80
pdfToPng(pdf_file_path, rendered_page_1, dpi=dpi, page=1)
rendered_page_2 = os.path.join(self.basetestpath, 'test_exporttopdfdpi2.png')
pdfToPng(pdf_file_path, rendered_page_2, dpi=dpi, page=2)
self.assertTrue(self.checkImage('printdpi_page1', 'exporttopdfdpi_page1', rendered_page_1, size_tolerance=1))
self.assertTrue(self.checkImage('printdpi_page2', 'exporttopdfdpi_page2', rendered_page_2, size_tolerance=1))
def testExportWorldFile(self):
l = QgsLayout(QgsProject.instance())
l.initializeDefaults()
@ -727,6 +783,39 @@ class TestQgsLayoutExporter(unittest.TestCase):
pdfToPng(pdf_path, rendered_page_4, dpi=80, page=4)
self.assertTrue(os.path.exists(rendered_page_4))
def testPrintIterator(self):
project, layout = self.prepareIteratorLayout()
atlas = layout.atlas()
# setup settings
settings = QgsLayoutExporter.PrintExportSettings()
settings.dpi = 80
settings.rasterizeWholeImage = False
pdf_path = os.path.join(self.basetestpath, 'test_printiterator.pdf')
# make a qprinter directed to pdf
printer = QPrinter()
printer.setOutputFileName(pdf_path)
printer.setOutputFormat(QPrinter.PdfFormat)
result, error = QgsLayoutExporter.print(atlas, printer, settings)
self.assertEqual(result, QgsLayoutExporter.Success, error)
rendered_page_1 = os.path.join(self.basetestpath, 'test_printiterator1.png')
pdfToPng(pdf_path, rendered_page_1, dpi=80, page=1)
self.assertTrue(self.checkImage('printeriterator1', 'iteratortoimage1', rendered_page_1, size_tolerance=2))
rendered_page_2 = os.path.join(self.basetestpath, 'test_printiterator2.png')
pdfToPng(pdf_path, rendered_page_2, dpi=80, page=2)
self.assertTrue(self.checkImage('printiterator2', 'iteratortoimage2', rendered_page_2, size_tolerance=2))
rendered_page_3 = os.path.join(self.basetestpath, 'test_printiterator3.png')
pdfToPng(pdf_path, rendered_page_3, dpi=80, page=3)
self.assertTrue(os.path.exists(rendered_page_3))
rendered_page_4 = os.path.join(self.basetestpath, 'test_printiterator4.png')
pdfToPng(pdf_path, rendered_page_4, dpi=80, page=4)
self.assertTrue(os.path.exists(rendered_page_4))
def testExportReport(self):
p = QgsProject()
r = QgsReport(p)