• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KDEUI

kplotwidget.cpp

Go to the documentation of this file.
00001 /*  -*- C++ -*-
00002     This file is part of the KDE libraries
00003     Copyright (C) 2003 Jason Harris <kstars@30doradus.org>
00004 
00005     This library is free software; you can redistribute it and/or
00006     modify it under the terms of the GNU Library General Public
00007     License as published by the Free Software Foundation; either
00008     version 2 of the License, or (at your option) any later version.
00009 
00010     This library is distributed in the hope that it will be useful,
00011     but WITHOUT ANY WARRANTY; without even the implied warranty of
00012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013     Library General Public License for more details.
00014 
00015     You should have received a copy of the GNU Library General Public License
00016     along with this library; see the file COPYING.LIB.  If not, write to
00017     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018     Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include "kplotwidget.h"
00022 #include "kplotwidget.moc"
00023 
00024 #include <math.h>
00025 #include <kdebug.h>
00026 
00027 #include <QtGui/QActionEvent>
00028 #include <QHash>
00029 #include <QPainter>
00030 #include <QPixmap>
00031 #include <QToolTip>
00032 #include <QtAlgorithms>
00033 
00034 #include "kplotaxis.h"
00035 #include "kplotpoint.h"
00036 #include "kplotobject.h"
00037 
00038 #define XPADDING 20
00039 #define YPADDING 20
00040 #define BIGTICKSIZE 10
00041 #define SMALLTICKSIZE 4
00042 #define TICKOFFSET 0
00043 
00044 class KPlotWidget::Private
00045 {
00046 public:
00047     Private( KPlotWidget *qq )
00048       : q( qq ),
00049         cBackground( Qt::black ), cForeground( Qt::white ), cGrid( Qt::gray ),
00050         showGrid( false ), showObjectToolTip( true ), useAntialias( false )
00051     {
00052         // create the axes and setting their default properties
00053         KPlotAxis *leftAxis = new KPlotAxis();
00054         leftAxis->setTickLabelsShown( true );
00055         axes.insert( LeftAxis, leftAxis );
00056         KPlotAxis *bottomAxis = new KPlotAxis();
00057         bottomAxis->setTickLabelsShown( true );
00058         axes.insert( BottomAxis, bottomAxis );
00059         KPlotAxis *rightAxis = new KPlotAxis();
00060         axes.insert( RightAxis, rightAxis );
00061         KPlotAxis *topAxis = new KPlotAxis();
00062         axes.insert( TopAxis, topAxis );
00063     }
00064 
00065     ~Private()
00066     {
00067         qDeleteAll( objectList );
00068         qDeleteAll( axes );
00069     }
00070 
00071     KPlotWidget *q;
00072 
00073     void calcDataRectLimits( double x1, double x2, double y1, double y2 );
00082     float rectCost( const QRectF &r ) const;
00083 
00084     //Colors
00085     QColor cBackground, cForeground, cGrid;
00086     //draw options
00087     bool showGrid : 1;
00088     bool showObjectToolTip : 1;
00089     bool useAntialias : 1;
00090     //padding
00091     int leftPadding, rightPadding, topPadding, bottomPadding;
00092     // hashmap with the axes we have
00093     QHash<Axis, KPlotAxis*> axes;
00094     // List of KPlotObjects
00095     QList<KPlotObject*> objectList;
00096     // Limits of the plot area in data units
00097     QRectF dataRect, secondDataRect;
00098     // Limits of the plot area in pixel units
00099     QRect pixRect;
00100     //Array holding the mask of "used" regions of the plot
00101     QImage plotMask;
00102 };
00103 
00104 KPlotWidget::KPlotWidget( QWidget * parent )
00105     : QFrame( parent ), d( new Private( this ) )
00106 {
00107     setAttribute( Qt::WA_OpaquePaintEvent );
00108     setAttribute( Qt::WA_NoSystemBackground );
00109 
00110     d->secondDataRect = QRectF(); //default: no secondary data rect
00111     // sets the default limits
00112     d->calcDataRectLimits( 0.0, 1.0, 0.0, 1.0 );
00113 
00114     setDefaultPaddings();
00115 
00116     setMinimumSize( 150, 150 );
00117     resize( minimumSizeHint() );
00118 }
00119 
00120 KPlotWidget::~KPlotWidget()
00121 {
00122     delete d;
00123 }
00124 
00125 QSize KPlotWidget::minimumSizeHint() const
00126 {
00127     return QSize( 150, 150 );
00128 }
00129 
00130 QSize KPlotWidget::sizeHint() const
00131 {
00132     return size();
00133 }
00134 
00135 void KPlotWidget::setLimits( double x1, double x2, double y1, double y2 )
00136 {
00137     d->calcDataRectLimits( x1, x2, y1, y2 );
00138     update();
00139 }
00140 
00141 void KPlotWidget::Private::calcDataRectLimits( double x1, double x2, double y1, double y2 )
00142 {
00143     double XA1, XA2, YA1, YA2;
00144     if (x2<x1) { XA1=x2; XA2=x1; }
00145     else { XA1=x1; XA2=x2; }
00146     if ( y2<y1) { YA1=y2; YA2=y1; }
00147     else { YA1=y1; YA2=y2; }
00148 
00149     if ( XA2 == XA1 ) {
00150         kWarning() << "x1 and x2 cannot be equal. Setting x2 = x1 + 1.0";
00151         XA2 = XA1 + 1.0;
00152     }
00153     if ( YA2 == YA1 ) {
00154         kWarning() << "y1 and y2 cannot be equal. Setting y2 = y1 + 1.0";
00155         YA2 = YA1 + 1.0;
00156     }
00157     dataRect = QRectF( XA1, YA1, XA2 - XA1, YA2 - YA1 );
00158 
00159     q->axis( LeftAxis )->setTickMarks( dataRect.y(), dataRect.height() );
00160     q->axis( BottomAxis )->setTickMarks( dataRect.x(), dataRect.width() );
00161 
00162     if ( secondDataRect.isNull() )
00163     {
00164         q->axis( RightAxis )->setTickMarks( dataRect.y(), dataRect.height() );
00165         q->axis( TopAxis )->setTickMarks( dataRect.x(), dataRect.width() );
00166     }
00167 }
00168 
00169 void KPlotWidget::setSecondaryLimits( double x1, double x2, double y1, double y2 ) {
00170     double XA1, XA2, YA1, YA2;
00171     if (x2<x1) { XA1=x2; XA2=x1; }
00172     else { XA1=x1; XA2=x2; }
00173     if ( y2<y1) { YA1=y2; YA2=y1; }
00174     else { YA1=y1; YA2=y2; }
00175 
00176     if ( XA2 == XA1 ) {
00177         kWarning() << "x1 and x2 cannot be equal. Setting x2 = x1 + 1.0";
00178         XA2 = XA1 + 1.0;
00179     }
00180     if ( YA2 == YA1 ) {
00181         kWarning() << "y1 and y2 cannot be equal. Setting y2 = y1 + 1.0";
00182         YA2 = YA1 + 1.0;
00183     }
00184     d->secondDataRect = QRectF( XA1, YA1, XA2-XA1, YA2-YA1 );
00185 
00186     axis(RightAxis)->setTickMarks( d->secondDataRect.y(), d->secondDataRect.height() );
00187     axis(TopAxis)->setTickMarks( d->secondDataRect.x(), d->secondDataRect.width() );
00188 
00189     update();
00190 }
00191 
00192 void KPlotWidget::clearSecondaryLimits() {
00193     d->secondDataRect = QRectF();
00194     axis(RightAxis)->setTickMarks( d->dataRect.y(), d->dataRect.height() );
00195     axis(TopAxis)->setTickMarks( d->dataRect.x(), d->dataRect.width() );
00196 
00197     update();
00198 }
00199 
00200 QRectF KPlotWidget::dataRect() const
00201 {
00202     return d->dataRect;
00203 }
00204 
00205 QRectF KPlotWidget::secondaryDataRect() const
00206 {
00207     return d->secondDataRect;
00208 }
00209 
00210 void KPlotWidget::addPlotObject( KPlotObject *object )
00211 {
00212     // skip null pointers
00213     if ( !object )
00214         return;
00215     d->objectList.append( object );
00216     update();
00217 }
00218 
00219 void KPlotWidget::addPlotObjects( const QList< KPlotObject* >& objects )
00220 {
00221     bool addedsome = false;
00222     foreach ( KPlotObject *o, objects )
00223     {
00224         if ( !o )
00225             continue;
00226 
00227         d->objectList.append( o );
00228         addedsome = true;
00229     }
00230     if ( addedsome )
00231         update();
00232 }
00233 
00234 QList< KPlotObject* > KPlotWidget::plotObjects() const
00235 {
00236     return d->objectList;
00237 }
00238 
00239 void KPlotWidget::removeAllPlotObjects()
00240 {
00241     if ( d->objectList.isEmpty() )
00242         return;
00243 
00244     qDeleteAll( d->objectList );
00245     d->objectList.clear();
00246     update();
00247 }
00248 
00249 void KPlotWidget::resetPlotMask() {
00250     d->plotMask = QImage( pixRect().size(), QImage::Format_ARGB32 );
00251     QColor fillColor = Qt::black;
00252     fillColor.setAlpha( 128 );
00253     d->plotMask.fill( fillColor.rgb() );
00254 }
00255 
00256 void KPlotWidget::resetPlot() {
00257     qDeleteAll( d->objectList );
00258     d->objectList.clear();
00259     clearSecondaryLimits();
00260     d->calcDataRectLimits( 0.0, 1.0, 0.0, 1.0 );
00261     KPlotAxis *a = axis( RightAxis );
00262     a->setLabel( QString() );
00263     a->setTickLabelsShown( false );
00264     a = axis( TopAxis );
00265     a->setLabel( QString() );
00266     a->setTickLabelsShown( false );
00267     axis(KPlotWidget::LeftAxis)->setLabel( QString() );
00268     axis(KPlotWidget::BottomAxis)->setLabel( QString() );
00269     resetPlotMask();
00270 }
00271 
00272 void KPlotWidget::replacePlotObject( int i, KPlotObject *o )
00273 {
00274     // skip null pointers and invalid indexes
00275     if ( !o || i < 0 || i >= d->objectList.count() )
00276         return;
00277     d->objectList.replace( i, o );
00278     update();
00279 }
00280 
00281 QColor KPlotWidget::backgroundColor() const
00282 {
00283     return d->cBackground;
00284 }
00285 
00286 QColor KPlotWidget::foregroundColor() const
00287 {
00288     return d->cForeground;
00289 }
00290 
00291 QColor KPlotWidget::gridColor() const
00292 {
00293     return d->cGrid;
00294 }
00295 
00296 void KPlotWidget::setBackgroundColor( const QColor &bg ) {
00297     d->cBackground = bg;
00298     update();
00299 }
00300 
00301 void KPlotWidget::setForegroundColor( const QColor &fg )
00302 {
00303     d->cForeground = fg;
00304     update();
00305 }
00306 
00307 void KPlotWidget::setGridColor( const QColor &gc )
00308 {
00309     d->cGrid = gc;
00310     update();
00311 }
00312 
00313 bool KPlotWidget::isGridShown() const
00314 {
00315     return d->showGrid;
00316 }
00317 
00318 bool KPlotWidget::isObjectToolTipShown() const
00319 {
00320     return d->showObjectToolTip;
00321 }
00322 
00323 bool KPlotWidget::antialiasing() const
00324 {
00325     return d->useAntialias;
00326 }
00327 
00328 void KPlotWidget::setAntialiasing( bool b )
00329 {
00330     d->useAntialias = b;
00331     update();
00332 }
00333 
00334 void KPlotWidget::setShowGrid( bool show ) {
00335     d->showGrid = show;
00336     update();
00337 }
00338 
00339 void KPlotWidget::setObjectToolTipShown( bool show )
00340 {
00341     d->showObjectToolTip = show;
00342 }
00343 
00344 
00345 KPlotAxis* KPlotWidget::axis( Axis type )
00346 {
00347     QHash<Axis, KPlotAxis*>::Iterator it = d->axes.find( type );
00348     return it != d->axes.end() ? it.value() : 0;
00349 }
00350 
00351 const KPlotAxis* KPlotWidget::axis( Axis type ) const
00352 {
00353     QHash<Axis, KPlotAxis*>::ConstIterator it = d->axes.constFind( type );
00354     return it != d->axes.constEnd() ? it.value() : 0;
00355 }
00356 
00357 QRect KPlotWidget::pixRect() const
00358 {
00359     return d->pixRect;
00360 }
00361 
00362 QList<KPlotPoint*> KPlotWidget::pointsUnderPoint( const QPoint& p ) const {
00363     QList<KPlotPoint*> pts;
00364     foreach ( KPlotObject *po, d->objectList ) {
00365         foreach ( KPlotPoint *pp, po->points() ) {
00366             if ( ( p - mapToWidget( pp->position() ).toPoint() ).manhattanLength() <= 4 )
00367                 pts << pp;
00368         }
00369     }
00370 
00371     return pts;
00372 }
00373 
00374 
00375 bool KPlotWidget::event( QEvent* e ) {
00376     if ( e->type() == QEvent::ToolTip ) {
00377         if ( d->showObjectToolTip )
00378         {
00379             QHelpEvent *he = static_cast<QHelpEvent*>( e );
00380             QList<KPlotPoint*> pts = pointsUnderPoint( he->pos() - QPoint( leftPadding(), topPadding() ) - contentsRect().topLeft() );
00381             if ( pts.count() > 0 ) {
00382                 QToolTip::showText( he->globalPos(), pts.front()->label(), this );
00383             }
00384         }
00385         e->accept();
00386         return true;
00387     }
00388     else
00389         return QFrame::event( e );
00390 }
00391 
00392 void KPlotWidget::resizeEvent( QResizeEvent* e ) {
00393     QFrame::resizeEvent( e );
00394     setPixRect();
00395     resetPlotMask();
00396 }
00397 
00398 void KPlotWidget::setPixRect() {
00399     int newWidth = contentsRect().width() - leftPadding() - rightPadding();
00400     int newHeight = contentsRect().height() - topPadding() - bottomPadding();
00401     // PixRect starts at (0,0) because we will translate by leftPadding(), topPadding()
00402     d->pixRect = QRect( 0, 0, newWidth, newHeight );
00403 }
00404 
00405 QPointF KPlotWidget::mapToWidget( const QPointF& p ) const
00406 {
00407     float px = d->pixRect.left() + d->pixRect.width() * ( p.x() - d->dataRect.x() ) / d->dataRect.width();
00408     float py = d->pixRect.top() + d->pixRect.height() * ( d->dataRect.y() + d->dataRect.height() - p.y() ) / d->dataRect.height();
00409     return QPointF( px, py );
00410 }
00411 
00412 void KPlotWidget::maskRect( const QRectF& rf, float fvalue ) {
00413     QRect r = rf.toRect().intersected( d->pixRect );
00414     int value = int( fvalue );
00415     QColor newColor;
00416     for ( int ix=r.left(); ix<r.right(); ++ix ) {
00417         for ( int iy=r.top(); iy<r.bottom(); ++iy ) {
00418             newColor = QColor( d->plotMask.pixel(ix,iy) );
00419             newColor.setAlpha( 200 );
00420             newColor.setRed( qMin( newColor.red() + value, 255 ) );
00421             d->plotMask.setPixel( ix, iy, newColor.rgba() );
00422         }
00423     }
00424 
00425 }
00426 
00427 void KPlotWidget::maskAlongLine( const QPointF &p1, const QPointF &p2, float fvalue ) {
00428     if ( ! d->pixRect.contains( p1.toPoint() ) && ! d->pixRect.contains( p2.toPoint() ) ) {
00429         return;
00430     }
00431     
00432     int value = int( fvalue );
00433     
00434     //Determine slope and zeropoint of line
00435     double m = (p2.y() - p1.y())/(p2.x() - p1.x());
00436     double y0 = p1.y() - m*p1.x();
00437     QColor newColor;
00438 
00439     //Mask each pixel along the line joining p1 and p2
00440     if ( m > 1.0 || m < -1.0 ) { //step in y-direction
00441         int y1 = int( p1.y() );
00442         int y2 = int( p2.y() );
00443         if ( y1 > y2 ) {
00444             y1 = int( p2.y() );
00445             y2 = int( p1.y() );
00446         }
00447 
00448         for ( int y=y1; y<=y2; ++y ) {
00449             int x = int( (y - y0)/m );
00450             if ( d->pixRect.contains( x, y ) ) {
00451                 newColor = QColor( d->plotMask.pixel(x,y) );
00452                 newColor.setAlpha( 100 );
00453                 newColor.setRed( qMin( newColor.red() + value, 255 ) );
00454                 d->plotMask.setPixel( x, y, newColor.rgba() );
00455             }
00456         }
00457 
00458     } else { //step in x-direction
00459         int x1 = int( p1.x() );
00460         int x2 = int( p2.x() );
00461         if ( x1 > x2 ) {
00462             x1 = int( p2.x() ); 
00463             x2 = int( p1.x() );
00464         }
00465 
00466         for ( int x=x1; x<=x2; ++x ) {
00467             int y = int( y0 + m*x );
00468             if ( d->pixRect.contains( x, y ) ) {
00469                 newColor = QColor( d->plotMask.pixel(x,y) );
00470                 newColor.setAlpha( 100 );
00471                 newColor.setRed( qMin( newColor.red() + value, 255 ) );
00472                 d->plotMask.setPixel( x, y, newColor.rgba() );
00473             }
00474         }
00475     }
00476 }
00477 
00478 //Determine optimal placement for a text label for point pp.  We want
00479 //the label to be near point pp, but we don't want it to overlap with
00480 //other labels or plot elements.  We will use a "downhill simplex"
00481 //algorithm to find a label position that minimizes the pixel values
00482 //in the plotMask image over the label's rect().  The sum of pixel
00483 //values in the label's rect is the "cost" of placing the label there.
00484 //
00485 //Because a downhill simplex follows the local gradient to find low
00486 //values, it can get stuck in local minima.  To mitigate this, we will
00487 //iteratively attempt each of the initial path offset directions (up,
00488 //down, right, left) in the order of increasing cost at each location.
00489 void KPlotWidget::placeLabel( QPainter *painter, KPlotPoint *pp ) {
00490     int textFlags = Qt::TextSingleLine | Qt::AlignCenter;
00491 
00492     QPointF pos = mapToWidget( pp->position() );
00493     if ( ! d->pixRect.contains( pos.toPoint() ) ) return;
00494 
00495     QFontMetricsF fm( painter->font(), painter->device() );
00496     QRectF bestRect = fm.boundingRect( QRectF( pos.x(), pos.y(), 1, 1 ), textFlags, pp->label() );
00497     float xStep = 0.5*bestRect.width();
00498     float yStep = 0.5*bestRect.height();
00499     float maxCost = 0.05 * bestRect.width() * bestRect.height();
00500     float bestCost = d->rectCost( bestRect );
00501 
00502     //We will travel along a path defined by the maximum decrease in
00503     //the cost at each step.  If this path takes us to a local minimum
00504     //whose cost exceeds maxCost, then we will restart at the
00505     //beginning and select the next-best path.  The indices of
00506     //already-tried paths are stored in the TriedPathIndex list.
00507     //
00508     //If we try all four first-step paths and still don't get below 
00509     //maxCost, then we'll adopt the local minimum position with the 
00510     //best cost (designated as bestBadCost).
00511     int iter = 0;
00512     QList<int> TriedPathIndex;
00513     float bestBadCost = 10000;
00514     QRectF bestBadRect;
00515 
00516     //needed to halt iteration from inside the switch
00517     bool flagStop = false;
00518 
00519     while ( bestCost > maxCost ) {
00520         //Displace the label up, down, left, right; determine which 
00521         //step provides the lowest cost
00522         QRectF upRect = bestRect;
00523         upRect.moveTop( upRect.top() + yStep );
00524         float upCost = d->rectCost( upRect );
00525         QRectF downRect = bestRect;
00526         downRect.moveTop( downRect.top() - yStep );
00527         float downCost = d->rectCost( downRect );
00528         QRectF leftRect = bestRect;
00529         leftRect.moveLeft( leftRect.left() - xStep );
00530         float leftCost = d->rectCost( leftRect );
00531         QRectF rightRect = bestRect;
00532         rightRect.moveLeft( rightRect.left() + xStep );
00533         float rightCost = d->rectCost( rightRect );
00534         
00535         //which direction leads to the lowest cost?
00536         QList<float> costList;
00537         costList << upCost << downCost << leftCost << rightCost;
00538         int imin = -1;
00539         for ( int i=0; i<costList.size(); ++i ) {
00540             if ( iter == 0 && TriedPathIndex.contains( i ) ) {
00541                 continue; //Skip this first-step path, we already tried it!
00542             }
00543             
00544             //If this first-step path doesn't improve the cost, 
00545             //skip this direction from now on
00546             if ( iter == 0 && costList[i] >= bestCost ) {
00547                 TriedPathIndex.append( i );
00548                 continue;
00549             }
00550             
00551             if ( costList[i] < bestCost && (imin < 0 || costList[i] < costList[imin]) ) {
00552                 
00553                 imin = i;
00554             }
00555         }
00556 
00557         //Make a note that we've tried the current first-step path
00558         if ( iter == 0 && imin >= 0 ) {
00559             TriedPathIndex.append( imin );
00560         }
00561 
00562         //Adopt the step that produced the best cost
00563         switch ( imin ) {
00564         case 0: //up
00565             bestRect.moveTop( upRect.top() );
00566             bestCost = upCost;
00567             break;
00568         case 1: //down
00569             bestRect.moveTop( downRect.top() );
00570             bestCost = downCost;
00571             break;
00572         case 2: //left
00573             bestRect.moveLeft( leftRect.left() );
00574             bestCost = leftCost;
00575             break;
00576         case 3: //right
00577             bestRect.moveLeft( rightRect.left() );
00578             bestCost = rightCost;
00579             break;
00580         case -1: //no lower cost found!  
00581             //We hit a local minimum.  Keep the best of these as bestBadRect
00582             if ( bestCost < bestBadCost ) {
00583                 bestBadCost = bestCost;
00584                 bestBadRect = bestRect;
00585             }
00586 
00587             //If all of the first-step paths have now been searched, we'll
00588             //have to adopt the bestBadRect
00589             if ( TriedPathIndex.size() == 4 ) {
00590                 bestRect = bestBadRect;
00591                 flagStop = true; //halt iteration
00592                 break;
00593             }
00594 
00595             //If we haven't yet tried all of the first-step paths, start over
00596             if ( TriedPathIndex.size() < 4 ) {
00597                 iter = -1; //anticipating the ++iter below      
00598                 bestRect = fm.boundingRect( QRectF( pos.x(), pos.y(), 1, 1 ), textFlags, pp->label() );
00599                 bestCost = d->rectCost( bestRect );
00600             }
00601             break;
00602         }
00603 
00604         //Halt iteration, because we've tried all directions and 
00605         //haven't gotten below maxCost (we'll adopt the best 
00606         //local minimum found)
00607         if ( flagStop ) {
00608             break;
00609         }
00610         
00611         ++iter;
00612     }
00613 
00614     painter->drawText( bestRect, textFlags, pp->label() );
00615 
00616     //Is a line needed to connect the label to the point?
00617     float deltax = pos.x() - bestRect.center().x();
00618     float deltay = pos.y() - bestRect.center().y();
00619     float rbest = sqrt( deltax*deltax + deltay*deltay );
00620     if ( rbest > 20.0 ) {
00621         //Draw a rectangle around the label 
00622         painter->setBrush( QBrush() );
00623         //QPen pen = painter->pen();
00624         //pen.setStyle( Qt::DotLine );
00625         //painter->setPen( pen );
00626         painter->drawRoundRect( bestRect );
00627 
00628         //Now connect the label to the point with a line.
00629         //The line is drawn from the center of the near edge of the rectangle
00630         float xline = bestRect.center().x();
00631         if ( bestRect.left() > pos.x() )
00632             xline = bestRect.left();
00633         if ( bestRect.right() < pos.x() )
00634             xline = bestRect.right();
00635 
00636         float yline = bestRect.center().y();
00637         if ( bestRect.top() > pos.y() )
00638             yline = bestRect.top();
00639         if ( bestRect.bottom() < pos.y() )
00640             yline = bestRect.bottom();
00641 
00642         painter->drawLine( QPointF( xline, yline ), pos );
00643     }
00644                                             
00645     //Mask the label's rectangle so other labels won't overlap it.
00646     maskRect( bestRect );
00647 }
00648 
00649 float KPlotWidget::Private::rectCost( const QRectF &r ) const
00650 {
00651     if ( ! plotMask.rect().contains( r.toRect() ) ) {
00652         return 10000.;
00653     }
00654     
00655     //Compute sum of mask values in the rect r
00656     QImage subMask = plotMask.copy( r.toRect() );
00657     int cost = 0;
00658     for ( int ix=0; ix<subMask.width(); ++ix ) {
00659         for ( int iy=0; iy<subMask.height(); ++iy ) {
00660             cost += QColor( subMask.pixel( ix, iy ) ).red();
00661         }
00662     }
00663     
00664     return float(cost);
00665 }
00666 
00667 void KPlotWidget::paintEvent( QPaintEvent *e ) {
00668     // let QFrame draw its default stuff (like the frame)
00669     QFrame::paintEvent( e );
00670     QPainter p;
00671 
00672     p.begin( this );
00673     p.setRenderHint( QPainter::Antialiasing, d->useAntialias );
00674     p.fillRect( rect(), backgroundColor() );
00675     p.translate( leftPadding() + 0.5, topPadding() + 0.5 );
00676 
00677     setPixRect();
00678     p.setClipRect( d->pixRect );
00679     p.setClipping( true );
00680 
00681     resetPlotMask();
00682 
00683     foreach( KPlotObject *po, d->objectList )
00684         po->draw( &p, this );
00685 
00686 //DEBUG: Draw the plot mask
00687 //    p.drawImage( 0, 0, d->plotMask );
00688 
00689     p.setClipping( false );
00690     drawAxes( &p );
00691 
00692     p.end();
00693 }
00694 
00695 void KPlotWidget::drawAxes( QPainter *p ) {
00696     if ( d->showGrid ) {
00697         p->setPen( gridColor() );
00698 
00699         //Grid lines are placed at locations of primary axes' major tickmarks
00700         //vertical grid lines
00701         foreach ( double xx, axis(BottomAxis)->majorTickMarks() ) {
00702             double px = d->pixRect.width() * (xx - d->dataRect.x()) / d->dataRect.width();
00703             p->drawLine( QPointF( px, 0.0 ), QPointF( px, double(d->pixRect.height()) ) );
00704         }
00705         //horizontal grid lines
00706         foreach( double yy, axis(LeftAxis)->majorTickMarks() ) {
00707             double py = d->pixRect.height() * (yy - d->dataRect.y()) / d->dataRect.height();
00708             p->drawLine( QPointF( 0.0, py ), QPointF( double(d->pixRect.width()), py ) );
00709         }
00710     }
00711 
00712     p->setPen( foregroundColor() );
00713     p->setBrush( Qt::NoBrush );
00714 
00715     //set small font for tick labels
00716     QFont f = p->font();
00717     int s = f.pointSize();
00718     f.setPointSize( s - 2 );
00719     p->setFont( f );
00720 
00721     /*** BottomAxis ***/
00722     KPlotAxis *a = axis(BottomAxis);
00723     if (a->isVisible()) {
00724         //Draw axis line
00725         p->drawLine( 0, d->pixRect.height(), d->pixRect.width(), d->pixRect.height() );
00726 
00727         // Draw major tickmarks
00728         foreach( double xx, a->majorTickMarks() ) {
00729             double px = d->pixRect.width() * (xx - d->dataRect.x()) / d->dataRect.width();
00730             if ( px > 0 && px < d->pixRect.width() ) {
00731                 p->drawLine( QPointF( px, double(d->pixRect.height() - TICKOFFSET)), 
00732                         QPointF( px, double(d->pixRect.height() - BIGTICKSIZE - TICKOFFSET)) );
00733 
00734                 //Draw ticklabel
00735                 if ( a->areTickLabelsShown() ) {
00736                     QRect r( int(px) - BIGTICKSIZE, d->pixRect.height()+BIGTICKSIZE, 2*BIGTICKSIZE, BIGTICKSIZE );
00737                     p->drawText( r, Qt::AlignCenter | Qt::TextDontClip, a->tickLabel( xx ) );
00738                 }
00739             }
00740         }
00741 
00742         // Draw minor tickmarks
00743         foreach ( double xx, a->minorTickMarks() ) {
00744             double px = d->pixRect.width() * (xx - d->dataRect.x()) / d->dataRect.width();
00745             if ( px > 0 && px < d->pixRect.width() ) {
00746                 p->drawLine( QPointF( px, double(d->pixRect.height() - TICKOFFSET)), 
00747                         QPointF( px, double(d->pixRect.height() - SMALLTICKSIZE -TICKOFFSET)) );
00748             }
00749         }
00750 
00751         // Draw BottomAxis Label
00752         if ( ! a->label().isEmpty() ) {
00753             QRect r( 0, d->pixRect.height() + 2*YPADDING, d->pixRect.width(), YPADDING );
00754             p->drawText( r, Qt::AlignCenter, a->label() );
00755         }
00756     }  //End of BottomAxis
00757 
00758     /*** LeftAxis ***/
00759     a = axis(LeftAxis);
00760     if (a->isVisible()) {
00761         //Draw axis line
00762         p->drawLine( 0, 0, 0, d->pixRect.height() );
00763 
00764         // Draw major tickmarks
00765         foreach( double yy, a->majorTickMarks() ) {
00766             double py = d->pixRect.height() * ( 1.0 - (yy - d->dataRect.y()) / d->dataRect.height() );
00767             if ( py > 0 && py < d->pixRect.height() ) {
00768                 p->drawLine( QPointF( TICKOFFSET, py ), QPointF( double(TICKOFFSET + BIGTICKSIZE), py ) );
00769 
00770                 //Draw ticklabel
00771                 if ( a->areTickLabelsShown() ) {
00772                     QRect r( -2*BIGTICKSIZE-SMALLTICKSIZE, int(py)-SMALLTICKSIZE, 2*BIGTICKSIZE, 2*SMALLTICKSIZE );
00773                     p->drawText( r, Qt::AlignRight | Qt::AlignVCenter | Qt::TextDontClip, a->tickLabel( yy ) );
00774                 }
00775             }
00776         }
00777 
00778         // Draw minor tickmarks
00779         foreach ( double yy, a->minorTickMarks() ) {
00780             double py = d->pixRect.height() * ( 1.0 - (yy - d->dataRect.y()) / d->dataRect.height() );
00781             if ( py > 0 && py < d->pixRect.height() ) {
00782                 p->drawLine( QPointF( TICKOFFSET, py ), QPointF( double(TICKOFFSET + SMALLTICKSIZE), py ) );
00783             }
00784         }
00785 
00786         //Draw LeftAxis Label.  We need to draw the text sideways.
00787         if ( ! a->label().isEmpty() ) {
00788             //store current painter translation/rotation state
00789             p->save();
00790     
00791             //translate coord sys to left corner of axis label rectangle, then rotate 90 degrees.
00792             p->translate( -3*XPADDING, d->pixRect.height() );
00793             p->rotate( -90.0 );
00794     
00795             QRect r( 0, 0, d->pixRect.height(), XPADDING );
00796             p->drawText( r, Qt::AlignCenter, a->label() ); //draw the label, now that we are sideways
00797     
00798             p->restore();  //restore translation/rotation state
00799         }
00800     }  //End of LeftAxis
00801 
00802     //Prepare for top and right axes; we may need the secondary data rect
00803     double x0 = d->dataRect.x();
00804     double y0 = d->dataRect.y();
00805     double dw = d->dataRect.width();
00806     double dh = d->dataRect.height();
00807     if ( secondaryDataRect().isValid() ) {
00808         x0 = secondaryDataRect().x();
00809         y0 = secondaryDataRect().y();
00810         dw = secondaryDataRect().width();
00811         dh = secondaryDataRect().height();
00812     }
00813 
00814     /*** TopAxis ***/
00815     a = axis(TopAxis);
00816     if (a->isVisible()) {
00817         //Draw axis line
00818         p->drawLine( 0, 0, d->pixRect.width(), 0 );
00819 
00820         // Draw major tickmarks
00821         foreach( double xx, a->majorTickMarks() ) {
00822             double px = d->pixRect.width() * (xx - x0) / dw;
00823             if ( px > 0 && px < d->pixRect.width() ) {
00824                 p->drawLine( QPointF( px, TICKOFFSET ), QPointF( px, double(BIGTICKSIZE + TICKOFFSET)) );
00825 
00826                 //Draw ticklabel
00827                 if ( a->areTickLabelsShown() ) {
00828                     QRect r( int(px) - BIGTICKSIZE, (int)-1.5*BIGTICKSIZE, 2*BIGTICKSIZE, BIGTICKSIZE );
00829                     p->drawText( r, Qt::AlignCenter | Qt::TextDontClip, a->tickLabel( xx ) );
00830                 }
00831             }
00832         }
00833 
00834         // Draw minor tickmarks
00835         foreach ( double xx, a->minorTickMarks() ) {
00836             double px = d->pixRect.width() * (xx - x0) / dw;
00837             if ( px > 0 && px < d->pixRect.width() ) {
00838                 p->drawLine( QPointF( px, TICKOFFSET ), QPointF( px, double(SMALLTICKSIZE + TICKOFFSET)) );
00839             }
00840         }
00841 
00842         // Draw TopAxis Label
00843         if ( ! a->label().isEmpty() ) {
00844             QRect r( 0, 0 - 3*YPADDING, d->pixRect.width(), YPADDING );
00845             p->drawText( r, Qt::AlignCenter, a->label() );
00846         }
00847     }  //End of TopAxis
00848 
00849     /*** RightAxis ***/
00850     a = axis(RightAxis);
00851     if (a->isVisible()) {
00852         //Draw axis line
00853         p->drawLine( d->pixRect.width(), 0, d->pixRect.width(), d->pixRect.height() );
00854 
00855         // Draw major tickmarks
00856         foreach( double yy, a->majorTickMarks() ) {
00857             double py = d->pixRect.height() * ( 1.0 - (yy - y0) / dh );
00858             if ( py > 0 && py < d->pixRect.height() ) {
00859                 p->drawLine( QPointF( double(d->pixRect.width() - TICKOFFSET), py ), 
00860                         QPointF( double(d->pixRect.width() - TICKOFFSET - BIGTICKSIZE), py ) );
00861 
00862                 //Draw ticklabel
00863                 if ( a->areTickLabelsShown() ) {
00864                     QRect r( d->pixRect.width() + SMALLTICKSIZE, int(py)-SMALLTICKSIZE, 2*BIGTICKSIZE, 2*SMALLTICKSIZE );
00865                     p->drawText( r, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextDontClip, a->tickLabel( yy ) );
00866                 }
00867             }
00868         }
00869 
00870         // Draw minor tickmarks
00871         foreach ( double yy, a->minorTickMarks() ) {
00872             double py = d->pixRect.height() * ( 1.0 - (yy - y0) / dh );
00873             if ( py > 0 && py < d->pixRect.height() ) {
00874                 p->drawLine( QPointF( double(d->pixRect.width() - 0.0), py ), 
00875                         QPointF( double(d->pixRect.width() - 0.0 - SMALLTICKSIZE), py ) );
00876             }
00877         }
00878 
00879         //Draw RightAxis Label.  We need to draw the text sideways.
00880         if ( ! a->label().isEmpty() ) {
00881             //store current painter translation/rotation state
00882             p->save();
00883     
00884             //translate coord sys to left corner of axis label rectangle, then rotate 90 degrees.
00885             p->translate( d->pixRect.width() + 2*XPADDING, d->pixRect.height() );
00886             p->rotate( -90.0 );
00887     
00888             QRect r( 0, 0, d->pixRect.height(), XPADDING );
00889             p->drawText( r, Qt::AlignCenter, a->label() ); //draw the label, now that we are sideways
00890     
00891             p->restore();  //restore translation/rotation state
00892         }
00893     }  //End of RightAxis
00894 }
00895 
00896 int KPlotWidget::leftPadding() const
00897 {
00898     if ( d->leftPadding >= 0 )
00899         return d->leftPadding;
00900     const KPlotAxis *a = axis( LeftAxis );
00901     if ( a && a->isVisible() && a->areTickLabelsShown() )
00902     {
00903         return !a->label().isEmpty() ? 3 * XPADDING : 2 * XPADDING;
00904     }
00905     return XPADDING;
00906 }
00907 
00908 int KPlotWidget::rightPadding() const
00909 {
00910     if ( d->rightPadding >= 0 )
00911         return d->rightPadding;
00912     const KPlotAxis *a = axis( RightAxis );
00913     if ( a && a->isVisible() && a->areTickLabelsShown() )
00914     {
00915         return !a->label().isEmpty() ? 3 * XPADDING : 2 * XPADDING;
00916     }
00917     return XPADDING;
00918 }
00919 
00920 int KPlotWidget::topPadding() const
00921 {
00922     if ( d->topPadding >= 0 )
00923         return d->topPadding;
00924     const KPlotAxis *a = axis( TopAxis );
00925     if ( a && a->isVisible() && a->areTickLabelsShown() )
00926     {
00927         return !a->label().isEmpty() ? 3 * YPADDING : 2 * YPADDING;
00928     }
00929     return YPADDING;
00930 }
00931 
00932 int KPlotWidget::bottomPadding() const
00933 {
00934     if ( d->bottomPadding >= 0 )
00935         return d->bottomPadding;
00936     const KPlotAxis *a = axis( BottomAxis );
00937     if ( a && a->isVisible() && a->areTickLabelsShown() )
00938     {
00939         return !a->label().isEmpty() ? 3 * YPADDING : 2 * YPADDING;
00940     }
00941     return YPADDING;
00942 }
00943 
00944 void KPlotWidget::setLeftPadding( int padding )
00945 {
00946     d->leftPadding = padding;
00947 }
00948 
00949 void KPlotWidget::setRightPadding( int padding )
00950 {
00951     d->rightPadding = padding;
00952 }
00953 
00954 void KPlotWidget::setTopPadding( int padding )
00955 {
00956     d->topPadding = padding;
00957 }
00958 
00959 void KPlotWidget::setBottomPadding( int padding )
00960 {
00961     d->bottomPadding = padding;
00962 }
00963 
00964 void KPlotWidget::setDefaultPaddings()
00965 {
00966    d->leftPadding = -1;
00967    d->rightPadding = -1;
00968    d->topPadding = -1;
00969    d->bottomPadding = -1;
00970 }
00971 

KDEUI

Skip menu "KDEUI"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal