You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

438 lines
11 KiB
C++

/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
* QwtPolar Widget Library
* Copyright (C) 2008 Uwe Rathmann
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the Qwt License, Version 1.0
*****************************************************************************/
#include "qwt_polar_renderer.h"
#include "qwt_polar_plot.h"
#include "qwt_polar_layout.h"
#include <qwt_legend.h>
#include <qwt_dyngrid_layout.h>
#include <qwt_text_label.h>
#include <qwt_text.h>
#include <qpainter.h>
#include <qprinter.h>
#include <qprintdialog.h>
#include <qfiledialog.h>
#include <qimagewriter.h>
#include <qfileinfo.h>
#include <qmath.h>
#ifndef QWT_NO_POLAR_POLAR_SVG
#ifdef QT_SVG_LIB
#include <qsvggenerator.h>
#endif
#endif
static inline double qwtDistance(
const QPointF &p1, const QPointF &p2 )
{
double dx = p2.x() - p1.x();
double dy = p2.y() - p1.y();
return qSqrt( dx * dx + dy * dy );
}
class QwtPolarRenderer::PrivateData
{
public:
PrivateData():
plot( NULL )
{
}
QwtPolarPlot *plot;
};
/*!
Constructor
\param parent Parent object
*/
QwtPolarRenderer::QwtPolarRenderer( QObject *parent ):
QObject( parent )
{
d_data = new PrivateData;
}
//! Destructor
QwtPolarRenderer::~QwtPolarRenderer()
{
delete d_data;
}
/*!
Render a polar plot to a file
The format of the document will be autodetected from the
suffix of the filename.
\param plot Plot widget
\param fileName Path of the file, where the document will be stored
\param sizeMM Size for the document in millimeters.
\param resolution Resolution in dots per Inch (dpi)
*/
void QwtPolarRenderer::renderDocument( QwtPolarPlot *plot,
const QString &fileName, const QSizeF &sizeMM, int resolution )
{
renderDocument( plot, fileName,
QFileInfo( fileName ).suffix(), sizeMM, resolution );
}
/*!
Render a plot to a file
Supported formats are:
- pdf\n
- ps\n
- svg\n
- all image formats supported by Qt, see QImageWriter::supportedImageFormats()
\param plot Plot widget
\param fileName Path of the file, where the document will be stored
\param format Format for the document
\param sizeMM Size for the document in millimeters.
\param resolution Resolution in dots per Inch (dpi)
\sa renderTo(), render(), QwtPainter::setRoundingAlignment()
*/
void QwtPolarRenderer::renderDocument( QwtPolarPlot *plot,
const QString &fileName, const QString &format,
const QSizeF &sizeMM, int resolution )
{
if ( plot == NULL || sizeMM.isEmpty() || resolution <= 0 )
return;
QString title = plot->title().text();
if ( title.isEmpty() )
title = "Plot Document";
const double mmToInch = 1.0 / 25.4;
const QSizeF size = sizeMM * mmToInch * resolution;
const QRectF documentRect( 0.0, 0.0, size.width(), size.height() );
const QString fmt = format.toLower();
if ( format == "pdf" )
{
#ifndef QT_NO_PRINTER
QPrinter printer;
printer.setColorMode( QPrinter::Color );
printer.setFullPage( true );
printer.setPaperSize( sizeMM, QPrinter::Millimeter );
printer.setDocName( title );
printer.setOutputFileName( fileName );
printer.setOutputFormat( QPrinter::PdfFormat );
printer.setResolution( resolution );
QPainter painter( &printer );
render( plot, &painter, documentRect );
#endif
}
else if ( format == "ps" )
{
#if QT_VERSION < 0x050000
#ifndef QT_NO_PRINTER
QPrinter printer;
printer.setColorMode( QPrinter::Color );
printer.setFullPage( true );
printer.setPaperSize( sizeMM, QPrinter::Millimeter );
printer.setDocName( title );
printer.setOutputFileName( fileName );
printer.setOutputFormat( QPrinter::PostScriptFormat );
printer.setResolution( resolution );
QPainter painter( &printer );
render( plot, &painter, documentRect );
#endif
#endif
}
#ifndef QWT_NO_POLAR_SVG
#ifdef QT_SVG_LIB
#if QT_VERSION >= 0x040500
else if ( format == "svg" )
{
QSvgGenerator generator;
generator.setTitle( title );
generator.setFileName( fileName );
generator.setResolution( resolution );
generator.setViewBox( documentRect );
QPainter painter( &generator );
render( plot, &painter, documentRect );
}
#endif
#endif
#endif
else
{
if ( QImageWriter::supportedImageFormats().indexOf(
format.toLatin1() ) >= 0 )
{
const QRect imageRect = documentRect.toRect();
const int dotsPerMeter = qRound( resolution * mmToInch * 1000.0 );
QImage image( imageRect.size(), QImage::Format_ARGB32 );
image.setDotsPerMeterX( dotsPerMeter );
image.setDotsPerMeterY( dotsPerMeter );
image.fill( QColor( Qt::white ).rgb() );
QPainter painter( &image );
render( plot, &painter, imageRect );
painter.end();
image.save( fileName, format.toLatin1() );
}
}
}
/*!
\brief Render the plot to a \c QPaintDevice
This function renders the contents of a QwtPolarPlot instance to
\c QPaintDevice object. The target rectangle is derived from
its device metrics.
\param plot Plot to be rendered
\param paintDevice device to paint on, f.e a QImage
\sa renderDocument(), render(), QwtPainter::setRoundingAlignment()
*/
void QwtPolarRenderer::renderTo(
QwtPolarPlot *plot, QPaintDevice &paintDevice ) const
{
int w = paintDevice.width();
int h = paintDevice.height();
QPainter p( &paintDevice );
render( plot, &p, QRectF( 0, 0, w, h ) );
}
/*!
\brief Render the plot to a QPrinter
This function renders the contents of a QwtPolarPlot instance to
\c QPaintDevice object. The size is derived from the printer
metrics.
\param plot Plot to be rendered
\param printer Printer to paint on
\sa renderDocument(), render(), QwtPainter::setRoundingAlignment()
*/
void QwtPolarRenderer::renderTo(
QwtPolarPlot *plot, QPrinter &printer ) const
{
int w = printer.width();
int h = printer.height();
QRectF rect( 0, 0, w, h );
double aspect = rect.width() / rect.height();
if ( ( aspect < 1.0 ) )
rect.setHeight( aspect * rect.width() );
QPainter p( &printer );
render( plot, &p, rect );
}
#ifndef QWT_NO_POLAR_SVG
#ifdef QT_SVG_LIB
#if QT_VERSION >= 0x040500
/*!
\brief Render the plot to a QSvgGenerator
If the generator has a view box, the plot will be rendered into it.
If it has no viewBox but a valid size the target coordinates
will be (0, 0, generator.width(), generator.height()). Otherwise
the target rectangle will be QRectF(0, 0, 800, 600);
\param plot Plot to be rendered
\param generator SVG generator
*/
void QwtPolarRenderer::renderTo(
QwtPolarPlot *plot, QSvgGenerator &generator ) const
{
QRectF rect = generator.viewBoxF();
if ( rect.isEmpty() )
rect.setRect( 0, 0, generator.width(), generator.height() );
if ( rect.isEmpty() )
rect.setRect( 0, 0, 800, 600 ); // something
QPainter p( &generator );
render( plot, &p, rect );
}
#endif
#endif
#endif
/*!
\brief Render the plot to a given rectangle ( f.e QPrinter, QSvgRenderer )
\param plot Plot widget to be rendered
\param painter Painter
\param plotRect Bounding rectangle for the plot
*/
void QwtPolarRenderer::render( QwtPolarPlot *plot,
QPainter *painter, const QRectF &plotRect ) const
{
if ( plot == NULL || painter == NULL || !painter->isActive() ||
!plotRect.isValid() || plot->size().isNull() )
{
return;
}
d_data->plot = plot;
/*
The layout engine uses the same methods as they are used
by the Qt layout system. Therefore we need to calculate the
layout in screen coordinates and paint with a scaled painter.
*/
QTransform transform;
transform.scale(
double( painter->device()->logicalDpiX() ) / plot->logicalDpiX(),
double( painter->device()->logicalDpiY() ) / plot->logicalDpiY() );
const QRectF layoutRect = transform.inverted().mapRect( plotRect );
QwtPolarLayout *layout = plot->plotLayout();
// All paint operations need to be scaled according to
// the paint device metrics.
QwtPolarLayout::Options layoutOptions =
QwtPolarLayout::IgnoreScrollbars | QwtPolarLayout::IgnoreFrames;
layout->activate( plot, layoutRect, layoutOptions );
painter->save();
painter->setWorldTransform( transform, true );
painter->save();
renderTitle( painter, layout->titleRect() );
painter->restore();
painter->save();
renderLegend( plot, painter, layout->legendRect() );
painter->restore();
const QRectF canvasRect = layout->canvasRect();
painter->save();
painter->setClipRect( canvasRect );
plot->drawCanvas( painter, canvasRect );
painter->restore();
painter->restore();
layout->invalidate();
d_data->plot = NULL;
}
/*!
Render the title into a given rectangle.
\param painter Painter
\param rect Bounding rectangle
*/
void QwtPolarRenderer::renderTitle( QPainter *painter, const QRectF &rect ) const
{
QwtTextLabel *title = d_data->plot->titleLabel();
painter->setFont( title->font() );
const QColor color = title->palette().color(
QPalette::Active, QPalette::Text );
painter->setPen( color );
title->text().draw( painter, rect );
}
/*!
Render the legend into a given rectangle.
\param plot Plot widget
\param painter Painter
\param rect Bounding rectangle
*/
void QwtPolarRenderer::renderLegend( const QwtPolarPlot *plot,
QPainter *painter, const QRectF &rect ) const
{
if ( plot->legend() )
plot->legend()->renderLegend( painter, rect, true );
}
/*!
\brief Execute a file dialog and render the plot to the selected file
The document will be rendered in 85 dpi for a size 30x30 cm
\param plot Plot widget
\param documentName Default document name
\param sizeMM Size for the document in millimeters.
\param resolution Resolution in dots per Inch (dpi)
\sa renderDocument()
*/
bool QwtPolarRenderer::exportTo( QwtPolarPlot *plot,
const QString &documentName, const QSizeF &sizeMM, int resolution )
{
if ( plot == NULL )
return false;
QString fileName = documentName;
// What about translation
#ifndef QT_NO_FILEDIALOG
const QList<QByteArray> imageFormats =
QImageWriter::supportedImageFormats();
QStringList filter;
#ifndef QT_NO_PRINTER
filter += QString( "PDF " ) + tr( "Documents" ) + " (*.pdf)";
#endif
#ifndef QWT_NO_SVG
filter += QString( "SVG " ) + tr( "Documents" ) + " (*.svg)";
#endif
#ifndef QT_NO_PRINTER
filter += QString( "Postscript " ) + tr( "Documents" ) + " (*.ps)";
#endif
if ( imageFormats.size() > 0 )
{
QString imageFilter( tr( "Images" ) );
imageFilter += " (";
for ( int i = 0; i < imageFormats.size(); i++ )
{
if ( i > 0 )
imageFilter += " ";
imageFilter += "*.";
imageFilter += imageFormats[i];
}
imageFilter += ")";
filter += imageFilter;
}
fileName = QFileDialog::getSaveFileName(
NULL, tr( "Export File Name" ), fileName,
filter.join( ";;" ), NULL, QFileDialog::DontConfirmOverwrite );
#endif
if ( fileName.isEmpty() )
return false;
renderDocument( plot, fileName, sizeMM, resolution );
return true;
}