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++
438 lines
11 KiB
C++
3 weeks ago
|
/* -*- 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;
|
||
|
}
|