source: ntrip/trunk/BNC/qwt/qwt_plot_multi_barchart.cpp@ 8963

Last change on this file since 8963 was 8127, checked in by stoecker, 7 years ago

update qwt and qwtpolar, many QT5 fixes (unfinished)

File size: 18.5 KB
Line 
1/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
2 * Qwt Widget Library
3 * Copyright (C) 1997 Josef Wilgen
4 * Copyright (C) 2002 Uwe Rathmann
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the Qwt License, Version 1.0
8 *****************************************************************************/
9
10#include "qwt_plot_multi_barchart.h"
11#include "qwt_scale_map.h"
12#include "qwt_column_symbol.h"
13#include "qwt_painter.h"
14#include <qpainter.h>
15#include <qpalette.h>
16#include <qmap.h>
17
18inline static bool qwtIsIncreasing(
19 const QwtScaleMap &map, const QVector<double> &values )
20{
21 bool isInverting = map.isInverting();
22
23 for ( int i = 0; i < values.size(); i++ )
24 {
25 const double y = values[ i ];
26 if ( y != 0.0 )
27 return ( map.isInverting() != ( y > 0.0 ) );
28 }
29
30 return !isInverting;
31}
32
33class QwtPlotMultiBarChart::PrivateData
34{
35public:
36 PrivateData():
37 style( QwtPlotMultiBarChart::Grouped )
38 {
39 }
40
41 QwtPlotMultiBarChart::ChartStyle style;
42 QList<QwtText> barTitles;
43 QMap<int, QwtColumnSymbol *> symbolMap;
44};
45
46/*!
47 Constructor
48 \param title Title of the chart
49*/
50QwtPlotMultiBarChart::QwtPlotMultiBarChart( const QwtText &title ):
51 QwtPlotAbstractBarChart( title )
52{
53 init();
54}
55
56/*!
57 Constructor
58 \param title Title of the chart
59*/
60QwtPlotMultiBarChart::QwtPlotMultiBarChart( const QString &title ):
61 QwtPlotAbstractBarChart( QwtText( title ) )
62{
63 init();
64}
65
66//! Destructor
67QwtPlotMultiBarChart::~QwtPlotMultiBarChart()
68{
69 resetSymbolMap();
70 delete d_data;
71}
72
73void QwtPlotMultiBarChart::init()
74{
75 d_data = new PrivateData;
76 setData( new QwtSetSeriesData() );
77}
78
79//! \return QwtPlotItem::Rtti_PlotBarChart
80int QwtPlotMultiBarChart::rtti() const
81{
82 return QwtPlotItem::Rtti_PlotMultiBarChart;
83}
84
85/*!
86 Initialize data with an array of samples.
87 \param samples Vector of points
88*/
89void QwtPlotMultiBarChart::setSamples(
90 const QVector<QwtSetSample> &samples )
91{
92 setData( new QwtSetSeriesData( samples ) );
93}
94
95/*!
96 Initialize data with an array of samples.
97 \param samples Vector of points
98*/
99void QwtPlotMultiBarChart::setSamples(
100 const QVector< QVector<double> > &samples )
101{
102 QVector<QwtSetSample> s;
103 for ( int i = 0; i < samples.size(); i++ )
104 s += QwtSetSample( i, samples[ i ] );
105
106 setData( new QwtSetSeriesData( s ) );
107}
108
109/*!
110 Assign a series of samples
111
112 setSamples() is just a wrapper for setData() without any additional
113 value - beside that it is easier to find for the developer.
114
115 \param data Data
116 \warning The item takes ownership of the data object, deleting
117 it when its not used anymore.
118*/
119void QwtPlotMultiBarChart::setSamples(
120 QwtSeriesData<QwtSetSample> *data )
121{
122 setData( data );
123}
124
125/*!
126 \brief Set the titles for the bars
127
128 The titles are used for the legend.
129
130 \param titles Bar titles
131
132 \sa barTitles(), legendData()
133 */
134void QwtPlotMultiBarChart::setBarTitles( const QList<QwtText> &titles )
135{
136 d_data->barTitles = titles;
137 itemChanged();
138}
139
140/*!
141 \return Bar titles
142 \sa setBarTitles(), legendData()
143 */
144QList<QwtText> QwtPlotMultiBarChart::barTitles() const
145{
146 return d_data->barTitles;
147}
148
149/*!
150 \brief Add a symbol to the symbol map
151
152 Assign a default symbol for drawing the bar representing all values
153 with the same index in a set.
154
155 \param valueIndex Index of a value in a set
156 \param symbol Symbol used for drawing a bar
157
158 \sa symbol(), resetSymbolMap(), specialSymbol()
159*/
160void QwtPlotMultiBarChart::setSymbol( int valueIndex, QwtColumnSymbol *symbol )
161{
162 if ( valueIndex < 0 )
163 return;
164
165 QMap<int, QwtColumnSymbol *>::iterator it =
166 d_data->symbolMap.find(valueIndex);
167 if ( it == d_data->symbolMap.end() )
168 {
169 if ( symbol != NULL )
170 {
171 d_data->symbolMap.insert( valueIndex, symbol );
172
173 legendChanged();
174 itemChanged();
175 }
176 }
177 else
178 {
179 if ( symbol != it.value() )
180 {
181 delete it.value();
182
183 if ( symbol == NULL )
184 {
185 d_data->symbolMap.remove( valueIndex );
186 }
187 else
188 {
189 it.value() = symbol;
190 }
191
192 legendChanged();
193 itemChanged();
194 }
195 }
196}
197
198/*!
199 Find a symbol in the symbol map
200
201 \param valueIndex Index of a value in a set
202 \return The symbol, that had been set by setSymbol() or NULL.
203
204 \sa setSymbol(), specialSymbol(), drawBar()
205*/
206const QwtColumnSymbol *QwtPlotMultiBarChart::symbol( int valueIndex ) const
207{
208 QMap<int, QwtColumnSymbol *>::const_iterator it =
209 d_data->symbolMap.find( valueIndex );
210
211 return ( it == d_data->symbolMap.end() ) ? NULL : it.value();
212}
213
214/*!
215 Find a symbol in the symbol map
216
217 \param valueIndex Index of a value in a set
218 \return The symbol, that had been set by setSymbol() or NULL.
219
220 \sa setSymbol(), specialSymbol(), drawBar()
221*/
222QwtColumnSymbol *QwtPlotMultiBarChart::symbol( int valueIndex )
223{
224 QMap<int, QwtColumnSymbol *>::iterator it =
225 d_data->symbolMap.find( valueIndex );
226
227 return ( it == d_data->symbolMap.end() ) ? NULL : it.value();
228}
229
230/*!
231 Remove all symbols from the symbol map
232 */
233void QwtPlotMultiBarChart::resetSymbolMap()
234{
235 for ( QMap<int, QwtColumnSymbol *>::iterator it
236 = d_data->symbolMap.begin(); it != d_data->symbolMap.end(); ++it )
237 {
238 delete it.value();
239 }
240
241 d_data->symbolMap.clear();
242}
243
244/*!
245 \brief Create a symbol for special values
246
247 Usually the symbols for displaying a bar are set by setSymbols() and
248 common for all sets. By overloading specialSymbol() it is possible to
249 create a temporary symbol() for displaying a special value.
250
251 The symbol has to be created by new each time specialSymbol() is
252 called. As soon as the symbol is painted this symbol gets deleted.
253
254 When no symbol ( NULL ) is returned, the value will be displayed
255 with the standard symbol that is used for all symbols with the same
256 valueIndex.
257
258 \param sampleIndex Index of the sample
259 \param valueIndex Index of the value in the set
260
261 \return NULL, meaning that the value is not special
262
263 */
264QwtColumnSymbol *QwtPlotMultiBarChart::specialSymbol(
265 int sampleIndex, int valueIndex ) const
266{
267 Q_UNUSED( sampleIndex );
268 Q_UNUSED( valueIndex );
269
270 return NULL;
271}
272
273/*!
274 Set the style of the chart
275
276 \param style Chart style
277 \sa style()
278 */
279void QwtPlotMultiBarChart::setStyle( ChartStyle style )
280{
281 if ( style != d_data->style )
282 {
283 d_data->style = style;
284
285 legendChanged();
286 itemChanged();
287 }
288}
289
290/*!
291 \return Style of the chart
292 \sa setStyle()
293 */
294QwtPlotMultiBarChart::ChartStyle QwtPlotMultiBarChart::style() const
295{
296 return d_data->style;
297}
298
299/*!
300 \return Bounding rectangle of all samples.
301 For an empty series the rectangle is invalid.
302*/
303QRectF QwtPlotMultiBarChart::boundingRect() const
304{
305 const size_t numSamples = dataSize();
306
307 if ( numSamples == 0 )
308 return QwtPlotSeriesItem::boundingRect();
309
310 const double baseLine = baseline();
311
312 QRectF rect;
313
314 if ( d_data->style != QwtPlotMultiBarChart::Stacked )
315 {
316 rect = QwtPlotSeriesItem::boundingRect();
317
318 if ( rect.height() >= 0 )
319 {
320 if ( rect.bottom() < baseLine )
321 rect.setBottom( baseLine );
322 if ( rect.top() > baseLine )
323 rect.setTop( baseLine );
324 }
325 }
326 else
327 {
328 double xMin, xMax, yMin, yMax;
329
330 xMin = xMax = 0.0;
331 yMin = yMax = baseLine;
332
333 const QwtSeriesData<QwtSetSample> *series = data();
334
335 for ( size_t i = 0; i < numSamples; i++ )
336 {
337 const QwtSetSample sample = series->sample( i );
338 if ( i == 0 )
339 {
340 xMin = xMax = sample.value;
341 }
342 else
343 {
344 xMin = qMin( xMin, sample.value );
345 xMax = qMax( xMax, sample.value );
346 }
347
348 const double y = baseLine + sample.added();
349
350 yMin = qMin( yMin, y );
351 yMax = qMax( yMax, y );
352 }
353 rect.setRect( xMin, yMin, xMax - xMin, yMax - yMin );
354 }
355
356 if ( orientation() == Qt::Horizontal )
357 rect.setRect( rect.y(), rect.x(), rect.height(), rect.width() );
358
359 return rect;
360}
361
362/*!
363 Draw an interval of the bar chart
364
365 \param painter Painter
366 \param xMap Maps x-values into pixel coordinates.
367 \param yMap Maps y-values into pixel coordinates.
368 \param canvasRect Contents rectangle of the canvas
369 \param from Index of the first point to be painted
370 \param to Index of the last point to be painted. If to < 0 the
371 curve will be painted to its last point.
372
373 \sa drawSymbols()
374*/
375void QwtPlotMultiBarChart::drawSeries( QPainter *painter,
376 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
377 const QRectF &canvasRect, int from, int to ) const
378{
379 if ( to < 0 )
380 to = dataSize() - 1;
381
382 if ( from < 0 )
383 from = 0;
384
385 if ( from > to )
386 return;
387
388
389 const QRectF br = data()->boundingRect();
390 const QwtInterval interval( br.left(), br.right() );
391
392 painter->save();
393
394 for ( int i = from; i <= to; i++ )
395 {
396 drawSample( painter, xMap, yMap,
397 canvasRect, interval, i, sample( i ) );
398 }
399
400 painter->restore();
401}
402
403/*!
404 Draw a sample
405
406 \param painter Painter
407 \param xMap x map
408 \param yMap y map
409 \param canvasRect Contents rectangle of the canvas
410 \param boundingInterval Bounding interval of sample values
411 \param index Index of the sample to be painted
412 \param sample Sample value
413
414 \sa drawSeries()
415*/
416void QwtPlotMultiBarChart::drawSample( QPainter *painter,
417 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
418 const QRectF &canvasRect, const QwtInterval &boundingInterval,
419 int index, const QwtSetSample& sample ) const
420{
421 if ( sample.set.size() <= 0 )
422 return;
423
424 double sampleW;
425
426 if ( orientation() == Qt::Horizontal )
427 {
428 sampleW = sampleWidth( yMap, canvasRect.height(),
429 boundingInterval.width(), sample.value );
430 }
431 else
432 {
433 sampleW = sampleWidth( xMap, canvasRect.width(),
434 boundingInterval.width(), sample.value );
435 }
436
437 if ( d_data->style == Stacked )
438 {
439 drawStackedBars( painter, xMap, yMap,
440 canvasRect, index, sampleW, sample );
441 }
442 else
443 {
444 drawGroupedBars( painter, xMap, yMap,
445 canvasRect, index, sampleW, sample );
446 }
447}
448
449/*!
450 Draw a grouped sample
451
452 \param painter Painter
453 \param xMap x map
454 \param yMap y map
455 \param canvasRect Contents rectangle of the canvas
456 \param index Index of the sample to be painted
457 \param sampleWidth Boundng width for all bars of the smaple
458 \param sample Sample
459
460 \sa drawSeries(), sampleWidth()
461*/
462void QwtPlotMultiBarChart::drawGroupedBars( QPainter *painter,
463 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
464 const QRectF &canvasRect, int index, double sampleWidth,
465 const QwtSetSample& sample ) const
466{
467 Q_UNUSED( canvasRect );
468
469 const int numBars = sample.set.size();
470 if ( numBars == 0 )
471 return;
472
473 if ( orientation() == Qt::Vertical )
474 {
475 const double barWidth = sampleWidth / numBars;
476
477 const double y1 = yMap.transform( baseline() );
478 const double x0 = xMap.transform( sample.value ) - 0.5 * sampleWidth;
479
480 for ( int i = 0; i < numBars; i++ )
481 {
482 const double x1 = x0 + i * barWidth;
483 const double x2 = x1 + barWidth;
484
485 const double y2 = yMap.transform( sample.set[i] );
486
487 QwtColumnRect barRect;
488 barRect.direction = ( y1 < y2 ) ?
489 QwtColumnRect::TopToBottom : QwtColumnRect::BottomToTop;
490
491 barRect.hInterval = QwtInterval( x1, x2 ).normalized();
492 if ( i != 0 )
493 barRect.hInterval.setBorderFlags( QwtInterval::ExcludeMinimum );
494
495 barRect.vInterval = QwtInterval( y1, y2 ).normalized();
496
497 drawBar( painter, index, i, barRect );
498 }
499 }
500 else
501 {
502 const double barHeight = sampleWidth / numBars;
503
504 const double x1 = xMap.transform( baseline() );
505 const double y0 = yMap.transform( sample.value ) - 0.5 * sampleWidth;
506
507 for ( int i = 0; i < numBars; i++ )
508 {
509 double y1 = y0 + i * barHeight;
510 double y2 = y1 + barHeight;
511
512 double x2 = xMap.transform( sample.set[i] );
513
514 QwtColumnRect barRect;
515 barRect.direction = x1 < x2 ?
516 QwtColumnRect::LeftToRight : QwtColumnRect::RightToLeft;
517
518 barRect.hInterval = QwtInterval( x1, x2 ).normalized();
519
520 barRect.vInterval = QwtInterval( y1, y2 );
521 if ( i != 0 )
522 barRect.vInterval.setBorderFlags( QwtInterval::ExcludeMinimum );
523
524 drawBar( painter, index, i, barRect );
525 }
526 }
527}
528
529/*!
530 Draw a stacked sample
531
532 \param painter Painter
533 \param xMap x map
534 \param yMap y map
535 \param canvasRect Contents rectangle of the canvas
536 \param index Index of the sample to be painted
537 \param sampleWidth Width of the bars
538 \param sample Sample
539
540 \sa drawSeries(), sampleWidth()
541*/
542void QwtPlotMultiBarChart::drawStackedBars( QPainter *painter,
543 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
544 const QRectF &canvasRect, int index,
545 double sampleWidth, const QwtSetSample& sample ) const
546{
547 Q_UNUSED( canvasRect ); // clipping the bars ?
548
549 const int numBars = sample.set.size();
550 if ( numBars == 0 )
551 return;
552
553 QwtInterval::BorderFlag borderFlags = QwtInterval::IncludeBorders;
554
555 if ( orientation() == Qt::Vertical )
556 {
557 const double x1 = xMap.transform( sample.value ) - 0.5 * sampleWidth;
558 const double x2 = x1 + sampleWidth;
559
560 const bool increasing = qwtIsIncreasing( yMap, sample.set );
561
562 QwtColumnRect bar;
563 bar.direction = increasing ?
564 QwtColumnRect::TopToBottom : QwtColumnRect::BottomToTop;
565
566 bar.hInterval = QwtInterval( x1, x2 ).normalized();
567
568 double sum = baseline();
569
570 const int numBars = sample.set.size();
571 for ( int i = 0; i < numBars; i++ )
572 {
573 const double si = sample.set[ i ];
574 if ( si == 0.0 )
575 continue;
576
577 const double y1 = yMap.transform( sum );
578 const double y2 = yMap.transform( sum + si );
579
580 if ( ( y2 > y1 ) != increasing )
581 {
582 // stacked bars need to be in the same direction
583 continue;
584 }
585
586 bar.vInterval = QwtInterval( y1, y2 ).normalized();
587 bar.vInterval.setBorderFlags( borderFlags );
588
589 drawBar( painter, index, i, bar );
590
591 sum += si;
592
593 if ( increasing )
594 borderFlags = QwtInterval::ExcludeMinimum;
595 else
596 borderFlags = QwtInterval::ExcludeMaximum;
597 }
598 }
599 else
600 {
601 const double y1 = yMap.transform( sample.value ) - 0.5 * sampleWidth;
602 const double y2 = y1 + sampleWidth;
603
604 const bool increasing = qwtIsIncreasing( xMap, sample.set );
605
606 QwtColumnRect bar;
607 bar.direction = increasing ?
608 QwtColumnRect::LeftToRight : QwtColumnRect::RightToLeft;
609 bar.vInterval = QwtInterval( y1, y2 ).normalized();
610
611 double sum = baseline();
612
613 for ( int i = 0; i < sample.set.size(); i++ )
614 {
615 const double si = sample.set[ i ];
616 if ( si == 0.0 )
617 continue;
618
619 const double x1 = xMap.transform( sum );
620 const double x2 = xMap.transform( sum + si );
621
622 if ( ( x2 > x1 ) != increasing )
623 {
624 // stacked bars need to be in the same direction
625 continue;
626 }
627
628 bar.hInterval = QwtInterval( x1, x2 ).normalized();
629 bar.hInterval.setBorderFlags( borderFlags );
630
631 drawBar( painter, index, i, bar );
632
633 sum += si;
634
635 if ( increasing )
636 borderFlags = QwtInterval::ExcludeMinimum;
637 else
638 borderFlags = QwtInterval::ExcludeMaximum;
639 }
640 }
641}
642
643/*!
644 Draw a bar
645
646 \param painter Painter
647 \param sampleIndex Index of the sample - might be -1 when the
648 bar is painted for the legend
649 \param valueIndex Index of a value in a set
650 \param rect Directed target rectangle for the bar
651
652 \sa drawSeries()
653*/
654void QwtPlotMultiBarChart::drawBar( QPainter *painter,
655 int sampleIndex, int valueIndex, const QwtColumnRect &rect ) const
656{
657 const QwtColumnSymbol *specialSym = NULL;
658 if ( sampleIndex >= 0 )
659 specialSym = specialSymbol( sampleIndex, valueIndex );
660
661 const QwtColumnSymbol *sym = specialSym;
662 if ( sym == NULL )
663 sym = symbol( valueIndex );
664
665 if ( sym )
666 {
667 sym->draw( painter, rect );
668 }
669 else
670 {
671 // we build a temporary default symbol
672 QwtColumnSymbol sym( QwtColumnSymbol::Box );
673 sym.setLineWidth( 1 );
674 sym.setFrameStyle( QwtColumnSymbol::Plain );
675 sym.draw( painter, rect );
676 }
677
678 delete specialSym;
679}
680
681/*!
682 \return Information to be displayed on the legend
683
684 The chart is represented by a list of entries - one for each bar title.
685 Each element contains a bar title and an icon showing its corresponding bar.
686
687 \sa barTitles(), legendIcon(), legendIconSize()
688*/
689QList<QwtLegendData> QwtPlotMultiBarChart::legendData() const
690{
691 QList<QwtLegendData> list;
692
693 for ( int i = 0; i < d_data->barTitles.size(); i++ )
694 {
695 QwtLegendData data;
696
697 QVariant titleValue;
698 qVariantSetValue( titleValue, d_data->barTitles[i] );
699 data.setValue( QwtLegendData::TitleRole, titleValue );
700
701 if ( !legendIconSize().isEmpty() )
702 {
703 QVariant iconValue;
704 qVariantSetValue( iconValue,
705 legendIcon( i, legendIconSize() ) );
706
707 data.setValue( QwtLegendData::IconRole, iconValue );
708 }
709
710 list += data;
711 }
712
713 return list;
714}
715
716/*!
717 \return Icon for representing a bar on the legend
718
719 \param index Index of the bar
720 \param size Icon size
721
722 \return An icon showing a bar
723 \sa drawBar(), legendData()
724 */
725QwtGraphic QwtPlotMultiBarChart::legendIcon( int index,
726 const QSizeF &size ) const
727{
728 QwtColumnRect column;
729 column.hInterval = QwtInterval( 0.0, size.width() - 1.0 );
730 column.vInterval = QwtInterval( 0.0, size.height() - 1.0 );
731
732 QwtGraphic icon;
733 icon.setDefaultSize( size );
734 icon.setRenderHint( QwtGraphic::RenderPensUnscaled, true );
735
736 QPainter painter( &icon );
737 painter.setRenderHint( QPainter::Antialiasing,
738 testRenderHint( QwtPlotItem::RenderAntialiased ) );
739
740 drawBar( &painter, -1, index, column );
741
742 return icon;
743}
744
Note: See TracBrowser for help on using the repository browser.