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