22 #include "kplotwidget.moc"
27 #include <QtGui/QActionEvent>
32 #include <QtAlgorithms>
40 #define BIGTICKSIZE 10
41 #define SMALLTICKSIZE 4
44 class KPlotWidget::Private
49 cBackground( Qt::black ), cForeground( Qt::white ), cGrid( Qt::gray ),
50 showGrid(
false ), showObjectToolTip(
true ), useAntialias(
false )
62 axes.insert(
TopAxis, topAxis );
67 qDeleteAll( objectList );
73 void calcDataRectLimits(
double x1,
double x2,
double y1,
double y2 );
82 float rectCost(
const QRectF &r )
const;
85 QColor cBackground, cForeground, cGrid;
88 bool showObjectToolTip : 1;
89 bool useAntialias : 1;
105 :
QFrame( parent ), d( new Private( this ) )
107 setAttribute( Qt::WA_OpaquePaintEvent );
108 setAttribute( Qt::WA_NoSystemBackground );
110 d->secondDataRect = QRectF();
112 d->calcDataRectLimits( 0.0, 1.0, 0.0, 1.0 );
116 setMinimumSize( 150, 150 );
127 return QSize( 150, 150 );
137 d->calcDataRectLimits( x1, x2, y1, y2 );
141 void KPlotWidget::Private::calcDataRectLimits(
double x1,
double x2,
double y1,
double y2 )
143 double XA1, XA2, YA1, YA2;
144 if (x2<x1) { XA1=x2; XA2=x1; }
145 else { XA1=x1; XA2=x2; }
146 if ( y2<y1) { YA1=y2; YA2=y1; }
147 else { YA1=y1; YA2=y2; }
150 kWarning() <<
"x1 and x2 cannot be equal. Setting x2 = x1 + 1.0";
154 kWarning() <<
"y1 and y2 cannot be equal. Setting y2 = y1 + 1.0";
157 dataRect = QRectF( XA1, YA1, XA2 - XA1, YA2 - YA1 );
159 q->axis( LeftAxis )->setTickMarks( dataRect.y(), dataRect.height() );
160 q->axis( BottomAxis )->setTickMarks( dataRect.x(), dataRect.width() );
162 if ( secondDataRect.isNull() )
164 q->axis( RightAxis )->setTickMarks( dataRect.y(), dataRect.height() );
165 q->axis( TopAxis )->setTickMarks( dataRect.x(), dataRect.width() );
170 double XA1, XA2, YA1, YA2;
171 if (x2<x1) { XA1=x2; XA2=x1; }
172 else { XA1=x1; XA2=x2; }
173 if ( y2<y1) { YA1=y2; YA2=y1; }
174 else { YA1=y1; YA2=y2; }
177 kWarning() <<
"x1 and x2 cannot be equal. Setting x2 = x1 + 1.0";
181 kWarning() <<
"y1 and y2 cannot be equal. Setting y2 = y1 + 1.0";
184 d->secondDataRect = QRectF( XA1, YA1, XA2-XA1, YA2-YA1 );
193 d->secondDataRect = QRectF();
207 return d->secondDataRect;
215 d->objectList.append(
object );
221 bool addedsome =
false;
227 d->objectList.append( o );
236 return d->objectList;
241 if ( d->objectList.isEmpty() )
244 qDeleteAll( d->objectList );
245 d->objectList.clear();
250 d->plotMask = QImage(
pixRect().size(), QImage::Format_ARGB32 );
251 QColor fillColor = Qt::black;
252 fillColor.setAlpha( 128 );
253 d->plotMask.fill( fillColor.rgb() );
257 qDeleteAll( d->objectList );
258 d->objectList.clear();
260 d->calcDataRectLimits( 0.0, 1.0, 0.0, 1.0 );
275 if ( !o || i < 0 || i >= d->objectList.count() )
277 d->objectList.replace( i, o );
283 return d->cBackground;
288 return d->cForeground;
320 return d->showObjectToolTip;
325 return d->useAntialias;
341 d->showObjectToolTip = show;
348 return it != d->axes.end() ? it.value() : 0;
354 return it != d->axes.constEnd() ? it.value() : 0;
376 if ( e->type() == QEvent::ToolTip ) {
377 if ( d->showObjectToolTip )
379 QHelpEvent *he =
static_cast<QHelpEvent*
>( e );
381 if ( pts.count() > 0 ) {
382 QToolTip::showText( he->globalPos(), pts.front()->label(), this );
389 return QFrame::event( e );
393 QFrame::resizeEvent( e );
402 d->pixRect =
QRect( 0, 0, newWidth, newHeight );
407 float px = d->pixRect.left() + d->pixRect.width() * ( p.x() - d->dataRect.x() ) / d->dataRect.width();
408 float py = d->pixRect.top() + d->pixRect.height() * ( d->dataRect.y() + d->dataRect.height() - p.y() ) / d->dataRect.height();
409 return QPointF( px, py );
413 QRect r = rf.toRect().intersected( d->pixRect );
414 int value = int( fvalue );
416 for (
int ix=r.left(); ix<r.right(); ++ix ) {
417 for (
int iy=r.top(); iy<r.bottom(); ++iy ) {
418 newColor =
QColor( d->plotMask.pixel(ix,iy) );
419 newColor.setAlpha( 200 );
420 newColor.setRed( qMin( newColor.red() + value, 255 ) );
421 d->plotMask.setPixel( ix, iy, newColor.rgba() );
428 if ( ! d->pixRect.contains( p1.toPoint() ) && ! d->pixRect.contains( p2.toPoint() ) ) {
432 int value = int( fvalue );
435 double m = (p2.y() - p1.y())/(p2.x() - p1.x());
436 double y0 = p1.y() - m*p1.x();
440 if ( m > 1.0 || m < -1.0 ) {
441 int y1 = int( p1.y() );
442 int y2 = int( p2.y() );
448 for (
int y=y1; y<=y2; ++y ) {
449 int x = int( (y - y0)/m );
450 if ( d->pixRect.contains( x, y ) ) {
451 newColor =
QColor( d->plotMask.pixel(x,y) );
452 newColor.setAlpha( 100 );
453 newColor.setRed( qMin( newColor.red() + value, 255 ) );
454 d->plotMask.setPixel( x, y, newColor.rgba() );
459 int x1 = int( p1.x() );
460 int x2 = int( p2.x() );
466 for (
int x=x1; x<=x2; ++x ) {
467 int y = int( y0 + m*x );
468 if ( d->pixRect.contains( x, y ) ) {
469 newColor =
QColor( d->plotMask.pixel(x,y) );
470 newColor.setAlpha( 100 );
471 newColor.setRed( qMin( newColor.red() + value, 255 ) );
472 d->plotMask.setPixel( x, y, newColor.rgba() );
490 int textFlags = Qt::TextSingleLine | Qt::AlignCenter;
493 if ( ! d->pixRect.contains( pos.toPoint() ) )
return;
495 QFontMetricsF fm( painter->font(), painter->device() );
496 QRectF bestRect = fm.boundingRect( QRectF( pos.x(), pos.y(), 1, 1 ), textFlags, pp->
label() );
497 float xStep = 0.5*bestRect.width();
498 float yStep = 0.5*bestRect.height();
499 float maxCost = 0.05 * bestRect.width() * bestRect.height();
500 float bestCost = d->rectCost( bestRect );
513 float bestBadCost = 10000;
517 bool flagStop =
false;
519 while ( bestCost > maxCost ) {
522 QRectF upRect = bestRect;
523 upRect.moveTop( upRect.top() + yStep );
524 float upCost = d->rectCost( upRect );
525 QRectF downRect = bestRect;
526 downRect.moveTop( downRect.top() - yStep );
527 float downCost = d->rectCost( downRect );
528 QRectF leftRect = bestRect;
529 leftRect.moveLeft( leftRect.left() - xStep );
530 float leftCost = d->rectCost( leftRect );
531 QRectF rightRect = bestRect;
532 rightRect.moveLeft( rightRect.left() + xStep );
533 float rightCost = d->rectCost( rightRect );
537 costList << upCost << downCost << leftCost << rightCost;
539 for (
int i=0; i<costList.size(); ++i ) {
540 if ( iter == 0 && TriedPathIndex.contains( i ) ) {
546 if ( iter == 0 && costList[i] >= bestCost ) {
547 TriedPathIndex.append( i );
551 if ( costList[i] < bestCost && (imin < 0 || costList[i] < costList[imin]) ) {
558 if ( iter == 0 && imin >= 0 ) {
559 TriedPathIndex.append( imin );
565 bestRect.moveTop( upRect.top() );
569 bestRect.moveTop( downRect.top() );
573 bestRect.moveLeft( leftRect.left() );
577 bestRect.moveLeft( rightRect.left() );
578 bestCost = rightCost;
582 if ( bestCost < bestBadCost ) {
583 bestBadCost = bestCost;
584 bestBadRect = bestRect;
589 if ( TriedPathIndex.size() == 4 ) {
590 bestRect = bestBadRect;
596 if ( TriedPathIndex.size() < 4 ) {
598 bestRect = fm.boundingRect( QRectF( pos.x(), pos.y(), 1, 1 ), textFlags, pp->
label() );
599 bestCost = d->rectCost( bestRect );
614 painter->drawText( bestRect, textFlags, pp->
label() );
617 float deltax = pos.x() - bestRect.center().x();
618 float deltay = pos.y() - bestRect.center().y();
619 float rbest = sqrt( deltax*deltax + deltay*deltay );
620 if ( rbest > 20.0 ) {
622 painter->setBrush( QBrush() );
626 painter->drawRoundRect( bestRect );
630 float xline = bestRect.center().x();
631 if ( bestRect.left() > pos.x() )
632 xline = bestRect.left();
633 if ( bestRect.right() < pos.x() )
634 xline = bestRect.right();
636 float yline = bestRect.center().y();
637 if ( bestRect.top() > pos.y() )
638 yline = bestRect.top();
639 if ( bestRect.bottom() < pos.y() )
640 yline = bestRect.bottom();
642 painter->drawLine( QPointF( xline, yline ), pos );
649 float KPlotWidget::Private::rectCost(
const QRectF &r )
const
651 if ( ! plotMask.rect().contains( r.toRect() ) ) {
656 QImage subMask = plotMask.copy( r.toRect() );
658 for (
int ix=0; ix<subMask.width(); ++ix ) {
659 for (
int iy=0; iy<subMask.height(); ++iy ) {
660 cost +=
QColor( subMask.pixel( ix, iy ) ).red();
669 QFrame::paintEvent( e );
673 p.setRenderHint( QPainter::Antialiasing, d->useAntialias );
678 p.setClipRect( d->pixRect );
679 p.setClipping(
true );
684 po->
draw( &p,
this );
689 p.setClipping(
false );
702 double px = d->pixRect.width() * (xx - d->dataRect.x()) / d->dataRect.width();
703 p->drawLine( QPointF( px, 0.0 ), QPointF( px,
double(d->pixRect.height()) ) );
707 double py = d->pixRect.height() * ( 1.0 - (yy - d->dataRect.y()) / d->dataRect.height() );
708 p->drawLine( QPointF( 0.0, py ), QPointF(
double(d->pixRect.width()), py ) );
713 p->setBrush( Qt::NoBrush );
717 int s = f.pointSize();
718 f.setPointSize( s - 2 );
725 p->drawLine( 0, d->pixRect.height(), d->pixRect.width(), d->pixRect.height() );
729 double px = d->pixRect.width() * (xx - d->dataRect.x()) / d->dataRect.width();
730 if ( px > 0 && px < d->
pixRect.width() ) {
731 p->drawLine( QPointF( px,
double(d->pixRect.height() -
TICKOFFSET)),
737 p->drawText( r, Qt::AlignCenter | Qt::TextDontClip, a->
tickLabel( xx ) );
744 double px = d->pixRect.width() * (xx - d->dataRect.x()) / d->dataRect.width();
745 if ( px > 0 && px < d->
pixRect.width() ) {
746 p->drawLine( QPointF( px,
double(d->pixRect.height() -
TICKOFFSET)),
752 if ( ! a->
label().isEmpty() ) {
754 p->drawText( r, Qt::AlignCenter, a->
label() );
762 p->drawLine( 0, 0, 0, d->pixRect.height() );
766 double py = d->pixRect.height() * ( 1.0 - (yy - d->dataRect.y()) / d->dataRect.height() );
767 if ( py > 0 && py < d->
pixRect.height() ) {
773 p->drawText( r, Qt::AlignRight | Qt::AlignVCenter | Qt::TextDontClip, a->
tickLabel( yy ) );
780 double py = d->pixRect.height() * ( 1.0 - (yy - d->dataRect.y()) / d->dataRect.height() );
781 if ( py > 0 && py < d->
pixRect.height() ) {
787 if ( ! a->
label().isEmpty() ) {
792 p->translate( -3*
XPADDING, d->pixRect.height() );
796 p->drawText( r, Qt::AlignCenter, a->
label() );
803 double x0 = d->dataRect.x();
804 double y0 = d->dataRect.y();
805 double dw = d->dataRect.width();
806 double dh = d->dataRect.height();
818 p->drawLine( 0, 0, d->pixRect.width(), 0 );
822 double px = d->pixRect.width() * (xx - x0) / dw;
823 if ( px > 0 && px < d->
pixRect.width() ) {
829 p->drawText( r, Qt::AlignCenter | Qt::TextDontClip, a->
tickLabel( xx ) );
836 double px = d->pixRect.width() * (xx - x0) / dw;
837 if ( px > 0 && px < d->
pixRect.width() ) {
843 if ( ! a->
label().isEmpty() ) {
845 p->drawText( r, Qt::AlignCenter, a->
label() );
853 p->drawLine( d->pixRect.width(), 0, d->pixRect.width(), d->pixRect.height() );
857 double py = d->pixRect.height() * ( 1.0 - (yy - y0) / dh );
858 if ( py > 0 && py < d->
pixRect.height() ) {
859 p->drawLine( QPointF(
double(d->pixRect.width() -
TICKOFFSET), py ),
865 p->drawText( r, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextDontClip, a->
tickLabel( yy ) );
872 double py = d->pixRect.height() * ( 1.0 - (yy - y0) / dh );
873 if ( py > 0 && py < d->
pixRect.height() ) {
874 p->drawLine( QPointF(
double(d->pixRect.width() - 0.0), py ),
875 QPointF(
double(d->pixRect.width() - 0.0 -
SMALLTICKSIZE), py ) );
880 if ( ! a->
label().isEmpty() ) {
885 p->translate( d->pixRect.width() + 2*
XPADDING, d->pixRect.height() );
889 p->drawText( r, Qt::AlignCenter, a->
label() );
898 if ( d->leftPadding >= 0 )
899 return d->leftPadding;
910 if ( d->rightPadding >= 0 )
911 return d->rightPadding;
922 if ( d->topPadding >= 0 )
923 return d->topPadding;
934 if ( d->bottomPadding >= 0 )
935 return d->bottomPadding;
946 d->leftPadding = padding;
951 d->rightPadding = padding;
956 d->topPadding = padding;
961 d->bottomPadding = padding;
967 d->rightPadding = -1;
969 d->bottomPadding = -1;
Encapsulates a point in the plot.
void draw(QPainter *p, KPlotWidget *pw)
Draw this KPlotObject on the given QPainter.
QList< KPlotPoint * > points() const
QList< double > minorTickMarks() const
QString tickLabel(double value) const
Encapsulates a data set to be plotted in a KPlotWidget.
void setTickLabelsShown(bool b)
Determine whether tick labels will be drawn for this axis.
bool areTickLabelsShown() const
void setLabel(const QString &label)
Sets the axis label.
void setTickMarks(double x0, double length)
Determine the positions of major and minor tickmarks for this axis.
static QDebug kWarning(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
QList< double > majorTickMarks() const