trafficgraphwidget.cpp
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 }