You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
492 lines
19 KiB
492 lines
19 KiB
/* -*- Mode: C++ -*-
|
|
KDChart - a multi-platform charting engine
|
|
*/
|
|
|
|
/****************************************************************************
|
|
** Copyright (C) 2001-2003 Klarälvdalens Datakonsult AB. All rights reserved.
|
|
**
|
|
** This file is part of the KDChart library.
|
|
**
|
|
** This file may be distributed and/or modified under the terms of the
|
|
** GNU General Public License version 2 as published by the Free Software
|
|
** Foundation and appearing in the file LICENSE.GPL included in the
|
|
** packaging of this file.
|
|
**
|
|
** Licensees holding valid commercial KDChart licenses may use this file in
|
|
** accordance with the KDChart Commercial License Agreement provided with
|
|
** the Software.
|
|
**
|
|
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
|
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
**
|
|
** See http://www.klaralvdalens-datakonsult.se/?page=products for
|
|
** information about KDChart Commercial License Agreements.
|
|
**
|
|
** Contact info@klaralvdalens-datakonsult.se if any conditions of this
|
|
** licensing are not clear to you.
|
|
**
|
|
**********************************************************************/
|
|
#if defined KDAB_EVAL
|
|
#include "../evaldialog/evaldialog.h"
|
|
#endif
|
|
|
|
/**
|
|
\dontinclude KDChartPainter.h
|
|
*/
|
|
#include <KDChart.h>
|
|
#include <KDChartPainter.h>
|
|
#include <KDChartParams.h>
|
|
#include <KDChartGlobal.h>
|
|
#include <KDChartAxisParams.h>
|
|
|
|
#include <qglobal.h>
|
|
#include <qpainter.h>
|
|
#include <qpaintdevice.h>
|
|
#include <qpaintdevicemetrics.h>
|
|
|
|
#ifdef QSA
|
|
#if 0 // Disabled by ingwa to make it compile
|
|
#include <qsinterpreter.h>
|
|
#include "KDChartWrapperFactory.h"
|
|
#include "KDChartObjectFactory.h"
|
|
#endif
|
|
#endif
|
|
|
|
/**
|
|
\class KDChart KDChart.h
|
|
|
|
\brief Provides a single entry-point to the charting engine for
|
|
applications that wish to provide their own QPainter.
|
|
|
|
It is not useful to instantiate this class as it contains
|
|
static methods only.
|
|
|
|
\note If for some reason you are NOT using the
|
|
KDChartWidget class but calling the painting methods of KDChart directly,
|
|
you probably will also use the KDChartDataRegionList class:
|
|
This class is derived from QPtrList, so all of the Qt documentation
|
|
for this class is valid for KDChartDataRegionList too, e.g. freeing
|
|
of the pointers stored can either be done automatically or
|
|
manually - so PLEASE take the time to read the reference information for this class!
|
|
|
|
\sa KDChartWidget, KDChartDataRegionList
|
|
*/
|
|
|
|
KDChartParams* KDChart::oldParams = 0;
|
|
KDChartPainter* KDChart::cpainter = 0;
|
|
KDChartPainter* KDChart::cpainter2 = 0;
|
|
KDChartParams::ChartType KDChart::cpainterType = KDChartParams::NoType;
|
|
KDChartParams::ChartType KDChart::cpainterType2 = KDChartParams::NoType;
|
|
|
|
/**
|
|
A global function that cleans up possible KDChartPainter objects at
|
|
application shutdown.
|
|
*/
|
|
void cleanupPainter();
|
|
|
|
|
|
bool hasCartesianAxes( KDChartParams::ChartType chartType )
|
|
{
|
|
switch( chartType ){
|
|
case KDChartParams::NoType: return false;
|
|
case KDChartParams::Bar: return true;
|
|
case KDChartParams::Line: return true;
|
|
case KDChartParams::Area: return true;
|
|
case KDChartParams::Pie: return false;
|
|
case KDChartParams::HiLo: return true;
|
|
case KDChartParams::Ring: return false;
|
|
case KDChartParams::Polar: return false; // Polar axes are NO cartesian axes!
|
|
case KDChartParams::BoxWhisker: return true;
|
|
default:
|
|
qDebug("\n\n\n\nKDCHART ERROR: Type missing in KDChart.cpp hasCartesianAxes()\n"
|
|
"=============================================================\n"
|
|
"=============================================================\n\n\n\n");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
Calculates the drawing area from a given QPainter.
|
|
|
|
Use this function to get a QRect that you may pass to
|
|
KDChart::setupGeometry() if you need to know the positions and
|
|
sizes of the axis areas and/or the data area *before* drawing
|
|
the chart. After calling KDChart::setupGeometry() you may use
|
|
KDChartParams::axisArea() and/or KDChartParams::dataArea()
|
|
to retrieve the desired information.
|
|
|
|
\return True if the painter was valid and the drawing area
|
|
could be calculated successfully, else false.
|
|
*/
|
|
bool KDChart::painterToDrawRect( QPainter* painter, QRect& drawRect )
|
|
{
|
|
if( painter ){
|
|
QPaintDeviceMetrics painterMetrics( painter->device() );
|
|
drawRect = QRect( 0, 0, painterMetrics.width(), painterMetrics.height() );
|
|
drawRect.setWidth( drawRect.width() -2 );
|
|
drawRect.setHeight( drawRect.height()-2 );
|
|
return true;
|
|
}else{
|
|
drawRect = QRect( QPoint(0,0), QSize(0,0) );
|
|
qDebug("ERROR: KDChartPainter::painterToDrawRect() was called with *no* painter.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Calculates the axis and data area rects of a chart with the
|
|
specified parameters on the specified painter.
|
|
|
|
\note Call this function if you need to know the positions and
|
|
sizes of the axis areas and/or the data area *before* drawing
|
|
the chart. After calling this function you may use
|
|
KDChartParams::axisArea() and/or KDChartParams::dataArea()
|
|
to retrieve the desired information.
|
|
|
|
To get the right drawing area from a given QPainter please
|
|
use the static method KDChart::painterToDrawRect().
|
|
|
|
\param painter the painter that is eventually to be used for drawing
|
|
\param params the parameters defining the chart
|
|
\param data the data that should be displayed as a chart
|
|
\param drawRect the position and size of the drawing area to be used
|
|
*/
|
|
bool KDChart::setupGeometry( QPainter* painter,
|
|
KDChartParams* params,
|
|
KDChartTableDataBase* data,
|
|
const QRect& drawRect )
|
|
{
|
|
//qDebug("INVOKING: KDChart::setupGeometry()");
|
|
if( !params ){
|
|
qDebug("ERROR: setupGeometry::paint() was called with *no* params.");
|
|
return false;
|
|
}
|
|
if( !data ){
|
|
qDebug("ERROR: setupGeometry::paint() was called with *no* data.");
|
|
return false;
|
|
}
|
|
// don't crash due to memory problems when running on windows
|
|
#ifdef Q_WS_WIN
|
|
QPixmap::setDefaultOptimization(QPixmap::MemoryOptim);
|
|
#endif
|
|
|
|
// Install a cleanup routine that is called when the Qt
|
|
// application shuts down and cleans up any potentially still
|
|
// existing painters. Only do this once.
|
|
static bool bFirstCleanUpInstall = true;
|
|
if( bFirstCleanUpInstall ) {
|
|
bFirstCleanUpInstall = false;
|
|
qAddPostRoutine( cleanupPainter );
|
|
}
|
|
|
|
// Check whether last call of this methode gave us the same params pointer.
|
|
// If params changed we must create new painter(s).
|
|
bool paramsHasChanged = ( params != oldParams );
|
|
if( paramsHasChanged )
|
|
oldParams = params;
|
|
|
|
// Check whether there already is painter and, if that is the
|
|
// case, whether the painter still has the correct type (the chart
|
|
// type might have changed in the meantime).
|
|
if ( paramsHasChanged || !cpainter || cpainterType != params->chartType() )
|
|
{
|
|
delete cpainter; /* save, since always 0 if there was not yet
|
|
a chart painter */
|
|
// create a new painter
|
|
cpainter = KDChartPainter::create( params, false );
|
|
cpainterType = params->chartType();
|
|
}
|
|
|
|
// Check whether there already is a 2nd painter and, if that is the
|
|
// case, whether the painter still has the correct type (the
|
|
// additional chart type might have changed in the meantime).
|
|
if ( paramsHasChanged || !cpainter2 || cpainterType2 != params->additionalChartType() )
|
|
{
|
|
delete cpainter2; /* save, since always 0 if there was not yet
|
|
a chart painter */
|
|
// create a new painter
|
|
if( hasCartesianAxes( params->chartType() )
|
|
&& hasCartesianAxes( params->additionalChartType() ) ){
|
|
cpainter2 = KDChartPainter::create( params, true );
|
|
cpainterType2 = params->additionalChartType();
|
|
}else{
|
|
cpainter2 = 0;
|
|
cpainterType2 = KDChartParams::NoType;
|
|
}
|
|
}
|
|
|
|
if ( cpainter ){ // can be 0 if no exceptions are used
|
|
cpainter->setupGeometry( painter, data, drawRect );
|
|
}
|
|
|
|
if ( cpainter2 ){ // can be 0 if no exceptions are used
|
|
cpainter2->setupGeometry( painter, data, drawRect );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
Paints a chart with the specified parameters on the specified
|
|
painter.
|
|
|
|
\note If you are passing \c regions pointer, KD Chart will call
|
|
the \c clear() method on it, to delete any regions that might
|
|
still be registered from previous painting.
|
|
Make sure to copy any regions information into your own, private
|
|
data structure, in case you need to keep track of region information,
|
|
that was valid for such previous times.
|
|
|
|
\param painter the QPainter onto which the chart should be painted
|
|
\param params the parameters defining the chart
|
|
\param data the data that should be displayed as a chart
|
|
\param regions if not null, this points to a
|
|
KDChartDataRegionList that will be filled with the regions
|
|
of the data segments. This information is needed internally for both
|
|
recognizing the data segment when reporting mouse clicks and
|
|
for finding the correct position to draw the respective data value texts.
|
|
\param rect the position and size of the drawing area to be used,
|
|
if this parameter is zero the painter's device metrics will be used.
|
|
\param mustCalculateGeometry may be set to false if paint() is called
|
|
immediately after a previous call of setupGeometry() to save some
|
|
time in case you have specified a lot of data cells.
|
|
*/
|
|
void KDChart::paint( QPainter* painter,
|
|
KDChartParams* paraParams,
|
|
KDChartTableDataBase* paraData,
|
|
KDChartDataRegionList* regions,
|
|
const QRect* rect,
|
|
bool mustCalculateGeometry )
|
|
{
|
|
//qDebug("KDChart::paint() mustCalculateGeometry: "+QString(mustCalculateGeometry?"TRUE":"FALSE") );
|
|
#if defined KDAB_EVAL
|
|
EvalDialog::checkEvalLicense( "KD Chart" );
|
|
#endif
|
|
|
|
// delete old contents, to avoid the region from constantly growing
|
|
if( regions )
|
|
regions->clear();
|
|
|
|
KDChartParams* params = paraParams;
|
|
KDChartTableDataBase* data = paraData;
|
|
if( !paraParams && !paraData ){
|
|
qDebug("-----");
|
|
qDebug("Note: KDChart::paint() was called without \"params\" and without \"data\".");
|
|
qDebug("----- Showing a default bar chart.");
|
|
params = new KDChartParams();
|
|
params->setDatasetGap(3 * params->valueBlockGap());
|
|
params->setPrintDataValues( false );
|
|
params->setLegendPosition( KDChartParams::NoLegend );
|
|
params->setAxisLabelsVisible( KDChartAxisParams::AxisPosBottom, false );
|
|
params->setAxisShowGrid( KDChartAxisParams::AxisPosBottom, false );
|
|
params->setHeader1Text( "KDChartWidget" );
|
|
data = new KDChartTableData( 3, 1 );
|
|
// 1st series
|
|
data->setCell( 0, 0, 12.5 );
|
|
// 2nd series
|
|
data->setCell( 1, 0, 8.0 );
|
|
// 3rd series
|
|
data->setCell( 2, 0, 15.0 );
|
|
}
|
|
|
|
QRect drawRect;
|
|
bool bOk = true;
|
|
if( mustCalculateGeometry || !cpainter || cpainter->outermostRect().isNull() ){
|
|
if( rect )
|
|
drawRect = *rect;
|
|
else if( !painterToDrawRect( painter, drawRect ) ){
|
|
qDebug("ERROR: KDChart::paint() could not calculate a drawing area.");
|
|
bOk = false;
|
|
}
|
|
//qDebug("xxx" );
|
|
if( (params || data) && !setupGeometry( painter, params, data, drawRect ) ){
|
|
qDebug("ERROR: KDChart::paint() could not calculate the chart geometry.");
|
|
bOk = false;
|
|
}
|
|
}else{
|
|
drawRect = cpainter->outermostRect();
|
|
}
|
|
|
|
//qDebug("yyy" );
|
|
|
|
if( bOk ){
|
|
// Note: the following *must* paint the main-chart first
|
|
// and the additional chart afterwards
|
|
// since all axes computations are only done when
|
|
// the first chart is painted but will be needed for both of course.
|
|
//
|
|
bool paintFirst = true;
|
|
bool paintLast = ! ( cpainter && cpainter2 );
|
|
if ( cpainter ) { // can be 0 if no exceptions are used
|
|
//qDebug("zzz" );
|
|
cpainter->paint( painter, data, paintFirst, paintLast, regions, &drawRect, false );
|
|
|
|
paintFirst = false;
|
|
}
|
|
paintLast = true;
|
|
if ( cpainter2 ) // can be 0 if no exceptions are used
|
|
cpainter2->paint( painter, data, paintFirst, paintLast, regions, &drawRect, false );
|
|
}
|
|
|
|
if( !paraParams && !paraData ){
|
|
delete params;
|
|
delete data;
|
|
}
|
|
KDChartAutoColor::freeInstance(); // stuff that memory leak
|
|
}
|
|
|
|
|
|
/**
|
|
Paints a chart with the specified parameters on the specified
|
|
painter which should use a QPrinter as it's output device.
|
|
|
|
This method is provided for your convenience, it behaves
|
|
like the paint() method described above but additionally
|
|
it takes care for the output mode flag: Before painting is
|
|
started the internal optimizeOutputForScreen flag is set
|
|
to FALSE and after painting is done it is restored to
|
|
it's previous value.
|
|
|
|
\sa paint
|
|
*/
|
|
void KDChart::print( QPainter* painter, KDChartParams* params,
|
|
KDChartTableDataBase* data,
|
|
KDChartDataRegionList* regions,
|
|
const QRect* rect,
|
|
bool mustCalculateGeometry )
|
|
{
|
|
bool oldOpt=true;
|
|
if( params ){
|
|
oldOpt = params->optimizeOutputForScreen();
|
|
params->setOptimizeOutputForScreen( false );
|
|
}
|
|
paint( painter, params, data, regions, rect, mustCalculateGeometry );
|
|
if( params )
|
|
params->setOptimizeOutputForScreen( oldOpt );
|
|
}
|
|
|
|
|
|
/*
|
|
This method is called at application shut-down and cleans up the
|
|
last created painter.
|
|
*/
|
|
void cleanupPainter()
|
|
{
|
|
delete KDChart::cpainter;
|
|
delete KDChart::cpainter2;
|
|
KDChart::oldParams = 0;
|
|
}
|
|
|
|
#ifdef QSA
|
|
void KDChart::initInterpreter( QSInterpreter* interpreter )
|
|
{
|
|
privateInitInterpreter( interpreter );
|
|
interpreter->evaluate( globals() );
|
|
}
|
|
|
|
void KDChart::initProject( QSProject* project )
|
|
{
|
|
project->createScript( QString::fromLatin1( "KDCHART_Globals" ), globals() );
|
|
privateInitInterpreter( project->interpreter() );
|
|
}
|
|
|
|
QString KDChart::globals()
|
|
{
|
|
QString globals;
|
|
QMap<char*, double> intMap;
|
|
|
|
intMap.insert( "KDCHART_POS_INFINITE", KDCHART_POS_INFINITE );
|
|
intMap.insert( "KDCHART_NEG_INFINITE", KDCHART_NEG_INFINITE );
|
|
intMap.insert( "KDCHART_AlignAuto", KDCHART_AlignAuto );
|
|
intMap.insert( "KDCHART_ALL_AXES", KDCHART_ALL_AXES );
|
|
intMap.insert( "KDCHART_NO_AXIS", KDCHART_NO_AXIS );
|
|
intMap.insert( "KDCHART_ALL_DATASETS", KDCHART_ALL_DATASETS );
|
|
intMap.insert( "KDCHART_NO_DATASET", KDCHART_NO_DATASET );
|
|
intMap.insert( "KDCHART_UNKNOWN_CHART", KDCHART_UNKNOWN_CHART );
|
|
intMap.insert( "KDCHART_ALL_CHARTS", KDCHART_ALL_CHARTS );
|
|
intMap.insert( "KDCHART_NO_CHART", KDCHART_NO_CHART );
|
|
intMap.insert( "KDCHART_GLOBAL_LINE_STYLE", KDCHART_GLOBAL_LINE_STYLE );
|
|
intMap.insert( "KDCHART_AUTO_SIZE", KDCHART_AUTO_SIZE );
|
|
intMap.insert( "KDCHART_DATA_VALUE_AUTO_DIGITS", KDCHART_DATA_VALUE_AUTO_DIGITS );
|
|
intMap.insert( "KDCHART_SAGITTAL_ROTATION", KDCHART_SAGITTAL_ROTATION );
|
|
intMap.insert( "KDCHART_TANGENTIAL_ROTATION", KDCHART_TANGENTIAL_ROTATION );
|
|
intMap.insert( "KDCHART_PROPSET_NORMAL_DATA", KDCHART_PROPSET_NORMAL_DATA );
|
|
intMap.insert( "KDCHART_PROPSET_TRANSPARENT_DATA", KDCHART_PROPSET_TRANSPARENT_DATA );
|
|
intMap.insert( "KDCHART_PROPSET_HORI_LINE", KDCHART_PROPSET_HORI_LINE );
|
|
intMap.insert( "KDCHART_PROPSET_VERT_LINE", KDCHART_PROPSET_VERT_LINE );
|
|
intMap.insert( "KDCHART_SAGGITAL_ROTATION", KDCHART_SAGGITAL_ROTATION );
|
|
intMap.insert( "KDCHART_CNT_ORDINATES", KDCHART_CNT_ORDINATES );
|
|
intMap.insert( "KDCHART_MAX_POLAR_DELIMS_AND_LABELS_POS", KDCHART_MAX_POLAR_DELIMS_AND_LABELS_POS );
|
|
intMap.insert( "KDCHART_MAX_AXES", KDCHART_MAX_AXES );
|
|
intMap.insert( "KDCHART_AXIS_LABELS_AUTO_DELTA", KDCHART_AXIS_LABELS_AUTO_DELTA );
|
|
intMap.insert( "KDCHART_AXIS_LABELS_AUTO_LEAVEOUT", KDCHART_AXIS_LABELS_AUTO_LEAVEOUT );
|
|
intMap.insert( "KDCHART_AXIS_LABELS_AUTO_DIGITS", KDCHART_AXIS_LABELS_AUTO_DIGITS );
|
|
intMap.insert( "KDCHART_AXIS_GRID_AUTO_LINEWIDTH", KDCHART_AXIS_GRID_AUTO_LINEWIDTH );
|
|
intMap.insert( "KDCHART_AXIS_IGNORE_EMPTY_INNER_SPAN", KDCHART_AXIS_IGNORE_EMPTY_INNER_SPAN );
|
|
intMap.insert( "KDCHART_DONT_CHANGE_EMPTY_INNER_SPAN_NOW", KDCHART_DONT_CHANGE_EMPTY_INNER_SPAN_NOW );
|
|
intMap.insert( "DBL_MIN", DBL_MIN );
|
|
intMap.insert( "DBL_MAX", DBL_MAX );
|
|
|
|
for( QMapIterator<char*,double> it= intMap.begin(); it != intMap.end(); ++it ) {
|
|
// This is written this way to be efficient
|
|
globals += QString::fromLatin1( "const " );
|
|
globals += it.key();
|
|
globals += " = ";
|
|
globals += QString::number( it.data() );
|
|
globals += ";\n";
|
|
}
|
|
|
|
globals += QString::fromLatin1( "const KDCHART_AXIS_LABELS_AUTO_DATETIME_FORMAT=\"%1\";\n" )
|
|
.arg( QString::fromLatin1( KDCHART_AXIS_LABELS_AUTO_DATETIME_FORMAT ) );
|
|
globals += QString::fromLatin1( "const KDCHART_AXIS_LABELS_AUTO_LIMIT = 140319.64;\n" );
|
|
globals += QString::fromLatin1( "const KDCHART_DEFAULT_AXIS_GRID_COLOR = new Color(\"%1\");\n" )
|
|
.arg(KDCHART_DEFAULT_AXIS_GRID_COLOR.name());
|
|
globals += QString::fromLatin1( "const KDCHART_DATA_VALUE_AUTO_COLOR = new Color(\"%1\");\n" )
|
|
.arg( (KDCHART_DATA_VALUE_AUTO_COLOR)->name());
|
|
|
|
|
|
QMap<char*,QColor> colorMap;
|
|
colorMap.insert( "Qt.color0", Qt::color0 );
|
|
colorMap.insert( "Qt.color1", Qt::color1 );
|
|
colorMap.insert( "Qt.black", Qt::black );
|
|
colorMap.insert( "Qt.white", Qt::white );
|
|
colorMap.insert( "Qt.darkGray", Qt::darkGray );
|
|
colorMap.insert( "Qt.gray", Qt::gray );
|
|
colorMap.insert( "Qt.lightGray", Qt::lightGray );
|
|
colorMap.insert( "Qt.red", Qt::red );
|
|
colorMap.insert( "Qt.green", Qt::green );
|
|
colorMap.insert( "Qt.blue", Qt::blue );
|
|
colorMap.insert( "Qt.cyan", Qt::cyan );
|
|
colorMap.insert( "Qt.magenta", Qt::magenta );
|
|
colorMap.insert( "Qt.yellow", Qt::yellow );
|
|
colorMap.insert( "Qt.darkRed", Qt::darkRed );
|
|
colorMap.insert( "Qt.darkGreen", Qt::darkGreen );
|
|
colorMap.insert( "Qt.darkBlue", Qt::darkBlue );
|
|
colorMap.insert( "Qt.darkCyan", Qt::darkCyan );
|
|
colorMap.insert( "Qt.darkMagenta", Qt::darkMagenta );
|
|
colorMap.insert( "Qt.darkYellow", Qt::darkYellow );
|
|
for( QMapIterator<char*,QColor> it2= colorMap.begin(); it2 != colorMap.end(); ++it2 ) {
|
|
// This is written this way to be efficient
|
|
globals += QString::fromLatin1( it2.key() );
|
|
globals += QString::fromLatin1( " = new Color( " );
|
|
globals += QString::number( it2.data().red() );
|
|
globals += ',';
|
|
globals += QString::number( it2.data().green() );
|
|
globals += ',';
|
|
globals += QString::number( it2.data().blue() );
|
|
globals += QString::fromLatin1( " );\n" );
|
|
}
|
|
//qDebug( "%s",globals.latin1() );
|
|
return globals;
|
|
}
|
|
|
|
void KDChart::privateInitInterpreter( QSInterpreter* interpreter )
|
|
{
|
|
interpreter->addWrapperFactory( new KDChartWrapperFactory );
|
|
interpreter->addObjectFactory ( new KDChartObjectFactory );
|
|
}
|
|
|
|
#endif
|