1 /*
2 SPDX-FileCopyrightText: 2007 Krzysztof Kundzicz <athantor@gmail.com>
3 SPDX-License-Identifier: GPL-2.0-or-later
4 */
5
6 #include <PlainChartDrawer.h>
7
8 #include <KLocalizedString>
9 #include <QFileDialog>
10
11 namespace kt
12 {
13
PlainChartDrawer(QWidget * p)14 PlainChartDrawer::PlainChartDrawer(QWidget* p) : QFrame(p), ChartDrawer(), pmCtxMenu(new QMenu(this))
15 {
16 setStyleSheet(QStringLiteral(" background-color: ") % QPalette().color(QPalette::Active, QPalette::Base).name() % QLatin1Char(';'));
17
18 setContextMenuPolicy(Qt::CustomContextMenu);
19 MakeCtxMenu();
20
21 connect(this, &PlainChartDrawer::customContextMenuRequested, this, &PlainChartDrawer::showContextMenu);
22 }
23
~PlainChartDrawer()24 PlainChartDrawer::~PlainChartDrawer()
25 {
26 }
27
height() const28 inline PlainChartDrawer::wgtunit_t PlainChartDrawer::height() const
29 {
30 return QWidget::height() - 15;
31 }
32
width() const33 inline PlainChartDrawer::wgtunit_t PlainChartDrawer::width() const
34 {
35 return QWidget::width() - 78;
36 }
37
TY(const wgtunit_t y) const38 inline PlainChartDrawer::wgtunit_t PlainChartDrawer::TY(const wgtunit_t y) const
39 {
40 return height() - y;
41 }
42
FindXScreenCoords(const wgtunit_t x) const43 inline PlainChartDrawer::wgtunit_t PlainChartDrawer::FindXScreenCoords(const wgtunit_t x) const
44 {
45 return (width() / mXMax) * x ;
46 }
47
FindYScreenCoords(const wgtunit_t y) const48 inline PlainChartDrawer::wgtunit_t PlainChartDrawer::FindYScreenCoords(const wgtunit_t y) const
49 {
50 return (height() / mYMax) * y;
51 }
52
paintEvent(QPaintEvent *)53 void PlainChartDrawer::paintEvent(QPaintEvent*)
54 {
55
56 QStyleOption opt;
57 opt.init(this);
58
59 QPainter pnt(this);
60
61 style()->drawPrimitive(QStyle::PE_Widget, &opt, &pnt, this);
62
63 pnt.setRenderHint(QPainter::Antialiasing, mAntiAlias);
64 pnt.setRenderHint(QPainter::TextAntialiasing, mAntiAlias);
65
66 DrawScale(pnt);
67 DrawFrame(pnt);
68 DrawChart(pnt);
69 }
70
DrawScale(QPainter & rPnt)71 void PlainChartDrawer::DrawScale(QPainter& rPnt)
72 {
73 if (!mYMax) {
74 return;
75 }
76
77
78 QPen oldpen = rPnt.pen();
79
80 QPen pen;
81
82 if (mBgdGrid) {
83
84 pen.setColor(QPalette().color(QPalette::AlternateBase));
85 rPnt.setPen(pen);
86
87 for (wgtunit_t i = 5; i < height(); i += 10) {
88 rPnt.drawLine(0, TY(i), width(), TY(i));
89 }
90
91 for (wgtunit_t i = 5; i < width(); i += 10) {
92 rPnt.drawLine(i, TY(0), i, TY(height()));
93 }
94 }
95
96 wgtunit_t scale = height() / 8;
97
98 pen.setColor(QPalette().color(QPalette::Text));
99 pen.setWidth(1);
100 pen.setStyle(Qt::DotLine);
101
102 rPnt.setPen(pen);
103
104 QFont oldfont(rPnt.font()), qf(oldfont);
105 qf.setStretch(QFont::SemiCondensed);
106 rPnt.setFont(qf);
107
108 rPnt.drawLine(0, TY(height() - 10), width(), TY(height() - 10));
109 rPnt.drawText(width() + 4, TY(height() - 10) + 4, QString::number(mYMax, 'f', 1));
110
111 for (wgtunit_t i = 0; i < height() - 15; i += scale) {
112 rPnt.drawLine(0, TY(i), width(), TY(i));
113 rPnt.drawText(width() + 5, TY(i) + 5, QString::number((mYMax / 8.0) * (i / scale), 'f', 1));
114 }
115
116 rPnt.setPen(oldpen);
117
118 rPnt.setFont(oldfont);
119 }
120
DrawFrame(QPainter & rPnt)121 void PlainChartDrawer::DrawFrame(QPainter& rPnt)
122 {
123 QPen oldpen = rPnt.pen();
124 QPen pen;
125
126 pen.setColor(QPalette().color(QPalette::Text));
127 pen.setWidth(3);
128 rPnt.setPen(pen);
129
130 QPoint points[3] = {
131 QPoint(0, TY(0)),
132 QPoint(width(), TY(0)),
133 QPoint(width(), TY(height()))
134 };
135
136 rPnt.drawPolyline(points, 3);
137
138 QFont oldf(rPnt.font());
139 QFont newf(oldf);
140 newf.setWeight(QFont::Bold);
141 newf.setStretch(QFont::SemiCondensed);
142 newf.setPointSize(10);
143 newf.setUnderline(1);
144 rPnt.setFont(newf);
145
146
147 QColor qc(pen.color());
148 qc.setAlphaF(0.75);
149 pen.setColor(qc);
150 rPnt.setPen(pen);
151
152 rPnt.drawText(width() + 42., TY(-10.), pmUnitName);
153
154 rPnt.setFont(oldf);
155 rPnt.setPen(oldpen);
156
157 }
158
DrawChart(QPainter & rPnt)159 void PlainChartDrawer::DrawChart(QPainter& rPnt)
160 {
161 QPen oldpen = rPnt.pen();
162
163 for (size_t i = 0; i < pmVals.size(); i++) {
164 DrawChartLine(rPnt, pmVals.at(i));
165 DrawCurrentValue(rPnt, pmVals.at(i), i);
166
167 if (pmVals.at(i).getMarkMax()) {
168 DrawMaximum(rPnt, pmVals.at(i), i);
169 }
170 }
171
172 rPnt.setPen(oldpen);
173 }
174
DrawChartLine(QPainter & rPnt,const ChartDrawerData & rCdd)175 void PlainChartDrawer::DrawChartLine(QPainter& rPnt, const ChartDrawerData& rCdd)
176 {
177
178 QPen qp = rCdd.getPen();
179 qp.setJoinStyle(Qt::RoundJoin);
180 rPnt.setPen(qp);
181
182 const ChartDrawerData::val_t& vals = rCdd.getValues();
183
184 QPointF* l = new QPointF[vals.size()];
185
186 for (size_t i = 0; i < vals.size(); i++) {
187 l[i] = QPointF(
188 FindXScreenCoords(i),
189 TY(FindYScreenCoords(vals.at(i)))
190 );
191 }
192
193 l[vals.size() - 1] = QPointF(
194
195 width(),
196 TY(FindYScreenCoords(*(vals.end() - 1)))
197 );
198
199 rPnt.drawPolyline(l, vals.size());
200 delete [] l;
201 }
202
DrawCurrentValue(QPainter & rPnt,const ChartDrawerData & rCdd,size_t idx)203 void PlainChartDrawer::DrawCurrentValue(QPainter& rPnt, const ChartDrawerData& rCdd, size_t idx)
204 {
205 QPen qp = rCdd.getPen();
206 qp.setJoinStyle(Qt::RoundJoin);
207
208 QColor qc(qp.color());
209
210 QFont oldfont(rPnt.font()), qf(oldfont);
211 qf.setStretch(QFont::SemiCondensed);
212
213 rPnt.setFont(qf);
214 rPnt.setPen(qp);
215
216 idx++;
217 wgtunit_t y = -5 + (idx * 16);
218
219 wgtunit_t val = *(rCdd.getValues().end() - 1);
220
221 wgtunit_t lenmod;
222
223 if (val <= 9.99) {
224 lenmod = 19;
225 } else if (val <= 99.99) {
226 lenmod = 14;
227 } else if (val <= 999.99) {
228 lenmod = 7.5;
229 } else if (val <= 9999.99) {
230 lenmod = 1.5;
231 } else {
232 lenmod = -5;
233 }
234
235
236 rPnt.setBackgroundMode(Qt::OpaqueMode);
237
238 rPnt.drawText(QWidget::width() - (40 - lenmod), y, QString::number(val, 'f', 2));
239 rPnt.setBackgroundMode(Qt::TransparentMode);
240
241 qc.setAlphaF(0.35);
242 qp.setColor(qc);
243 qp.setStyle(Qt::DashLine);
244 rPnt.setPen(qp);
245
246 QPointF l[3] = {
247 QPointF(width(), TY(FindYScreenCoords(*(rCdd.getValues().end() - 1)))),
248 QPointF(width() + (38 + lenmod), y + 2),
249 QPointF(QWidget::width(), y + 2.5),
250 };
251
252 rPnt.drawPolyline(l, 3);
253
254 rPnt.setFont(oldfont);
255 }
256
DrawMaximum(QPainter & rPnt,const ChartDrawerData & rCdd,size_t idx)257 void PlainChartDrawer::DrawMaximum(QPainter& rPnt, const ChartDrawerData& rCdd, size_t idx)
258 {
259 QPen qp = rCdd.getPen();
260 QBrush oldb = qp.brush();
261 QColor qc(qp.color());
262
263 std::pair<qreal, size_t> max = rCdd.findMax();
264
265 qc.setAlphaF(0.7);
266 qp.setColor(qc);
267 qp.setStyle(Qt::DashLine);
268 rPnt.setPen(qp);
269 rPnt.drawLine(FindXScreenCoords(max.second), TY(0), FindXScreenCoords(max.second), TY(height()));
270
271
272 idx++;
273 wgtunit_t y = 5.0 + (idx * 14);
274 wgtunit_t x = FindXScreenCoords(max.second);
275
276 if (x < 35) {
277 x += 5;
278 } else {
279 x -= 35;
280 }
281
282
283 qc.setAlphaF(1);
284
285 qp.setColor(qc);
286 rPnt.setPen(qp);
287 qp.setStyle(Qt::SolidLine);
288 rPnt.setBackgroundMode(Qt::OpaqueMode);
289
290 QFont oldfont(rPnt.font()), qf(oldfont);
291 qf.setStretch(QFont::SemiCondensed);
292 rPnt.setFont(qf);
293
294 rPnt.drawText(x, y, QString::number(max.first, 'f', 1));
295 rPnt.setFont(oldfont);
296
297 rPnt.setBackgroundMode(Qt::TransparentMode);
298 }
299
MakeCtxMenu()300 void PlainChartDrawer::MakeCtxMenu()
301 {
302
303 connect(pmCtxMenu->addAction(i18nc("@action:inmenu", "Save as image…")),
304 &QAction::triggered, this, [this](bool) {
305 renderToImage();
306 });
307
308 pmCtxMenu->addSeparator();
309
310 connect(pmCtxMenu->addAction(i18nc("@action:inmenu Recalculate the 0Y axis and then redraw the chart", "Rescale")),
311 &QAction::triggered, this, [this](bool) {
312 findSetMax();
313 });
314
315 pmCtxMenu->addSeparator();
316
317 QAction* rst = pmCtxMenu->addAction(i18nc("@action:inmenu", "Reset"));
318
319 connect(rst, &QAction::triggered, this, [this](bool) {
320 zeroAll();
321 });
322 }
323
showContextMenu(const QPoint & pos)324 void PlainChartDrawer::showContextMenu(const QPoint& pos)
325 {
326 pmCtxMenu->exec(mapToGlobal(pos));
327 }
328
renderToImage()329 void PlainChartDrawer::renderToImage()
330 {
331 QString saveloc = QFileDialog::getSaveFileName(this, i18n("Select path to save image…"), i18n("Image files") + QLatin1String(" (*.png)"));
332 if (!saveloc.length())
333 return;
334
335 QImage qi(QWidget::width(), QWidget::height(), QImage::Format_RGB32);
336 render(&qi);
337 qi.save(saveloc, "PNG", 0);
338 }
339
addValue(const size_t idx,const wgtunit_t val,const bool upd)340 void PlainChartDrawer::addValue(const size_t idx, const wgtunit_t val, const bool upd)
341 {
342 if (idx >= pmVals.size()) {
343 return;
344 } else {
345 pmVals[idx].addValue(val);
346 }
347
348 if (mCurrMaxMode == MM_Top) {
349 if ((val > 1) && (val > mYMax)) {
350 mYMax = val + 5;
351 }
352 } else if (mCurrMaxMode == MM_Exact) {
353 findSetMax();
354 }
355
356 if (upd) {
357 update();
358 }
359
360 }
361
addDataSet(ChartDrawerData Cdd)362 void PlainChartDrawer::addDataSet(ChartDrawerData Cdd)
363 {
364 Cdd.setSize(mXMax);
365 pmVals.push_back(Cdd);
366
367 setLegend(makeLegendString());
368 }
369
insertDataSet(const size_t idx,ChartDrawerData Cdd)370 void PlainChartDrawer::insertDataSet(const size_t idx, ChartDrawerData Cdd)
371 {
372 pmVals.insert(pmVals.begin() + idx, Cdd);
373 setLegend(makeLegendString());
374 }
375
removeDataSet(const size_t idx)376 void PlainChartDrawer::removeDataSet(const size_t idx)
377 {
378 if (idx >= pmVals.size()) {
379 return;
380 } else {
381 pmVals.erase((pmVals.begin()) + idx);
382 }
383
384 setLegend(makeLegendString());
385 }
386
387
zero(const size_t idx)388 void PlainChartDrawer::zero(const size_t idx)
389 {
390 if (idx >= pmVals.size()) {
391 return;
392 } else {
393 pmVals[idx].zero();
394 }
395
396 findSetMax();
397 }
398
zeroAll()399 void PlainChartDrawer::zeroAll()
400 {
401 for (size_t idx = 0; idx < pmVals.size(); idx++) {
402 pmVals[idx].zero();
403 }
404
405 findSetMax();
406
407 Q_EMIT Zeroed(this);
408 }
409
setMaxMode(const MaxMode mm)410 void PlainChartDrawer::setMaxMode(const MaxMode mm)
411 {
412 mCurrMaxMode = mm;
413 }
414
setXMax(const wgtunit_t x)415 void PlainChartDrawer::setXMax(const wgtunit_t x)
416 {
417 mXMax = x;
418
419 for (size_t i = 0; i < pmVals.size(); i++) {
420 pmVals.at(i).setSize(x);
421 }
422 }
423
424
setYMax(const wgtunit_t y)425 void PlainChartDrawer::setYMax(const wgtunit_t y)
426 {
427 mYMax = y;
428 }
429
setPen(const size_t idx,const QPen & rP)430 void PlainChartDrawer::setPen(const size_t idx, const QPen& rP)
431 {
432 if (idx >= pmVals.size()) {
433 return;
434 }
435
436 pmVals.at(idx).setPen(rP);
437
438 makeLegendString();
439 }
440
getUuid(const size_t idx) const441 QUuid PlainChartDrawer::getUuid(const size_t idx) const
442 {
443 if (idx >= pmVals.size()) {
444 return nullptr;
445 }
446
447 return pmVals.at(idx).getUuid();
448 }
449
setUuid(const size_t idx,const QUuid & rU)450 void PlainChartDrawer::setUuid(const size_t idx, const QUuid& rU)
451 {
452 if (idx >= pmVals.size()) {
453 return;
454 }
455
456 pmVals.at(idx).setUuid(rU);
457 }
458
findUuidInSet(const QUuid & rU) const459 int16_t PlainChartDrawer::findUuidInSet(const QUuid& rU) const
460 {
461
462 for (int16_t i = 0; i < static_cast<int16_t>(pmVals.size()); i++) {
463 if (pmVals.at(i).getUuid() == rU) {
464 return i;
465 }
466 }
467
468 return -1;
469 }
470
enableAntiAlias(bool aa)471 void PlainChartDrawer::enableAntiAlias(bool aa)
472 {
473 mAntiAlias = aa;
474 }
475
findSetMax()476 void PlainChartDrawer::findSetMax()
477 {
478 wgtunit_t max = 1;
479
480 for (size_t i = 0; i < pmVals.size(); i++) {
481 wgtunit_t locval = pmVals.at(i).findMax().first;
482
483 if (locval > max) {
484 max = locval;
485 }
486 }
487
488 mYMax = max + 5;
489 }
490
makeLegendString()491 QString PlainChartDrawer::makeLegendString()
492 {
493 QString lgnd = i18n("<h1 align='center' style='font-size: large; text-decoration: underline'>Legend:</h1><ul type='square'>");
494
495 for (size_t i = 0; i < pmVals.size(); i++) {
496 lgnd += i18n("<li><span style='background-color: %1; font-size: 14px; font-family: monospace'> </span> — %2</li>",
497 pmVals.at(i).getPen().color().name(),
498 pmVals.at(i).getName()
499 );
500 }
501
502 return lgnd + QStringLiteral("</ul>");
503 }
504
setLegend(const QString & rL)505 void PlainChartDrawer::setLegend(const QString& rL)
506 {
507 setToolTip(rL);
508 }
509
update()510 void PlainChartDrawer::update()
511 {
512 QFrame::update();
513 }
514
enableBackgroundGrid(bool bg)515 void PlainChartDrawer::enableBackgroundGrid(bool bg)
516 {
517 mBgdGrid = bg;
518 }
519
520 } //NS end
521
522