Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions src/qt/guiutil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,40 @@ QString formatBytes(uint64_t bytes)
return QObject::tr("%1 GB").arg(bytes / 1'000'000'000);
}

QString formatBytesps(float val)
{
if (val < 10)
//: "Byes per second"
return QObject::tr("%1 B/s").arg(0.01 * int(val * 100));
if (val < 100)
//: "Bytes per second"
return QObject::tr("%1 B/s").arg(0.1 * int(val * 10));
if (val < 1'000)
//: "Bytes per second"
return QObject::tr("%1 B/s").arg((int)val);
if (val < 10'000)
//: "Kilobytes per second"
return QObject::tr("%1 kB/s").arg(0.01 * ((int)val / 10));
if (val < 100'000)
//: "Kilobytes per second"
return QObject::tr("%1 kB/s").arg(0.1 * ((int)val / 100));
if (val < 1'000'000)
//: "Kilobytes per second"
return QObject::tr("%1 kB/s").arg((int)val / 1'000);
if (val < 10'000'000)
//: "Megabytes per second"
return QObject::tr("%1 MB/s").arg(0.01 * ((int)val / 10'000));
if (val < 100'000'000)
//: "Megabytes per second"
return QObject::tr("%1 MB/s").arg(0.1 * ((int)val / 100'000));
if (val < 10'000'000'000)
//: "Megabytes per second"
return QObject::tr("%1 MB/s").arg((long)val / 1'000'000);

//: "Gigabytes per second"
return QObject::tr("%1 GB/s").arg((long)val / 1'000'000'000);
}

qreal calculateIdealFontSize(int width, const QString& text, QFont font, qreal minPointSize, qreal font_size) {
while(font_size >= minPointSize) {
font.setPointSizeF(font_size);
Expand Down
1 change: 1 addition & 0 deletions src/qt/guiutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ namespace GUIUtil
QString formatNiceTimeOffset(qint64 secs);

QString formatBytes(uint64_t bytes);
QString formatBytesps(float bytes);

qreal calculateIdealFontSize(int width, const QString& text, QFont font, qreal minPointSize = 4, qreal startPointSize = 14);

Expand Down
121 changes: 112 additions & 9 deletions src/qt/trafficgraphwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@
#include <interfaces/node.h>
#include <qt/trafficgraphwidget.h>
#include <qt/clientmodel.h>
#include <qt/guiutil.h>

#include <QPainter>
#include <QPainterPath>
#include <QColor>
#include <QTimer>
#include <QHelpEvent>
#include <QToolTip>

#include <cmath>

Expand All @@ -21,16 +24,23 @@
TrafficGraphWidget::TrafficGraphWidget(QWidget *parent) :
QWidget(parent),
timer(nullptr),
tt_timer(nullptr),
fMax(0.0f),
nMins(0),
vSamplesIn(),
vSamplesOut(),
vTimeStamp(),
nLastBytesIn(0),
nLastBytesOut(0),
clientModel(nullptr)
{
timer = new QTimer(this);
tt_timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &TrafficGraphWidget::updateRates);
connect(tt_timer, &QTimer::timeout, this, &TrafficGraphWidget::updateToolTip);
tt_timer->setInterval(500);
tt_timer->start();
setMouseTracking(true);
}

void TrafficGraphWidget::setClientModel(ClientModel *model)
Expand All @@ -47,22 +57,66 @@ int TrafficGraphWidget::getGraphRangeMins() const
return nMins;
}

int TrafficGraphWidget::y_value(float value)
{
int h = height() - YMARGIN * 2;
return YMARGIN + h - (h * 1.0 * value / fMax);
}

void TrafficGraphWidget::paintPath(QPainterPath &path, QQueue<float> &samples)
{
int sampleCount = samples.size();
if(sampleCount > 0) {
if (sampleCount > 0) {
int h = height() - YMARGIN * 2, w = width() - XMARGIN * 2;
int x = XMARGIN + w;
path.moveTo(x, YMARGIN + h);
for(int i = 0; i < sampleCount; ++i) {
x = XMARGIN + w - w * i / DESIRED_SAMPLES;
int y = YMARGIN + h - (int)(h * samples.at(i) / fMax);
int y = y_value(samples.at(i));
path.lineTo(x, y);
}
path.lineTo(x, YMARGIN + h);
}
}

float floatmax(float a, float b)
{
if (a > b) return a;
else return b;
}

void TrafficGraphWidget::mouseMoveEvent(QMouseEvent *event)
{
QWidget::mouseMoveEvent(event);
static int last_x = -1;
static int last_y = -1;
int x = event->x();
int y = event->y();
x_offset = event->globalX() - x;
y_offset = event->globalY() - y;
if (last_x == x && last_y == y) return; // Do nothing if mouse hasn't moved
int h = height() - YMARGIN * 2, w = width() - XMARGIN * 2;
int i = (w + XMARGIN - x) * DESIRED_SAMPLES / w;
unsigned int smallest_distance = 50; int closest_i = -1;
int sampleSize = vTimeStamp.size();
if (sampleSize && i >= -10 && i < sampleSize + 2 && y <= h + YMARGIN + 3) {
for (int test_i = std::max(i - 2, 0); test_i < std::min(i + 10, sampleSize); test_i++) {
float val = floatmax(vSamplesIn.at(test_i), vSamplesOut.at(test_i));
int y_data = y_value(val);
unsigned int distance = abs(y - y_data);
if (distance < smallest_distance) {
smallest_distance = distance;
closest_i = test_i;
}
}
}
if (ttpoint != closest_i) {
ttpoint = closest_i;
update(); // Calls paintEvent() to draw or delete the highlighted point
}
last_x = x; last_y = y;
}

void TrafficGraphWidget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
Expand All @@ -84,7 +138,7 @@ void TrafficGraphWidget::paintEvent(QPaintEvent *)

// draw lines
painter.setPen(axisCol);
painter.drawText(XMARGIN, YMARGIN + h - h * val / fMax-yMarginText, QString("%1 %2").arg(val).arg(units));
painter.drawText(XMARGIN, y_value(val)-yMarginText, QString("%1 %2").arg(val).arg(units));
for(float y = val; y < fMax; y += val) {
int yy = YMARGIN + h - h * y / fMax;
painter.drawLine(XMARGIN, yy, width() - XMARGIN, yy);
Expand All @@ -94,13 +148,13 @@ void TrafficGraphWidget::paintEvent(QPaintEvent *)
axisCol = axisCol.darker();
val = pow(10.0f, base - 1);
painter.setPen(axisCol);
painter.drawText(XMARGIN, YMARGIN + h - h * val / fMax-yMarginText, QString("%1 %2").arg(val).arg(units));
painter.drawText(XMARGIN, y_value(val)-yMarginText, QString("%1 %2").arg(val).arg(units));
int count = 1;
for(float y = val; y < fMax; y += val, count++) {
// don't overwrite lines drawn above
if(count % 10 == 0)
continue;
int yy = YMARGIN + h - h * y / fMax;
int yy = y_value(y);
painter.drawLine(XMARGIN, yy, width() - XMARGIN, yy);
}
}
Expand All @@ -120,18 +174,63 @@ void TrafficGraphWidget::paintEvent(QPaintEvent *)
painter.setPen(Qt::red);
painter.drawPath(p);
}
int sampleCount = vTimeStamp.size();
if (ttpoint >= 0 && ttpoint < sampleCount) {
painter.setPen(Qt::yellow);
int w = width() - XMARGIN * 2;
int x = XMARGIN + w - w * ttpoint / DESIRED_SAMPLES;
int y = y_value(floatmax(vSamplesIn.at(ttpoint), vSamplesOut.at(ttpoint)));
painter.drawEllipse(QPointF(x, y), 3, 3);
QString strTime;
int64_t sampleTime = vTimeStamp.at(ttpoint);
int age = GetTime() - sampleTime/1000;
if (age < 60*60*23)
strTime = QString::fromStdString(FormatISO8601Time(sampleTime/1000));
else
strTime = QString::fromStdString(FormatISO8601DateTime(sampleTime/1000));
int milliseconds_between_samples = 1000;
if (ttpoint > 0)
milliseconds_between_samples = std::min(milliseconds_between_samples, int(vTimeStamp.at(ttpoint-1) - sampleTime));
if (ttpoint + 1 < sampleCount)
milliseconds_between_samples = std::min(milliseconds_between_samples, int(sampleTime - vTimeStamp.at(ttpoint+1)));
if (milliseconds_between_samples < 1000)
strTime += QString::fromStdString(strprintf(".%03d", (sampleTime%1000)));
QString strData = tr("In") + " " + GUIUtil::formatBytesps(vSamplesIn.at(ttpoint)*1000) + "\n" + tr("Out") + " " + GUIUtil::formatBytesps(vSamplesOut.at(ttpoint)*1000);
// Line below allows ToolTip to move faster than once every 10 seconds.
QToolTip::showText(QPoint(x + x_offset, y + y_offset), strTime + "\n. " + strData);
QToolTip::showText(QPoint(x + x_offset, y + y_offset), strTime + "\n " + strData);
tt_time = GetTime();
} else
QToolTip::hideText();
}

void TrafficGraphWidget::updateToolTip()
{
if (!QToolTip::isVisible()) {
if (ttpoint >= 0) { // Remove the yellow circle if the ToolTip has gone due to mouse moving elsewhere.
ttpoint = -1;
update();
}
} else if (GetTime() >= tt_time + 9) { // ToolTip is about to expire so refresh it.
update();
}
}

void TrafficGraphWidget::updateRates()
{
if(!clientModel) return;

int64_t nTime = GetTimeMillis();
static int64_t nLastTime = nTime - timer->interval();
int nRealInterval = nTime - nLastTime;
quint64 bytesIn = clientModel->node().getTotalBytesRecv(),
bytesOut = clientModel->node().getTotalBytesSent();
float in_rate_kilobytes_per_sec = static_cast<float>(bytesIn - nLastBytesIn) / timer->interval();
float out_rate_kilobytes_per_sec = static_cast<float>(bytesOut - nLastBytesOut) / timer->interval();
float in_rate_kilobytes_per_sec = static_cast<float>(bytesIn - nLastBytesIn) / nRealInterval;
float out_rate_kilobytes_per_sec = static_cast<float>(bytesOut - nLastBytesOut) / nRealInterval;
vSamplesIn.push_front(in_rate_kilobytes_per_sec);
vSamplesOut.push_front(out_rate_kilobytes_per_sec);
vTimeStamp.push_front(nLastTime);
nLastTime = nTime;
nLastBytesIn = bytesIn;
nLastBytesOut = bytesOut;

Expand All @@ -141,6 +240,9 @@ void TrafficGraphWidget::updateRates()
while(vSamplesOut.size() > DESIRED_SAMPLES) {
vSamplesOut.pop_back();
}
while(vTimeStamp.size() > DESIRED_SAMPLES) {
vTimeStamp.pop_back();
}

float tmax = 0.0f;
for (const float f : vSamplesIn) {
Expand All @@ -150,6 +252,7 @@ void TrafficGraphWidget::updateRates()
if(f > tmax) tmax = f;
}
fMax = tmax;
if (ttpoint >=0 && ttpoint < vTimeStamp.size()) ttpoint++; // Move the selected point to the left
update();
}

Expand All @@ -159,8 +262,7 @@ void TrafficGraphWidget::setGraphRangeMins(int mins)
int msecsPerSample = nMins * 60 * 1000 / DESIRED_SAMPLES;
timer->stop();
timer->setInterval(msecsPerSample);

clear();
timer->start();

This comment was marked as off-topic.

}

void TrafficGraphWidget::clear()
Expand All @@ -169,6 +271,7 @@ void TrafficGraphWidget::clear()

vSamplesOut.clear();
vSamplesIn.clear();
vTimeStamp.clear();
fMax = 0.0f;

if(clientModel) {
Expand Down
9 changes: 9 additions & 0 deletions src/qt/trafficgraphwidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,29 @@ class TrafficGraphWidget : public QWidget

protected:
void paintEvent(QPaintEvent *) override;
int y_value(float value);
void mouseMoveEvent(QMouseEvent *event) override;
int ttpoint = -1;
int x_offset = 0;
int y_offset = 0;
int64_t tt_time = 0;

public Q_SLOTS:
void updateRates();
void updateToolTip();
void setGraphRangeMins(int mins);
void clear();

private:
void paintPath(QPainterPath &path, QQueue<float> &samples);

QTimer *timer;
QTimer *tt_timer;
float fMax;
int nMins;
QQueue<float> vSamplesIn;
QQueue<float> vSamplesOut;
QQueue<int64_t> vTimeStamp;
quint64 nLastBytesIn;
quint64 nLastBytesOut;
ClientModel *clientModel;
Expand Down
11 changes: 11 additions & 0 deletions src/util/time.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,17 @@ std::string FormatISO8601Date(int64_t nTime) {
return strprintf("%04i-%02i-%02i", ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday);
}

std::string FormatISO8601Time(int64_t nTime) {
struct tm ts;
time_t time_val = nTime;
#ifdef HAVE_GMTIME_R
gmtime_r(&time_val, &ts);
#else
gmtime_s(&ts, &time_val);
#endif
return strprintf("%02i:%02i:%02iZ", ts.tm_hour, ts.tm_min, ts.tm_sec);
}

int64_t ParseISO8601DateTime(const std::string& str)
{
static const boost::posix_time::ptime epoch = boost::posix_time::from_time_t(0);
Expand Down
1 change: 1 addition & 0 deletions src/util/time.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ T GetTime();
*/
std::string FormatISO8601DateTime(int64_t nTime);
std::string FormatISO8601Date(int64_t nTime);
std::string FormatISO8601Time(int64_t nTime);
int64_t ParseISO8601DateTime(const std::string& str);

/**
Expand Down