1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include <QtCharts/QChartView>
5#include <private/qchartview_p.h>
6#include <private/qchart_p.h>
7#include <QtWidgets/QGraphicsScene>
8#include <QtWidgets/QRubberBand>
9#include <QtWidgets/QGraphicsItem>
10
11/*!
12 \enum QChartView::RubberBand
13
14 This enum describes the different types of rubber band effects that can
15 be applied to the rectangular zooming area.
16
17 \value NoRubberBand
18 No zooming area is specified, and therefore zooming is not enabled.
19 \value VerticalRubberBand
20 The rubber band is locked to the size of the chart horizontally
21 and can be pulled vertically to specify the zooming area.
22 \value HorizontalRubberBand
23 The rubber band is locked to the size of the chart vertically
24 and can be pulled horizontally to specify the zooming area.
25 \value RectangleRubberBand
26 The rubber band is fixed to the point that was clicked and can be
27 pulled both vertically and horizontally.
28 \value ClickThroughRubberBand
29 An option on the above rubber band choices that allows left clicks
30 to be passed on to chart items if those chart items accept clicks.
31 To select this, OR it with one of the rubber band selection modes.
32 \since 6.2
33*/
34
35/*!
36 \class QChartView
37 \inmodule QtCharts
38 \brief The QChartView is a standalone widget that can display charts.
39
40 A chart view does not require a QGraphicsScene object to work. To display
41 a chart in an existing QGraphicsScene, the QChart or QPolarChart class should
42 be used instead.
43
44 \sa QChart, QPolarChart
45*/
46
47QT_BEGIN_NAMESPACE
48
49/*!
50 Constructs a chart view object with the parent \a parent.
51*/
52
53QChartView::QChartView(QWidget *parent)
54 : QGraphicsView(parent),
55 d_ptr(new QChartViewPrivate(this))
56{
57
58}
59
60/*!
61 Constructs a chart view object with the parent \a parent to display the
62 chart \a chart. The ownership of the chart is passed to the chart view.
63*/
64
65QChartView::QChartView(QChart *chart, QWidget *parent)
66 : QGraphicsView(parent),
67 d_ptr(new QChartViewPrivate(this, chart))
68{
69
70}
71
72
73/*!
74 Deletes the chart view object and the associated chart.
75*/
76QChartView::~QChartView()
77{
78}
79
80/*!
81 Returns the pointer to the associated chart.
82*/
83QChart *QChartView::chart() const
84{
85 return d_ptr->m_chart;
86}
87
88/*!
89 Sets the current chart to \a chart. The ownership of the new chart is passed to
90 the chart view and the ownership of the previous chart is released.
91
92 To avoid memory leaks, the previous chart must be deleted.
93*/
94
95void QChartView::setChart(QChart *chart)
96{
97 d_ptr->setChart(chart);
98}
99
100/*!
101 Sets the rubber band flags to \a rubberBand.
102 The selected flags determine the way zooming is performed.
103
104 \note Rubber band zooming is not supported for polar charts.
105*/
106void QChartView::setRubberBand(const RubberBands &rubberBand)
107{
108#ifndef QT_NO_RUBBERBAND
109 d_ptr->m_rubberBandFlags = rubberBand;
110
111 if (!(d_ptr->m_rubberBandFlags & ~RubberBands(ClickThroughRubberBand))) {
112 delete d_ptr->m_rubberBand;
113 d_ptr->m_rubberBand = nullptr;
114 return;
115 }
116
117 if (!d_ptr->m_rubberBand) {
118 d_ptr->m_rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
119 d_ptr->m_rubberBand->setEnabled(true);
120 }
121#else
122 Q_UNUSED(rubberBand);
123 qWarning("Unable to set rubber band because Qt is configured without it.");
124#endif
125}
126
127/*!
128 Returns the rubber band flags that are currently being used by the chart view.
129*/
130QChartView::RubberBands QChartView::rubberBand() const
131{
132 return d_ptr->m_rubberBandFlags;
133}
134
135/*!
136 If the left mouse button is pressed and the rubber band is enabled, the event
137 \a event is accepted and the rubber band is displayed on the screen. This
138 enables the user to select the zoom area.
139
140 If some other mouse button is pressed or the rubber band is disabled, the event
141 is passed to QGraphicsView::mousePressEvent().
142*/
143void QChartView::mousePressEvent(QMouseEvent *event)
144{
145#ifndef QT_NO_RUBBERBAND
146 QGraphicsItem *itemUnderCursor = itemAt(pos: event->pos());
147 bool itemUnderCursorAcceptsLMB = itemUnderCursor && (itemUnderCursor->acceptedMouseButtons() & Qt::LeftButton);
148 bool clickThrough = d_ptr->m_rubberBandFlags.testFlag(flag: ClickThroughRubberBand);
149 QRectF plotArea = d_ptr->m_chart->plotArea();
150 if (d_ptr->m_rubberBand && d_ptr->m_rubberBand->isEnabled()
151 && event->button() == Qt::LeftButton
152 && plotArea.contains(p: event->pos())
153 && !(clickThrough && itemUnderCursorAcceptsLMB)) {
154 d_ptr->m_rubberBandOrigin = event->pos();
155 d_ptr->m_rubberBand->setGeometry(QRect(d_ptr->m_rubberBandOrigin, QSize()));
156 d_ptr->m_rubberBand->show();
157 event->accept();
158 } else {
159#endif
160 QGraphicsView::mousePressEvent(event);
161#ifndef QT_NO_RUBBERBAND
162 }
163#endif
164}
165
166/*!
167 If the rubber band rectangle is displayed in the press event specified by
168 \a event, the event data is used to update the rubber band geometry.
169 Otherwise, the default QGraphicsView::mouseMoveEvent() implementation is called.
170*/
171void QChartView::mouseMoveEvent(QMouseEvent *event)
172{
173#ifndef QT_NO_RUBBERBAND
174 if (d_ptr->m_rubberBand && d_ptr->m_rubberBand->isVisible()) {
175 QRect rect = d_ptr->m_chart->plotArea().toRect();
176 int width = event->pos().x() - d_ptr->m_rubberBandOrigin.x();
177 int height = event->pos().y() - d_ptr->m_rubberBandOrigin.y();
178 if (!d_ptr->m_rubberBandFlags.testFlag(flag: VerticalRubberBand)) {
179 d_ptr->m_rubberBandOrigin.setY(rect.top());
180 height = rect.height();
181 }
182 if (!d_ptr->m_rubberBandFlags.testFlag(flag: HorizontalRubberBand)) {
183 d_ptr->m_rubberBandOrigin.setX(rect.left());
184 width = rect.width();
185 }
186 d_ptr->m_rubberBand->setGeometry(QRect(d_ptr->m_rubberBandOrigin.x(), d_ptr->m_rubberBandOrigin.y(), width, height).normalized());
187 } else {
188#endif
189 QGraphicsView::mouseMoveEvent(event);
190#ifndef QT_NO_RUBBERBAND
191 }
192#endif
193}
194
195/*!
196 If the left mouse button is released and the rubber band is enabled, the
197 event \a event is accepted and the view is zoomed into the rectangle
198 specified by the rubber band. If releasing the right mouse button triggered
199 the event, the view is zoomed out.
200*/
201void QChartView::mouseReleaseEvent(QMouseEvent *event)
202{
203#ifndef QT_NO_RUBBERBAND
204 if (d_ptr->m_rubberBand && d_ptr->m_rubberBand->isVisible()) {
205 if (event->button() == Qt::LeftButton) {
206 d_ptr->m_rubberBand->hide();
207 QRectF rect = d_ptr->m_rubberBand->geometry();
208 // Since plotArea uses QRectF and rubberband uses QRect, we can't just blindly use
209 // rubberband's dimensions for vertical and horizontal rubberbands, where one
210 // dimension must match the corresponding plotArea dimension exactly.
211 if (!d_ptr->m_rubberBandFlags.testFlag(flag: RectangleRubberBand)) {
212 if (d_ptr->m_rubberBandFlags.testFlag(flag: VerticalRubberBand)) {
213 rect.setX(d_ptr->m_chart->plotArea().x());
214 rect.setWidth(d_ptr->m_chart->plotArea().width());
215 } else if (d_ptr->m_rubberBandFlags.testFlag(flag: HorizontalRubberBand)) {
216 rect.setY(d_ptr->m_chart->plotArea().y());
217 rect.setHeight(d_ptr->m_chart->plotArea().height());
218 }
219 }
220 d_ptr->m_chart->zoomIn(rect);
221 event->accept();
222 }
223 } else if (d_ptr->m_rubberBand && event->button() == Qt::RightButton) {
224 // If vertical or horizontal rubberband mode, restrict zoom out to specified axis.
225 // Since there is no suitable API for that, use zoomIn with rect bigger than the
226 // plot area.
227 if (d_ptr->m_rubberBandFlags.testFlag(flag: VerticalRubberBand)
228 || d_ptr->m_rubberBandFlags.testFlag(flag: HorizontalRubberBand)) {
229 QRectF rect = d_ptr->m_chart->plotArea();
230 if (d_ptr->m_rubberBandFlags.testFlag(flag: VerticalRubberBand)) {
231 qreal adjustment = rect.height() / 2;
232 rect.adjust(xp1: 0, yp1: -adjustment, xp2: 0, yp2: adjustment);
233 }
234 if (d_ptr->m_rubberBandFlags.testFlag(flag: HorizontalRubberBand)) {
235 qreal adjustment = rect.width() / 2;
236 rect.adjust(xp1: -adjustment, yp1: 0, xp2: adjustment, yp2: 0);
237 }
238 d_ptr->m_chart->zoomIn(rect);
239 }
240 event->accept();
241 } else {
242#endif
243 QGraphicsView::mouseReleaseEvent(event);
244#ifndef QT_NO_RUBBERBAND
245 }
246#endif
247}
248
249#ifdef Q_OS_MACOS
250#if QT_CONFIG(wheelevent)
251void QChartView::wheelEvent(QWheelEvent *event)
252{
253 QGraphicsView::wheelEvent(event);
254}
255#endif
256#endif
257
258/*!
259 Resizes and updates the chart area using the data specified by \a event.
260*/
261void QChartView::resizeEvent(QResizeEvent *event)
262{
263 QGraphicsView::resizeEvent(event);
264 d_ptr->resize();
265}
266
267///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
268
269QChartViewPrivate::QChartViewPrivate(QChartView *q, QChart *chart)
270 : q_ptr(q),
271 m_scene(new QGraphicsScene(q)),
272 m_chart(chart),
273#ifndef QT_NO_RUBBERBAND
274 m_rubberBand(nullptr),
275#endif
276 m_rubberBandFlags(QChartView::NoRubberBand)
277{
278 q_ptr->setFrameShape(QFrame::NoFrame);
279 q_ptr->setBackgroundRole(QPalette::Window);
280 q_ptr->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
281 q_ptr->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
282 q_ptr->setScene(m_scene);
283 q_ptr->setSizePolicy(hor: QSizePolicy::Expanding, ver: QSizePolicy::Expanding);
284 if (!m_chart)
285 m_chart = new QChart();
286 m_scene->addItem(item: m_chart);
287}
288
289QChartViewPrivate::~QChartViewPrivate()
290{
291}
292
293void QChartViewPrivate::setChart(QChart *chart)
294{
295 Q_ASSERT(chart);
296
297 if (m_chart == chart)
298 return;
299
300 if (m_chart)
301 m_scene->removeItem(item: m_chart);
302
303 m_chart = chart;
304 m_scene->addItem(item: m_chart);
305
306 resize();
307}
308
309void QChartViewPrivate::resize()
310{
311 // Fit the chart into view if it has been rotated
312 qreal sinA = qAbs(t: q_ptr->transform().m21());
313 qreal cosA = qAbs(t: q_ptr->transform().m11());
314 QSize chartSize = q_ptr->size();
315
316 if (sinA == 1.0) {
317 chartSize.setHeight(q_ptr->size().width());
318 chartSize.setWidth(q_ptr->size().height());
319 } else if (sinA != 0.0) {
320 // Non-90 degree rotation, find largest square chart that can fit into the view.
321 qreal minDimension = qMin(a: q_ptr->size().width(), b: q_ptr->size().height());
322 qreal h = (minDimension - (minDimension / ((sinA / cosA) + 1.0))) / sinA;
323 chartSize.setHeight(h);
324 chartSize.setWidth(h);
325 }
326
327 m_chart->resize(size: chartSize);
328 q_ptr->setMinimumSize(m_chart->minimumSize().toSize().expandedTo(otherSize: q_ptr->minimumSize()));
329 q_ptr->setMaximumSize(q_ptr->maximumSize().boundedTo(otherSize: m_chart->maximumSize().toSize()));
330 q_ptr->setSceneRect(m_chart->geometry());
331}
332
333QT_END_NAMESPACE
334
335#include "moc_qchartview.cpp"
336

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtcharts/src/charts/qchartview.cpp