1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include <QtCharts/QCategoryAxis>
5#include <private/qcategoryaxis_p.h>
6#include <private/chartcategoryaxisx_p.h>
7#include <private/chartcategoryaxisy_p.h>
8#include <private/polarchartcategoryaxisangular_p.h>
9#include <private/polarchartcategoryaxisradial_p.h>
10#include <QtCharts/QChart>
11#include <QtCore/QtMath>
12#include <QtCore/QDebug>
13
14QT_BEGIN_NAMESPACE
15/*!
16 \class QCategoryAxis
17 \inmodule QtCharts
18 \brief The QCategoryAxis class places named ranges on the axis.
19
20 This class can be used to explain the underlying data by adding labeled categories.
21 Unlike QBarCategoryAxis, QCategoryAxis allows the widths of the category ranges to
22 be specified freely.
23
24 Example code on how to use QCategoryAxis:
25 \image api_category_axis.png
26 \code
27 QChartView *chartView = new QChartView;
28 QLineSeries *series = new QLineSeries;
29 // ...
30 chartView->chart()->addSeries(series);
31
32 QCategoryAxis *axisY = new QCategoryAxis;
33 axisY->setMin(0);
34 axisY->setMax(52);
35 axisY->setStartValue(15);
36 axisY->append("First", 20);
37 axisY->append("Second", 37);
38 axisY->append("Third", 52);
39 chartView->chart()->setAxisY(axisY, series);
40 \endcode
41*/
42/*!
43 \qmltype CategoryAxis
44 \instantiates QCategoryAxis
45 \inqmlmodule QtCharts
46
47 \inherits AbstractAxis
48 \brief CategoryAxis places named ranges on the axis.
49
50 This type can be used to explain the underlying data by adding labeled categories.
51 The widths of the category ranges can be specified freely.
52
53 For example:
54 \image examples_qmlaxes3.png
55 \snippet qmlchartsgallery/qml/CategoryAxis.qml 1
56*/
57
58/*!
59 \property QCategoryAxis::startValue
60 \brief The low end of the first category on the axis.
61*/
62/*!
63 \qmlproperty int CategoryAxis::startValue
64 The low end of the first category on the axis.
65*/
66
67/*!
68 \property QCategoryAxis::count
69 \brief The number of categories.
70*/
71/*!
72 \qmlproperty int CategoryAxis::count
73 The number of categories.
74*/
75
76/*!
77 \property QCategoryAxis::categoriesLabels
78 \brief The category labels as a string list.
79*/
80/*!
81 \qmlproperty StringList CategoryAxis::categoriesLabels
82 The category labels as a list of strings.
83*/
84
85/*!
86 \fn void QCategoryAxis::categoriesChanged()
87 This signal is emitted when the categories of the axis change.
88*/
89
90/*!
91 \enum QCategoryAxis::AxisLabelsPosition
92
93 This enum describes the position of the category labels.
94
95 \value AxisLabelsPositionCenter Labels are centered to category.
96 \value AxisLabelsPositionOnValue Labels are positioned to the high end limit of the category.
97 */
98/*!
99 \property QCategoryAxis::labelsPosition
100 \brief The position of the category labels. The labels in the beginning and in the end of the
101 axes may overlap other axes' labels when positioned on value.
102*/
103/*!
104 \qmlproperty enumeration CategoryAxis::labelsPosition
105
106 The position of the category labels. The labels in the beginning and in the end of the
107 axes may overlap other axes' labels when positioned on value.
108
109 \value CategoryAxis.AxisLabelsPositionCenter
110 Labels are centered to category.
111 \value CategoryAxis.AxisLabelsPositionOnValue
112 Labels are positioned to the high end limit of the category.
113*/
114
115
116/*!
117 Constructs an axis object that is a child of \a parent.
118*/
119QCategoryAxis::QCategoryAxis(QObject *parent):
120 QValueAxis(*new QCategoryAxisPrivate(this), parent)
121{
122}
123
124/*!
125 Destroys the object.
126*/
127QCategoryAxis::~QCategoryAxis()
128{
129 Q_D(QCategoryAxis);
130 if (d->m_chart)
131 d->m_chart->removeAxis(axis: this);
132}
133
134/*!
135 \internal
136*/
137QCategoryAxis::QCategoryAxis(QCategoryAxisPrivate &d, QObject *parent): QValueAxis(d, parent)
138{
139
140}
141
142/*!
143 \qmlmethod CategoryAxis::append(string label, real endValue)
144 Appends a new category to the axis with the label \a label. A category label has to be unique.
145 \a endValue specifies the high end limit of the category.
146 It has to be greater than the high end limit of the previous category.
147 Otherwise the method returns without adding a new category.
148*/
149/*!
150 Appends a new category to the axis with the label \a categoryLabel.
151 A category label has to be unique.
152 \a categoryEndValue specifies the high end limit of the category.
153 It has to be greater than the high end limit of the previous category.
154 Otherwise the method returns without adding a new category.
155*/
156void QCategoryAxis::append(const QString &categoryLabel, qreal categoryEndValue)
157{
158 Q_D(QCategoryAxis);
159
160 if (!d->m_categories.contains(str: categoryLabel)) {
161 if (d->m_categories.isEmpty()) {
162 Range range(d->m_categoryMinimum, categoryEndValue);
163 d->m_categoriesMap.insert(key: categoryLabel, value: range);
164 d->m_categories.append(t: categoryLabel);
165 emit categoriesChanged();
166 } else if (categoryEndValue > endValue(categoryLabel: d->m_categories.last())) {
167 Range previousRange = d->m_categoriesMap.value(key: d->m_categories.last());
168 d->m_categoriesMap.insert(key: categoryLabel, value: Range(previousRange.second, categoryEndValue));
169 d->m_categories.append(t: categoryLabel);
170 emit categoriesChanged();
171 }
172 }
173}
174
175/*!
176 Sets \a min to be the low end limit of the first category on the axis.
177 If categories have already been added to the axis, the passed value must be less
178 than the high end value of the already defined first category range.
179 Otherwise nothing is done.
180*/
181void QCategoryAxis::setStartValue(qreal min)
182{
183 Q_D(QCategoryAxis);
184 if (d->m_categories.isEmpty()) {
185 d->m_categoryMinimum = min;
186 emit categoriesChanged();
187 } else {
188 Range range = d->m_categoriesMap.value(key: d->m_categories.first());
189 if (min < range.second) {
190 d->m_categoriesMap.insert(key: d->m_categories.first(), value: Range(min, range.second));
191 emit categoriesChanged();
192 }
193 }
194}
195
196/*!
197 Returns the low end limit of the category specified by \a categoryLabel.
198*/
199qreal QCategoryAxis::startValue(const QString &categoryLabel) const
200{
201 Q_D(const QCategoryAxis);
202 if (categoryLabel.isEmpty())
203 return d->m_categoryMinimum;
204 return d->m_categoriesMap.value(key: categoryLabel).first;
205}
206
207/*!
208 Returns the high end limit of the category specified by \a categoryLabel.
209*/
210qreal QCategoryAxis::endValue(const QString &categoryLabel) const
211{
212 Q_D(const QCategoryAxis);
213 return d->m_categoriesMap.value(key: categoryLabel).second;
214}
215
216/*!
217 \qmlmethod CategoryAxis::remove(string label)
218 Removes a category specified by the label \a label from the axis.
219*/
220/*!
221 Removes a category specified by the label \a categoryLabel from the axis.
222*/
223void QCategoryAxis::remove(const QString &categoryLabel)
224{
225 Q_D(QCategoryAxis);
226 int labelIndex = d->m_categories.indexOf(str: categoryLabel);
227
228 // check if such label exists
229 if (labelIndex != -1) {
230 d->m_categories.removeAt(i: labelIndex);
231 d->m_categoriesMap.remove(key: categoryLabel);
232
233 // the range of the interval that follows (if exists) needs to be updated
234 if (labelIndex < d->m_categories.size()) {
235 QString label = d->m_categories.at(i: labelIndex);
236 Range range = d->m_categoriesMap.value(key: label);
237
238 // set the range
239 if (labelIndex == 0) {
240 range.first = d->m_categoryMinimum;
241 d->m_categoriesMap.insert(key: label, value: range);
242 } else {
243 range.first = d->m_categoriesMap.value(key: d->m_categories.at(i: labelIndex - 1)).second;
244 d->m_categoriesMap.insert(key: label, value: range);
245 }
246 }
247 emit categoriesChanged();
248 }
249}
250
251/*!
252 \qmlmethod CategoryAxis::replace(string oldLabel, string newLabel)
253 Replaces an existing category label specified by \a oldLabel with \a newLabel.
254 If the old label does not exist, the method returns without making any changes.
255*/
256/*!
257 Replaces an existing category label specified by \a oldLabel with \a newLabel.
258 If the old label does not exist, the method returns without making any changes.
259 */
260void QCategoryAxis::replaceLabel(const QString &oldLabel, const QString &newLabel)
261{
262 Q_D(QCategoryAxis);
263 int labelIndex = d->m_categories.indexOf(str: oldLabel);
264
265 // check if such label exists
266 if (labelIndex != -1) {
267 d->m_categories.replace(i: labelIndex, t: newLabel);
268 Range range = d->m_categoriesMap.value(key: oldLabel);
269 d->m_categoriesMap.remove(key: oldLabel);
270 d->m_categoriesMap.insert(key: newLabel, value: range);
271 emit categoriesChanged();
272 }
273}
274
275/*!
276 Returns the list of the categories' labels.
277 */
278QStringList QCategoryAxis::categoriesLabels()
279{
280 Q_D(QCategoryAxis);
281 return d->m_categories;
282}
283
284/*!
285 Returns the number of categories.
286 */
287int QCategoryAxis::count() const
288{
289 Q_D(const QCategoryAxis);
290 return d->m_categories.size();
291}
292
293/*!
294 Returns the type of the axis.
295*/
296QAbstractAxis::AxisType QCategoryAxis::type() const
297{
298 return QAbstractAxis::AxisTypeCategory;
299}
300
301void QCategoryAxis::setLabelsPosition(QCategoryAxis::AxisLabelsPosition position)
302{
303 Q_D(QCategoryAxis);
304 if (d->m_labelsPosition != position) {
305 d->m_labelsPosition = position;
306 emit labelsPositionChanged(position);
307 }
308}
309
310QCategoryAxis::AxisLabelsPosition QCategoryAxis::labelsPosition() const
311{
312 Q_D(const QCategoryAxis);
313 return d->m_labelsPosition;
314}
315
316//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
317
318QCategoryAxisPrivate::QCategoryAxisPrivate(QCategoryAxis *q)
319 : QValueAxisPrivate(q),
320 m_categoryMinimum(0),
321 m_labelsPosition(QCategoryAxis::AxisLabelsPositionCenter)
322{
323
324}
325
326QCategoryAxisPrivate::~QCategoryAxisPrivate()
327{
328
329}
330
331int QCategoryAxisPrivate::ticksCount() const
332{
333 return m_categories.size() + 1;
334}
335
336void QCategoryAxisPrivate::initializeGraphics(QGraphicsItem *parent)
337{
338 Q_Q(QCategoryAxis);
339 ChartAxisElement *axis(0);
340 if (m_chart->chartType() == QChart::ChartTypeCartesian) {
341 if (orientation() == Qt::Vertical)
342 axis = new ChartCategoryAxisY(q,parent);
343 else if (orientation() == Qt::Horizontal)
344 axis = new ChartCategoryAxisX(q,parent);
345 }
346
347 if (m_chart->chartType() == QChart::ChartTypePolar) {
348 if (orientation() == Qt::Vertical)
349 axis = new PolarChartCategoryAxisRadial(q, parent);
350 if (orientation() == Qt::Horizontal)
351 axis = new PolarChartCategoryAxisAngular(q, parent);
352 }
353
354 m_item.reset(p: axis);
355 QAbstractAxisPrivate::initializeGraphics(parent);
356}
357
358QT_END_NAMESPACE
359
360#include "moc_qcategoryaxis.cpp"
361#include "moc_qcategoryaxis_p.cpp"
362

source code of qtcharts/src/charts/axis/categoryaxis/qcategoryaxis.cpp