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 | |
14 | QT_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 | */ |
119 | QCategoryAxis::QCategoryAxis(QObject *parent): |
120 | QValueAxis(*new QCategoryAxisPrivate(this), parent) |
121 | { |
122 | } |
123 | |
124 | /*! |
125 | Destroys the object. |
126 | */ |
127 | QCategoryAxis::~QCategoryAxis() |
128 | { |
129 | Q_D(QCategoryAxis); |
130 | if (d->m_chart) |
131 | d->m_chart->removeAxis(axis: this); |
132 | } |
133 | |
134 | /*! |
135 | \internal |
136 | */ |
137 | QCategoryAxis::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 | */ |
156 | void 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 | */ |
181 | void 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 | */ |
199 | qreal 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 | */ |
210 | qreal 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 | */ |
223 | void 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 | */ |
260 | void 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 | */ |
278 | QStringList QCategoryAxis::categoriesLabels() |
279 | { |
280 | Q_D(QCategoryAxis); |
281 | return d->m_categories; |
282 | } |
283 | |
284 | /*! |
285 | Returns the number of categories. |
286 | */ |
287 | int 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 | */ |
296 | QAbstractAxis::AxisType QCategoryAxis::type() const |
297 | { |
298 | return QAbstractAxis::AxisTypeCategory; |
299 | } |
300 | |
301 | void 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 | |
310 | QCategoryAxis::AxisLabelsPosition QCategoryAxis::labelsPosition() const |
311 | { |
312 | Q_D(const QCategoryAxis); |
313 | return d->m_labelsPosition; |
314 | } |
315 | |
316 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
317 | |
318 | QCategoryAxisPrivate::QCategoryAxisPrivate(QCategoryAxis *q) |
319 | : QValueAxisPrivate(q), |
320 | m_categoryMinimum(0), |
321 | m_labelsPosition(QCategoryAxis::AxisLabelsPositionCenter) |
322 | { |
323 | |
324 | } |
325 | |
326 | QCategoryAxisPrivate::~QCategoryAxisPrivate() |
327 | { |
328 | |
329 | } |
330 | |
331 | int QCategoryAxisPrivate::ticksCount() const |
332 | { |
333 | return m_categories.size() + 1; |
334 | } |
335 | |
336 | void 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 | |
358 | QT_END_NAMESPACE |
359 | |
360 | #include "moc_qcategoryaxis.cpp" |
361 | #include "moc_qcategoryaxis_p.cpp" |
362 | |