##// END OF EJS Templates
Fix chart axis label format...
Titta Heikkala -
r2715:38dcfecdb595
parent child
Show More
@@ -1,404 +1,404
1 1 /****************************************************************************
2 2 **
3 3 ** Copyright (C) 2014 Digia Plc
4 4 ** All rights reserved.
5 5 ** For any questions to Digia, please use contact form at http://qt.digia.com
6 6 **
7 7 ** This file is part of the Qt Enterprise Charts Add-on.
8 8 **
9 9 ** $QT_BEGIN_LICENSE$
10 10 ** Licensees holding valid Qt Enterprise licenses may use this file in
11 11 ** accordance with the Qt Enterprise License Agreement provided with the
12 12 ** Software or, alternatively, in accordance with the terms contained in
13 13 ** a written agreement between you and Digia.
14 14 **
15 15 ** If you have questions regarding the use of this file, please use
16 16 ** contact form at http://qt.digia.com
17 17 ** $QT_END_LICENSE$
18 18 **
19 19 ****************************************************************************/
20 20
21 21 #include <private/chartaxiselement_p.h>
22 22 #include <private/qabstractaxis_p.h>
23 23 #include <private/chartpresenter_p.h>
24 24 #include <private/abstractchartlayout_p.h>
25 25 #include <QtCore/QtMath>
26 26 #include <QtCore/QDateTime>
27 27 #include <QtGui/QTextDocument>
28 28
29 29 QT_CHARTS_BEGIN_NAMESPACE
30 30
31 static const char *labelFormatMatchString = "%[\\-\\+#\\s\\d\\.lhjztL]*([dicuoxfegXFEG])";
31 static const char *labelFormatMatchString = "%[\\-\\+#\\s\\d\\.\\'lhjztL]*([dicuoxfegXFEG])";
32 32 static const char *labelFormatMatchLocalizedString = "^([^%]*)%\\.(\\d+)([defgiEG])(.*)$";
33 33 static QRegExp *labelFormatMatcher = 0;
34 34 static QRegExp *labelFormatMatcherLocalized = 0;
35 35 class StaticLabelFormatMatcherDeleter
36 36 {
37 37 public:
38 38 StaticLabelFormatMatcherDeleter() {}
39 39 ~StaticLabelFormatMatcherDeleter() {
40 40 delete labelFormatMatcher;
41 41 delete labelFormatMatcherLocalized;
42 42 }
43 43 };
44 44 static StaticLabelFormatMatcherDeleter staticLabelFormatMatcherDeleter;
45 45
46 46 ChartAxisElement::ChartAxisElement(QAbstractAxis *axis, QGraphicsItem *item, bool intervalAxis)
47 47 : ChartElement(item),
48 48 m_axis(axis),
49 49 m_animation(0),
50 50 m_grid(new QGraphicsItemGroup(item)),
51 51 m_arrow(new QGraphicsItemGroup(item)),
52 52 m_shades(new QGraphicsItemGroup(item)),
53 53 m_labels(new QGraphicsItemGroup(item)),
54 54 m_title(new QGraphicsTextItem(item)),
55 55 m_intervalAxis(intervalAxis)
56 56
57 57 {
58 58 //initial initialization
59 59 m_arrow->setHandlesChildEvents(false);
60 60 m_arrow->setZValue(ChartPresenter::AxisZValue);
61 61 m_labels->setZValue(ChartPresenter::AxisZValue);
62 62 m_shades->setZValue(ChartPresenter::ShadesZValue);
63 63 m_grid->setZValue(ChartPresenter::GridZValue);
64 64 m_title->setZValue(ChartPresenter::GridZValue);
65 65 m_title->document()->setDocumentMargin(ChartPresenter::textMargin());
66 66 handleVisibleChanged(axis->isVisible());
67 67 connectSlots();
68 68
69 69 setFlag(QGraphicsItem::ItemHasNoContents, true);
70 70 }
71 71
72 72 ChartAxisElement::~ChartAxisElement()
73 73 {
74 74 }
75 75
76 76 void ChartAxisElement::connectSlots()
77 77 {
78 78 QObject::connect(axis(), SIGNAL(visibleChanged(bool)), this, SLOT(handleVisibleChanged(bool)));
79 79 QObject::connect(axis(), SIGNAL(lineVisibleChanged(bool)), this, SLOT(handleArrowVisibleChanged(bool)));
80 80 QObject::connect(axis(), SIGNAL(gridVisibleChanged(bool)), this, SLOT(handleGridVisibleChanged(bool)));
81 81 QObject::connect(axis(), SIGNAL(labelsVisibleChanged(bool)), this, SLOT(handleLabelsVisibleChanged(bool)));
82 82 QObject::connect(axis(), SIGNAL(shadesVisibleChanged(bool)), this, SLOT(handleShadesVisibleChanged(bool)));
83 83 QObject::connect(axis(), SIGNAL(labelsAngleChanged(int)), this, SLOT(handleLabelsAngleChanged(int)));
84 84 QObject::connect(axis(), SIGNAL(linePenChanged(const QPen&)), this, SLOT(handleArrowPenChanged(const QPen&)));
85 85 QObject::connect(axis(), SIGNAL(labelsPenChanged(const QPen&)), this, SLOT(handleLabelsPenChanged(const QPen&)));
86 86 QObject::connect(axis(), SIGNAL(labelsBrushChanged(const QBrush&)), this, SLOT(handleLabelsBrushChanged(const QBrush&)));
87 87 QObject::connect(axis(), SIGNAL(labelsFontChanged(const QFont&)), this, SLOT(handleLabelsFontChanged(const QFont&)));
88 88 QObject::connect(axis(), SIGNAL(gridLinePenChanged(const QPen&)), this, SLOT(handleGridPenChanged(const QPen&)));
89 89 QObject::connect(axis(), SIGNAL(shadesPenChanged(const QPen&)), this, SLOT(handleShadesPenChanged(const QPen&)));
90 90 QObject::connect(axis(), SIGNAL(shadesBrushChanged(const QBrush&)), this, SLOT(handleShadesBrushChanged(const QBrush&)));
91 91 QObject::connect(axis(), SIGNAL(titleTextChanged(const QString&)), this, SLOT(handleTitleTextChanged(const QString&)));
92 92 QObject::connect(axis(), SIGNAL(titleFontChanged(const QFont&)), this, SLOT(handleTitleFontChanged(const QFont&)));
93 93 QObject::connect(axis(), SIGNAL(titlePenChanged(const QPen&)), this, SLOT(handleTitlePenChanged(const QPen&)));
94 94 QObject::connect(axis(), SIGNAL(titleBrushChanged(const QBrush&)), this, SLOT(handleTitleBrushChanged(const QBrush&)));
95 95 QObject::connect(axis(), SIGNAL(titleVisibleChanged(bool)), this, SLOT(handleTitleVisibleChanged(bool)));
96 96 QObject::connect(axis()->d_ptr.data(), SIGNAL(rangeChanged(qreal, qreal)), this, SLOT(handleRangeChanged(qreal, qreal)));
97 97 }
98 98
99 99 void ChartAxisElement::handleArrowVisibleChanged(bool visible)
100 100 {
101 101 m_arrow->setVisible(visible);
102 102 }
103 103
104 104 void ChartAxisElement::handleGridVisibleChanged(bool visible)
105 105 {
106 106 m_grid->setVisible(visible);
107 107 }
108 108
109 109 void ChartAxisElement::handleLabelsVisibleChanged(bool visible)
110 110 {
111 111 QGraphicsLayoutItem::updateGeometry();
112 112 presenter()->layout()->invalidate();
113 113 m_labels->setVisible(visible);
114 114 }
115 115
116 116 void ChartAxisElement::handleShadesVisibleChanged(bool visible)
117 117 {
118 118 m_shades->setVisible(visible);
119 119 }
120 120
121 121 void ChartAxisElement::handleTitleVisibleChanged(bool visible)
122 122 {
123 123 QGraphicsLayoutItem::updateGeometry();
124 124 presenter()->layout()->invalidate();
125 125 m_title->setVisible(visible);
126 126 }
127 127
128 128 void ChartAxisElement::handleLabelsAngleChanged(int angle)
129 129 {
130 130 foreach (QGraphicsItem *item, m_labels->childItems())
131 131 item->setRotation(angle);
132 132
133 133 QGraphicsLayoutItem::updateGeometry();
134 134 presenter()->layout()->invalidate();
135 135 }
136 136
137 137 void ChartAxisElement::handleLabelsPenChanged(const QPen &pen)
138 138 {
139 139 Q_UNUSED(pen)
140 140 }
141 141
142 142 void ChartAxisElement::handleLabelsBrushChanged(const QBrush &brush)
143 143 {
144 144 foreach (QGraphicsItem *item, m_labels->childItems())
145 145 static_cast<QGraphicsTextItem *>(item)->setDefaultTextColor(brush.color());
146 146 }
147 147
148 148 void ChartAxisElement::handleLabelsFontChanged(const QFont &font)
149 149 {
150 150 foreach (QGraphicsItem *item, m_labels->childItems())
151 151 static_cast<QGraphicsTextItem *>(item)->setFont(font);
152 152 QGraphicsLayoutItem::updateGeometry();
153 153 presenter()->layout()->invalidate();
154 154 }
155 155
156 156 void ChartAxisElement::handleTitleTextChanged(const QString &title)
157 157 {
158 158 QGraphicsLayoutItem::updateGeometry();
159 159 presenter()->layout()->invalidate();
160 160 if (title.isEmpty() || !m_title->isVisible())
161 161 m_title->setHtml(title);
162 162 }
163 163
164 164 void ChartAxisElement::handleTitlePenChanged(const QPen &pen)
165 165 {
166 166 Q_UNUSED(pen)
167 167 }
168 168
169 169 void ChartAxisElement::handleTitleBrushChanged(const QBrush &brush)
170 170 {
171 171 m_title->setDefaultTextColor(brush.color());
172 172 }
173 173
174 174 void ChartAxisElement::handleTitleFontChanged(const QFont &font)
175 175 {
176 176 if (m_title->font() != font) {
177 177 m_title->setFont(font);
178 178 QGraphicsLayoutItem::updateGeometry();
179 179 presenter()->layout()->invalidate();
180 180 }
181 181 }
182 182
183 183 void ChartAxisElement::handleVisibleChanged(bool visible)
184 184 {
185 185 setVisible(visible);
186 186 if (!visible) {
187 187 m_grid->setVisible(visible);
188 188 m_arrow->setVisible(visible);
189 189 m_shades->setVisible(visible);
190 190 m_labels->setVisible(visible);
191 191 m_title->setVisible(visible);
192 192 } else {
193 193 m_grid->setVisible(axis()->isGridLineVisible());
194 194 m_arrow->setVisible(axis()->isLineVisible());
195 195 m_shades->setVisible(axis()->shadesVisible());
196 196 m_labels->setVisible(axis()->labelsVisible());
197 197 m_title->setVisible(axis()->isTitleVisible());
198 198 }
199 199
200 200 if (presenter()) presenter()->layout()->invalidate();
201 201 }
202 202
203 203 void ChartAxisElement::handleRangeChanged(qreal min, qreal max)
204 204 {
205 205 Q_UNUSED(min);
206 206 Q_UNUSED(max);
207 207
208 208 if (!isEmpty()) {
209 209 QVector<qreal> layout = calculateLayout();
210 210 updateLayout(layout);
211 211 QSizeF before = effectiveSizeHint(Qt::PreferredSize);
212 212 QSizeF after = sizeHint(Qt::PreferredSize);
213 213
214 214 if (before != after) {
215 215 QGraphicsLayoutItem::updateGeometry();
216 216 // We don't want to call invalidate on layout, since it will change minimum size of
217 217 // component, which we would like to avoid since it causes nasty flips when scrolling
218 218 // or zooming, instead recalculate layout and use plotArea for extra space.
219 219 presenter()->layout()->setGeometry(presenter()->layout()->geometry());
220 220 }
221 221 }
222 222 }
223 223
224 224 bool ChartAxisElement::isEmpty()
225 225 {
226 226 return axisGeometry().isEmpty()
227 227 || gridGeometry().isEmpty()
228 228 || qFuzzyCompare(min(), max());
229 229 }
230 230
231 231 qreal ChartAxisElement::min() const
232 232 {
233 233 return m_axis->d_ptr->min();
234 234 }
235 235
236 236 qreal ChartAxisElement::max() const
237 237 {
238 238 return m_axis->d_ptr->max();
239 239 }
240 240
241 241 QString ChartAxisElement::formatLabel(const QString &formatSpec, const QByteArray &array,
242 242 qreal value, int precision, const QString &preStr,
243 243 const QString &postStr) const
244 244 {
245 245 QString retVal;
246 246 if (!formatSpec.isEmpty()) {
247 247 if (formatSpec.at(0) == QLatin1Char('d')
248 248 || formatSpec.at(0) == QLatin1Char('i')
249 249 || formatSpec.at(0) == QLatin1Char('c')) {
250 250 if (presenter()->localizeNumbers())
251 251 retVal = preStr + presenter()->locale().toString(qint64(value)) + postStr;
252 252 else
253 253 retVal = QString().sprintf(array, qint64(value));
254 254 } else if (formatSpec.at(0) == QLatin1Char('u')
255 255 || formatSpec.at(0) == QLatin1Char('o')
256 256 || formatSpec.at(0) == QLatin1Char('x')
257 257 || formatSpec.at(0) == QLatin1Char('X')) {
258 258 // These formats are not supported by localized numbers
259 259 retVal = QString().sprintf(array, quint64(value));
260 260 } else if (formatSpec.at(0) == QLatin1Char('f')
261 261 || formatSpec.at(0) == QLatin1Char('F')
262 262 || formatSpec.at(0) == QLatin1Char('e')
263 263 || formatSpec.at(0) == QLatin1Char('E')
264 264 || formatSpec.at(0) == QLatin1Char('g')
265 265 || formatSpec.at(0) == QLatin1Char('G')) {
266 266 if (presenter()->localizeNumbers()) {
267 267 retVal = preStr
268 268 + presenter()->locale().toString(value, formatSpec.at(0).toLatin1(),
269 269 precision)
270 270 + postStr;
271 271 } else {
272 272 retVal = QString().sprintf(array, value);
273 273 }
274 274 }
275 275 }
276 276 return retVal;
277 277 }
278 278
279 279 QStringList ChartAxisElement::createValueLabels(qreal min, qreal max, int ticks,
280 280 const QString &format) const
281 281 {
282 282 QStringList labels;
283 283
284 284 if (max <= min || ticks < 1)
285 285 return labels;
286 286
287 287 if (format.isNull()) {
288 288 int n = qMax(int(-qFloor(log10((max - min) / (ticks - 1)))), 0) + 1;
289 289 for (int i = 0; i < ticks; i++) {
290 290 qreal value = min + (i * (max - min) / (ticks - 1));
291 291 labels << presenter()->numberToString(value, 'f', n);
292 292 }
293 293 } else {
294 294 QByteArray array = format.toLatin1();
295 295 QString formatSpec;
296 296 QString preStr;
297 297 QString postStr;
298 298 int precision = 6; // Six is the default precision in Qt API
299 299 if (presenter()->localizeNumbers()) {
300 300 if (!labelFormatMatcherLocalized)
301 301 labelFormatMatcherLocalized
302 302 = new QRegExp(QString::fromLatin1(labelFormatMatchLocalizedString));
303 303 if (labelFormatMatcherLocalized->indexIn(format, 0) != -1) {
304 304 preStr = labelFormatMatcherLocalized->cap(1);
305 305 if (!labelFormatMatcherLocalized->cap(2).isEmpty())
306 306 precision = labelFormatMatcherLocalized->cap(2).toInt();
307 307 formatSpec = labelFormatMatcherLocalized->cap(3);
308 308 postStr = labelFormatMatcherLocalized->cap(4);
309 309 }
310 310 } else {
311 311 if (!labelFormatMatcher)
312 312 labelFormatMatcher = new QRegExp(QString::fromLatin1(labelFormatMatchString));
313 313 if (labelFormatMatcher->indexIn(format, 0) != -1)
314 314 formatSpec = labelFormatMatcher->cap(1);
315 315 }
316 316 for (int i = 0; i < ticks; i++) {
317 317 qreal value = min + (i * (max - min) / (ticks - 1));
318 318 labels << formatLabel(formatSpec, array, value, precision, preStr, postStr);
319 319 }
320 320 }
321 321
322 322 return labels;
323 323 }
324 324
325 325 QStringList ChartAxisElement::createLogValueLabels(qreal min, qreal max, qreal base, int ticks,
326 326 const QString &format) const
327 327 {
328 328 QStringList labels;
329 329
330 330 if (max <= min || ticks < 1)
331 331 return labels;
332 332
333 333 int firstTick;
334 334 if (base > 1)
335 335 firstTick = ceil(log10(min) / log10(base));
336 336 else
337 337 firstTick = ceil(log10(max) / log10(base));
338 338
339 339 if (format.isNull()) {
340 340 int n = 0;
341 341 if (ticks > 1)
342 342 n = qMax(int(-qFloor(log10((max - min) / (ticks - 1)))), 0);
343 343 n++;
344 344 for (int i = firstTick; i < ticks + firstTick; i++) {
345 345 qreal value = qPow(base, i);
346 346 labels << presenter()->numberToString(value, 'f', n);
347 347 }
348 348 } else {
349 349 QByteArray array = format.toLatin1();
350 350 QString formatSpec;
351 351 QString preStr;
352 352 QString postStr;
353 353 int precision = 6; // Six is the default precision in Qt API
354 354 if (presenter()->localizeNumbers()) {
355 355 if (!labelFormatMatcherLocalized)
356 356 labelFormatMatcherLocalized =
357 357 new QRegExp(QString::fromLatin1(labelFormatMatchLocalizedString));
358 358 if (labelFormatMatcherLocalized->indexIn(format, 0) != -1) {
359 359 preStr = labelFormatMatcherLocalized->cap(1);
360 360 if (!labelFormatMatcherLocalized->cap(2).isEmpty())
361 361 precision = labelFormatMatcherLocalized->cap(2).toInt();
362 362 formatSpec = labelFormatMatcherLocalized->cap(3);
363 363 postStr = labelFormatMatcherLocalized->cap(4);
364 364 }
365 365 } else {
366 366 if (!labelFormatMatcher)
367 367 labelFormatMatcher = new QRegExp(QString::fromLatin1(labelFormatMatchString));
368 368 if (labelFormatMatcher->indexIn(format, 0) != -1)
369 369 formatSpec = labelFormatMatcher->cap(1);
370 370 }
371 371 for (int i = firstTick; i < ticks + firstTick; i++) {
372 372 qreal value = qPow(base, i);
373 373 labels << formatLabel(formatSpec, array, value, precision, preStr, postStr);
374 374 }
375 375 }
376 376
377 377 return labels;
378 378 }
379 379
380 380 QStringList ChartAxisElement::createDateTimeLabels(qreal min, qreal max,int ticks,
381 381 const QString &format) const
382 382 {
383 383 QStringList labels;
384 384
385 385 if (max <= min || ticks < 1)
386 386 return labels;
387 387
388 388 int n = qMax(int(-floor(log10((max - min) / (ticks - 1)))), 0);
389 389 n++;
390 390 for (int i = 0; i < ticks; i++) {
391 391 qreal value = min + (i * (max - min) / (ticks - 1));
392 392 labels << presenter()->locale().toString(QDateTime::fromMSecsSinceEpoch(value), format);
393 393 }
394 394 return labels;
395 395 }
396 396
397 397 void ChartAxisElement::axisSelected()
398 398 {
399 399 emit clicked();
400 400 }
401 401
402 402 #include "moc_chartaxiselement_p.cpp"
403 403
404 404 QT_CHARTS_END_NAMESPACE
General Comments 0
You need to be logged in to leave comments. Login now