##// END OF EJS Templates
Fix label clipping issues using QOpenGLWidget as ChartView viewport...
Miikka Heikkinen -
r2828:7557d1ea51b5
parent child
Show More
@@ -1,374 +1,377
1 1 /****************************************************************************
2 2 **
3 3 ** Copyright (C) 2015 The Qt Company Ltd
4 4 ** All rights reserved.
5 5 ** For any questions to The Qt Company, please use contact form at http://qt.io
6 6 **
7 7 ** This file is part of the Qt Charts module.
8 8 **
9 9 ** Licensees holding valid commercial license for Qt may use this file in
10 10 ** accordance with the Qt License Agreement provided with the Software
11 11 ** or, alternatively, in accordance with the terms contained in a written
12 12 ** agreement between you and The Qt Company.
13 13 **
14 14 ** If you have questions regarding the use of this file, please use
15 15 ** contact form at http://qt.io
16 16 **
17 17 ****************************************************************************/
18 18
19 19 #include <private/horizontalaxis_p.h>
20 20 #include <private/qabstractaxis_p.h>
21 21 #include <private/chartpresenter_p.h>
22 22 #include <QtCharts/QCategoryAxis>
23 23 #include <QtCore/QtMath>
24 24 #include <QtCore/QDebug>
25 25
26 26 QT_CHARTS_BEGIN_NAMESPACE
27 27
28 28 HorizontalAxis::HorizontalAxis(QAbstractAxis *axis, QGraphicsItem *item, bool intervalAxis)
29 29 : CartesianChartAxis(axis, item, intervalAxis)
30 30 {
31 31 }
32 32
33 33 HorizontalAxis::~HorizontalAxis()
34 34 {
35 35 }
36 36
37 37 void HorizontalAxis::updateGeometry()
38 38 {
39 39 const QVector<qreal> &layout = ChartAxisElement::layout();
40 40
41 41 if (layout.isEmpty() && axis()->type() != QAbstractAxis::AxisTypeLogValue)
42 42 return;
43 43
44 44 QStringList labelList = labels();
45 45
46 46 QList<QGraphicsItem *> labels = labelItems();
47 47 QList<QGraphicsItem *> arrow = arrowItems();
48 48 QGraphicsTextItem *title = titleItem();
49 49
50 50 Q_ASSERT(labels.size() == labelList.size());
51 51 Q_ASSERT(layout.size() == labelList.size());
52 52
53 53 const QRectF &axisRect = axisGeometry();
54 54 const QRectF &gridRect = gridGeometry();
55 55
56 56 //arrow
57 57 QGraphicsLineItem *arrowItem = static_cast<QGraphicsLineItem *>(arrow.at(0));
58 58
59 59 if (axis()->alignment() == Qt::AlignTop)
60 60 arrowItem->setLine(gridRect.left(), axisRect.bottom(), gridRect.right(), axisRect.bottom());
61 61 else if (axis()->alignment() == Qt::AlignBottom)
62 62 arrowItem->setLine(gridRect.left(), axisRect.top(), gridRect.right(), axisRect.top());
63 63
64 64 qreal width = 0;
65 65 const QLatin1String ellipsis("...");
66 66
67 67 //title
68 68 QRectF titleBoundingRect;
69 69 QString titleText = axis()->titleText();
70 70 qreal availableSpace = axisRect.height() - labelPadding();
71 71 if (!titleText.isEmpty() && titleItem()->isVisible()) {
72 72 availableSpace -= titlePadding() * 2.0;
73 73 qreal minimumLabelHeight = ChartPresenter::textBoundingRect(axis()->labelsFont(),
74 74 QStringLiteral("...")).height();
75 75 qreal titleSpace = availableSpace - minimumLabelHeight;
76 76 title->setHtml(ChartPresenter::truncatedText(axis()->titleFont(), titleText, qreal(0.0),
77 77 gridRect.width(), titleSpace,
78 78 titleBoundingRect));
79 79 title->setTextWidth(titleBoundingRect.width());
80 80
81 81 titleBoundingRect = title->boundingRect();
82 82
83 83 QPointF center = gridRect.center() - titleBoundingRect.center();
84 84 if (axis()->alignment() == Qt::AlignTop)
85 85 title->setPos(center.x(), axisRect.top() + titlePadding());
86 86 else if (axis()->alignment() == Qt::AlignBottom)
87 87 title->setPos(center.x(), axisRect.bottom() - titleBoundingRect.height() - titlePadding());
88 88
89 89 availableSpace -= titleBoundingRect.height();
90 90 }
91 91
92 92 if (layout.isEmpty() && axis()->type() == QAbstractAxis::AxisTypeLogValue)
93 93 return;
94 94
95 95 QList<QGraphicsItem *> lines = gridItems();
96 96 QList<QGraphicsItem *> shades = shadeItems();
97 97 QList<QGraphicsItem *> minorLines = minorGridItems();
98 98 QList<QGraphicsItem *> minorArrows = minorArrowItems();
99 99
100 100 for (int i = 0; i < layout.size(); ++i) {
101 101 //items
102 102 QGraphicsLineItem *gridItem = static_cast<QGraphicsLineItem*>(lines.at(i));
103 103 QGraphicsLineItem *tickItem = static_cast<QGraphicsLineItem*>(arrow.at(i + 1));
104 104 QGraphicsTextItem *labelItem = static_cast<QGraphicsTextItem *>(labels.at(i));
105 105
106 106 //grid line
107 107 if (axis()->isReverse()) {
108 108 gridItem->setLine(gridRect.right() - layout[i] + gridRect.left(), gridRect.top(),
109 109 gridRect.right() - layout[i] + gridRect.left(), gridRect.bottom());
110 110 } else {
111 111 gridItem->setLine(layout[i], gridRect.top(), layout[i], gridRect.bottom());
112 112 }
113 113
114 114 //label text wrapping
115 115 QString text;
116 116 if (axis()->isReverse() && axis()->type() != QAbstractAxis::AxisTypeCategory)
117 117 text = labelList.at(labelList.count() - i - 1);
118 118 else
119 119 text = labelList.at(i);
120 120
121 121 QRectF boundingRect;
122 122 // don't truncate empty labels
123 123 if (text.isEmpty()) {
124 124 labelItem->setHtml(text);
125 125 } else {
126 126 qreal labelWidth = axisRect.width() / layout.count() - (2 * labelPadding());
127 127 QString truncatedText = ChartPresenter::truncatedText(axis()->labelsFont(), text,
128 128 axis()->labelsAngle(),
129 129 labelWidth,
130 130 availableSpace, boundingRect);
131 131 labelItem->setTextWidth(ChartPresenter::textBoundingRect(axis()->labelsFont(),
132 132 truncatedText).width());
133 133 labelItem->setHtml(truncatedText);
134 134 }
135 135
136 136 //label transformation origin point
137 137 const QRectF& rect = labelItem->boundingRect();
138 138 QPointF center = rect.center();
139 139 labelItem->setTransformOriginPoint(center.x(), center.y());
140 140 qreal heightDiff = rect.height() - boundingRect.height();
141 141 qreal widthDiff = rect.width() - boundingRect.width();
142 142
143 143 //ticks and label position
144 QPointF labelPos;
144 145 if (axis()->alignment() == Qt::AlignTop) {
145 146 if (axis()->isReverse()) {
146 labelItem->setPos(gridRect.right() - layout[layout.size() - i - 1]
147 labelPos = QPointF(gridRect.right() - layout[layout.size() - i - 1]
147 148 + gridRect.left() - center.x(),
148 149 axisRect.bottom() - rect.height()
149 150 + (heightDiff / 2.0) - labelPadding());
150 151 tickItem->setLine(gridRect.right() + gridRect.left() - layout[i],
151 152 axisRect.bottom(),
152 153 gridRect.right() + gridRect.left() - layout[i],
153 154 axisRect.bottom() - labelPadding());
154 155 } else {
155 labelItem->setPos(layout[i] - center.x(), axisRect.bottom() - rect.height()
156 labelPos = QPointF(layout[i] - center.x(), axisRect.bottom() - rect.height()
156 157 + (heightDiff / 2.0) - labelPadding());
157 158 tickItem->setLine(layout[i], axisRect.bottom(),
158 159 layout[i], axisRect.bottom() - labelPadding());
159 160 }
160 161 } else if (axis()->alignment() == Qt::AlignBottom) {
161 162 if (axis()->isReverse()) {
162 labelItem->setPos(gridRect.right() - layout[layout.size() - i - 1]
163 labelPos = QPointF(gridRect.right() - layout[layout.size() - i - 1]
163 164 + gridRect.left() - center.x(),
164 165 axisRect.top() - (heightDiff / 2.0) + labelPadding());
165 166 tickItem->setLine(gridRect.right() + gridRect.left() - layout[i], axisRect.top(),
166 167 gridRect.right() + gridRect.left() - layout[i],
167 168 axisRect.top() + labelPadding());
168 169 } else {
169 labelItem->setPos(layout[i] - center.x(), axisRect.top() - (heightDiff / 2.0)
170 labelPos = QPointF(layout[i] - center.x(), axisRect.top() - (heightDiff / 2.0)
170 171 + labelPadding());
171 172 tickItem->setLine(layout[i], axisRect.top(),
172 173 layout[i], axisRect.top() + labelPadding());
173 174 }
174 175 }
175 176
176 177 //label in between
177 178 bool forceHide = false;
178 179 if (intervalAxis() && (i + 1) != layout.size()) {
179 180 qreal leftBound;
180 181 qreal rightBound;
181 182 if (axis()->isReverse()) {
182 183 leftBound = qMax(gridRect.right() + gridRect.left() - layout[i + 1],
183 184 gridRect.left());
184 185 rightBound = qMin(gridRect.right() + gridRect.left() - layout[i], gridRect.right());
185 186 } else {
186 187 leftBound = qMax(layout[i], gridRect.left());
187 188 rightBound = qMin(layout[i + 1], gridRect.right());
188 189 }
189 190 const qreal delta = rightBound - leftBound;
190 191 if (axis()->type() != QAbstractAxis::AxisTypeCategory) {
191 192 // Hide label in case visible part of the category at the grid edge is too narrow
192 193 if (delta < boundingRect.width()
193 194 && (leftBound == gridRect.left() || rightBound == gridRect.right())) {
194 195 forceHide = true;
195 196 } else {
196 labelItem->setPos(leftBound + (delta / 2.0) - center.x(), labelItem->pos().y());
197 labelPos.setX(leftBound + (delta / 2.0) - center.x());
197 198 }
198 199 } else {
199 200 QCategoryAxis *categoryAxis = static_cast<QCategoryAxis *>(axis());
200 201 if (categoryAxis->labelsPosition() == QCategoryAxis::AxisLabelsPositionCenter) {
201 202 if (delta < boundingRect.width()
202 203 && (leftBound == gridRect.left() || rightBound == gridRect.right())) {
203 204 forceHide = true;
204 205 } else {
205 labelItem->setPos(leftBound + (delta / 2.0) - center.x(),
206 labelItem->pos().y());
206 labelPos.setX(leftBound + (delta / 2.0) - center.x());
207 207 }
208 208 } else if (categoryAxis->labelsPosition()
209 209 == QCategoryAxis::AxisLabelsPositionOnValue) {
210 210 if (axis()->isReverse())
211 labelItem->setPos(leftBound - center.x(), labelItem->pos().y());
211 labelPos.setX(leftBound - center.x());
212 212 else
213 labelItem->setPos(rightBound - center.x(), labelItem->pos().y());
213 labelPos.setX(rightBound - center.x());
214 214 }
215 215 }
216 216 }
217 217
218 // Round to full pixel via QPoint to avoid one pixel clipping on the edge in some cases
219 labelItem->setPos(labelPos.toPoint());
220
218 221 //label overlap detection - compensate one pixel for rounding errors
219 222 if ((labelItem->pos().x() < width && labelItem->toPlainText() == ellipsis) || forceHide ||
220 223 (labelItem->pos().x() + (widthDiff / 2.0)) < (axisRect.left() - 1.0) ||
221 224 (labelItem->pos().x() + (widthDiff / 2.0) - 1.0) > axisRect.right()) {
222 225 labelItem->setVisible(false);
223 226 } else {
224 227 labelItem->setVisible(true);
225 228 width = boundingRect.width() + labelItem->pos().x();
226 229 }
227 230
228 231 //shades
229 232 QGraphicsRectItem *shadeItem = 0;
230 233 if (i == 0)
231 234 shadeItem = static_cast<QGraphicsRectItem *>(shades.at(0));
232 235 else if (i % 2)
233 236 shadeItem = static_cast<QGraphicsRectItem *>(shades.at((i / 2) + 1));
234 237 if (shadeItem) {
235 238 qreal leftBound;
236 239 qreal rightBound;
237 240 if (i == 0) {
238 241 if (axis()->isReverse()) {
239 242 leftBound = gridRect.right() + gridRect.left() - layout[i];
240 243 rightBound = gridRect.right();
241 244 } else {
242 245 leftBound = gridRect.left();
243 246 rightBound = layout[0];
244 247 }
245 248 } else {
246 249 if (axis()->isReverse()) {
247 250 rightBound = gridRect.right() + gridRect.left() - layout[i];
248 251 if (i == layout.size() - 1) {
249 252 leftBound = gridRect.left();
250 253 } else {
251 254 leftBound = qMax(gridRect.right() + gridRect.left() - layout[i + 1],
252 255 gridRect.left());
253 256 }
254 257 } else {
255 258 leftBound = layout[i];
256 259 if (i == layout.size() - 1)
257 260 rightBound = gridRect.right();
258 261 else
259 262 rightBound = qMin(layout[i + 1], gridRect.right());
260 263 }
261 264 }
262 265 if (leftBound < gridRect.left())
263 266 leftBound = gridRect.left();
264 267 if (rightBound > gridRect.right())
265 268 rightBound = gridRect.right();
266 269 shadeItem->setRect(leftBound, gridRect.top(), rightBound - leftBound,
267 270 gridRect.height());
268 271 if (shadeItem->rect().width() <= 0.0)
269 272 shadeItem->setVisible(false);
270 273 else
271 274 shadeItem->setVisible(true);
272 275 }
273 276
274 277 // check if the grid line and the axis tick should be shown
275 278 qreal x = gridItem->line().p1().x();
276 279 if (x < gridRect.left() || x > gridRect.right()) {
277 280 gridItem->setVisible(false);
278 281 tickItem->setVisible(false);
279 282 } else {
280 283 gridItem->setVisible(true);
281 284 tickItem->setVisible(true);
282 285 }
283 286
284 287 // add minor ticks
285 288 QValueAxis *valueAxis = qobject_cast<QValueAxis *>(axis());
286 289 if ((i + 1) != layout.size() && valueAxis) {
287 290 int minorTickCount = valueAxis->minorTickCount();
288 291 if (minorTickCount != 0) {
289 292 qreal minorTickDistance = (layout[i] - layout[i + 1]) / qreal(minorTickCount + 1);
290 293 for (int j = 0; j < minorTickCount; j++) {
291 294 QGraphicsLineItem *minorGridItem =
292 295 static_cast<QGraphicsLineItem *>(minorLines.at(i * minorTickCount + j));
293 296 QGraphicsLineItem *minorArrowItem =
294 297 static_cast<QGraphicsLineItem *>(minorArrows.at(i * minorTickCount + j));
295 298 if (i == 0) {
296 299 minorGridItem->setLine(gridRect.left() - minorTickDistance * qreal(j + 1),
297 300 gridRect.top(),
298 301 gridRect.left() - minorTickDistance * qreal(j + 1),
299 302 gridRect.bottom());
300 303 } else {
301 304 minorGridItem->setLine(gridItem->line().p1().x()
302 305 - minorTickDistance * qreal(j + 1),
303 306 gridRect.top(),
304 307 gridItem->line().p1().x()
305 308 - minorTickDistance * qreal(j + 1),
306 309 gridRect.bottom());
307 310 }
308 311 if (axis()->alignment() == Qt::AlignTop) {
309 312 minorArrowItem->setLine(minorGridItem->line().p1().x(),
310 313 axisRect.bottom(),
311 314 minorGridItem->line().p1().x(),
312 315 axisRect.bottom() - labelPadding() / 2);
313 316 } else if (axis()->alignment() == Qt::AlignBottom){
314 317 minorArrowItem->setLine(minorGridItem->line().p1().x(),
315 318 axisRect.top(),
316 319 minorGridItem->line().p1().x(),
317 320 axisRect.top() + labelPadding() / 2);
318 321 }
319 322
320 323 // check if the minor grid line and the axis tick should be shown
321 324 qreal minorXPos = minorGridItem->line().p1().x();
322 325 if (minorXPos < gridRect.left() || minorXPos > gridRect.right()) {
323 326 minorGridItem->setVisible(false);
324 327 minorArrowItem->setVisible(false);
325 328 } else {
326 329 minorGridItem->setVisible(true);
327 330 minorArrowItem->setVisible(true);
328 331 }
329 332 }
330 333 }
331 334 }
332 335 }
333 336
334 337 //begin/end grid line in case labels between
335 338 if (intervalAxis()) {
336 339 QGraphicsLineItem *gridLine;
337 340 gridLine = static_cast<QGraphicsLineItem *>(lines.at(layout.size()));
338 341 gridLine->setLine(gridRect.right(), gridRect.top(), gridRect.right(), gridRect.bottom());
339 342 gridLine->setVisible(true);
340 343 gridLine = static_cast<QGraphicsLineItem*>(lines.at(layout.size()+1));
341 344 gridLine->setLine(gridRect.left(), gridRect.top(), gridRect.left(), gridRect.bottom());
342 345 gridLine->setVisible(true);
343 346 }
344 347 }
345 348
346 349 QSizeF HorizontalAxis::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
347 350 {
348 351 Q_UNUSED(constraint);
349 352 QSizeF sh(0,0);
350 353
351 354 if (axis()->titleText().isEmpty() || !titleItem()->isVisible())
352 355 return sh;
353 356
354 357 switch (which) {
355 358 case Qt::MinimumSize: {
356 359 QRectF titleRect = ChartPresenter::textBoundingRect(axis()->titleFont(),
357 360 QStringLiteral("..."));
358 361 sh = QSizeF(titleRect.width(), titleRect.height() + (titlePadding() * 2.0));
359 362 break;
360 363 }
361 364 case Qt::MaximumSize:
362 365 case Qt::PreferredSize: {
363 366 QRectF titleRect = ChartPresenter::textBoundingRect(axis()->titleFont(), axis()->titleText());
364 367 sh = QSizeF(titleRect.width(), titleRect.height() + (titlePadding() * 2.0));
365 368 break;
366 369 }
367 370 default:
368 371 break;
369 372 }
370 373
371 374 return sh;
372 375 }
373 376
374 377 QT_CHARTS_END_NAMESPACE
@@ -1,390 +1,392
1 1 /****************************************************************************
2 2 **
3 3 ** Copyright (C) 2015 The Qt Company Ltd
4 4 ** All rights reserved.
5 5 ** For any questions to The Qt Company, please use contact form at http://qt.io
6 6 **
7 7 ** This file is part of the Qt Charts module.
8 8 **
9 9 ** Licensees holding valid commercial license for Qt may use this file in
10 10 ** accordance with the Qt License Agreement provided with the Software
11 11 ** or, alternatively, in accordance with the terms contained in a written
12 12 ** agreement between you and The Qt Company.
13 13 **
14 14 ** If you have questions regarding the use of this file, please use
15 15 ** contact form at http://qt.io
16 16 **
17 17 ****************************************************************************/
18 18
19 19 #include <private/verticalaxis_p.h>
20 20 #include <QtCharts/QAbstractAxis>
21 21 #include <private/chartpresenter_p.h>
22 22 #include <QtCharts/QCategoryAxis>
23 23 #include <QtCore/QDebug>
24 24
25 25 QT_CHARTS_BEGIN_NAMESPACE
26 26
27 27 VerticalAxis::VerticalAxis(QAbstractAxis *axis, QGraphicsItem *item, bool intervalAxis)
28 28 : CartesianChartAxis(axis, item, intervalAxis)
29 29 {
30 30 }
31 31
32 32 VerticalAxis::~VerticalAxis()
33 33 {
34 34 }
35 35
36 36 void VerticalAxis::updateGeometry()
37 37 {
38 38 const QVector<qreal> &layout = ChartAxisElement::layout();
39 39
40 40 if (layout.isEmpty() && axis()->type() != QAbstractAxis::AxisTypeLogValue)
41 41 return;
42 42
43 43 QStringList labelList = labels();
44 44
45 45 QList<QGraphicsItem *> labels = labelItems();
46 46 QList<QGraphicsItem *> arrow = arrowItems();
47 47 QGraphicsTextItem *title = titleItem();
48 48
49 49 Q_ASSERT(labels.size() == labelList.size());
50 50 Q_ASSERT(layout.size() == labelList.size());
51 51
52 52 const QRectF &axisRect = axisGeometry();
53 53 const QRectF &gridRect = gridGeometry();
54 54
55 55 qreal height = axisRect.bottom();
56 56
57 57 //arrow
58 58 QGraphicsLineItem *arrowItem = static_cast<QGraphicsLineItem*>(arrow.at(0));
59 59
60 60 //arrow position
61 61 if (axis()->alignment() == Qt::AlignLeft)
62 62 arrowItem->setLine(axisRect.right(), gridRect.top(), axisRect.right(), gridRect.bottom());
63 63 else if (axis()->alignment() == Qt::AlignRight)
64 64 arrowItem->setLine(axisRect.left(), gridRect.top(), axisRect.left(), gridRect.bottom());
65 65
66 66 //title
67 67 QRectF titleBoundingRect;
68 68 QString titleText = axis()->titleText();
69 69 qreal availableSpace = axisRect.width() - labelPadding();
70 70 if (!titleText.isEmpty() && titleItem()->isVisible()) {
71 71 availableSpace -= titlePadding() * 2.0;
72 72 qreal minimumLabelWidth = ChartPresenter::textBoundingRect(axis()->labelsFont(),
73 73 QStringLiteral("...")).width();
74 74 qreal titleSpace = availableSpace - minimumLabelWidth;
75 75 title->setHtml(ChartPresenter::truncatedText(axis()->titleFont(), titleText, qreal(90.0),
76 76 titleSpace, gridRect.height(),
77 77 titleBoundingRect));
78 78 title->setTextWidth(titleBoundingRect.height());
79 79
80 80 titleBoundingRect = title->boundingRect();
81 81
82 82 QPointF center = gridRect.center() - titleBoundingRect.center();
83 83 if (axis()->alignment() == Qt::AlignLeft)
84 84 title->setPos(axisRect.left() - titleBoundingRect.width() / 2.0 + titleBoundingRect.height() / 2.0 + titlePadding(), center.y());
85 85 else if (axis()->alignment() == Qt::AlignRight)
86 86 title->setPos(axisRect.right() - titleBoundingRect.width() / 2.0 - titleBoundingRect.height() / 2.0 - titlePadding(), center.y());
87 87
88 88 title->setTransformOriginPoint(titleBoundingRect.center());
89 89 title->setRotation(270);
90 90
91 91 availableSpace -= titleBoundingRect.height();
92 92 }
93 93
94 94 if (layout.isEmpty() && axis()->type() == QAbstractAxis::AxisTypeLogValue)
95 95 return;
96 96
97 97 QList<QGraphicsItem *> lines = gridItems();
98 98 QList<QGraphicsItem *> shades = shadeItems();
99 99 QList<QGraphicsItem *> minorLines = minorGridItems();
100 100 QList<QGraphicsItem *> minorArrows = minorArrowItems();
101 101
102 102 for (int i = 0; i < layout.size(); ++i) {
103 103 //items
104 104 QGraphicsLineItem *gridItem = static_cast<QGraphicsLineItem *>(lines.at(i));
105 105 QGraphicsLineItem *tickItem = static_cast<QGraphicsLineItem *>(arrow.at(i + 1));
106 106 QGraphicsTextItem *labelItem = static_cast<QGraphicsTextItem *>(labels.at(i));
107 107
108 108 //grid line
109 109 if (axis()->isReverse()) {
110 110 gridItem->setLine(gridRect.left(), gridRect.top() + gridRect.bottom() - layout[i],
111 111 gridRect.right(), gridRect.top() + gridRect.bottom() - layout[i]);
112 112 } else {
113 113 gridItem->setLine(gridRect.left(), layout[i], gridRect.right(), layout[i]);
114 114 }
115 115
116 116 //label text wrapping
117 117 QString text;
118 118 if (axis()->isReverse() && axis()->type() != QAbstractAxis::AxisTypeCategory)
119 119 text = labelList.at(labelList.count() - i - 1);
120 120 else
121 121 text = labelList.at(i);
122 122
123 123 QRectF boundingRect;
124 124 // don't truncate empty labels
125 125 if (text.isEmpty()) {
126 126 labelItem->setHtml(text);
127 127 } else {
128 128 qreal labelHeight = (axisRect.height() / layout.count()) - (2 * labelPadding());
129 129 QString truncatedText = ChartPresenter::truncatedText(axis()->labelsFont(), text,
130 130 axis()->labelsAngle(),
131 131 availableSpace,
132 132 labelHeight, boundingRect);
133 133 labelItem->setTextWidth(ChartPresenter::textBoundingRect(axis()->labelsFont(),
134 134 truncatedText).width());
135 135 labelItem->setHtml(truncatedText);
136 136 }
137 137
138 138 //label transformation origin point
139 139 const QRectF &rect = labelItem->boundingRect();
140 140 QPointF center = rect.center();
141 141 labelItem->setTransformOriginPoint(center.x(), center.y());
142 142 qreal widthDiff = rect.width() - boundingRect.width();
143 143 qreal heightDiff = rect.height() - boundingRect.height();
144 144
145 145 //ticks and label position
146 QPointF labelPos;
146 147 if (axis()->alignment() == Qt::AlignLeft) {
147 148 if (axis()->isReverse()) {
148 labelItem->setPos(axisRect.right() - rect.width() + (widthDiff / 2.0)
149 labelPos = QPointF(axisRect.right() - rect.width() + (widthDiff / 2.0)
149 150 - labelPadding(),
150 151 gridRect.top() + gridRect.bottom()
151 152 - layout[layout.size() - i - 1] - center.y());
152 153 tickItem->setLine(axisRect.right() - labelPadding(),
153 154 gridRect.top() + gridRect.bottom() - layout[i],
154 155 axisRect.right(),
155 156 gridRect.top() + gridRect.bottom() - layout[i]);
156 157 } else {
157 labelItem->setPos(axisRect.right() - rect.width() + (widthDiff / 2.0)
158 labelPos = QPointF(axisRect.right() - rect.width() + (widthDiff / 2.0)
158 159 - labelPadding(),
159 160 layout[i] - center.y());
160 161 tickItem->setLine(axisRect.right() - labelPadding(), layout[i],
161 162 axisRect.right(), layout[i]);
162 163 }
163 164 } else if (axis()->alignment() == Qt::AlignRight) {
164 165 if (axis()->isReverse()) {
165 166 tickItem->setLine(axisRect.left(),
166 167 gridRect.top() + gridRect.bottom() - layout[i],
167 168 axisRect.left() + labelPadding(),
168 169 gridRect.top() + gridRect.bottom() - layout[i]);
169 labelItem->setPos(axisRect.left() + labelPadding() - (widthDiff / 2.0),
170 labelPos = QPointF(axisRect.left() + labelPadding() - (widthDiff / 2.0),
170 171 gridRect.top() + gridRect.bottom()
171 172 - layout[layout.size() - i - 1] - center.y());
172 173 } else {
173 labelItem->setPos(axisRect.left() + labelPadding() - (widthDiff / 2.0),
174 labelPos = QPointF(axisRect.left() + labelPadding() - (widthDiff / 2.0),
174 175 layout[i] - center.y());
175 176 tickItem->setLine(axisRect.left(), layout[i],
176 177 axisRect.left() + labelPadding(), layout[i]);
177 178 }
178 179 }
179 180
180 181 //label in between
181 182 bool forceHide = false;
182 183 bool labelOnValue = false;
183 184 if (intervalAxis() && (i + 1) != layout.size()) {
184 185 qreal lowerBound;
185 186 qreal upperBound;
186 187 if (axis()->isReverse()) {
187 188 lowerBound = qMax(gridRect.top() + gridRect.bottom() - layout[i + 1],
188 189 gridRect.top());
189 190 upperBound = qMin(gridRect.top() + gridRect.bottom() - layout[i],
190 191 gridRect.bottom());
191 192 } else {
192 193 lowerBound = qMin(layout[i], gridRect.bottom());
193 194 upperBound = qMax(layout[i + 1], gridRect.top());
194 195 }
195 196 const qreal delta = lowerBound - upperBound;
196 197 if (axis()->type() != QAbstractAxis::AxisTypeCategory) {
197 198 // Hide label in case visible part of the category at the grid edge is too narrow
198 199 if (delta < boundingRect.height()
199 200 && (lowerBound == gridRect.bottom() || upperBound == gridRect.top())) {
200 201 forceHide = true;
201 202 } else {
202 labelItem->setPos(labelItem->pos().x(),
203 lowerBound - (delta / 2.0) - center.y());
203 labelPos.setY(lowerBound - (delta / 2.0) - center.y());
204 204 }
205 205 } else {
206 206 QCategoryAxis *categoryAxis = static_cast<QCategoryAxis *>(axis());
207 207 if (categoryAxis->labelsPosition() == QCategoryAxis::AxisLabelsPositionCenter) {
208 208 if (delta < boundingRect.height()
209 209 && (lowerBound == gridRect.bottom() || upperBound == gridRect.top())) {
210 210 forceHide = true;
211 211 } else {
212 labelItem->setPos(labelItem->pos().x(),
213 lowerBound - (delta / 2.0) - center.y());
212 labelPos.setY(lowerBound - (delta / 2.0) - center.y());
214 213 }
215 214 } else if (categoryAxis->labelsPosition()
216 215 == QCategoryAxis::AxisLabelsPositionOnValue) {
217 216 labelOnValue = true;
218 217 if (axis()->isReverse()) {
219 labelItem->setPos(labelItem->pos().x(), gridRect.top() + gridRect.bottom()
218 labelPos.setY(gridRect.top() + gridRect.bottom()
220 219 - layout[i + 1] - center.y());
221 220 } else {
222 labelItem->setPos(labelItem->pos().x(), upperBound - center.y());
221 labelPos.setY(upperBound - center.y());
223 222 }
224 223 }
225 224 }
226 225 }
227 226
227 // Round to full pixel via QPoint to avoid one pixel clipping on the edge in some cases
228 labelItem->setPos(labelPos.toPoint());
229
228 230 //label overlap detection - compensate one pixel for rounding errors
229 231 if (axis()->isReverse()) {
230 232 if (forceHide)
231 233 labelItem->setVisible(false);
232 234 } else if (labelItem->pos().y() + boundingRect.height() > height || forceHide ||
233 235 ((labelItem->pos().y() + (heightDiff / 2.0) - 1.0) > axisRect.bottom()
234 236 && !labelOnValue) ||
235 237 (labelItem->pos().y() + (heightDiff / 2.0) < (axisRect.top() - 1.0) && !labelOnValue)) {
236 238 labelItem->setVisible(false);
237 239 }
238 240 else {
239 241 labelItem->setVisible(true);
240 242 height=labelItem->pos().y();
241 243 }
242 244
243 245 //shades
244 246 QGraphicsRectItem *shadeItem = 0;
245 247 if (i == 0)
246 248 shadeItem = static_cast<QGraphicsRectItem *>(shades.at(0));
247 249 else if (i % 2)
248 250 shadeItem = static_cast<QGraphicsRectItem *>(shades.at((i / 2) + 1));
249 251 if (shadeItem) {
250 252 qreal lowerBound;
251 253 qreal upperBound;
252 254 if (i == 0) {
253 255 if (axis()->isReverse()) {
254 256 upperBound = gridRect.top();
255 257 lowerBound = gridRect.top() + gridRect.bottom() - layout[i];
256 258 } else {
257 259 lowerBound = gridRect.bottom();
258 260 upperBound = layout[0];
259 261 }
260 262 } else {
261 263 if (axis()->isReverse()) {
262 264 upperBound = gridRect.top() + gridRect.bottom() - layout[i];
263 265 if (i == layout.size() - 1) {
264 266 lowerBound = gridRect.bottom();
265 267 } else {
266 268 lowerBound = qMax(gridRect.top() + gridRect.bottom() - layout[i + 1],
267 269 gridRect.top());
268 270 }
269 271 } else {
270 272 lowerBound = layout[i];
271 273 if (i == layout.size() - 1)
272 274 upperBound = gridRect.top();
273 275 else
274 276 upperBound = qMax(layout[i + 1], gridRect.top());
275 277 }
276 278
277 279 }
278 280 if (lowerBound > gridRect.bottom())
279 281 lowerBound = gridRect.bottom();
280 282 if (upperBound < gridRect.top())
281 283 upperBound = gridRect.top();
282 284 shadeItem->setRect(gridRect.left(), upperBound, gridRect.width(),
283 285 lowerBound - upperBound);
284 286 if (shadeItem->rect().height() <= 0.0)
285 287 shadeItem->setVisible(false);
286 288 else
287 289 shadeItem->setVisible(true);
288 290 }
289 291
290 292 // check if the grid line and the axis tick should be shown
291 293 qreal y = gridItem->line().p1().y();
292 294 if ((y < gridRect.top() || y > gridRect.bottom()))
293 295 {
294 296 gridItem->setVisible(false);
295 297 tickItem->setVisible(false);
296 298 }else{
297 299 gridItem->setVisible(true);
298 300 tickItem->setVisible(true);
299 301 }
300 302
301 303 // add minor ticks
302 304 QValueAxis *valueAxis = qobject_cast<QValueAxis *>(axis());
303 305 if ((i + 1) != layout.size() && valueAxis) {
304 306 int minorTickCount = valueAxis->minorTickCount();
305 307 if (minorTickCount != 0) {
306 308 qreal minorTickDistance = (layout[i] - layout[i + 1]) / qreal(minorTickCount + 1);
307 309 for (int j = 0; j < minorTickCount; j++) {
308 310 QGraphicsLineItem *minorGridItem =
309 311 static_cast<QGraphicsLineItem *>(minorLines.at(i * minorTickCount + j));
310 312 QGraphicsLineItem *minorArrowItem =
311 313 static_cast<QGraphicsLineItem *>(minorArrows.at(i * minorTickCount + j));
312 314 if (i == 0) {
313 315 minorGridItem->setLine(gridRect.left(),
314 316 gridRect.bottom() - minorTickDistance * qreal(j + 1),
315 317 gridRect.right(),
316 318 gridRect.bottom() - minorTickDistance * qreal(j + 1));
317 319 } else {
318 320 minorGridItem->setLine(gridRect.left(),
319 321 gridItem->line().p1().y()
320 322 - minorTickDistance * qreal(j + 1),
321 323 gridRect.right(),
322 324 gridItem->line().p1().y()
323 325 - minorTickDistance * qreal(j + 1));
324 326 }
325 327 if (axis()->alignment() == Qt::AlignLeft) {
326 328 minorArrowItem->setLine(gridRect.left() - labelPadding() / 2,
327 329 minorGridItem->line().p1().y(),
328 330 gridRect.left(),
329 331 minorGridItem->line().p1().y());
330 332 } else if (axis()->alignment() == Qt::AlignRight){
331 333 minorArrowItem->setLine(gridRect.right(),
332 334 minorGridItem->line().p1().y(),
333 335 gridRect.right() + labelPadding() / 2,
334 336 minorGridItem->line().p1().y());
335 337 }
336 338
337 339 // check if the minor grid line and the axis tick should be shown
338 340 qreal minorYPos = minorGridItem->line().p1().y();
339 341 if (minorYPos < gridRect.top() || minorYPos > gridRect.bottom()) {
340 342 minorGridItem->setVisible(false);
341 343 minorArrowItem->setVisible(false);
342 344 } else {
343 345 minorGridItem->setVisible(true);
344 346 minorArrowItem->setVisible(true);
345 347 }
346 348 }
347 349 }
348 350 }
349 351 }
350 352 //begin/end grid line in case labels between
351 353 if (intervalAxis()) {
352 354 QGraphicsLineItem *gridLine;
353 355 gridLine = static_cast<QGraphicsLineItem *>(lines.at(layout.size()));
354 356 gridLine->setLine(gridRect.left(), gridRect.top(), gridRect.right(), gridRect.top());
355 357 gridLine->setVisible(true);
356 358 gridLine = static_cast<QGraphicsLineItem*>(lines.at(layout.size() + 1));
357 359 gridLine->setLine(gridRect.left(), gridRect.bottom(), gridRect.right(), gridRect.bottom());
358 360 gridLine->setVisible(true);
359 361 }
360 362 }
361 363
362 364 QSizeF VerticalAxis::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
363 365 {
364 366 Q_UNUSED(constraint);
365 367 QSizeF sh(0, 0);
366 368
367 369 if (axis()->titleText().isEmpty() || !titleItem()->isVisible())
368 370 return sh;
369 371
370 372 switch (which) {
371 373 case Qt::MinimumSize: {
372 374 QRectF titleRect = ChartPresenter::textBoundingRect(axis()->titleFont(),
373 375 QStringLiteral("..."));
374 376 sh = QSizeF(titleRect.height() + (titlePadding() * 2.0), titleRect.width());
375 377 break;
376 378 }
377 379 case Qt::MaximumSize:
378 380 case Qt::PreferredSize: {
379 381 QRectF titleRect = ChartPresenter::textBoundingRect(axis()->titleFont(), axis()->titleText());
380 382 sh = QSizeF(titleRect.height() + (titlePadding() * 2.0), titleRect.width());
381 383 break;
382 384 }
383 385 default:
384 386 break;
385 387 }
386 388
387 389 return sh;
388 390 }
389 391
390 392 QT_CHARTS_END_NAMESPACE
@@ -1,201 +1,203
1 1 /****************************************************************************
2 2 **
3 3 ** Copyright (C) 2015 The Qt Company Ltd
4 4 ** All rights reserved.
5 5 ** For any questions to The Qt Company, please use contact form at http://qt.io
6 6 **
7 7 ** This file is part of the Qt Charts module.
8 8 **
9 9 ** Licensees holding valid commercial license for Qt may use this file in
10 10 ** accordance with the Qt License Agreement provided with the Software
11 11 ** or, alternatively, in accordance with the terms contained in a written
12 12 ** agreement between you and The Qt Company.
13 13 **
14 14 ** If you have questions regarding the use of this file, please use
15 15 ** contact form at http://qt.io
16 16 **
17 17 ****************************************************************************/
18 18
19 19 #include <private/abstractchartlayout_p.h>
20 20 #include <private/chartpresenter_p.h>
21 21 #include <private/qlegend_p.h>
22 22 #include <private/chartaxiselement_p.h>
23 23 #include <private/charttitle_p.h>
24 24 #include <private/chartbackground_p.h>
25 25 #include <QtCore/QDebug>
26 26
27 27 QT_CHARTS_BEGIN_NAMESPACE
28 28
29 29 static const qreal golden_ratio = 0.4;
30 30
31 31 AbstractChartLayout::AbstractChartLayout(ChartPresenter *presenter)
32 32 : m_presenter(presenter),
33 33 m_margins(20, 20, 20, 20),
34 34 m_minChartRect(0, 0, 200, 200)
35 35 {
36 36 }
37 37
38 38 AbstractChartLayout::~AbstractChartLayout()
39 39 {
40 40 }
41 41
42 42 void AbstractChartLayout::setGeometry(const QRectF &rect)
43 43 {
44 44 if (!rect.isValid())
45 45 return;
46 46
47 47 if (m_presenter->chart()->isVisible()) {
48 48 QList<ChartAxisElement *> axes = m_presenter->axisItems();
49 49 ChartTitle *title = m_presenter->titleElement();
50 50 QLegend *legend = m_presenter->legend();
51 51 ChartBackground *background = m_presenter->backgroundElement();
52 52
53 53 QRectF contentGeometry = calculateBackgroundGeometry(rect, background);
54 54
55 55 contentGeometry = calculateContentGeometry(contentGeometry);
56 56
57 57 if (title && title->isVisible() && !title->text().isEmpty())
58 58 contentGeometry = calculateTitleGeometry(contentGeometry, title);
59 59
60 60 if (legend->isAttachedToChart() && legend->isVisible())
61 61 contentGeometry = calculateLegendGeometry(contentGeometry, legend);
62 62
63 63 contentGeometry = calculateAxisGeometry(contentGeometry, axes);
64 64
65 65 m_presenter->setGeometry(contentGeometry);
66 66 if (m_presenter->chart()->chartType() == QChart::ChartTypeCartesian)
67 67 static_cast<QGraphicsRectItem *>(m_presenter->plotAreaElement())->setRect(contentGeometry);
68 68 else
69 69 static_cast<QGraphicsEllipseItem *>(m_presenter->plotAreaElement())->setRect(contentGeometry);
70 70 }
71 71
72 72 QGraphicsLayout::setGeometry(rect);
73 73 }
74 74
75 75 QRectF AbstractChartLayout::calculateContentGeometry(const QRectF &geometry) const
76 76 {
77 77 return geometry.adjusted(m_margins.left(), m_margins.top(), -m_margins.right(), -m_margins.bottom());
78 78 }
79 79
80 80 QRectF AbstractChartLayout::calculateContentMinimum(const QRectF &minimum) const
81 81 {
82 82 return minimum.adjusted(0, 0, m_margins.left() + m_margins.right(), m_margins.top() + m_margins.bottom());
83 83 }
84 84
85 85
86 86 QRectF AbstractChartLayout::calculateBackgroundGeometry(const QRectF &geometry, ChartBackground *background) const
87 87 {
88 88 qreal left;
89 89 qreal top;
90 90 qreal right;
91 91 qreal bottom;
92 92 getContentsMargins(&left, &top, &right, &bottom);
93 93 QRectF backgroundGeometry = geometry.adjusted(left, top, -right, -bottom);
94 94 if (background)
95 95 background->setRect(backgroundGeometry);
96 96 return backgroundGeometry;
97 97 }
98 98
99 99 QRectF AbstractChartLayout::calculateBackgroundMinimum(const QRectF &minimum) const
100 100 {
101 101 qreal left;
102 102 qreal top;
103 103 qreal right;
104 104 qreal bottom;
105 105 getContentsMargins(&left, &top, &right, &bottom);
106 106 return minimum.adjusted(0, 0, left + right, top + bottom);
107 107 }
108 108
109 109 QRectF AbstractChartLayout::calculateLegendGeometry(const QRectF &geometry, QLegend *legend) const
110 110 {
111 111 QSizeF size = legend->effectiveSizeHint(Qt::PreferredSize, QSizeF(-1, -1));
112 112 QRectF legendRect;
113 113 QRectF result;
114 114
115 115 switch (legend->alignment()) {
116 116 case Qt::AlignTop: {
117 117 legendRect = QRectF(geometry.topLeft(), QSizeF(geometry.width(), size.height()));
118 118 result = geometry.adjusted(0, legendRect.height(), 0, 0);
119 119 break;
120 120 }
121 121 case Qt::AlignBottom: {
122 122 legendRect = QRectF(QPointF(geometry.left(), geometry.bottom() - size.height()), QSizeF(geometry.width(), size.height()));
123 123 result = geometry.adjusted(0, 0, 0, -legendRect.height());
124 124 break;
125 125 }
126 126 case Qt::AlignLeft: {
127 127 qreal width = qMin(size.width(), geometry.width() * golden_ratio);
128 128 legendRect = QRectF(geometry.topLeft(), QSizeF(width, geometry.height()));
129 129 result = geometry.adjusted(width, 0, 0, 0);
130 130 break;
131 131 }
132 132 case Qt::AlignRight: {
133 133 qreal width = qMin(size.width(), geometry.width() * golden_ratio);
134 134 legendRect = QRectF(QPointF(geometry.right() - width, geometry.top()), QSizeF(width, geometry.height()));
135 135 result = geometry.adjusted(0, 0, -width, 0);
136 136 break;
137 137 }
138 138 default: {
139 139 legendRect = QRectF(0, 0, 0, 0);
140 140 result = geometry;
141 141 break;
142 142 }
143 143 }
144 144
145 145 legend->setGeometry(legendRect);
146 146
147 147 return result;
148 148 }
149 149
150 150 QRectF AbstractChartLayout::calculateLegendMinimum(const QRectF &geometry, QLegend *legend) const
151 151 {
152 152 QSizeF minSize = legend->effectiveSizeHint(Qt::MinimumSize, QSizeF(-1, -1));
153 153 return geometry.adjusted(0, 0, minSize.width(), minSize.height());
154 154 }
155 155
156 156 QRectF AbstractChartLayout::calculateTitleGeometry(const QRectF &geometry, ChartTitle *title) const
157 157 {
158 158 title->setGeometry(geometry);
159 QPointF center = geometry.center() - title->boundingRect().center();
159 // Round to full pixel via QPoint to avoid one pixel clipping on the edge in some cases
160 QPointF center((geometry.center() - title->boundingRect().center()).toPoint());
161
160 162 title->setPos(center.x(), title->pos().y());
161 163 return geometry.adjusted(0, title->boundingRect().height()+1, 0, 0);
162 164 }
163 165
164 166 QRectF AbstractChartLayout::calculateTitleMinimum(const QRectF &minimum, ChartTitle *title) const
165 167 {
166 168 QSizeF min = title->sizeHint(Qt::MinimumSize);
167 169 return minimum.adjusted(0, 0, min.width(), min.height());
168 170 }
169 171
170 172 QSizeF AbstractChartLayout::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
171 173 {
172 174 Q_UNUSED(constraint);
173 175 if (which == Qt::MinimumSize) {
174 176 QList<ChartAxisElement *> axes = m_presenter->axisItems();
175 177 ChartTitle *title = m_presenter->titleElement();
176 178 QLegend *legend = m_presenter->legend();
177 179 QRectF minimumRect(0, 0, 0, 0);
178 180 minimumRect = calculateBackgroundMinimum(minimumRect);
179 181 minimumRect = calculateContentMinimum(minimumRect);
180 182 minimumRect = calculateTitleMinimum(minimumRect, title);
181 183 minimumRect = calculateLegendMinimum(minimumRect, legend);
182 184 minimumRect = calculateAxisMinimum(minimumRect, axes);
183 185 return minimumRect.united(m_minChartRect).size().toSize();
184 186 }
185 187 return QSize(-1, -1);
186 188 }
187 189
188 190 void AbstractChartLayout::setMargins(const QMargins &margins)
189 191 {
190 192 if (m_margins != margins) {
191 193 m_margins = margins;
192 194 updateGeometry();
193 195 }
194 196 }
195 197
196 198 QMargins AbstractChartLayout::margins() const
197 199 {
198 200 return m_margins;
199 201 }
200 202
201 203 QT_CHARTS_END_NAMESPACE
@@ -1,504 +1,510
1 1 /****************************************************************************
2 2 **
3 3 ** Copyright (C) 2015 The Qt Company Ltd
4 4 ** All rights reserved.
5 5 ** For any questions to The Qt Company, please use contact form at http://qt.io
6 6 **
7 7 ** This file is part of the Qt Charts module.
8 8 **
9 9 ** Licensees holding valid commercial license for Qt may use this file in
10 10 ** accordance with the Qt License Agreement provided with the Software
11 11 ** or, alternatively, in accordance with the terms contained in a written
12 12 ** agreement between you and The Qt Company.
13 13 **
14 14 ** If you have questions regarding the use of this file, please use
15 15 ** contact form at http://qt.io
16 16 **
17 17 ****************************************************************************/
18 18
19 19 #include <private/legendlayout_p.h>
20 20 #include <private/chartpresenter_p.h>
21 21 #include <private/qlegend_p.h>
22 22 #include <private/abstractchartlayout_p.h>
23 23
24 24 #include <private/qlegendmarker_p.h>
25 25 #include <private/legendmarkeritem_p.h>
26 26 #include <QtCharts/QLegendMarker>
27 27
28 28 QT_CHARTS_BEGIN_NAMESPACE
29 29
30 30 LegendLayout::LegendLayout(QLegend *legend)
31 31 : m_legend(legend),
32 32 m_offsetX(0),
33 33 m_offsetY(0)
34 34 {
35 35
36 36 }
37 37
38 38 LegendLayout::~LegendLayout()
39 39 {
40 40
41 41 }
42 42
43 43 void LegendLayout::setOffset(qreal x, qreal y)
44 44 {
45 45 bool scrollHorizontal = true;
46 46 switch (m_legend->alignment()) {
47 47 case Qt::AlignTop:
48 48 case Qt::AlignBottom:
49 49 scrollHorizontal = true;
50 50 break;
51 51 case Qt::AlignLeft:
52 52 case Qt::AlignRight:
53 53 scrollHorizontal = false;
54 54 break;
55 55 }
56 56
57 57 // If detached, the scrolling direction is vertical instead of horizontal and vice versa.
58 58 if (!m_legend->isAttachedToChart())
59 59 scrollHorizontal = !scrollHorizontal;
60 60
61 61 QRectF boundingRect = geometry();
62 62 qreal left, top, right, bottom;
63 63 getContentsMargins(&left, &top, &right, &bottom);
64 64 boundingRect.adjust(left, top, -right, -bottom);
65 65
66 66 // Limit offset between m_minOffset and m_maxOffset
67 67 if (scrollHorizontal) {
68 68 if (m_width <= boundingRect.width())
69 69 return;
70 70
71 71 if (x != m_offsetX) {
72 72 m_offsetX = qBound(m_minOffsetX, x, m_maxOffsetX);
73 73 m_legend->d_ptr->items()->setPos(-m_offsetX, boundingRect.top());
74 74 }
75 75 } else {
76 76 if (m_height <= boundingRect.height())
77 77 return;
78 78
79 79 if (y != m_offsetY) {
80 80 m_offsetY = qBound(m_minOffsetY, y, m_maxOffsetY);
81 81 m_legend->d_ptr->items()->setPos(boundingRect.left(), -m_offsetY);
82 82 }
83 83 }
84 84 }
85 85
86 86 QPointF LegendLayout::offset() const
87 87 {
88 88 return QPointF(m_offsetX, m_offsetY);
89 89 }
90 90
91 91 void LegendLayout::invalidate()
92 92 {
93 93 QGraphicsLayout::invalidate();
94 94 if (m_legend->isAttachedToChart())
95 95 m_legend->d_ptr->m_presenter->layout()->invalidate();
96 96 }
97 97
98 98 void LegendLayout::setGeometry(const QRectF &rect)
99 99 {
100 100 m_legend->d_ptr->items()->setVisible(m_legend->isVisible());
101 101
102 102 QGraphicsLayout::setGeometry(rect);
103 103
104 104 if (m_legend->isAttachedToChart())
105 105 setAttachedGeometry(rect);
106 106 else
107 107 setDettachedGeometry(rect);
108 108 }
109 109
110 110 void LegendLayout::setAttachedGeometry(const QRectF &rect)
111 111 {
112 112 if (!rect.isValid())
113 113 return;
114 114
115 115 qreal oldOffsetX = m_offsetX;
116 116 qreal oldOffsetY = m_offsetY;
117 117 m_offsetX = 0;
118 118 m_offsetY = 0;
119 119
120 120 QSizeF size(0, 0);
121 121
122 122 if (m_legend->d_ptr->markers().isEmpty()) {
123 123 return;
124 124 }
125 125
126 126 m_width = 0;
127 127 m_height = 0;
128 128
129 129 qreal left, top, right, bottom;
130 130 getContentsMargins(&left, &top, &right, &bottom);
131 131
132 132 QRectF geometry = rect.adjusted(left, top, -right, -bottom);
133 133
134 134 switch(m_legend->alignment()) {
135 135 case Qt::AlignTop:
136 136 case Qt::AlignBottom: {
137 137 // Calculate the space required for items and add them to a sorted list.
138 138 qreal markerItemsWidth = 0;
139 139 qreal itemMargins = 0;
140 140 QList<LegendWidthStruct *> legendWidthList;
141 141 foreach (QLegendMarker *marker, m_legend->d_ptr->markers()) {
142 142 LegendMarkerItem *item = marker->d_ptr->item();
143 143 if (item->isVisible()) {
144 144 QSizeF dummySize;
145 145 qreal itemWidth = item->sizeHint(Qt::PreferredSize, dummySize).width();
146 146 LegendWidthStruct *structItem = new LegendWidthStruct;
147 147 structItem->item = item;
148 148 structItem->width = itemWidth;
149 149 legendWidthList.append(structItem);
150 150 markerItemsWidth += itemWidth;
151 151 itemMargins += marker->d_ptr->item()->m_margin;
152 152 }
153 153 }
154 154 std::sort(legendWidthList.begin(), legendWidthList.end(), widthLongerThan);
155 155
156 156 // If the items would occupy more space than is available, start truncating them
157 157 // from the longest one.
158 158 qreal availableGeometry = geometry.width() - right - left * 2 - itemMargins;
159 159 if (markerItemsWidth >= availableGeometry && legendWidthList.count() > 0) {
160 160 bool truncated(false);
161 161 int count = legendWidthList.count();
162 162 for (int i = 1; i < count; i++) {
163 163 int truncateIndex = i - 1;
164 164
165 165 while (legendWidthList.at(truncateIndex)->width >= legendWidthList.at(i)->width
166 166 && !truncated) {
167 167 legendWidthList.at(truncateIndex)->width--;
168 168 markerItemsWidth--;
169 169 if (i > 1) {
170 170 // Truncate the items that are before the truncated one in the list.
171 171 for (int j = truncateIndex - 1; j >= 0; j--) {
172 172 if (legendWidthList.at(truncateIndex)->width
173 173 < legendWidthList.at(j)->width) {
174 174 legendWidthList.at(j)->width--;
175 175 markerItemsWidth--;
176 176 }
177 177 }
178 178 }
179 179 if (markerItemsWidth < availableGeometry)
180 180 truncated = true;
181 181 }
182 182 // Truncate the last item if needed.
183 183 if (i == count - 1) {
184 184 if (legendWidthList.at(count - 1)->width
185 185 > legendWidthList.at(truncateIndex)->width) {
186 186 legendWidthList.at(count - 1)->width--;
187 187 markerItemsWidth--;
188 188 }
189 189 }
190 190
191 191 if (truncated)
192 192 break;
193 193 }
194 194 // Items are of same width and all of them need to be truncated
195 195 // or there is just one item that is truncated.
196 196 while (markerItemsWidth >= availableGeometry) {
197 197 for (int i = 0; i < count; i++) {
198 198 legendWidthList.at(i)->width--;
199 199 markerItemsWidth--;
200 200 }
201 201 }
202 202 }
203 203
204 204 QPointF point(0,0);
205 205
206 206 int markerCount = m_legend->d_ptr->markers().count();
207 207 for (int i = 0; i < markerCount; i++) {
208 208 QLegendMarker *marker;
209 209 if (m_legend->d_ptr->m_reverseMarkers)
210 210 marker = m_legend->d_ptr->markers().at(markerCount - 1 - i);
211 211 else
212 212 marker = m_legend->d_ptr->markers().at(i);
213 213 LegendMarkerItem *item = marker->d_ptr->item();
214 214 if (item->isVisible()) {
215 215 QRectF itemRect = geometry;
216 216 qreal availableWidth = 0;
217 217 for (int i = 0; i < legendWidthList.size(); ++i) {
218 218 if (legendWidthList.at(i)->item == item) {
219 219 availableWidth = legendWidthList.at(i)->width;
220 220 break;
221 221 }
222 222 }
223 223 itemRect.setWidth(availableWidth);
224 224 item->setGeometry(itemRect);
225 225 item->setPos(point.x(),geometry.height()/2 - item->boundingRect().height()/2);
226 226 const QRectF &rect = item->boundingRect();
227 227 size = size.expandedTo(rect.size());
228 228 qreal w = rect.width();
229 229 m_width = m_width + w - item->m_margin;
230 230 point.setX(point.x() + w);
231 231 }
232 232 }
233 233 // Delete structs from the container
234 234 qDeleteAll(legendWidthList);
235 235
236 if (m_width < geometry.width())
237 m_legend->d_ptr->items()->setPos(geometry.width() / 2 - m_width / 2, geometry.top());
238 else
239 m_legend->d_ptr->items()->setPos(geometry.topLeft());
236 // Round to full pixel via QPoint to avoid one pixel clipping on the edge in some cases
237 if (m_width < geometry.width()) {
238 m_legend->d_ptr->items()->setPos(QPoint(geometry.width() / 2 - m_width / 2,
239 geometry.top()));
240 } else {
241 m_legend->d_ptr->items()->setPos(geometry.topLeft().toPoint());
242 }
240 243 m_height = size.height();
241 244 }
242 245 break;
243 246 case Qt::AlignLeft:
244 247 case Qt::AlignRight: {
245 248 QPointF point(0,0);
246 249 int markerCount = m_legend->d_ptr->markers().count();
247 250 for (int i = 0; i < markerCount; i++) {
248 251 QLegendMarker *marker;
249 252 if (m_legend->d_ptr->m_reverseMarkers)
250 253 marker = m_legend->d_ptr->markers().at(markerCount - 1 - i);
251 254 else
252 255 marker = m_legend->d_ptr->markers().at(i);
253 256 LegendMarkerItem *item = marker->d_ptr->item();
254 257 if (item->isVisible()) {
255 258 item->setGeometry(geometry);
256 259 item->setPos(point);
257 260 const QRectF &rect = item->boundingRect();
258 261 qreal h = rect.height();
259 262 size = size.expandedTo(rect.size());
260 263 m_height+=h;
261 264 point.setY(point.y() + h);
262 265 }
263 266 }
264 267
265 if (m_height < geometry.height())
266 m_legend->d_ptr->items()->setPos(geometry.left(), geometry.height() / 2 - m_height / 2);
267 else
268 m_legend->d_ptr->items()->setPos(geometry.topLeft());
268 // Round to full pixel via QPoint to avoid one pixel clipping on the edge in some cases
269 if (m_height < geometry.height()) {
270 m_legend->d_ptr->items()->setPos(QPoint(geometry.left(),
271 geometry.height() / 2 - m_height / 2));
272 } else {
273 m_legend->d_ptr->items()->setPos(geometry.topLeft().toPoint());
274 }
269 275 m_width = size.width();
270 276 break;
271 277 }
272 278 }
273 279
274 280 m_minOffsetX = -left;
275 281 m_minOffsetY = - top;
276 282 m_maxOffsetX = m_width - geometry.width() - right;
277 283 m_maxOffsetY = m_height - geometry.height() - bottom;
278 284
279 285 setOffset(oldOffsetX, oldOffsetY);
280 286 }
281 287
282 288 void LegendLayout::setDettachedGeometry(const QRectF &rect)
283 289 {
284 290 if (!rect.isValid())
285 291 return;
286 292
287 293 // Detached layout is different.
288 294 // In detached mode legend may have multiple rows and columns, so layout calculations
289 295 // differ a log from attached mode.
290 296 // Also the scrolling logic is bit different.
291 297
292 298 qreal oldOffsetX = m_offsetX;
293 299 qreal oldOffsetY = m_offsetY;
294 300 m_offsetX = 0;
295 301 m_offsetY = 0;
296 302
297 303 qreal left, top, right, bottom;
298 304 getContentsMargins(&left, &top, &right, &bottom);
299 305 QRectF geometry = rect.adjusted(left, top, -right, -bottom);
300 306
301 307 QSizeF size(0, 0);
302 308
303 309 QList<QLegendMarker *> markers = m_legend->d_ptr->markers();
304 310
305 311 if (markers.isEmpty())
306 312 return;
307 313
308 314 switch (m_legend->alignment()) {
309 315 case Qt::AlignTop: {
310 316 QPointF point(0, 0);
311 317 m_width = 0;
312 318 m_height = 0;
313 319 for (int i = 0; i < markers.count(); i++) {
314 320 LegendMarkerItem *item = markers.at(i)->d_ptr->item();
315 321 if (item->isVisible()) {
316 322 item->setGeometry(geometry);
317 323 item->setPos(point.x(),point.y());
318 324 const QRectF &boundingRect = item->boundingRect();
319 325 qreal w = boundingRect.width();
320 326 qreal h = boundingRect.height();
321 327 m_width = qMax(m_width,w);
322 328 m_height = qMax(m_height,h);
323 329 point.setX(point.x() + w);
324 330 if (point.x() + w > geometry.left() + geometry.width() - right) {
325 331 // Next item would go off rect.
326 332 point.setX(0);
327 333 point.setY(point.y() + h);
328 334 if (i+1 < markers.count()) {
329 335 m_height += h;
330 336 }
331 337 }
332 338 }
333 339 }
334 340 m_legend->d_ptr->items()->setPos(geometry.topLeft());
335 341
336 342 m_minOffsetX = -left;
337 343 m_minOffsetY = -top;
338 344 m_maxOffsetX = m_width - geometry.width() - right;
339 345 m_maxOffsetY = m_height - geometry.height() - bottom;
340 346 }
341 347 break;
342 348 case Qt::AlignBottom: {
343 349 QPointF point(0, geometry.height());
344 350 m_width = 0;
345 351 m_height = 0;
346 352 for (int i = 0; i < markers.count(); i++) {
347 353 LegendMarkerItem *item = markers.at(i)->d_ptr->item();
348 354 if (item->isVisible()) {
349 355 item->setGeometry(geometry);
350 356 const QRectF &boundingRect = item->boundingRect();
351 357 qreal w = boundingRect.width();
352 358 qreal h = boundingRect.height();
353 359 m_width = qMax(m_width,w);
354 360 m_height = qMax(m_height,h);
355 361 item->setPos(point.x(),point.y() - h);
356 362 point.setX(point.x() + w);
357 363 if (point.x() + w > geometry.left() + geometry.width() - right) {
358 364 // Next item would go off rect.
359 365 point.setX(0);
360 366 point.setY(point.y() - h);
361 367 if (i+1 < markers.count()) {
362 368 m_height += h;
363 369 }
364 370 }
365 371 }
366 372 }
367 373 m_legend->d_ptr->items()->setPos(geometry.topLeft());
368 374
369 375 m_minOffsetX = -left;
370 376 m_minOffsetY = -m_height + geometry.height() - top;
371 377 m_maxOffsetX = m_width - geometry.width() - right;
372 378 m_maxOffsetY = -bottom;
373 379 }
374 380 break;
375 381 case Qt::AlignLeft: {
376 382 QPointF point(0, 0);
377 383 m_width = 0;
378 384 m_height = 0;
379 385 qreal maxWidth = 0;
380 386 for (int i = 0; i < markers.count(); i++) {
381 387 LegendMarkerItem *item = markers.at(i)->d_ptr->item();
382 388 if (item->isVisible()) {
383 389 item->setGeometry(geometry);
384 390 const QRectF &boundingRect = item->boundingRect();
385 391 qreal w = boundingRect.width();
386 392 qreal h = boundingRect.height();
387 393 m_height = qMax(m_height,h);
388 394 maxWidth = qMax(maxWidth,w);
389 395 item->setPos(point.x(),point.y());
390 396 point.setY(point.y() + h);
391 397 if (point.y() + h > geometry.bottom() - bottom) {
392 398 // Next item would go off rect.
393 399 point.setX(point.x() + maxWidth);
394 400 point.setY(0);
395 401 if (i+1 < markers.count()) {
396 402 m_width += maxWidth;
397 403 maxWidth = 0;
398 404 }
399 405 }
400 406 }
401 407 }
402 408 m_width += maxWidth;
403 409 m_legend->d_ptr->items()->setPos(geometry.topLeft());
404 410
405 411 m_minOffsetX = -left;
406 412 m_minOffsetY = -top;
407 413 m_maxOffsetX = m_width - geometry.width() - right;
408 414 m_maxOffsetY = m_height - geometry.height() - bottom;
409 415 }
410 416 break;
411 417 case Qt::AlignRight: {
412 418 QPointF point(geometry.width(), 0);
413 419 m_width = 0;
414 420 m_height = 0;
415 421 qreal maxWidth = 0;
416 422 for (int i = 0; i < markers.count(); i++) {
417 423 LegendMarkerItem *item = markers.at(i)->d_ptr->item();
418 424 if (item->isVisible()) {
419 425 item->setGeometry(geometry);
420 426 const QRectF &boundingRect = item->boundingRect();
421 427 qreal w = boundingRect.width();
422 428 qreal h = boundingRect.height();
423 429 m_height = qMax(m_height,h);
424 430 maxWidth = qMax(maxWidth,w);
425 431 item->setPos(point.x() - w,point.y());
426 432 point.setY(point.y() + h);
427 433 if (point.y() + h > geometry.bottom()-bottom) {
428 434 // Next item would go off rect.
429 435 point.setX(point.x() - maxWidth);
430 436 point.setY(0);
431 437 if (i+1 < markers.count()) {
432 438 m_width += maxWidth;
433 439 maxWidth = 0;
434 440 }
435 441 }
436 442 }
437 443 }
438 444 m_width += maxWidth;
439 445 m_legend->d_ptr->items()->setPos(geometry.topLeft());
440 446
441 447 m_minOffsetX = - m_width + geometry.width() - left;
442 448 m_minOffsetY = -top;
443 449 m_maxOffsetX = - right;
444 450 m_maxOffsetY = m_height - geometry.height() - bottom;
445 451 }
446 452 break;
447 453 default:
448 454 break;
449 455 }
450 456
451 457 setOffset(oldOffsetX, oldOffsetY);
452 458 }
453 459
454 460 QSizeF LegendLayout::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
455 461 {
456 462 QSizeF size(0, 0);
457 463 qreal left, top, right, bottom;
458 464 getContentsMargins(&left, &top, &right, &bottom);
459 465
460 466 if(constraint.isValid()) {
461 467 foreach(QLegendMarker *marker, m_legend->d_ptr->markers()) {
462 468 LegendMarkerItem *item = marker->d_ptr->item();
463 469 size = size.expandedTo(item->effectiveSizeHint(which));
464 470 }
465 471 size = size.boundedTo(constraint);
466 472 }
467 473 else if (constraint.width() >= 0) {
468 474 qreal width = 0;
469 475 qreal height = 0;
470 476 foreach(QLegendMarker *marker, m_legend->d_ptr->markers()) {
471 477 LegendMarkerItem *item = marker->d_ptr->item();
472 478 width+=item->effectiveSizeHint(which).width();
473 479 height=qMax(height,item->effectiveSizeHint(which).height());
474 480 }
475 481
476 482 size = QSizeF(qMin(constraint.width(),width), height);
477 483 }
478 484 else if (constraint.height() >= 0) {
479 485 qreal width = 0;
480 486 qreal height = 0;
481 487 foreach(QLegendMarker *marker, m_legend->d_ptr->markers()) {
482 488 LegendMarkerItem *item = marker->d_ptr->item();
483 489 width=qMax(width,item->effectiveSizeHint(which).width());
484 490 height+=height,item->effectiveSizeHint(which).height();
485 491 }
486 492 size = QSizeF(width,qMin(constraint.height(),height));
487 493 }
488 494 else {
489 495 foreach(QLegendMarker *marker, m_legend->d_ptr->markers()) {
490 496 LegendMarkerItem *item = marker->d_ptr->item();
491 497 size = size.expandedTo(item->effectiveSizeHint(which));
492 498 }
493 499 }
494 500 size += QSize(left + right, top + bottom);
495 501 return size;
496 502 }
497 503
498 504 bool LegendLayout::widthLongerThan(const LegendWidthStruct *item1,
499 505 const LegendWidthStruct *item2)
500 506 {
501 507 return item1->width > item2->width;
502 508 }
503 509
504 510 QT_CHARTS_END_NAMESPACE
@@ -1,10 +1,9
1 1 !include( ../../tests.pri ) {
2 2 error( "Couldn't find the test.pri file!" )
3 3 }
4 4 include(charts/charts.pri)
5 5 TARGET = chartviewer
6 QT += opengl
7 6 INCLUDEPATH += .
8 7 SOURCES += main.cpp window.cpp view.cpp grid.cpp
9 8 HEADERS += window.h view.h charts.h model.h grid.h
10 9
@@ -1,585 +1,585
1 1 /****************************************************************************
2 2 **
3 3 ** Copyright (C) 2015 The Qt Company Ltd
4 4 ** All rights reserved.
5 5 ** For any questions to The Qt Company, please use contact form at http://qt.io
6 6 **
7 7 ** This file is part of the Qt Charts module.
8 8 **
9 9 ** Licensees holding valid commercial license for Qt may use this file in
10 10 ** accordance with the Qt License Agreement provided with the Software
11 11 ** or, alternatively, in accordance with the terms contained in a written
12 12 ** agreement between you and The Qt Company.
13 13 **
14 14 ** If you have questions regarding the use of this file, please use
15 15 ** contact form at http://qt.io
16 16 **
17 17 ****************************************************************************/
18 18
19 19 #include "window.h"
20 20 #include "view.h"
21 21 #include "grid.h"
22 22 #include "charts.h"
23 23 #include <QtCharts/QChartView>
24 24 #include <QtCharts/QAreaSeries>
25 25 #include <QtCharts/QLegend>
26 26 #include <QtCharts/QValueAxis>
27 27 #include <QtWidgets/QGridLayout>
28 28 #include <QtWidgets/QFormLayout>
29 29 #include <QtWidgets/QComboBox>
30 30 #include <QtWidgets/QSpinBox>
31 31 #include <QtWidgets/QCheckBox>
32 32 #include <QtWidgets/QGroupBox>
33 33 #include <QtWidgets/QLabel>
34 34 #include <QtWidgets/QGraphicsScene>
35 35 #include <QtWidgets/QGraphicsLinearLayout>
36 36 #include <QtWidgets/QGraphicsProxyWidget>
37 #include <QtOpenGL/QGLWidget>
37 #include <QtWidgets/QOpenGLWidget>
38 38 #include <QtWidgets/QApplication>
39 39 #include <QtCore/QDebug>
40 40 #include <QtWidgets/QMenu>
41 41 #include <QtWidgets/QPushButton>
42 42
43 43 Window::Window(const QVariantHash &parameters, QWidget *parent)
44 44 : QMainWindow(parent),
45 45 m_scene(new QGraphicsScene(this)),
46 46 m_view(0),
47 47 m_form(0),
48 48 m_themeComboBox(0),
49 49 m_antialiasCheckBox(0),
50 50 m_animatedComboBox(0),
51 51 m_legendComboBox(0),
52 52 m_templateComboBox(0),
53 53 m_viewComboBox(0),
54 54 m_xTickSpinBox(0),
55 55 m_yTickSpinBox(0),
56 56 m_minorXTickSpinBox(0),
57 57 m_minorYTickSpinBox(0),
58 58 m_openGLCheckBox(0),
59 59 m_zoomCheckBox(0),
60 60 m_scrollCheckBox(0),
61 61 m_baseLayout(new QGraphicsLinearLayout()),
62 62 m_menu(createMenu()),
63 63 m_template(0),
64 64 m_grid(new Grid(-1))
65 65 {
66 66 createProxyWidgets();
67 67 // create layout
68 68 QGraphicsLinearLayout *settingsLayout = new QGraphicsLinearLayout();
69 69
70 70 settingsLayout->setOrientation(Qt::Vertical);
71 71 settingsLayout->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
72 72 settingsLayout->addItem(m_widgetHash["openGLCheckBox"]);
73 73 settingsLayout->addItem(m_widgetHash["antialiasCheckBox"]);
74 74 settingsLayout->addItem(m_widgetHash["viewLabel"]);
75 75 settingsLayout->addItem(m_widgetHash["viewComboBox"]);
76 76 settingsLayout->addItem(m_widgetHash["themeLabel"]);
77 77 settingsLayout->addItem(m_widgetHash["themeComboBox"]);
78 78 settingsLayout->addItem(m_widgetHash["animationsLabel"]);
79 79 settingsLayout->addItem(m_widgetHash["animatedComboBox"]);
80 80 settingsLayout->addItem(m_widgetHash["legendLabel"]);
81 81 settingsLayout->addItem(m_widgetHash["legendComboBox"]);
82 82 settingsLayout->addItem(m_widgetHash["templateLabel"]);
83 83 settingsLayout->addItem(m_widgetHash["templateComboBox"]);
84 84 settingsLayout->addItem(m_widgetHash["scrollCheckBox"]);
85 85 settingsLayout->addItem(m_widgetHash["zoomCheckBox"]);
86 86 settingsLayout->addItem(m_widgetHash["xTickLabel"]);
87 87 settingsLayout->addItem(m_widgetHash["xTickSpinBox"]);
88 88 settingsLayout->addItem(m_widgetHash["yTickLabel"]);
89 89 settingsLayout->addItem(m_widgetHash["yTickSpinBox"]);
90 90 settingsLayout->addItem(m_widgetHash["minorXTickLabel"]);
91 91 settingsLayout->addItem(m_widgetHash["minorXTickSpinBox"]);
92 92 settingsLayout->addItem(m_widgetHash["minorYTickLabel"]);
93 93 settingsLayout->addItem(m_widgetHash["minorYTickSpinBox"]);
94 94 settingsLayout->addStretch();
95 95
96 96 m_baseLayout->setOrientation(Qt::Horizontal);
97 97 m_baseLayout->addItem(m_grid);
98 98 m_baseLayout->addItem(settingsLayout);
99 99
100 100 m_form = new QGraphicsWidget();
101 101 m_form->setLayout(m_baseLayout);
102 102 m_scene->addItem(m_form);
103 103
104 104 m_view = new View(m_scene, m_form);
105 105 m_view->setMinimumSize(m_form->minimumSize().toSize());
106 106
107 107 // Set defaults
108 108 m_antialiasCheckBox->setChecked(true);
109 109 initializeFromParamaters(parameters);
110 110 updateUI();
111 111 if(!m_category.isEmpty() && !m_subcategory.isEmpty() && !m_name.isEmpty())
112 112 m_grid->createCharts(m_category,m_subcategory,m_name);
113 113
114 114
115 115 handleGeometryChanged();
116 116 setCentralWidget(m_view);
117 117
118 118 connectSignals();
119 119 }
120 120
121 121 Window::~Window()
122 122 {
123 123 }
124 124
125 125 void Window::connectSignals()
126 126 {
127 127 QObject::connect(m_form, SIGNAL(geometryChanged()), this , SLOT(handleGeometryChanged()));
128 128 QObject::connect(m_viewComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(updateUI()));
129 129 QObject::connect(m_themeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(updateUI()));
130 130 QObject::connect(m_xTickSpinBox, SIGNAL(valueChanged(int)), this, SLOT(updateUI()));
131 131 QObject::connect(m_yTickSpinBox, SIGNAL(valueChanged(int)), this, SLOT(updateUI()));
132 132 QObject::connect(m_minorXTickSpinBox, SIGNAL(valueChanged(int)), this, SLOT(updateUI()));
133 133 QObject::connect(m_minorYTickSpinBox, SIGNAL(valueChanged(int)), this, SLOT(updateUI()));
134 134 QObject::connect(m_antialiasCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateUI()));
135 135 QObject::connect(m_openGLCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateUI()));
136 136 QObject::connect(m_zoomCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateUI()));
137 137 QObject::connect(m_scrollCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateUI()));
138 138 QObject::connect(m_animatedComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(updateUI()));
139 139 QObject::connect(m_legendComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(updateUI()));
140 140 QObject::connect(m_templateComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(updateUI()));
141 141 QObject::connect(m_grid, SIGNAL(chartSelected(QChart*)), this, SLOT(handleChartSelected(QChart*)));
142 142 }
143 143
144 144 void Window::createProxyWidgets()
145 145 {
146 146 m_themeComboBox = createThemeBox();
147 147 m_viewComboBox = createViewBox();
148 148 m_xTickSpinBox = new QSpinBox();
149 149 m_xTickSpinBox->setMinimum(2);
150 150 m_xTickSpinBox->setValue(5);
151 151 m_yTickSpinBox = new QSpinBox();
152 152 m_yTickSpinBox->setMinimum(2);
153 153 m_yTickSpinBox->setValue(5);
154 154 m_minorXTickSpinBox = new QSpinBox();
155 155 m_minorYTickSpinBox = new QSpinBox();
156 156 m_antialiasCheckBox = new QCheckBox(tr("Anti-aliasing"));
157 157 m_animatedComboBox = createAnimationBox();
158 158 m_legendComboBox = createLegendBox();
159 159 m_openGLCheckBox = new QCheckBox(tr("OpenGL"));
160 160 m_zoomCheckBox = new QCheckBox(tr("Zoom"));
161 161 m_scrollCheckBox = new QCheckBox(tr("Scroll"));
162 162 m_templateComboBox = createTempleteBox();
163 163 m_widgetHash["viewLabel"] = m_scene->addWidget(new QLabel("View"));
164 164 m_widgetHash["viewComboBox"] = m_scene->addWidget(m_viewComboBox);
165 165 m_widgetHash["themeComboBox"] = m_scene->addWidget(m_themeComboBox);
166 166 m_widgetHash["antialiasCheckBox"] = m_scene->addWidget(m_antialiasCheckBox);
167 167 m_widgetHash["animatedComboBox"] = m_scene->addWidget(m_animatedComboBox);
168 168 m_widgetHash["legendComboBox"] = m_scene->addWidget(m_legendComboBox);
169 169 m_widgetHash["xTickLabel"] = m_scene->addWidget(new QLabel("X Tick"));
170 170 m_widgetHash["xTickSpinBox"] = m_scene->addWidget(m_xTickSpinBox);
171 171 m_widgetHash["yTickLabel"] = m_scene->addWidget(new QLabel("Y Tick"));
172 172 m_widgetHash["yTickSpinBox"] = m_scene->addWidget(m_yTickSpinBox);
173 173 m_widgetHash["minorXTickLabel"] = m_scene->addWidget(new QLabel("Minor X Tick"));
174 174 m_widgetHash["minorXTickSpinBox"] = m_scene->addWidget(m_minorXTickSpinBox);
175 175 m_widgetHash["minorYTickLabel"] = m_scene->addWidget(new QLabel("Minor Y Tick"));
176 176 m_widgetHash["minorYTickSpinBox"] = m_scene->addWidget(m_minorYTickSpinBox);
177 177 m_widgetHash["openGLCheckBox"] = m_scene->addWidget(m_openGLCheckBox);
178 178 m_widgetHash["themeLabel"] = m_scene->addWidget(new QLabel("Theme"));
179 179 m_widgetHash["animationsLabel"] = m_scene->addWidget(new QLabel("Animations"));
180 180 m_widgetHash["legendLabel"] = m_scene->addWidget(new QLabel("Legend"));
181 181 m_widgetHash["templateLabel"] = m_scene->addWidget(new QLabel("Chart template"));
182 182 m_widgetHash["templateComboBox"] = m_scene->addWidget(m_templateComboBox);
183 183 m_widgetHash["zoomCheckBox"] = m_scene->addWidget(m_zoomCheckBox);
184 184 m_widgetHash["scrollCheckBox"] = m_scene->addWidget(m_scrollCheckBox);
185 185 }
186 186
187 187 QComboBox *Window::createThemeBox()
188 188 {
189 189 QComboBox *themeComboBox = new ComboBox(this);
190 190 themeComboBox->addItem("Light", QChart::ChartThemeLight);
191 191 themeComboBox->addItem("Blue Cerulean", QChart::ChartThemeBlueCerulean);
192 192 themeComboBox->addItem("Dark", QChart::ChartThemeDark);
193 193 themeComboBox->addItem("Brown Sand", QChart::ChartThemeBrownSand);
194 194 themeComboBox->addItem("Blue NCS", QChart::ChartThemeBlueNcs);
195 195 themeComboBox->addItem("High Contrast", QChart::ChartThemeHighContrast);
196 196 themeComboBox->addItem("Blue Icy", QChart::ChartThemeBlueIcy);
197 197 themeComboBox->addItem("Qt", QChart::ChartThemeQt);
198 198 return themeComboBox;
199 199 }
200 200
201 201 QComboBox *Window::createViewBox()
202 202 {
203 203 QComboBox *viewComboBox = new ComboBox(this);
204 204 viewComboBox->addItem("1 chart", 1);
205 205 viewComboBox->addItem("4 charts", 2);
206 206 viewComboBox->addItem("9 charts", 3);
207 207 viewComboBox->addItem("16 charts", 4);
208 208 return viewComboBox;
209 209 }
210 210
211 211 QComboBox *Window::createAnimationBox()
212 212 {
213 213 QComboBox *animationComboBox = new ComboBox(this);
214 214 animationComboBox->addItem("No Animations", QChart::NoAnimation);
215 215 animationComboBox->addItem("GridAxis Animations", QChart::GridAxisAnimations);
216 216 animationComboBox->addItem("Series Animations", QChart::SeriesAnimations);
217 217 animationComboBox->addItem("All Animations", QChart::AllAnimations);
218 218 return animationComboBox;
219 219 }
220 220
221 221 QComboBox *Window::createLegendBox()
222 222 {
223 223 QComboBox *legendComboBox = new ComboBox(this);
224 224 legendComboBox->addItem("No Legend ", 0);
225 225 legendComboBox->addItem("Legend Top", Qt::AlignTop);
226 226 legendComboBox->addItem("Legend Bottom", Qt::AlignBottom);
227 227 legendComboBox->addItem("Legend Left", Qt::AlignLeft);
228 228 legendComboBox->addItem("Legend Right", Qt::AlignRight);
229 229 return legendComboBox;
230 230 }
231 231
232 232 QComboBox *Window::createTempleteBox()
233 233 {
234 234 QComboBox *templateComboBox = new ComboBox(this);
235 235 templateComboBox->addItem("No Template", 0);
236 236
237 237 Charts::ChartList list = Charts::chartList();
238 238 QMultiMap<QString, Chart *> categoryMap;
239 239
240 240 foreach (Chart *chart, list)
241 241 categoryMap.insertMulti(chart->category(), chart);
242 242
243 243 foreach (const QString &category, categoryMap.uniqueKeys())
244 244 templateComboBox->addItem(category, category);
245 245
246 246 return templateComboBox;
247 247 }
248 248
249 249 void Window::initializeFromParamaters(const QVariantHash &parameters)
250 250 {
251 251 if (parameters.contains("view")) {
252 252 int t = parameters["view"].toInt();
253 253 for (int i = 0; i < m_viewComboBox->count(); ++i) {
254 254 if (m_viewComboBox->itemData(i).toInt() == t) {
255 255 m_viewComboBox->setCurrentIndex(i);
256 256 break;
257 257 }
258 258 }
259 259 }
260 260
261 261 if (parameters.contains("chart")) {
262 262 QString t = parameters["chart"].toString();
263 263
264 264 QRegExp rx("([a-zA-Z0-9_]*)::([a-zA-Z0-9_]*)::([a-zA-Z0-9_]*)");
265 265 int pos = rx.indexIn(t);
266 266
267 267 if (pos > -1) {
268 268 m_category = rx.cap(1);
269 269 m_subcategory = rx.cap(2);
270 270 m_name = rx.cap(3);
271 271 m_templateComboBox->setCurrentIndex(0);
272 272 }
273 273 else {
274 274 for (int i = 0; i < m_templateComboBox->count(); ++i) {
275 275 if (m_templateComboBox->itemText(i) == t) {
276 276 m_templateComboBox->setCurrentIndex(i);
277 277 break;
278 278 }
279 279 }
280 280 }
281 281 }
282 282 if (parameters.contains("opengl")) {
283 283 bool checked = parameters["opengl"].toBool();
284 284 m_openGLCheckBox->setChecked(checked);
285 285 }
286 286 if (parameters.contains("theme")) {
287 287 QString t = parameters["theme"].toString();
288 288 for (int i = 0; i < m_themeComboBox->count(); ++i) {
289 289 if (m_themeComboBox->itemText(i) == t) {
290 290 m_themeComboBox->setCurrentIndex(i);
291 291 break;
292 292 }
293 293 }
294 294 }
295 295 if (parameters.contains("animation")) {
296 296 QString t = parameters["animation"].toString();
297 297 for (int i = 0; i < m_animatedComboBox->count(); ++i) {
298 298 if (m_animatedComboBox->itemText(i) == t) {
299 299 m_animatedComboBox->setCurrentIndex(i);
300 300 break;
301 301 }
302 302 }
303 303 }
304 304 if (parameters.contains("legend")) {
305 305 QString t = parameters["legend"].toString();
306 306 for (int i = 0; i < m_legendComboBox->count(); ++i) {
307 307 if (m_legendComboBox->itemText(i) == t) {
308 308 m_legendComboBox->setCurrentIndex(i);
309 309 break;
310 310 }
311 311 }
312 312 }
313 313 }
314 314
315 315 void Window::updateUI()
316 316 {
317 317 checkView();
318 318 checkTemplate();
319 319 checkOpenGL();
320 320 checkTheme();
321 321 checkAnimationOptions();
322 322 checkLegend();
323 323 checkState();
324 324 checkXTick();
325 325 checkYTick();
326 326 checkMinorXTick();
327 327 checkMinorYTick();
328 328 }
329 329
330 330 void Window::checkView()
331 331 {
332 332 int count(m_viewComboBox->itemData(m_viewComboBox->currentIndex()).toInt());
333 333 if(m_grid->size()!=count){
334 334 m_grid->setSize(count);
335 335 m_template = 0;
336 336 }
337 337 }
338 338
339 339 void Window::checkXTick()
340 340 {
341 341 foreach (QChart *chart, m_grid->charts()) {
342 342 if (qobject_cast<QValueAxis *>(chart->axisX())) {
343 343 QValueAxis *valueAxis = qobject_cast<QValueAxis *>(chart->axisX());
344 344 valueAxis->setGridLineVisible();
345 345 valueAxis->setTickCount(m_xTickSpinBox->value());
346 346 }
347 347 }
348 348 }
349 349
350 350 void Window::checkYTick()
351 351 {
352 352 foreach (QChart *chart, m_grid->charts()) {
353 353 if (qobject_cast<QValueAxis *>(chart->axisY())) {
354 354 QValueAxis *valueAxis = qobject_cast<QValueAxis *>(chart->axisY());
355 355 valueAxis->setGridLineVisible();
356 356 valueAxis->setTickCount(m_yTickSpinBox->value());
357 357 }
358 358 }
359 359 }
360 360
361 361 void Window::checkMinorXTick()
362 362 {
363 363 foreach (QChart *chart, m_grid->charts()) {
364 364 if (qobject_cast<QValueAxis *>(chart->axisX())) {
365 365 QValueAxis *valueAxis = qobject_cast<QValueAxis *>(chart->axisX());
366 366 valueAxis->setMinorGridLineVisible();
367 367 valueAxis->setMinorTickCount(m_minorXTickSpinBox->value());
368 368 }
369 369 }
370 370 }
371 371
372 372 void Window::checkMinorYTick()
373 373 {
374 374 foreach (QChart *chart, m_grid->charts()) {
375 375 if (qobject_cast<QValueAxis *>(chart->axisY())) {
376 376 QValueAxis *valueAxis = qobject_cast<QValueAxis *>(chart->axisY());
377 377 valueAxis->setMinorGridLineVisible();
378 378 valueAxis->setMinorTickCount(m_minorYTickSpinBox->value());
379 379 }
380 380 }
381 381 }
382 382
383 383 void Window::checkLegend()
384 384 {
385 385 Qt::Alignment alignment(m_legendComboBox->itemData(m_legendComboBox->currentIndex()).toInt());
386 386
387 387 if (!alignment) {
388 388 foreach (QChart *chart, m_grid->charts())
389 389 chart->legend()->hide();
390 390 } else {
391 391 foreach (QChart *chart, m_grid->charts()) {
392 392 chart->legend()->setAlignment(alignment);
393 393 chart->legend()->show();
394 394 }
395 395 }
396 396 }
397 397
398 398 void Window::checkOpenGL()
399 399 {
400 400 bool opengl = m_openGLCheckBox->isChecked();
401 bool isOpengl = qobject_cast<QGLWidget *>(m_view->viewport());
401 bool isOpengl = qobject_cast<QOpenGLWidget *>(m_view->viewport());
402 402 if ((isOpengl && !opengl) || (!isOpengl && opengl)) {
403 403 m_view->deleteLater();
404 404 m_view = new View(m_scene, m_form);
405 m_view->setViewport(!opengl ? new QWidget() : new QGLWidget());
405 m_view->setViewport(!opengl ? new QWidget() : new QOpenGLWidget());
406 406 setCentralWidget(m_view);
407 407 }
408 408
409 409 bool antialias = m_antialiasCheckBox->isChecked();
410 410
411 411 if (opengl)
412 412 m_view->setRenderHint(QPainter::HighQualityAntialiasing, antialias);
413 413 else
414 414 m_view->setRenderHint(QPainter::Antialiasing, antialias);
415 415 }
416 416
417 417 void Window::checkAnimationOptions()
418 418 {
419 419 QChart::AnimationOptions options(
420 420 m_animatedComboBox->itemData(m_animatedComboBox->currentIndex()).toInt());
421 421
422 422 QList<QChart *> charts = m_grid->charts();
423 423
424 424 if (!charts.isEmpty() && charts.at(0)->animationOptions() != options) {
425 425 foreach (QChart *chart, charts)
426 426 chart->setAnimationOptions(options);
427 427 }
428 428 }
429 429
430 430 void Window::checkState()
431 431 {
432 432 bool scroll = m_scrollCheckBox->isChecked();
433 433
434 434
435 435 if (m_grid->state() != Grid::ScrollState && scroll) {
436 436 m_grid->setState(Grid::ScrollState);
437 437 m_zoomCheckBox->setChecked(false);
438 438 } else if (!scroll && m_grid->state() == Grid::ScrollState) {
439 439 m_grid->setState(Grid::NoState);
440 440 }
441 441
442 442 bool zoom = m_zoomCheckBox->isChecked();
443 443
444 444 if (m_grid->state() != Grid::ZoomState && zoom) {
445 445 m_grid->setState(Grid::ZoomState);
446 446 m_scrollCheckBox->setChecked(false);
447 447 } else if (!zoom && m_grid->state() == Grid::ZoomState) {
448 448 m_grid->setState(Grid::NoState);
449 449 }
450 450 }
451 451
452 452 void Window::checkTemplate()
453 453 {
454 454 int index = m_templateComboBox->currentIndex();
455 455 if (m_template == index || index == 0)
456 456 return;
457 457
458 458 m_template = index;
459 459 QString category = m_templateComboBox->itemData(index).toString();
460 460 m_grid->createCharts(category);
461 461 }
462 462
463 463 void Window::checkTheme()
464 464 {
465 465 QChart::ChartTheme theme = (QChart::ChartTheme) m_themeComboBox->itemData(
466 466 m_themeComboBox->currentIndex()).toInt();
467 467
468 468 foreach (QChart *chart, m_grid->charts())
469 469 chart->setTheme(theme);
470 470
471 471 QPalette pal = window()->palette();
472 472 if (theme == QChart::ChartThemeLight) {
473 473 pal.setColor(QPalette::Window, QRgb(0xf0f0f0));
474 474 pal.setColor(QPalette::WindowText, QRgb(0x404044));
475 475 } else if (theme == QChart::ChartThemeDark) {
476 476 pal.setColor(QPalette::Window, QRgb(0x121218));
477 477 pal.setColor(QPalette::WindowText, QRgb(0xd6d6d6));
478 478 } else if (theme == QChart::ChartThemeBlueCerulean) {
479 479 pal.setColor(QPalette::Window, QRgb(0x40434a));
480 480 pal.setColor(QPalette::WindowText, QRgb(0xd6d6d6));
481 481 } else if (theme == QChart::ChartThemeBrownSand) {
482 482 pal.setColor(QPalette::Window, QRgb(0x9e8965));
483 483 pal.setColor(QPalette::WindowText, QRgb(0x404044));
484 484 } else if (theme == QChart::ChartThemeBlueNcs) {
485 485 pal.setColor(QPalette::Window, QRgb(0x018bba));
486 486 pal.setColor(QPalette::WindowText, QRgb(0x404044));
487 487 } else if (theme == QChart::ChartThemeHighContrast) {
488 488 pal.setColor(QPalette::Window, QRgb(0xffab03));
489 489 pal.setColor(QPalette::WindowText, QRgb(0x181818));
490 490 } else if (theme == QChart::ChartThemeBlueIcy) {
491 491 pal.setColor(QPalette::Window, QRgb(0xcee7f0));
492 492 pal.setColor(QPalette::WindowText, QRgb(0x404044));
493 493 } else if (theme == QChart::ChartThemeQt) {
494 494 pal.setColor(QPalette::Window, QRgb(0xf0f0f0));
495 495 pal.setColor(QPalette::WindowText, QRgb(0x404044));
496 496 } else {
497 497 pal.setColor(QPalette::Window, QRgb(0xf0f0f0));
498 498 pal.setColor(QPalette::WindowText, QRgb(0x404044));
499 499 }
500 500 foreach (QGraphicsProxyWidget *widget, m_widgetHash)
501 501 widget->setPalette(pal);
502 502 m_view->setBackgroundBrush(pal.color((QPalette::Window)));
503 503 m_grid->setRubberPen(pal.color((QPalette::WindowText)));
504 504 }
505 505
506 506 void Window::comboBoxFocused(QComboBox *combobox)
507 507 {
508 508 foreach (QGraphicsProxyWidget *widget , m_widgetHash) {
509 509 if (widget->widget() == combobox)
510 510 widget->setZValue(2.0);
511 511 else
512 512 widget->setZValue(0.0);
513 513 }
514 514 }
515 515
516 516 void Window::handleChartSelected(QChart *qchart)
517 517 {
518 518 if (m_templateComboBox->currentIndex() != 0)
519 519 return;
520 520
521 521 QAction *chosen = m_menu->exec(QCursor::pos());
522 522
523 523 if (chosen) {
524 524 Chart *chart = (Chart *) chosen->data().value<void *>();
525 525 m_grid->replaceChart(qchart, chart);
526 526 updateUI();
527 527 }
528 528 }
529 529
530 530 QMenu *Window::createMenu()
531 531 {
532 532 Charts::ChartList list = Charts::chartList();
533 533 QMultiMap<QString, Chart *> categoryMap;
534 534
535 535 QMenu *result = new QMenu(this);
536 536
537 537 foreach (Chart *chart, list)
538 538 categoryMap.insertMulti(chart->category(), chart);
539 539
540 540 foreach (const QString &category, categoryMap.uniqueKeys()) {
541 541 QMenu *menu(0);
542 542 QMultiMap<QString, Chart *> subCategoryMap;
543 543 if (category.isEmpty()) {
544 544 menu = result;
545 545 } else {
546 546 menu = new QMenu(category, this);
547 547 result->addMenu(menu);
548 548 }
549 549
550 550 foreach (Chart *chart, categoryMap.values(category))
551 551 subCategoryMap.insert(chart->subCategory(), chart);
552 552
553 553 foreach (const QString &subCategory, subCategoryMap.uniqueKeys()) {
554 554 QMenu *subMenu(0);
555 555 if (subCategory.isEmpty()) {
556 556 subMenu = menu;
557 557 } else {
558 558 subMenu = new QMenu(subCategory, this);
559 559 menu->addMenu(subMenu);
560 560 }
561 561
562 562 foreach (Chart *chart, subCategoryMap.values(subCategory)) {
563 563 createMenuAction(subMenu, QIcon(), chart->name(),
564 564 qVariantFromValue((void *) chart));
565 565 }
566 566 }
567 567 }
568 568 return result;
569 569 }
570 570
571 571 QAction *Window::createMenuAction(QMenu *menu, const QIcon &icon, const QString &text,
572 572 const QVariant &data)
573 573 {
574 574 QAction *action = menu->addAction(icon, text);
575 575 action->setCheckable(false);
576 576 action->setData(data);
577 577 return action;
578 578 }
579 579
580 580 void Window::handleGeometryChanged()
581 581 {
582 582 QSizeF size = m_baseLayout->sizeHint(Qt::MinimumSize);
583 583 m_view->scene()->setSceneRect(0, 0, this->width(), this->height());
584 584 m_view->setMinimumSize(size.toSize());
585 585 }
@@ -1,15 +1,15
1 1 !include( ../../tests.pri ) {
2 2 error( "Couldn't find the test.pri file!" )
3 3 }
4 4
5 5 TEMPLATE = app
6 6
7 QT += core gui opengl widgets
7 QT += core gui widgets
8 8
9 9 SOURCES += main.cpp \
10 10 mainwidget.cpp \
11 11 dataseriedialog.cpp
12 12
13 13 HEADERS += \
14 14 mainwidget.h \
15 15 dataseriedialog.h
@@ -1,378 +1,377
1 1 /****************************************************************************
2 2 **
3 3 ** Copyright (C) 2015 The Qt Company Ltd
4 4 ** All rights reserved.
5 5 ** For any questions to The Qt Company, please use contact form at http://qt.io
6 6 **
7 7 ** This file is part of the Qt Charts module.
8 8 **
9 9 ** Licensees holding valid commercial license for Qt may use this file in
10 10 ** accordance with the Qt License Agreement provided with the Software
11 11 ** or, alternatively, in accordance with the terms contained in a written
12 12 ** agreement between you and The Qt Company.
13 13 **
14 14 ** If you have questions regarding the use of this file, please use
15 15 ** contact form at http://qt.io
16 16 **
17 17 ****************************************************************************/
18 18
19 19 #include "mainwidget.h"
20 20 #include "dataseriedialog.h"
21 21 #include <QtCharts/QChartView>
22 22 #include <QtCharts/QPieSeries>
23 23 #include <QtCharts/QScatterSeries>
24 24 #include <QtCharts/QLineSeries>
25 25 #include <QtCharts/QAreaSeries>
26 26 #include <QtCharts/QSplineSeries>
27 27 #include <QtCharts/QBarSet>
28 28 #include <QtCharts/QBarSeries>
29 29 #include <QtCharts/QStackedBarSeries>
30 30 #include <QtCharts/QPercentBarSeries>
31 31 #include <QtWidgets/QPushButton>
32 32 #include <QtWidgets/QComboBox>
33 33 #include <QtWidgets/QSpinBox>
34 34 #include <QtWidgets/QCheckBox>
35 35 #include <QtWidgets/QGridLayout>
36 36 #include <QtWidgets/QHBoxLayout>
37 37 #include <QtWidgets/QLabel>
38 38 #include <QtWidgets/QSpacerItem>
39 39 #include <QtWidgets/QMessageBox>
40 40 #include <cmath>
41 41 #include <QtCore/QDebug>
42 42 #include <QtGui/QStandardItemModel>
43 43 #include <QtCharts/QBarCategoryAxis>
44 #include <QtOpenGL/QGLWidget>
44 #include <QtWidgets/QOpenGLWidget>
45 45
46 46 QT_CHARTS_USE_NAMESPACE
47 47
48 48 MainWidget::MainWidget(QWidget *parent) :
49 49 QWidget(parent),
50 50 m_addSerieDialog(0),
51 51 m_chart(0)
52 52 {
53 53 m_chart = new QChart();
54 54
55 55 // Grid layout for the controls for configuring the chart widget
56 56 QGridLayout *grid = new QGridLayout();
57 57 QPushButton *addSeriesButton = new QPushButton("Add series");
58 58 connect(addSeriesButton, SIGNAL(clicked()), this, SLOT(addSeries()));
59 59 grid->addWidget(addSeriesButton, 0, 1);
60 60 initBackroundCombo(grid);
61 61 initScaleControls(grid);
62 62 initThemeCombo(grid);
63 63 initCheckboxes(grid);
64 64
65 65 // add row with empty label to make all the other rows static
66 66 grid->addWidget(new QLabel(""), grid->rowCount(), 0);
67 67 grid->setRowStretch(grid->rowCount() - 1, 1);
68 68
69 69 // Create chart view with the chart
70 70 m_chartView = new QChartView(m_chart, this);
71 71 m_chartView->setRubberBand(QChartView::HorizontalRubberBand);
72 72
73 73 // Another grid layout as a main layout
74 74 QGridLayout *mainLayout = new QGridLayout();
75 75 mainLayout->addLayout(grid, 0, 0);
76 76 mainLayout->addWidget(m_chartView, 0, 1, 3, 1);
77 77 setLayout(mainLayout);
78 78 }
79 79
80 80 // Combo box for selecting the chart's background
81 81 void MainWidget::initBackroundCombo(QGridLayout *grid)
82 82 {
83 83 QComboBox *backgroundCombo = new QComboBox(this);
84 84 backgroundCombo->addItem("Color");
85 85 backgroundCombo->addItem("Gradient");
86 86 backgroundCombo->addItem("Image");
87 87 connect(backgroundCombo, SIGNAL(currentIndexChanged(int)),
88 88 this, SLOT(backgroundChanged(int)));
89 89
90 90 grid->addWidget(new QLabel("Background:"), grid->rowCount(), 0);
91 91 grid->addWidget(backgroundCombo, grid->rowCount() - 1, 1);
92 92 }
93 93
94 94 // Scale related controls (auto-scale vs. manual min-max values)
95 95 void MainWidget::initScaleControls(QGridLayout *grid)
96 96 {
97 97 m_autoScaleCheck = new QCheckBox("Automatic scaling");
98 98 connect(m_autoScaleCheck, SIGNAL(stateChanged(int)), this, SLOT(autoScaleChanged(int)));
99 99 // Allow setting also non-sense values (like -2147483648 and 2147483647)
100 100 m_xMinSpin = new QSpinBox();
101 101 m_xMinSpin->setMinimum(INT_MIN);
102 102 m_xMinSpin->setMaximum(INT_MAX);
103 103 m_xMinSpin->setValue(0);
104 104 connect(m_xMinSpin, SIGNAL(valueChanged(int)), this, SLOT(xMinChanged(int)));
105 105 m_xMaxSpin = new QSpinBox();
106 106 m_xMaxSpin->setMinimum(INT_MIN);
107 107 m_xMaxSpin->setMaximum(INT_MAX);
108 108 m_xMaxSpin->setValue(10);
109 109 connect(m_xMaxSpin, SIGNAL(valueChanged(int)), this, SLOT(xMaxChanged(int)));
110 110 m_yMinSpin = new QSpinBox();
111 111 m_yMinSpin->setMinimum(INT_MIN);
112 112 m_yMinSpin->setMaximum(INT_MAX);
113 113 m_yMinSpin->setValue(0);
114 114 connect(m_yMinSpin, SIGNAL(valueChanged(int)), this, SLOT(yMinChanged(int)));
115 115 m_yMaxSpin = new QSpinBox();
116 116 m_yMaxSpin->setMinimum(INT_MIN);
117 117 m_yMaxSpin->setMaximum(INT_MAX);
118 118 m_yMaxSpin->setValue(10);
119 119 connect(m_yMaxSpin, SIGNAL(valueChanged(int)), this, SLOT(yMaxChanged(int)));
120 120
121 121 grid->addWidget(m_autoScaleCheck, grid->rowCount(), 0);
122 122 grid->addWidget(new QLabel("x min:"), grid->rowCount(), 0);
123 123 grid->addWidget(m_xMinSpin, grid->rowCount() - 1, 1);
124 124 grid->addWidget(new QLabel("x max:"), grid->rowCount(), 0);
125 125 grid->addWidget(m_xMaxSpin, grid->rowCount() - 1, 1);
126 126 grid->addWidget(new QLabel("y min:"), grid->rowCount(), 0);
127 127 grid->addWidget(m_yMinSpin, grid->rowCount() - 1, 1);
128 128 grid->addWidget(new QLabel("y max:"), grid->rowCount(), 0);
129 129 grid->addWidget(m_yMaxSpin, grid->rowCount() - 1, 1);
130 130
131 131 m_autoScaleCheck->setChecked(true);
132 132 }
133 133
134 134 // Combo box for selecting theme
135 135 void MainWidget::initThemeCombo(QGridLayout *grid)
136 136 {
137 137 QComboBox *chartTheme = new QComboBox();
138 138 chartTheme->addItem("Default");
139 139 chartTheme->addItem("Light");
140 140 chartTheme->addItem("Blue Cerulean");
141 141 chartTheme->addItem("Dark");
142 142 chartTheme->addItem("Brown Sand");
143 143 chartTheme->addItem("Blue NCS");
144 144 chartTheme->addItem("High Contrast");
145 145 chartTheme->addItem("Blue Icy");
146 146 chartTheme->addItem("Qt");
147 147 connect(chartTheme, SIGNAL(currentIndexChanged(int)),
148 148 this, SLOT(changeChartTheme(int)));
149 149 grid->addWidget(new QLabel("Chart theme:"), 8, 0);
150 150 grid->addWidget(chartTheme, 8, 1);
151 151 }
152 152
153 153 // Different check boxes for customizing chart
154 154 void MainWidget::initCheckboxes(QGridLayout *grid)
155 155 {
156 156 // TODO: setZoomEnabled slot has been removed from QChartView -> Re-implement zoom on/off
157 157 QCheckBox *zoomCheckBox = new QCheckBox("Drag'n drop Zoom");
158 158 // connect(zoomCheckBox, SIGNAL(toggled(bool)), m_chartView, SLOT(setZoomEnabled(bool)));
159 159 zoomCheckBox->setChecked(true);
160 160 grid->addWidget(zoomCheckBox, grid->rowCount(), 0);
161 161
162 162 QCheckBox *aliasCheckBox = new QCheckBox("Anti-alias");
163 163 connect(aliasCheckBox, SIGNAL(toggled(bool)), this, SLOT(antiAliasToggled(bool)));
164 164 aliasCheckBox->setChecked(false);
165 165 grid->addWidget(aliasCheckBox, grid->rowCount(), 0);
166 166
167 QCheckBox *openGLCheckBox = new QCheckBox("Use QGLWidget");
167 QCheckBox *openGLCheckBox = new QCheckBox("Use QOpenGLWidget");
168 168 connect(openGLCheckBox, SIGNAL(toggled(bool)), this, SLOT(openGLToggled(bool)));
169 169 openGLCheckBox->setChecked(false);
170 170 grid->addWidget(openGLCheckBox, grid->rowCount(), 0);
171 171 }
172 172
173 173 void MainWidget::antiAliasToggled(bool enabled)
174 174 {
175 175 m_chartView->setRenderHint(QPainter::Antialiasing, enabled);
176 176 }
177 177
178 178 void MainWidget::openGLToggled(bool enabled)
179 179 {
180 180 if (enabled) {
181 QGLFormat f = QGLFormat::defaultFormat();
182 f.setSampleBuffers(true);
181 QSurfaceFormat f = QSurfaceFormat::defaultFormat();
183 182 f.setSamples(4);
184 QGLFormat::setDefaultFormat(f);
185 QGLWidget *g = new QGLWidget();
183 QSurfaceFormat::setDefaultFormat(f);
184 QOpenGLWidget *g = new QOpenGLWidget();
186 185 m_chartView->setViewport(g);
187 186 } else {
188 187 m_chartView->setViewport(0);
189 188 }
190 189 }
191 190
192 191 void MainWidget::addSeries()
193 192 {
194 193 if (!m_addSerieDialog) {
195 194 m_addSerieDialog = new DataSerieDialog(this);
196 195 connect(m_addSerieDialog, SIGNAL(accepted(QString,int,int,QString,bool)),
197 196 this, SLOT(addSeries(QString,int,int,QString,bool)));
198 197 }
199 198 m_addSerieDialog->exec();
200 199 }
201 200
202 201 QList<RealList> MainWidget::generateTestData(int columnCount, int rowCount, QString dataCharacteristics)
203 202 {
204 203 QList<RealList> testData;
205 204 for (int j(0); j < columnCount; j++) {
206 205 QList <qreal> newColumn;
207 206 for (int i(0); i < rowCount; i++) {
208 207 if (dataCharacteristics == "Sin") {
209 208 newColumn.append(abs(sin(3.14159265358979 / 50 * i) * 100));
210 209 } else if (dataCharacteristics == "Sin + random") {
211 210 newColumn.append(abs(sin(3.14159265358979 / 50 * i) * 100) + (rand() % 5));
212 211 } else if (dataCharacteristics == "Random") {
213 212 newColumn.append(rand() % 10 + (qreal) rand() / (qreal) RAND_MAX);
214 213 } else if (dataCharacteristics == "Linear") {
215 214 //newColumn.append(i * (j + 1.0));
216 215 // TODO: temporary hack to make pie work; prevent zero values:
217 216 newColumn.append(i * (j + 1.0) + 0.1);
218 217 } else { // "constant"
219 218 newColumn.append((j + 1.0));
220 219 }
221 220 }
222 221 testData.append(newColumn);
223 222 }
224 223 return testData;
225 224 }
226 225
227 226 QStringList MainWidget::generateLabels(int count)
228 227 {
229 228 QStringList result;
230 229 for (int i(0); i < count; i++)
231 230 result.append("label" + QString::number(i));
232 231 return result;
233 232 }
234 233
235 234 void MainWidget::addSeries(QString seriesName, int columnCount, int rowCount, QString dataCharacteristics, bool labelsEnabled)
236 235 {
237 236 qDebug() << "addSeries: " << seriesName
238 237 << " columnCount: " << columnCount
239 238 << " rowCount: " << rowCount
240 239 << " dataCharacteristics: " << dataCharacteristics
241 240 << " labels enabled: " << labelsEnabled;
242 241 m_defaultSeriesName = seriesName;
243 242
244 243 QList<RealList> data = generateTestData(columnCount, rowCount, dataCharacteristics);
245 244
246 245 // Line series and scatter series use similar data
247 246 if (seriesName == "Line") {
248 247 for (int j(0); j < data.count(); j ++) {
249 248 QList<qreal> column = data.at(j);
250 249 QLineSeries *series = new QLineSeries();
251 250 series->setName("line" + QString::number(j));
252 251 for (int i(0); i < column.count(); i++)
253 252 series->append(i, column.at(i));
254 253 m_chart->addSeries(series);
255 254 }
256 255 } else if (seriesName == "Area") {
257 256 // TODO: lower series for the area?
258 257 for (int j(0); j < data.count(); j ++) {
259 258 QList<qreal> column = data.at(j);
260 259 QLineSeries *lineSeries = new QLineSeries();
261 260 for (int i(0); i < column.count(); i++)
262 261 lineSeries->append(i, column.at(i));
263 262 QAreaSeries *areaSeries = new QAreaSeries(lineSeries);
264 263 areaSeries->setName("area" + QString::number(j));
265 264 m_chart->addSeries(areaSeries);
266 265 }
267 266 } else if (seriesName == "Scatter") {
268 267 for (int j(0); j < data.count(); j++) {
269 268 QList<qreal> column = data.at(j);
270 269 QScatterSeries *series = new QScatterSeries();
271 270 series->setName("scatter" + QString::number(j));
272 271 for (int i(0); i < column.count(); i++)
273 272 series->append(i, column.at(i));
274 273 m_chart->addSeries(series);
275 274 }
276 275 } else if (seriesName == "Pie") {
277 276 QStringList labels = generateLabels(rowCount);
278 277 for (int j(0); j < data.count(); j++) {
279 278 QPieSeries *series = new QPieSeries();
280 279 QList<qreal> column = data.at(j);
281 280 for (int i(0); i < column.count(); i++)
282 281 series->append(labels.at(i), column.at(i));
283 282 m_chart->addSeries(series);
284 283 }
285 284 } else if (seriesName == "Bar"
286 285 || seriesName == "Stacked bar"
287 286 || seriesName == "Percent bar") {
288 287 QStringList category;
289 288 QStringList labels = generateLabels(rowCount);
290 289 foreach (QString label, labels)
291 290 category << label;
292 291 QAbstractBarSeries* series = 0;
293 292 if (seriesName == "Bar") {
294 293 series = new QBarSeries(this);
295 294 QBarCategoryAxis* axis = new QBarCategoryAxis();
296 295 axis->append(category);
297 296 m_chart->setAxisX(axis,series);
298 297 } else if (seriesName == "Stacked bar") {
299 298 series = new QStackedBarSeries(this);
300 299 QBarCategoryAxis* axis = new QBarCategoryAxis();
301 300 axis->append(category);
302 301 m_chart->setAxisX(axis,series);
303 302 } else {
304 303 series = new QPercentBarSeries(this);
305 304 QBarCategoryAxis* axis = new QBarCategoryAxis();
306 305 axis->append(category);
307 306 m_chart->setAxisX(axis,series);
308 307 }
309 308
310 309 for (int j(0); j < data.count(); j++) {
311 310 QList<qreal> column = data.at(j);
312 311 QBarSet *set = new QBarSet("set" + QString::number(j));
313 312 for (int i(0); i < column.count(); i++)
314 313 *set << column.at(i);
315 314 series->append(set);
316 315 }
317 316
318 317 m_chart->addSeries(series);
319 318 } else if (seriesName == "Spline") {
320 319 for (int j(0); j < data.count(); j ++) {
321 320 QList<qreal> column = data.at(j);
322 321 QSplineSeries *series = new QSplineSeries();
323 322 series->setName("spline" + QString::number(j));
324 323 for (int i(0); i < column.count(); i++)
325 324 series->append(i, column.at(i));
326 325 m_chart->addSeries(series);
327 326 }
328 327 }
329 328 m_chart->createDefaultAxes();
330 329 }
331 330
332 331 void MainWidget::backgroundChanged(int itemIndex)
333 332 {
334 333 qDebug() << "backgroundChanged: " << itemIndex;
335 334 }
336 335
337 336 void MainWidget::autoScaleChanged(int value)
338 337 {
339 338 if (value) {
340 339 // TODO: enable auto scaling
341 340 } else {
342 341 // TODO: set scaling manually (and disable auto scaling)
343 342 }
344 343
345 344 m_xMinSpin->setEnabled(!value);
346 345 m_xMaxSpin->setEnabled(!value);
347 346 m_yMinSpin->setEnabled(!value);
348 347 m_yMaxSpin->setEnabled(!value);
349 348 }
350 349
351 350 void MainWidget::xMinChanged(int value)
352 351 {
353 352 qDebug() << "xMinChanged: " << value;
354 353 }
355 354
356 355 void MainWidget::xMaxChanged(int value)
357 356 {
358 357 qDebug() << "xMaxChanged: " << value;
359 358 }
360 359
361 360 void MainWidget::yMinChanged(int value)
362 361 {
363 362 qDebug() << "yMinChanged: " << value;
364 363 }
365 364
366 365 void MainWidget::yMaxChanged(int value)
367 366 {
368 367 qDebug() << "yMaxChanged: " << value;
369 368 }
370 369
371 370 void MainWidget::changeChartTheme(int themeIndex)
372 371 {
373 372 qDebug() << "changeChartTheme: " << themeIndex;
374 373 if (themeIndex == 0)
375 374 m_chart->setTheme(QChart::ChartThemeLight);
376 375 else
377 376 m_chart->setTheme((QChart::ChartTheme) (themeIndex - 1));
378 377 }
@@ -1,42 +1,42
1 1 /****************************************************************************
2 2 **
3 3 ** Copyright (C) 2015 The Qt Company Ltd
4 4 ** All rights reserved.
5 5 ** For any questions to The Qt Company, please use contact form at http://qt.io
6 6 **
7 7 ** This file is part of the Qt Charts module.
8 8 **
9 9 ** Licensees holding valid commercial license for Qt may use this file in
10 10 ** accordance with the Qt License Agreement provided with the Software
11 11 ** or, alternatively, in accordance with the terms contained in a written
12 12 ** agreement between you and The Qt Company.
13 13 **
14 14 ** If you have questions regarding the use of this file, please use
15 15 ** contact form at http://qt.io
16 16 **
17 17 ****************************************************************************/
18 18
19 19 #include "wavechart.h"
20 20 #include <QtWidgets/QApplication>
21 21 #include <QtWidgets/QMainWindow>
22 #include <QtOpenGL/QGLWidget>
22 #include <QtWidgets/QOpenGLWidget>
23 23
24 24 int main(int argc, char *argv[])
25 25 {
26 26 QApplication a(argc, argv);
27 27
28 28 QMainWindow window;
29 29 QChart *chart = new QChart();
30 30 WaveChart *waveChart = new WaveChart(chart,&window);
31 31
32 waveChart->setViewport( new QGLWidget() );
32 waveChart->setViewport( new QOpenGLWidget() );
33 33 waveChart->setRenderHint(QPainter::Antialiasing);
34 34 chart->setAnimationOptions(QChart::AllAnimations);
35 35 chart->setTitle("This is wave generator.");
36 36
37 37 window.setCentralWidget(waveChart);
38 38 window.resize(400, 300);
39 39 window.show();
40 40
41 41 return a.exec();
42 42 }
@@ -1,8 +1,7
1 1 !include( ../../tests.pri ) {
2 2 error( "Couldn't find the test.pri file!" )
3 3 }
4 4
5 QT+=opengl
6 5 TARGET = wavechart
7 6 SOURCES += main.cpp wavechart.cpp
8 7 HEADERS += wavechart.h
General Comments 0
You need to be logged in to leave comments. Login now