KD Chart 2  [rev.2.7]
KDChartAbstractCoordinatePlane.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 ** Copyright (C) 2001-2020 Klaralvdalens Datakonsult AB. All rights reserved.
3 **
4 ** This file is part of the KD Chart library.
5 **
6 ** Licensees holding valid commercial KD Chart licenses may use this file in
7 ** accordance with the KD Chart Commercial License Agreement provided with
8 ** the Software.
9 **
10 **
11 ** This file may be distributed and/or modified under the terms of the
12 ** GNU General Public License version 2 and version 3 as published by the
13 ** Free Software Foundation and appearing in the file LICENSE.GPL.txt included.
14 **
15 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
16 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
17 **
18 ** Contact info@kdab.com if any conditions of this licensing are not
19 ** clear to you.
20 **
21 **********************************************************************/
22 
24 #include "KDChartAbstractCoordinatePlane_p.h"
25 
26 #include "KDChartChart.h"
27 #include "KDChartGridAttributes.h"
28 
29 #include <KDABLibFakes>
30 
31 #include <QGridLayout>
32 #include <QRubberBand>
33 #include <QMouseEvent>
34 #include <QtCore/qmath.h>
35 
36 using namespace KDChart;
37 
38 #define d d_func()
39 
40 AbstractCoordinatePlane::Private::Private()
41  : AbstractArea::Private()
42  , parent( 0 )
43  , grid( 0 )
44  , referenceCoordinatePlane( 0 )
45  , enableCornerSpacers( true )
46  , enableRubberBandZooming( false )
47  , rubberBand( 0 )
48 {
49  // this bloc left empty intentionally
50 }
51 
52 
53 AbstractCoordinatePlane::AbstractCoordinatePlane ( KDChart::Chart* parent )
54  : AbstractArea ( new Private() )
55 {
56  d->parent = parent;
57  d->init();
58 }
59 
61 {
62  emit destroyedCoordinatePlane( this );
63 }
64 
65 void AbstractCoordinatePlane::init()
66 {
67  d->initialize(); // virtual method to init the correct grid: cartesian, polar, ...
68  connect( this, SIGNAL(internal_geometryChanged( QRect, QRect )),
69  this, SIGNAL(geometryChanged( QRect, QRect )),
70  Qt::QueuedConnection );
71 }
72 
74 {
75  // diagrams are invisible and paint through their paint() method
76  diagram->hide();
77 
78  d->diagrams.append( diagram );
79  diagram->setParent( d->parent );
80  diagram->setCoordinatePlane( this );
82  layoutPlanes(); // there might be new axes, etc
83  connect( diagram, SIGNAL( modelsChanged() ), this, SLOT( layoutPlanes() ) );
84  connect( diagram, SIGNAL( modelDataChanged() ), this, SLOT( update()) );
85  connect( diagram, SIGNAL( modelDataChanged() ), this, SLOT( relayout()) );
86  connect( this, SIGNAL( boundariesChanged() ), diagram, SIGNAL( boundariesChanged() ) );
87 
88  update();
89  emit boundariesChanged();
90 }
91 
92 /*virtual*/
94 {
95  if ( diagram && oldDiagram_ != diagram ) {
96  AbstractDiagram* oldDiagram = oldDiagram_;
97  if ( d->diagrams.count() ) {
98  if ( ! oldDiagram ) {
99  oldDiagram = d->diagrams.first();
100  if ( oldDiagram == diagram )
101  return;
102  }
103  takeDiagram( oldDiagram );
104  }
105  delete oldDiagram;
106  addDiagram( diagram );
107  layoutDiagrams();
108  layoutPlanes(); // there might be new axes, etc
109  update();
110  }
111 }
112 
113 /*virtual*/
115 {
116  const int idx = d->diagrams.indexOf( diagram );
117  if ( idx != -1 ) {
118  d->diagrams.removeAt( idx );
119  diagram->setParent( 0 );
120  diagram->setCoordinatePlane( 0 );
121  disconnect( diagram, SIGNAL( modelsChanged() ), this, SLOT( layoutPlanes() ) );
122  disconnect( diagram, SIGNAL( modelDataChanged() ), this, SLOT( update()) );
123  disconnect( diagram, SIGNAL( modelDataChanged() ), this, SLOT( relayout()) );
124  layoutDiagrams();
125  update();
126  }
127 }
128 
129 
131 {
132  if ( d->diagrams.isEmpty() )
133  {
134  return 0;
135  } else {
136  return d->diagrams.first();
137  }
138 }
139 
141 {
142  return d->diagrams;
143 }
144 
146 {
148 #ifndef QT_NO_STL
149  qCopy( d->diagrams.begin(), d->diagrams.end(), std::back_inserter( list ) );
150 #else
151  Q_FOREACH( AbstractDiagram * a, d->diagrams )
152  list.push_back( a );
153 #endif
154  return list;
155 }
156 
158 {
159  d->gridAttributes = a;
160  update();
161 }
162 
164 {
165  return d->gridAttributes;
166 }
167 
169 {
170  return d->grid->updateData( this );
171 }
172 
174 {
175  d->grid->setNeedRecalculate();
176 }
177 
179 {
180  d->referenceCoordinatePlane = plane;
181 }
182 
184 {
185  return d->referenceCoordinatePlane;
186 }
187 
189 {
190  d->parent = parent;
191 }
192 
194 {
195  return d->parent;
196 }
197 
199 {
200  return d->parent;
201 }
202 
203 /* pure virtual in QLayoutItem */
205 {
206  return false; // never empty!
207  // coordinate planes with no associated diagrams
208  // are showing a default grid of ()1..10, 1..10) stepWidth 1
209 }
210 /* pure virtual in QLayoutItem */
212 {
213  return Qt::Vertical | Qt::Horizontal;
214 }
215 /* pure virtual in QLayoutItem */
217 {
218  // No maximum size set. Especially not parent()->size(), we are not layouting
219  // to the parent widget's size when using Chart::paint()!
220  return QSize(QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX);
221 }
222 /* pure virtual in QLayoutItem */
224 {
225  return QSize(60, 60); // this default can be overwritten by derived classes
226 }
227 /* pure virtual in QLayoutItem */
229 {
230  // we return our maxiumu (which is the full size of the Chart)
231  // even if we know the plane will be smaller
232  return maximumSize();
233 }
234 /* pure virtual in QLayoutItem */
236 {
237  if ( d->geometry != r ) {
238  // inform the outside word by Signal geometryChanged()
239  // via a queued connection to internal_geometryChanged()
240  emit internal_geometryChanged( d->geometry, r );
241 
242  d->geometry = r;
243  // Note: We do *not* call update() here
244  // because it would invoke KDChart::update() recursively.
245  }
246 }
247 /* pure virtual in QLayoutItem */
249 {
250  return d->geometry;
251 }
252 
254 {
255  //qDebug("KDChart::AbstractCoordinatePlane::update() called");
256  emit needUpdate();
257 }
258 
260 {
261  //qDebug("KDChart::AbstractCoordinatePlane::relayout() called");
262  emit needRelayout();
263 }
264 
266 {
267  //qDebug("KDChart::AbstractCoordinatePlane::relayout() called");
268  emit needLayoutPlanes();
269 }
270 
272 {
273  d->enableRubberBandZooming = enable;
274 
275  if ( !enable && d->rubberBand != 0 )
276  {
277  delete d->rubberBand;
278  d->rubberBand = 0;
279  }
280 }
281 
283 {
284  return d->enableRubberBandZooming;
285 }
286 
288 {
289  if ( d->enableCornerSpacers == enable ) return;
290 
291  d->enableCornerSpacers = enable;
292  emit needRelayout();
293 }
294 
296 {
297  return d->enableCornerSpacers;
298 }
299 
301 {
302  if ( event->button() == Qt::LeftButton )
303  {
304  if ( d->enableRubberBandZooming && d->rubberBand == 0 )
305  d->rubberBand = new QRubberBand( QRubberBand::Rectangle, qobject_cast< QWidget* >( parent() ) );
306 
307  if ( d->rubberBand != 0 )
308  {
309  d->rubberBandOrigin = event->pos();
310  d->rubberBand->setGeometry( QRect( event->pos(), QSize() ) );
311  d->rubberBand->show();
312 
313  event->accept();
314  }
315  }
316  else if ( event->button() == Qt::RightButton )
317  {
318  if ( d->enableRubberBandZooming && !d->rubberBandZoomConfigHistory.isEmpty() )
319  {
320  // restore the last config from the stack
321  ZoomParameters config = d->rubberBandZoomConfigHistory.pop();
322  setZoomFactorX( config.xFactor );
323  setZoomFactorY( config.yFactor );
324  setZoomCenter( config.center() );
325 
326  QWidget* const p = qobject_cast< QWidget* >( parent() );
327  if ( p != 0 )
328  p->update();
329 
330  event->accept();
331  }
332  }
333 
334  KDAB_FOREACH( AbstractDiagram * a, d->diagrams )
335  {
336  a->mousePressEvent( event );
337  }
338 }
339 
341 {
342  if ( event->button() == Qt::RightButton )
343  {
344  // othewise the second click gets lost
345  // which is pretty annoying when zooming out fast
346  mousePressEvent( event );
347  }
348  KDAB_FOREACH( AbstractDiagram * a, d->diagrams )
349  {
350  a->mouseDoubleClickEvent( event );
351  }
352 }
353 
355 {
356  if ( d->rubberBand != 0 )
357  {
358  // save the old config on the stack
359  d->rubberBandZoomConfigHistory.push( ZoomParameters( zoomFactorX(), zoomFactorY(), zoomCenter() ) );
360 
361  // this is the height/width of the rubber band in pixel space
362  const qreal rubberWidth = static_cast< qreal >( d->rubberBand->width() );
363  const qreal rubberHeight = static_cast< qreal >( d->rubberBand->height() );
364 
365  if ( rubberWidth > 0.0 && rubberHeight > 0.0 )
366  {
367  // this is the center of the rubber band in pixel space
368  const qreal centerX = qFloor( d->rubberBand->geometry().width() / 2.0 + d->rubberBand->geometry().x() );
369  const qreal centerY = qCeil( d->rubberBand->geometry().height() / 2.0 + d->rubberBand->geometry().y() );
370 
371  const qreal rubberCenterX = static_cast< qreal >( centerX - geometry().x() );
372  const qreal rubberCenterY = static_cast< qreal >( centerY - geometry().y() );
373 
374  // this is the height/width of the plane in pixel space
375  const qreal myWidth = static_cast< qreal >( geometry().width() );
376  const qreal myHeight = static_cast< qreal >( geometry().height() );
377 
378  // this describes the new center of zooming, relative to the plane pixel space
379  const qreal newCenterX = rubberCenterX / myWidth / zoomFactorX() + zoomCenter().x() - 0.5 / zoomFactorX();
380  const qreal newCenterY = rubberCenterY / myHeight / zoomFactorY() + zoomCenter().y() - 0.5 / zoomFactorY();
381 
382  // this will be the new zoom factor
383  const qreal newZoomFactorX = zoomFactorX() * myWidth / rubberWidth;
384  const qreal newZoomFactorY = zoomFactorY() * myHeight / rubberHeight;
385 
386  // and this the new center
387  const QPointF newZoomCenter( newCenterX, newCenterY );
388 
389  setZoomFactorX( newZoomFactorX );
390  setZoomFactorY( newZoomFactorY );
391  setZoomCenter( newZoomCenter );
392  }
393 
394  d->rubberBand->parentWidget()->update();
395  delete d->rubberBand;
396  d->rubberBand = 0;
397 
398  event->accept();
399  }
400 
401  KDAB_FOREACH( AbstractDiagram * a, d->diagrams )
402  {
403  a->mouseReleaseEvent( event );
404  }
405 }
406 
408 {
409  if ( d->rubberBand != 0 )
410  {
411  const QRect normalized = QRect( d->rubberBandOrigin, event->pos() ).normalized();
412  d->rubberBand->setGeometry( normalized & geometry() );
413 
414  event->accept();
415  }
416 
417  KDAB_FOREACH( AbstractDiagram * a, d->diagrams )
418  {
419  a->mouseMoveEvent( event );
420  }
421 }
422 
423 #if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE)
424 const
425 #endif
426 bool KDChart::AbstractCoordinatePlane::isVisiblePoint( const QPointF& point ) const
427 {
428  return d->isVisiblePoint( this, point );
429 }
430 
432 {
433  Q_UNUSED( p );
434  return this;
435 }
436 
437 #if !defined(QT_NO_DEBUG_STREAM)
438 #include "KDChartEnums.h"
439 
440 QDebug KDChart::operator<<( QDebug stream, const DataDimension& r )
441 {
442  stream << "DataDimension("
443  << " start=" << r.start
444  << " end=" << r.end
446  << " isCalculated=" << r.isCalculated
447  << " calcMode=" << ( r.calcMode == AbstractCoordinatePlane::Logarithmic ? "Logarithmic" : "Linear" )
448  << " stepWidth=" << r.stepWidth
449  << " subStepWidth=" << r.subStepWidth
450  << " )";
451  return stream;
452 }
453 #endif
454 
455 #undef d
void needUpdate()
Emitted when plane needs to update its drawings.
Base class common for all coordinate planes, CartesianCoordinatePlane, PolarCoordinatePlane, TernaryCoordinatePlane.
QSize minimumSize() const override
pure virtual in QLayoutItem
virtual AbstractCoordinatePlane * sharedAxisMasterPlane(QPainter *p=0)
virtual void takeDiagram(AbstractDiagram *diagram)
Removes the diagram from the plane, without deleting it.
bool isEmpty() const override
pure virtual in QLayoutItem
An area in the chart with a background, a frame, etc.
void setParent(Chart *parent)
Called internally by KDChart::Chart.
QRect geometry() const override
pure virtual in QLayoutItem
void internal_geometryChanged(QRect, QRect)
A chart with one or more diagrams.
Definition: KDChartChart.h:98
ZoomParameters stores the center and the factor of zooming internally.
virtual void layoutDiagrams()=0
Distribute the available space among the diagrams and axes.
void needRelayout()
Emitted when plane needs to trigger the Chart&#39;s layouting.
Definition of global enums.
void relayout()
Calling relayout() on the plane triggers the global KDChart::Chart::slotRelayout() ...
void layoutPlanes()
Calling layoutPlanes() on the plane triggers the global KDChart::Chart::slotLayoutPlanes() ...
void destroyedCoordinatePlane(AbstractCoordinatePlane *)
Emitted when this coordinate plane is destroyed.
void setGlobalGridAttributes(const GridAttributes &)
Set the grid attributes to be used by this coordinate plane.
virtual void mouseMoveEvent(QMouseEvent *event)
KDChartEnums::GranularitySequence sequence
void update()
Calling update() on the plane triggers the global KDChart::Chart::update()
virtual void replaceDiagram(AbstractDiagram *diagram, AbstractDiagram *oldDiagram=0)
Replaces the old diagram, or appends the diagram, it there is none yet.
AbstractDiagram defines the interface for diagram classes.
virtual void setZoomFactorX(qreal factor)
Sets the zoom factor in horizontal direction, that is applied to all coordinate transformations.
const QPointF center() const
static QString granularitySequenceToString(GranularitySequence sequence)
Converts the specified granularity sequence enum to a string representation.
Definition: KDChartEnums.h:109
QSize sizeHint() const override
pure virtual in QLayoutItem
void setReferenceCoordinatePlane(AbstractCoordinatePlane *plane)
Set another coordinate plane to be used as the reference plane for this one.
void geometryChanged(QRect, QRect)
Emitted after the geometry of the Coordinate Plane has been changed.
virtual void mouseReleaseEvent(QMouseEvent *event)
const bool isVisiblePoint(const QPointF &point) const
Tests, if a point is visible on the coordinate plane.
void setRubberBandZoomingEnabled(bool enable)
Enables or disables zooming with a rubber band using the mouse.
virtual void setZoomFactorY(qreal factor)
Sets the zoom factor in vertical direction, that is applied to all coordinate transformations.
virtual void setCoordinatePlane(AbstractCoordinatePlane *plane)
Set the coordinate plane associated with the diagram.
AbstractCoordinatePlane * referenceCoordinatePlane() const
There are two ways, in which planes can be caused to interact, in where they are put layouting wise: ...
QSize maximumSize() const override
pure virtual in QLayoutItem
A set of attributes controlling the appearance of grids.
DataDimensionsList gridDimensionsList()
Returns the dimensions used for drawing the grid lines.
void setCornerSpacersEnabled(bool enable)
Enables or disables the use of spacers in the plane corners.
virtual void mouseDoubleClickEvent(QMouseEvent *event)
QDebug operator<<(QDebug stream, const DataDimension &r)
Qt::Orientations expandingDirections() const override
pure virtual in QLayoutItem
AbstractCoordinatePlane::AxesCalcMode calcMode
virtual void addDiagram(AbstractDiagram *diagram)
Adds a diagram to this coordinate plane.
Class only listed here to document inheritance of some KDChart classes.
Helper class for one dimension of data, e.g.
void setGeometry(const QRect &r) override
pure virtual in QLayoutItem
virtual void setZoomCenter(const QPointF &center)
Set the point (in value coordinates) to be used as the center point in zoom operations.
void needLayoutPlanes()
Emitted when plane needs to trigger the Chart&#39;s layouting of the coord.
void setGridNeedsRecalculate()
Used by the chart to clear the cached grid data.
virtual void mousePressEvent(QMouseEvent *event)

Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/

https://www.kdab.com/development-resources/qt-tools/kd-chart/