trafficgraphwidget.cpp raw
1 // Copyright (c) 2011-present The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5 #include <interfaces/node.h>
6 #include <qt/trafficgraphwidget.h>
7 #include <qt/clientmodel.h>
8
9 #include <QPainter>
10 #include <QPainterPath>
11 #include <QColor>
12 #include <QTimer>
13
14 #include <chrono>
15 #include <cmath>
16
17 #define DESIRED_SAMPLES 800
18
19 #define XMARGIN 10
20 #define YMARGIN 10
21
22 TrafficGraphWidget::TrafficGraphWidget(QWidget* parent)
23 : QWidget(parent),
24 vSamplesIn(),
25 vSamplesOut()
26 {
27 timer = new QTimer(this);
28 connect(timer, &QTimer::timeout, this, &TrafficGraphWidget::updateRates);
29 }
30
31 void TrafficGraphWidget::setClientModel(ClientModel *model)
32 {
33 clientModel = model;
34 if(model) {
35 nLastBytesIn = model->node().getTotalBytesRecv();
36 nLastBytesOut = model->node().getTotalBytesSent();
37 }
38 }
39
40 std::chrono::minutes TrafficGraphWidget::getGraphRange() const { return m_range; }
41
42 void TrafficGraphWidget::paintPath(QPainterPath &path, QQueue<float> &samples)
43 {
44 int sampleCount = samples.size();
45 if(sampleCount > 0) {
46 int h = height() - YMARGIN * 2, w = width() - XMARGIN * 2;
47 int x = XMARGIN + w;
48 path.moveTo(x, YMARGIN + h);
49 for(int i = 0; i < sampleCount; ++i) {
50 x = XMARGIN + w - w * i / DESIRED_SAMPLES;
51 int y = YMARGIN + h - (int)(h * samples.at(i) / fMax);
52 path.lineTo(x, y);
53 }
54 path.lineTo(x, YMARGIN + h);
55 }
56 }
57
58 void TrafficGraphWidget::paintEvent(QPaintEvent *)
59 {
60 QPainter painter(this);
61 painter.fillRect(rect(), Qt::black);
62
63 if(fMax <= 0.0f) return;
64
65 QColor axisCol(Qt::gray);
66 int h = height() - YMARGIN * 2;
67 painter.setPen(axisCol);
68 painter.drawLine(XMARGIN, YMARGIN + h, width() - XMARGIN, YMARGIN + h);
69
70 // decide what order of magnitude we are
71 int base = std::floor(std::log10(fMax));
72 float val = std::pow(10.0f, base);
73
74 const QString units = tr("kB/s");
75 const float yMarginText = 2.0;
76
77 // draw lines
78 painter.setPen(axisCol);
79 painter.drawText(XMARGIN, YMARGIN + h - h * val / fMax-yMarginText, QString("%1 %2").arg(val).arg(units));
80 for(float y = val; y < fMax; y += val) {
81 int yy = YMARGIN + h - h * y / fMax;
82 painter.drawLine(XMARGIN, yy, width() - XMARGIN, yy);
83 }
84 // if we drew 3 or fewer lines, break them up at the next lower order of magnitude
85 if(fMax / val <= 3.0f) {
86 axisCol = axisCol.darker();
87 val = pow(10.0f, base - 1);
88 painter.setPen(axisCol);
89 painter.drawText(XMARGIN, YMARGIN + h - h * val / fMax-yMarginText, QString("%1 %2").arg(val).arg(units));
90 int count = 1;
91 for(float y = val; y < fMax; y += val, count++) {
92 // don't overwrite lines drawn above
93 if(count % 10 == 0)
94 continue;
95 int yy = YMARGIN + h - h * y / fMax;
96 painter.drawLine(XMARGIN, yy, width() - XMARGIN, yy);
97 }
98 }
99
100 painter.setRenderHint(QPainter::Antialiasing);
101 if(!vSamplesIn.empty()) {
102 QPainterPath p;
103 paintPath(p, vSamplesIn);
104 painter.fillPath(p, QColor(0, 255, 0, 128));
105 painter.setPen(Qt::green);
106 painter.drawPath(p);
107 }
108 if(!vSamplesOut.empty()) {
109 QPainterPath p;
110 paintPath(p, vSamplesOut);
111 painter.fillPath(p, QColor(255, 0, 0, 128));
112 painter.setPen(Qt::red);
113 painter.drawPath(p);
114 }
115 }
116
117 void TrafficGraphWidget::updateRates()
118 {
119 if(!clientModel) return;
120
121 quint64 bytesIn = clientModel->node().getTotalBytesRecv(),
122 bytesOut = clientModel->node().getTotalBytesSent();
123 float in_rate_kilobytes_per_sec = static_cast<float>(bytesIn - nLastBytesIn) / timer->interval();
124 float out_rate_kilobytes_per_sec = static_cast<float>(bytesOut - nLastBytesOut) / timer->interval();
125 vSamplesIn.push_front(in_rate_kilobytes_per_sec);
126 vSamplesOut.push_front(out_rate_kilobytes_per_sec);
127 nLastBytesIn = bytesIn;
128 nLastBytesOut = bytesOut;
129
130 while(vSamplesIn.size() > DESIRED_SAMPLES) {
131 vSamplesIn.pop_back();
132 }
133 while(vSamplesOut.size() > DESIRED_SAMPLES) {
134 vSamplesOut.pop_back();
135 }
136
137 float tmax = 0.0f;
138 for (const float f : vSamplesIn) {
139 if(f > tmax) tmax = f;
140 }
141 for (const float f : vSamplesOut) {
142 if(f > tmax) tmax = f;
143 }
144 fMax = tmax;
145 update();
146 }
147
148 void TrafficGraphWidget::setGraphRange(std::chrono::minutes new_range)
149 {
150 m_range = new_range;
151 const auto msecs_per_sample{std::chrono::duration_cast<std::chrono::milliseconds>(m_range) / DESIRED_SAMPLES};
152 timer->stop();
153 timer->setInterval(msecs_per_sample);
154
155 clear();
156 }
157
158 void TrafficGraphWidget::clear()
159 {
160 timer->stop();
161
162 vSamplesOut.clear();
163 vSamplesIn.clear();
164 fMax = 0.0f;
165
166 if(clientModel) {
167 nLastBytesIn = clientModel->node().getTotalBytesRecv();
168 nLastBytesOut = clientModel->node().getTotalBytesSent();
169 }
170 timer->start();
171 }
172