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