| 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_barchart.h" | 
|---|
| 11 | #include "qwt_scale_map.h" | 
|---|
| 12 | #include "qwt_column_symbol.h" | 
|---|
| 13 | #include "qwt_painter.h" | 
|---|
| 14 | #include <qpainter.h> | 
|---|
| 15 |  | 
|---|
| 16 | class QwtPlotBarChart::PrivateData | 
|---|
| 17 | { | 
|---|
| 18 | public: | 
|---|
| 19 | PrivateData(): | 
|---|
| 20 | symbol( NULL ), | 
|---|
| 21 | legendMode( QwtPlotBarChart::LegendChartTitle ) | 
|---|
| 22 | { | 
|---|
| 23 | } | 
|---|
| 24 |  | 
|---|
| 25 | ~PrivateData() | 
|---|
| 26 | { | 
|---|
| 27 | delete symbol; | 
|---|
| 28 | } | 
|---|
| 29 |  | 
|---|
| 30 | QwtColumnSymbol *symbol; | 
|---|
| 31 | QwtPlotBarChart::LegendMode legendMode; | 
|---|
| 32 | }; | 
|---|
| 33 |  | 
|---|
| 34 | /*! | 
|---|
| 35 | Constructor | 
|---|
| 36 | \param title Title of the curve | 
|---|
| 37 | */ | 
|---|
| 38 | QwtPlotBarChart::QwtPlotBarChart( const QwtText &title ): | 
|---|
| 39 | QwtPlotAbstractBarChart( title ) | 
|---|
| 40 | { | 
|---|
| 41 | init(); | 
|---|
| 42 | } | 
|---|
| 43 |  | 
|---|
| 44 | /*! | 
|---|
| 45 | Constructor | 
|---|
| 46 | \param title Title of the curve | 
|---|
| 47 | */ | 
|---|
| 48 | QwtPlotBarChart::QwtPlotBarChart( const QString &title ): | 
|---|
| 49 | QwtPlotAbstractBarChart( QwtText( title ) ) | 
|---|
| 50 | { | 
|---|
| 51 | init(); | 
|---|
| 52 | } | 
|---|
| 53 |  | 
|---|
| 54 | //! Destructor | 
|---|
| 55 | QwtPlotBarChart::~QwtPlotBarChart() | 
|---|
| 56 | { | 
|---|
| 57 | delete d_data; | 
|---|
| 58 | } | 
|---|
| 59 |  | 
|---|
| 60 | void QwtPlotBarChart::init() | 
|---|
| 61 | { | 
|---|
| 62 | d_data = new PrivateData; | 
|---|
| 63 | setData( new QwtPointSeriesData() ); | 
|---|
| 64 | } | 
|---|
| 65 |  | 
|---|
| 66 | //! \return QwtPlotItem::Rtti_PlotBarChart | 
|---|
| 67 | int QwtPlotBarChart::rtti() const | 
|---|
| 68 | { | 
|---|
| 69 | return QwtPlotItem::Rtti_PlotBarChart; | 
|---|
| 70 | } | 
|---|
| 71 |  | 
|---|
| 72 | /*! | 
|---|
| 73 | Initialize data with an array of points | 
|---|
| 74 |  | 
|---|
| 75 | \param samples Vector of points | 
|---|
| 76 | \note QVector is implicitly shared | 
|---|
| 77 | \note QPolygonF is derived from QVector<QPointF> | 
|---|
| 78 | */ | 
|---|
| 79 | void QwtPlotBarChart::setSamples( | 
|---|
| 80 | const QVector<QPointF> &samples ) | 
|---|
| 81 | { | 
|---|
| 82 | setData( new QwtPointSeriesData( samples ) ); | 
|---|
| 83 | } | 
|---|
| 84 |  | 
|---|
| 85 | /*! | 
|---|
| 86 | Initialize data with an array of doubles | 
|---|
| 87 |  | 
|---|
| 88 | The indices in the array are taken as x coordinate, | 
|---|
| 89 | while the doubles are interpreted as y values. | 
|---|
| 90 |  | 
|---|
| 91 | \param samples Vector of y coordinates | 
|---|
| 92 | \note QVector is implicitly shared | 
|---|
| 93 | */ | 
|---|
| 94 | void QwtPlotBarChart::setSamples( | 
|---|
| 95 | const QVector<double> &samples ) | 
|---|
| 96 | { | 
|---|
| 97 | QVector<QPointF> points; | 
|---|
| 98 | for ( int i = 0; i < samples.size(); i++ ) | 
|---|
| 99 | points += QPointF( i, samples[ i ] ); | 
|---|
| 100 |  | 
|---|
| 101 | setData( new QwtPointSeriesData( points ) ); | 
|---|
| 102 | } | 
|---|
| 103 |  | 
|---|
| 104 | /*! | 
|---|
| 105 | Assign a series of samples | 
|---|
| 106 |  | 
|---|
| 107 | setSamples() is just a wrapper for setData() without any additional | 
|---|
| 108 | value - beside that it is easier to find for the developer. | 
|---|
| 109 |  | 
|---|
| 110 | \param data Data | 
|---|
| 111 | \warning The item takes ownership of the data object, deleting | 
|---|
| 112 | it when its not used anymore. | 
|---|
| 113 | */ | 
|---|
| 114 | void QwtPlotBarChart::setSamples( QwtSeriesData<QPointF> *data ) | 
|---|
| 115 | { | 
|---|
| 116 | setData( data ); | 
|---|
| 117 | } | 
|---|
| 118 |  | 
|---|
| 119 | /*! | 
|---|
| 120 | \brief Assign a symbol | 
|---|
| 121 |  | 
|---|
| 122 | The bar chart will take the ownership of the symbol, hence the previously | 
|---|
| 123 | set symbol will be delete by setting a new one. If \p symbol is | 
|---|
| 124 | \c NULL no symbol will be drawn. | 
|---|
| 125 |  | 
|---|
| 126 | \param symbol Symbol | 
|---|
| 127 | \sa symbol() | 
|---|
| 128 | */ | 
|---|
| 129 | void QwtPlotBarChart::setSymbol( QwtColumnSymbol *symbol ) | 
|---|
| 130 | { | 
|---|
| 131 | if ( symbol != d_data->symbol ) | 
|---|
| 132 | { | 
|---|
| 133 | delete d_data->symbol; | 
|---|
| 134 | d_data->symbol = symbol; | 
|---|
| 135 |  | 
|---|
| 136 | legendChanged(); | 
|---|
| 137 | itemChanged(); | 
|---|
| 138 | } | 
|---|
| 139 | } | 
|---|
| 140 |  | 
|---|
| 141 | /*! | 
|---|
| 142 | \return Current symbol or NULL, when no symbol has been assigned | 
|---|
| 143 | \sa setSymbol() | 
|---|
| 144 | */ | 
|---|
| 145 | const QwtColumnSymbol *QwtPlotBarChart::symbol() const | 
|---|
| 146 | { | 
|---|
| 147 | return d_data->symbol; | 
|---|
| 148 | } | 
|---|
| 149 |  | 
|---|
| 150 | /*! | 
|---|
| 151 | Set the mode that decides what to display on the legend | 
|---|
| 152 |  | 
|---|
| 153 | In case of LegendBarTitles barTitle() needs to be overloaded | 
|---|
| 154 | to return individual titles for each bar. | 
|---|
| 155 |  | 
|---|
| 156 | \param mode New mode | 
|---|
| 157 | \sa legendMode(), legendData(), barTitle(), QwtPlotItem::ItemAttribute | 
|---|
| 158 | */ | 
|---|
| 159 | void QwtPlotBarChart::setLegendMode( LegendMode mode ) | 
|---|
| 160 | { | 
|---|
| 161 | if ( mode != d_data->legendMode ) | 
|---|
| 162 | { | 
|---|
| 163 | d_data->legendMode = mode; | 
|---|
| 164 | legendChanged(); | 
|---|
| 165 | } | 
|---|
| 166 | } | 
|---|
| 167 |  | 
|---|
| 168 | /*! | 
|---|
| 169 | \return Legend mode | 
|---|
| 170 | \sa setLegendMode() | 
|---|
| 171 | */ | 
|---|
| 172 | QwtPlotBarChart::LegendMode QwtPlotBarChart::legendMode() const | 
|---|
| 173 | { | 
|---|
| 174 | return d_data->legendMode; | 
|---|
| 175 | } | 
|---|
| 176 |  | 
|---|
| 177 | /*! | 
|---|
| 178 | \return Bounding rectangle of all samples. | 
|---|
| 179 | For an empty series the rectangle is invalid. | 
|---|
| 180 | */ | 
|---|
| 181 | QRectF QwtPlotBarChart::boundingRect() const | 
|---|
| 182 | { | 
|---|
| 183 | const size_t numSamples = dataSize(); | 
|---|
| 184 | if ( numSamples == 0 ) | 
|---|
| 185 | return QwtPlotSeriesItem::boundingRect(); | 
|---|
| 186 |  | 
|---|
| 187 | QRectF rect = QwtPlotSeriesItem::boundingRect(); | 
|---|
| 188 | if ( rect.height() >= 0 ) | 
|---|
| 189 | { | 
|---|
| 190 | const double baseLine = baseline(); | 
|---|
| 191 |  | 
|---|
| 192 | if ( rect.bottom() < baseLine ) | 
|---|
| 193 | rect.setBottom( baseLine ); | 
|---|
| 194 |  | 
|---|
| 195 | if ( rect.top() > baseLine ) | 
|---|
| 196 | rect.setTop( baseLine ); | 
|---|
| 197 | } | 
|---|
| 198 |  | 
|---|
| 199 | if ( orientation() == Qt::Horizontal ) | 
|---|
| 200 | rect.setRect( rect.y(), rect.x(), rect.height(), rect.width() ); | 
|---|
| 201 |  | 
|---|
| 202 | return rect; | 
|---|
| 203 | } | 
|---|
| 204 |  | 
|---|
| 205 | /*! | 
|---|
| 206 | Draw an interval of the bar chart | 
|---|
| 207 |  | 
|---|
| 208 | \param painter Painter | 
|---|
| 209 | \param xMap Maps x-values into pixel coordinates. | 
|---|
| 210 | \param yMap Maps y-values into pixel coordinates. | 
|---|
| 211 | \param canvasRect Contents rect of the canvas | 
|---|
| 212 | \param from Index of the first point to be painted | 
|---|
| 213 | \param to Index of the last point to be painted. If to < 0 the | 
|---|
| 214 | curve will be painted to its last point. | 
|---|
| 215 |  | 
|---|
| 216 | \sa drawSymbols() | 
|---|
| 217 | */ | 
|---|
| 218 | void QwtPlotBarChart::drawSeries( QPainter *painter, | 
|---|
| 219 | const QwtScaleMap &xMap, const QwtScaleMap &yMap, | 
|---|
| 220 | const QRectF &canvasRect, int from, int to ) const | 
|---|
| 221 | { | 
|---|
| 222 | if ( to < 0 ) | 
|---|
| 223 | to = dataSize() - 1; | 
|---|
| 224 |  | 
|---|
| 225 | if ( from < 0 ) | 
|---|
| 226 | from = 0; | 
|---|
| 227 |  | 
|---|
| 228 | if ( from > to ) | 
|---|
| 229 | return; | 
|---|
| 230 |  | 
|---|
| 231 |  | 
|---|
| 232 | const QRectF br = data()->boundingRect(); | 
|---|
| 233 | const QwtInterval interval( br.left(), br.right() ); | 
|---|
| 234 |  | 
|---|
| 235 | painter->save(); | 
|---|
| 236 |  | 
|---|
| 237 | for ( int i = from; i <= to; i++ ) | 
|---|
| 238 | { | 
|---|
| 239 | drawSample( painter, xMap, yMap, | 
|---|
| 240 | canvasRect, interval, i, sample( i ) ); | 
|---|
| 241 | } | 
|---|
| 242 |  | 
|---|
| 243 | painter->restore(); | 
|---|
| 244 | } | 
|---|
| 245 |  | 
|---|
| 246 | /*! | 
|---|
| 247 | Draw a sample | 
|---|
| 248 |  | 
|---|
| 249 | \param painter Painter | 
|---|
| 250 | \param xMap x map | 
|---|
| 251 | \param yMap y map | 
|---|
| 252 | \param canvasRect Contents rect of the canvas | 
|---|
| 253 | \param boundingInterval Bounding interval of sample values | 
|---|
| 254 | \param index Index of the sample | 
|---|
| 255 | \param sample Value of the sample | 
|---|
| 256 |  | 
|---|
| 257 | \sa drawSeries() | 
|---|
| 258 | */ | 
|---|
| 259 | void QwtPlotBarChart::drawSample( QPainter *painter, | 
|---|
| 260 | const QwtScaleMap &xMap, const QwtScaleMap &yMap, | 
|---|
| 261 | const QRectF &canvasRect, const QwtInterval &boundingInterval, | 
|---|
| 262 | int index, const QPointF &sample ) const | 
|---|
| 263 | { | 
|---|
| 264 | QwtColumnRect barRect; | 
|---|
| 265 |  | 
|---|
| 266 | if ( orientation() == Qt::Horizontal ) | 
|---|
| 267 | { | 
|---|
| 268 | const double barHeight = sampleWidth( yMap, canvasRect.height(), | 
|---|
| 269 | boundingInterval.width(), sample.y() ); | 
|---|
| 270 |  | 
|---|
| 271 | const double x1 = xMap.transform( baseline() ); | 
|---|
| 272 | const double x2 = xMap.transform( sample.y() ); | 
|---|
| 273 |  | 
|---|
| 274 | const double y = yMap.transform( sample.x() ); | 
|---|
| 275 | const double y1 = y - 0.5 * barHeight; | 
|---|
| 276 | const double y2 = y + 0.5 * barHeight; | 
|---|
| 277 |  | 
|---|
| 278 | barRect.direction = ( x1 < x2 ) ? | 
|---|
| 279 | QwtColumnRect::LeftToRight : QwtColumnRect::RightToLeft; | 
|---|
| 280 |  | 
|---|
| 281 | barRect.hInterval = QwtInterval( x1, x2 ).normalized(); | 
|---|
| 282 | barRect.vInterval = QwtInterval( y1, y2 ); | 
|---|
| 283 | } | 
|---|
| 284 | else | 
|---|
| 285 | { | 
|---|
| 286 | const double barWidth = sampleWidth( xMap, canvasRect.width(), | 
|---|
| 287 | boundingInterval.width(), sample.y() ); | 
|---|
| 288 |  | 
|---|
| 289 | const double x = xMap.transform( sample.x() ); | 
|---|
| 290 | const double x1 = x - 0.5 * barWidth; | 
|---|
| 291 | const double x2 = x + 0.5 * barWidth; | 
|---|
| 292 |  | 
|---|
| 293 | const double y1 = yMap.transform( baseline() ); | 
|---|
| 294 | const double y2 = yMap.transform( sample.y() ); | 
|---|
| 295 |  | 
|---|
| 296 | barRect.direction = ( y1 < y2 ) ? | 
|---|
| 297 | QwtColumnRect::TopToBottom : QwtColumnRect::BottomToTop; | 
|---|
| 298 |  | 
|---|
| 299 | barRect.hInterval = QwtInterval( x1, x2 ); | 
|---|
| 300 | barRect.vInterval = QwtInterval( y1, y2 ).normalized(); | 
|---|
| 301 | } | 
|---|
| 302 |  | 
|---|
| 303 | drawBar( painter, index, sample, barRect ); | 
|---|
| 304 | } | 
|---|
| 305 |  | 
|---|
| 306 | /*! | 
|---|
| 307 | Draw a bar | 
|---|
| 308 |  | 
|---|
| 309 | \param painter Painter | 
|---|
| 310 | \param sampleIndex Index of the sample represented by the bar | 
|---|
| 311 | \param sample Value of the sample | 
|---|
| 312 | \param rect Bounding rectangle of the bar | 
|---|
| 313 | */ | 
|---|
| 314 | void QwtPlotBarChart::drawBar( QPainter *painter, | 
|---|
| 315 | int sampleIndex, const QPointF &sample, | 
|---|
| 316 | const QwtColumnRect &rect ) const | 
|---|
| 317 | { | 
|---|
| 318 | const QwtColumnSymbol *specialSym = | 
|---|
| 319 | specialSymbol( sampleIndex, sample ); | 
|---|
| 320 |  | 
|---|
| 321 | const QwtColumnSymbol *sym = specialSym; | 
|---|
| 322 | if ( sym == NULL ) | 
|---|
| 323 | sym = d_data->symbol; | 
|---|
| 324 |  | 
|---|
| 325 | if ( sym ) | 
|---|
| 326 | { | 
|---|
| 327 | sym->draw( painter, rect ); | 
|---|
| 328 | } | 
|---|
| 329 | else | 
|---|
| 330 | { | 
|---|
| 331 | // we build a temporary default symbol | 
|---|
| 332 | QwtColumnSymbol columnSymbol( QwtColumnSymbol::Box ); | 
|---|
| 333 | columnSymbol.setLineWidth( 1 ); | 
|---|
| 334 | columnSymbol.setFrameStyle( QwtColumnSymbol::Plain ); | 
|---|
| 335 | columnSymbol.draw( painter, rect ); | 
|---|
| 336 | } | 
|---|
| 337 |  | 
|---|
| 338 | delete specialSym; | 
|---|
| 339 | } | 
|---|
| 340 |  | 
|---|
| 341 | /*! | 
|---|
| 342 | Needs to be overloaded to return a | 
|---|
| 343 | non default symbol for a specific sample | 
|---|
| 344 |  | 
|---|
| 345 | \param sampleIndex Index of the sample represented by the bar | 
|---|
| 346 | \param sample Value of the sample | 
|---|
| 347 |  | 
|---|
| 348 | \return NULL, indicating to use the default symbol | 
|---|
| 349 | */ | 
|---|
| 350 | QwtColumnSymbol *QwtPlotBarChart::specialSymbol( | 
|---|
| 351 | int sampleIndex, const QPointF &sample ) const | 
|---|
| 352 | { | 
|---|
| 353 | Q_UNUSED( sampleIndex ); | 
|---|
| 354 | Q_UNUSED( sample ); | 
|---|
| 355 |  | 
|---|
| 356 | return NULL; | 
|---|
| 357 | } | 
|---|
| 358 |  | 
|---|
| 359 | /*! | 
|---|
| 360 | \brief Return the title of a bar | 
|---|
| 361 |  | 
|---|
| 362 | In LegendBarTitles mode the title is displayed on | 
|---|
| 363 | the legend entry corresponding to a bar. | 
|---|
| 364 |  | 
|---|
| 365 | The default implementation is a dummy, that is intended | 
|---|
| 366 | to be overloaded. | 
|---|
| 367 |  | 
|---|
| 368 | \param sampleIndex Index of the bar | 
|---|
| 369 | \return An empty text | 
|---|
| 370 | \sa LegendBarTitles | 
|---|
| 371 | */ | 
|---|
| 372 | QwtText QwtPlotBarChart::barTitle( int sampleIndex ) const | 
|---|
| 373 | { | 
|---|
| 374 | Q_UNUSED( sampleIndex ); | 
|---|
| 375 | return QwtText(); | 
|---|
| 376 | } | 
|---|
| 377 |  | 
|---|
| 378 | /*! | 
|---|
| 379 | \brief Return all information, that is needed to represent | 
|---|
| 380 | the item on the legend | 
|---|
| 381 |  | 
|---|
| 382 | In case of LegendBarTitles an entry for each bar is returned, | 
|---|
| 383 | otherwise the chart is represented like any other plot item | 
|---|
| 384 | from its title() and the legendIcon(). | 
|---|
| 385 |  | 
|---|
| 386 | \return Information, that is needed to represent the item on the legend | 
|---|
| 387 | \sa title(), setLegendMode(), barTitle(), QwtLegend, QwtPlotLegendItem | 
|---|
| 388 | */ | 
|---|
| 389 | QList<QwtLegendData> QwtPlotBarChart::legendData() const | 
|---|
| 390 | { | 
|---|
| 391 | QList<QwtLegendData> list; | 
|---|
| 392 |  | 
|---|
| 393 | if ( d_data->legendMode == LegendBarTitles ) | 
|---|
| 394 | { | 
|---|
| 395 | const size_t numSamples = dataSize(); | 
|---|
| 396 | for ( size_t i = 0; i < numSamples; i++ ) | 
|---|
| 397 | { | 
|---|
| 398 | QwtLegendData data; | 
|---|
| 399 |  | 
|---|
| 400 | QVariant titleValue; | 
|---|
| 401 | qVariantSetValue( titleValue, barTitle( i ) ); | 
|---|
| 402 | data.setValue( QwtLegendData::TitleRole, titleValue ); | 
|---|
| 403 |  | 
|---|
| 404 | if ( !legendIconSize().isEmpty() ) | 
|---|
| 405 | { | 
|---|
| 406 | QVariant iconValue; | 
|---|
| 407 | qVariantSetValue( iconValue, | 
|---|
| 408 | legendIcon( i, legendIconSize() ) ); | 
|---|
| 409 |  | 
|---|
| 410 | data.setValue( QwtLegendData::IconRole, iconValue ); | 
|---|
| 411 | } | 
|---|
| 412 |  | 
|---|
| 413 | list += data; | 
|---|
| 414 | } | 
|---|
| 415 | } | 
|---|
| 416 | else | 
|---|
| 417 | { | 
|---|
| 418 | return QwtPlotAbstractBarChart::legendData(); | 
|---|
| 419 | } | 
|---|
| 420 |  | 
|---|
| 421 | return list; | 
|---|
| 422 | } | 
|---|
| 423 |  | 
|---|
| 424 | /*! | 
|---|
| 425 | \return Icon representing a bar or the chart on the legend | 
|---|
| 426 |  | 
|---|
| 427 | When the legendMode() is LegendBarTitles the icon shows | 
|---|
| 428 | the bar corresponding to index - otherwise the bar | 
|---|
| 429 | displays the default symbol. | 
|---|
| 430 |  | 
|---|
| 431 | \param index Index of the legend entry | 
|---|
| 432 | \param size Icon size | 
|---|
| 433 |  | 
|---|
| 434 | \sa setLegendMode(), drawBar(), | 
|---|
| 435 | QwtPlotItem::setLegendIconSize(), QwtPlotItem::legendData() | 
|---|
| 436 | */ | 
|---|
| 437 | QwtGraphic QwtPlotBarChart::legendIcon( | 
|---|
| 438 | int index, const QSizeF &size ) const | 
|---|
| 439 | { | 
|---|
| 440 | QwtColumnRect column; | 
|---|
| 441 | column.hInterval = QwtInterval( 0.0, size.width() - 1.0 ); | 
|---|
| 442 | column.vInterval = QwtInterval( 0.0, size.height() - 1.0 ); | 
|---|
| 443 |  | 
|---|
| 444 | QwtGraphic icon; | 
|---|
| 445 | icon.setDefaultSize( size ); | 
|---|
| 446 | icon.setRenderHint( QwtGraphic::RenderPensUnscaled, true ); | 
|---|
| 447 |  | 
|---|
| 448 | QPainter painter( &icon ); | 
|---|
| 449 | painter.setRenderHint( QPainter::Antialiasing, | 
|---|
| 450 | testRenderHint( QwtPlotItem::RenderAntialiased ) ); | 
|---|
| 451 |  | 
|---|
| 452 | int barIndex = -1; | 
|---|
| 453 | if ( d_data->legendMode == QwtPlotBarChart::LegendBarTitles ) | 
|---|
| 454 | barIndex = index; | 
|---|
| 455 |  | 
|---|
| 456 | drawBar( &painter, barIndex, QPointF(), column ); | 
|---|
| 457 |  | 
|---|
| 458 | return icon; | 
|---|
| 459 | } | 
|---|