From b2596f64cfd53bae74011bc46333d36ed8e89ef0 Mon Sep 17 00:00:00 2001 From: R E Broadley Date: Thu, 2 Dec 2021 13:27:34 +0100 Subject: [PATCH 1/8] Network Graph - Create y_value function --- src/qt/trafficgraphwidget.cpp | 14 ++++++++++---- src/qt/trafficgraphwidget.h | 1 + 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/qt/trafficgraphwidget.cpp b/src/qt/trafficgraphwidget.cpp index aebd44d5f7c..59c4d2cf1b4 100644 --- a/src/qt/trafficgraphwidget.cpp +++ b/src/qt/trafficgraphwidget.cpp @@ -44,6 +44,12 @@ void TrafficGraphWidget::setClientModel(ClientModel *model) std::chrono::minutes TrafficGraphWidget::getGraphRange() const { return m_range; } +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 &samples) { int sampleCount = samples.size(); @@ -53,7 +59,7 @@ void TrafficGraphWidget::paintPath(QPainterPath &path, QQueue &samples) 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); @@ -81,7 +87,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); @@ -91,13 +97,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); } } diff --git a/src/qt/trafficgraphwidget.h b/src/qt/trafficgraphwidget.h index a40b7345403..ac68fd442de 100644 --- a/src/qt/trafficgraphwidget.h +++ b/src/qt/trafficgraphwidget.h @@ -28,6 +28,7 @@ class TrafficGraphWidget : public QWidget protected: void paintEvent(QPaintEvent *) override; + int y_value(float value); public Q_SLOTS: void updateRates(); From 1a6644a9ca66402302f2e615baf4fa48161ce85b Mon Sep 17 00:00:00 2001 From: R E Broadley Date: Tue, 7 Dec 2021 11:58:01 +0100 Subject: [PATCH 2/8] Add formatBytesps function --- src/qt/guiutil.cpp | 34 ++++++++++++++++++++++++++++++++++ src/qt/guiutil.h | 1 + 2 files changed, 35 insertions(+) diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 9565fa508f1..a95a9d8c48c 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -807,6 +807,40 @@ QString formatBytes(uint64_t bytes) return QObject::tr("%1 GB").arg(bytes / 1'000'000'000); } +QString formatBytesps(float val) +{ + if (val < 10) + //: "Bytes per second" + return QObject::tr("%1 B/s").arg(0.01 * int(val * 100 + 0.5)); + if (val < 100) + //: "Bytes per second" + return QObject::tr("%1 B/s").arg(0.1 * int(val * 10 + 0.5)); + if (val < 1'000) + //: "Bytes per second" + return QObject::tr("%1 B/s").arg(int(val + 0.5)); + if (val < 10'000) + //: "Kilobytes per second" + return QObject::tr("%1 kB/s").arg(0.01 * int(val / 10 + 0.5)); + if (val < 100'000) + //: "Kilobytes per second" + return QObject::tr("%1 kB/s").arg(0.1 * int(val / 100 + 0.5)); + if (val < 1'000'000) + //: "Kilobytes per second" + return QObject::tr("%1 kB/s").arg(int(val / 1'000 + 0.5)); + if (val < 10'000'000) + //: "Megabytes per second" + return QObject::tr("%1 MB/s").arg(0.01 * int(val / 10'000 + 0.5)); + if (val < 100'000'000) + //: "Megabytes per second" + return QObject::tr("%1 MB/s").arg(0.1 * int(val / 100'000 + 0.5)); + if (val < 10'000'000'000) + //: "Megabytes per second" + return QObject::tr("%1 MB/s").arg(long(val / 1'000'000 + 0.5)); + + //: "Gigabytes per second" + return QObject::tr("%1 GB/s").arg(long(val / 1'000'000'000 + 0.5)); +} + qreal calculateIdealFontSize(int width, const QString& text, QFont font, qreal minPointSize, qreal font_size) { while(font_size >= minPointSize) { font.setPointSizeF(font_size); diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 0224b18b4ee..3e015f8085c 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -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); From 703e57f2be7681dd3cf0192e712be09625d4f8f5 Mon Sep 17 00:00:00 2001 From: R E Broadley Date: Wed, 1 Dec 2021 15:13:44 +0100 Subject: [PATCH 3/8] Show ToolTip on Network Traffic graph --- src/qt/trafficgraphwidget.cpp | 118 ++++++++++++++++++++++++++++++++-- src/qt/trafficgraphwidget.h | 9 +++ src/util/time.cpp | 11 ++++ src/util/time.h | 1 + 4 files changed, 135 insertions(+), 4 deletions(-) diff --git a/src/qt/trafficgraphwidget.cpp b/src/qt/trafficgraphwidget.cpp index 59c4d2cf1b4..8ccfe3cdaca 100644 --- a/src/qt/trafficgraphwidget.cpp +++ b/src/qt/trafficgraphwidget.cpp @@ -5,11 +5,14 @@ #include #include #include +#include #include #include #include #include +#include +#include #include #include @@ -22,15 +25,23 @@ TrafficGraphWidget::TrafficGraphWidget(QWidget *parent) : QWidget(parent), timer(nullptr), + disp_timer(nullptr), fMax(0.0f), vSamplesIn(), vSamplesOut(), + vTimeStamp(), nLastBytesIn(0), nLastBytesOut(0), + nLastTime(0), clientModel(nullptr) { timer = new QTimer(this); + disp_timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &TrafficGraphWidget::updateRates); + connect(disp_timer, &QTimer::timeout, this, &TrafficGraphWidget::updateDisplay); + disp_timer->setInterval(100); + disp_timer->start(); + setMouseTracking(true); } void TrafficGraphWidget::setClientModel(ClientModel *model) @@ -39,6 +50,7 @@ void TrafficGraphWidget::setClientModel(ClientModel *model) if(model) { nLastBytesIn = model->node().getTotalBytesRecv(); nLastBytesOut = model->node().getTotalBytesSent(); + nLastTime = GetTimeMillis(); } } @@ -66,6 +78,44 @@ void TrafficGraphWidget::paintPath(QPainterPath &path, QQueue &samples) } } +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; + int sampleSize = vTimeStamp.size(); + unsigned int smallest_distance = 50; int closest_i = (i >= 0 && i < sampleSize) ? i : -1; + 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); @@ -82,7 +132,6 @@ void TrafficGraphWidget::paintEvent(QPaintEvent *) int base = floor(log10(fMax)); float val = pow(10.0f, base); - const QString units = tr("kB/s"); const float yMarginText = 2.0; // draw lines @@ -97,7 +146,7 @@ void TrafficGraphWidget::paintEvent(QPaintEvent *) axisCol = axisCol.darker(); val = pow(10.0f, base - 1); painter.setPen(axisCol); - painter.drawText(XMARGIN, y_value(val)-yMarginText, QString("%1 %2").arg(val).arg(units)); + painter.drawText(XMARGIN, y_value(val)-yMarginText, GUIUtil::formatBytesps(val*1000)); int count = 1; for(float y = val; y < fMax; y += val, count++) { // don't overwrite lines drawn above @@ -123,18 +172,70 @@ 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 the default ToolTip timeout (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::updateDisplay() +{ + // This function refreshes or deletes the ToolTip. + + bool fUpdate = false; + if (!QToolTip::isVisible()) { + if (ttpoint >= 0) { // Remove the yellow circle if the ToolTip has gone due to mouse moving elsewhere. + ttpoint = -1; + fUpdate = true; + } + } else if (ttpoint >= 0 && GetTime() >= tt_time + 9) { // ToolTip is about to expire so refresh it. + fUpdate = true; + } + if (fUpdate) { + update(); + } } void TrafficGraphWidget::updateRates() { if(!clientModel) return; + int64_t nTime = GetTimeMillis(); + int nRealInterval = nTime - nLastTime; quint64 bytesIn = clientModel->node().getTotalBytesRecv(), bytesOut = clientModel->node().getTotalBytesSent(); - float in_rate_kilobytes_per_sec = static_cast(bytesIn - nLastBytesIn) / timer->interval(); - float out_rate_kilobytes_per_sec = static_cast(bytesOut - nLastBytesOut) / timer->interval(); + int nInterval = timer->interval(); + if (nRealInterval < nInterval * 0.5) return; + float in_rate_kilobytes_per_sec = static_cast(bytesIn - nLastBytesIn) / nRealInterval; + float out_rate_kilobytes_per_sec = static_cast(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; @@ -144,6 +245,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) { @@ -153,6 +257,10 @@ void TrafficGraphWidget::updateRates() if(f > tmax) tmax = f; } fMax = tmax; + if (ttpoint >= 0 && ttpoint < DESIRED_SAMPLES) { + ttpoint++; // Move the selected point to the left + if (ttpoint >= DESIRED_SAMPLES) ttpoint = -1; + } update(); } @@ -172,11 +280,13 @@ void TrafficGraphWidget::clear() vSamplesOut.clear(); vSamplesIn.clear(); + vTimeStamp.clear(); fMax = 0.0f; if(clientModel) { nLastBytesIn = clientModel->node().getTotalBytesRecv(); nLastBytesOut = clientModel->node().getTotalBytesSent(); + nLastTime = GetTimeMillis(); } timer->start(); } diff --git a/src/qt/trafficgraphwidget.h b/src/qt/trafficgraphwidget.h index ac68fd442de..b670725ee88 100644 --- a/src/qt/trafficgraphwidget.h +++ b/src/qt/trafficgraphwidget.h @@ -29,9 +29,15 @@ 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 updateDisplay(); void setGraphRange(std::chrono::minutes new_range); void clear(); @@ -39,12 +45,15 @@ public Q_SLOTS: void paintPath(QPainterPath &path, QQueue &samples); QTimer *timer; + QTimer *disp_timer; float fMax; std::chrono::minutes m_range{0}; QQueue vSamplesIn; QQueue vSamplesOut; + QQueue vTimeStamp; quint64 nLastBytesIn; quint64 nLastBytesOut; + int64_t nLastTime; ClientModel *clientModel; }; diff --git a/src/util/time.cpp b/src/util/time.cpp index f7712f0dc82..5b7aea848e4 100644 --- a/src/util/time.cpp +++ b/src/util/time.cpp @@ -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); diff --git a/src/util/time.h b/src/util/time.h index 9d92b237250..be244138bb4 100644 --- a/src/util/time.h +++ b/src/util/time.h @@ -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); /** From 8f033192aa4ff10e1cf925ad583fac7c7e740374 Mon Sep 17 00:00:00 2001 From: R E Broadley Date: Thu, 2 Dec 2021 15:49:12 +0100 Subject: [PATCH 4/8] Don't clear graph when changing interval --- src/qt/trafficgraphwidget.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/qt/trafficgraphwidget.cpp b/src/qt/trafficgraphwidget.cpp index 8ccfe3cdaca..2af80139551 100644 --- a/src/qt/trafficgraphwidget.cpp +++ b/src/qt/trafficgraphwidget.cpp @@ -270,8 +270,7 @@ void TrafficGraphWidget::setGraphRange(std::chrono::minutes new_range) const auto msecs_per_sample{std::chrono::duration_cast(m_range) / DESIRED_SAMPLES}; timer->stop(); timer->setInterval(msecs_per_sample); - - clear(); + timer->start(); } void TrafficGraphWidget::clear() From 6f70ba91058b01a59ba0a5dcc8f712210607f98f Mon Sep 17 00:00:00 2001 From: R E Broadley Date: Mon, 15 Nov 2021 15:22:05 +0100 Subject: [PATCH 5/8] Enable non-linear network traffic graph --- src/qt/trafficgraphwidget.cpp | 41 +++++++++++++++++++++++------------ src/qt/trafficgraphwidget.h | 2 ++ 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/qt/trafficgraphwidget.cpp b/src/qt/trafficgraphwidget.cpp index 2af80139551..38e89055415 100644 --- a/src/qt/trafficgraphwidget.cpp +++ b/src/qt/trafficgraphwidget.cpp @@ -59,7 +59,7 @@ std::chrono::minutes TrafficGraphWidget::getGraphRange() const { return m_range; int TrafficGraphWidget::y_value(float value) { int h = height() - YMARGIN * 2; - return YMARGIN + h - (h * 1.0 * value / fMax); + return YMARGIN + h - (h * 1.0 * (fToggle ? (pow(value, 0.30102) / pow(fMax, 0.30102)) : (value / fMax))); } void TrafficGraphWidget::paintPath(QPainterPath &path, QQueue &samples) @@ -116,6 +116,13 @@ void TrafficGraphWidget::mouseMoveEvent(QMouseEvent *event) last_x = x; last_y = y; } +void TrafficGraphWidget::mousePressEvent(QMouseEvent *event) +{ + QWidget::mousePressEvent(event); + fToggle = !fToggle; + update(); +} + void TrafficGraphWidget::paintEvent(QPaintEvent *) { QPainter painter(this); @@ -134,28 +141,34 @@ void TrafficGraphWidget::paintEvent(QPaintEvent *) const float yMarginText = 2.0; - // draw lines - painter.setPen(axisCol); - 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); - } - // if we drew 3 or fewer lines, break them up at the next lower order of magnitude - if(fMax / val <= 3.0f) { - axisCol = axisCol.darker(); + // if we drew 10 or 3 fewer lines, break them up at the next lower order of magnitude + if(fMax / val <= (fToggle ? 10.0f : 3.0f)) { + float oldval = val; val = pow(10.0f, base - 1); - painter.setPen(axisCol); + painter.setPen(axisCol.darker()); painter.drawText(XMARGIN, y_value(val)-yMarginText, GUIUtil::formatBytesps(val*1000)); int count = 1; - for(float y = val; y < fMax; y += val, count++) { - // don't overwrite lines drawn above + for(float y = val; y < (!fToggle || fMax / val < 20 ? fMax : oldval); y += val, count++) { if(count % 10 == 0) continue; int yy = y_value(y); painter.drawLine(XMARGIN, yy, width() - XMARGIN, yy); } + if (fToggle) { + int yy = y_value(val*0.1); + painter.setPen(axisCol.darker().darker()); + painter.drawText(XMARGIN, yy-yMarginText, GUIUtil::formatBytesps(val*100)); + painter.drawLine(XMARGIN, yy, width() - XMARGIN, yy); + } + val = oldval; + } + // draw lines + painter.setPen(axisCol); + for(float y = val; y < fMax; y += val) { + int yy = y_value(y); + painter.drawLine(XMARGIN, yy, width() - XMARGIN, yy); } + painter.drawText(XMARGIN, y_value(val)-yMarginText, GUIUtil::formatBytesps(val*1000)); painter.setRenderHint(QPainter::Antialiasing); if(!vSamplesIn.empty()) { diff --git a/src/qt/trafficgraphwidget.h b/src/qt/trafficgraphwidget.h index b670725ee88..e5f62a760d0 100644 --- a/src/qt/trafficgraphwidget.h +++ b/src/qt/trafficgraphwidget.h @@ -34,6 +34,8 @@ class TrafficGraphWidget : public QWidget int x_offset = 0; int y_offset = 0; int64_t tt_time = 0; + void mousePressEvent(QMouseEvent *event) override; + bool fToggle = true; public Q_SLOTS: void updateRates(); From fbcf3656f7077d6b0aa643fc1094531406710740 Mon Sep 17 00:00:00 2001 From: R E Broadley Date: Tue, 12 Oct 2021 19:31:43 +0200 Subject: [PATCH 6/8] Improve formatBytes in GUI --- src/qt/guiutil.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index a95a9d8c48c..783c45f3577 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -799,8 +799,16 @@ QString formatBytes(uint64_t bytes) { if (bytes < 1'000) return QObject::tr("%1 B").arg(bytes); + if (bytes < 10'000) + return QObject::tr("%1 kB").arg(0.01 * (bytes / 10)); + if (bytes < 100'000) + return QObject::tr("%1 kB").arg(0.1 * (bytes / 100)); if (bytes < 1'000'000) return QObject::tr("%1 kB").arg(bytes / 1'000); + if (bytes < 10'000'000) + return QObject::tr("%1 MB").arg(0.01 * (bytes / 10'000)); + if (bytes < 100'000'000) + return QObject::tr("%1 MB").arg(0.1 * (bytes / 100'000)); if (bytes < 1'000'000'000) return QObject::tr("%1 MB").arg(bytes / 1'000'000); From cc4215b88d4a36a5e98ed8801375e5b4680ed866 Mon Sep 17 00:00:00 2001 From: R E Broadley Date: Sat, 11 Dec 2021 13:08:51 +0100 Subject: [PATCH 7/8] Don't lose ToolTip when toggling network graph --- src/qt/trafficgraphwidget.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/qt/trafficgraphwidget.cpp b/src/qt/trafficgraphwidget.cpp index 38e89055415..619b32da9d4 100644 --- a/src/qt/trafficgraphwidget.cpp +++ b/src/qt/trafficgraphwidget.cpp @@ -220,9 +220,13 @@ void TrafficGraphWidget::updateDisplay() // This function refreshes or deletes the ToolTip. bool fUpdate = false; + static bool last_fToggle = fToggle; if (!QToolTip::isVisible()) { if (ttpoint >= 0) { // Remove the yellow circle if the ToolTip has gone due to mouse moving elsewhere. - ttpoint = -1; + if (last_fToggle == fToggle) { // Not lost due to a toggle + ttpoint = -1; + } else + last_fToggle = fToggle; fUpdate = true; } } else if (ttpoint >= 0 && GetTime() >= tt_time + 9) { // ToolTip is about to expire so refresh it. From 8c56419cdf12576d299c6cc373bb4527aa802ef9 Mon Sep 17 00:00:00 2001 From: R E Broadley Date: Wed, 16 Feb 2022 23:17:49 +0000 Subject: [PATCH 8/8] Make Network Graph sample multiple ranges --- src/qt/forms/debugwindow.ui | 24 ++-- src/qt/rpcconsole.cpp | 60 ++++++-- src/qt/rpcconsole.h | 6 +- src/qt/trafficgraphwidget.cpp | 264 +++++++++++++++++++++------------- src/qt/trafficgraphwidget.h | 35 +++-- 5 files changed, 248 insertions(+), 141 deletions(-) diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui index 21968010230..97c9ebe3f9d 100644 --- a/src/qt/forms/debugwindow.ui +++ b/src/qt/forms/debugwindow.ui @@ -630,20 +630,26 @@ - 1 + 0 - 288 + 2400 - 12 + 200 - 6 + 0 Qt::Horizontal + + QSlider::TicksBelow + + + 200 + @@ -659,16 +665,6 @@ - - - - &Reset - - - false - - - diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index c5e5e69df6a..b8b0c688128 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -57,7 +57,6 @@ #include const int CONSOLE_HISTORY = 50; -const int INITIAL_TRAFFIC_GRAPH_MINS = 30; const QSize FONT_RANGE(4, 40); const char fontSizeSettingsKey[] = "consoleFontSize"; @@ -560,7 +559,6 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty connect(ui->clearButton, &QAbstractButton::clicked, [this] { clear(); }); connect(ui->fontBiggerButton, &QAbstractButton::clicked, this, &RPCConsole::fontBigger); connect(ui->fontSmallerButton, &QAbstractButton::clicked, this, &RPCConsole::fontSmaller); - connect(ui->btnClearTrafficGraph, &QPushButton::clicked, ui->trafficGraph, &TrafficGraphWidget::clear); // disable the wallet selector by default ui->WalletSelector->setVisible(false); @@ -572,7 +570,7 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty // based timer interface m_node.rpcSetTimerInterfaceIfUnset(rpcTimerInterface); - setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS); + setTrafficGraphRange(1); // 1 is the lowest setting (0 bumps up) updateDetailWidget(); consoleFontSize = settings.value(fontSizeSettingsKey, QFont().pointSize()).toInt(); @@ -1133,21 +1131,63 @@ void RPCConsole::scrollToEnd() scrollbar->setValue(scrollbar->maximum()); } -void RPCConsole::on_sldGraphRange_valueChanged(int value) +void RPCConsole::on_sldGraphRange_valueChanged(int slider_value) { - const int multiplier = 5; // each position on the slider represents 5 min - int mins = value * multiplier; - setTrafficGraphRange(mins); + static int64_t last_click_time = 0; + static bool last_click_was_up = false; + unsigned int value = (slider_value + 100) / 200 + 1; // minimum of 1, 0 reserve for scale bump + if (!slider_in_use) { + // Avoid accidental boucing of direction + int64_t now = GetTimeMillis(); + bool this_click_is_up = false; + bool bouncing = false; + if (slider_value > set_slider_value) + this_click_is_up = true; + if (now - last_click_time < 250 && this_click_is_up != last_click_was_up) { + bouncing = true; + ui->sldGraphRange->blockSignals(true); + ui->sldGraphRange->setValue(set_slider_value); + ui->sldGraphRange->blockSignals(false); + } + last_click_time = now; + last_click_was_up = this_click_is_up; + set_slider_value = slider_value; + if (bouncing) return; + } + set_slider_value = slider_value; + setTrafficGraphRange(value); +} + +void RPCConsole::setTrafficGraphRange(unsigned int value) +{ + std::chrono::minutes mins = ui->trafficGraph->setGraphRange(value); + if (value) + set_slider_value = (value - 1) * 200; + else { + set_slider_value += 200; + ui->sldGraphRange->blockSignals(true); + ui->sldGraphRange->setValue(set_slider_value); + ui->sldGraphRange->blockSignals(false); + } + ui->lblGraphRange->setText(GUIUtil::formatDurationStr(mins)); } -void RPCConsole::setTrafficGraphRange(int mins) +void RPCConsole::on_sldGraphRange_sliderReleased() { - ui->trafficGraph->setGraphRange(std::chrono::minutes{mins}); - ui->lblGraphRange->setText(GUIUtil::formatDurationStr(std::chrono::minutes{mins})); + ui->sldGraphRange->setValue(set_slider_value); // Snap the slider to where this value is + slider_in_use = false; +} + +void RPCConsole::on_sldGraphRange_sliderPressed() +{ + slider_in_use = true; } void RPCConsole::updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut) { + if (!slider_in_use && ui->trafficGraph->GraphRangeBump()) { + setTrafficGraphRange(0); // bump it up + } ui->lblBytesIn->setText(GUIUtil::formatBytes(totalBytesIn)); ui->lblBytesOut->setText(GUIUtil::formatBytes(totalBytesOut)); } diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 528e2bef7d5..ffba9490165 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -84,6 +84,8 @@ private Q_SLOTS: void on_openDebugLogfileButton_clicked(); /** change the time range of the network traffic graph */ void on_sldGraphRange_valueChanged(int value); + void on_sldGraphRange_sliderReleased(); + void on_sldGraphRange_sliderPressed(); /** update traffic statistics */ void updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut); void resizeEvent(QResizeEvent *event) override; @@ -140,7 +142,7 @@ public Q_SLOTS: } const ts; void startExecutor(); - void setTrafficGraphRange(int mins); + void setTrafficGraphRange(unsigned int value); enum ColumnWidths { @@ -170,6 +172,8 @@ public Q_SLOTS: bool m_is_executing{false}; QByteArray m_peer_widget_header_state; QByteArray m_banlist_widget_header_state; + bool slider_in_use{false}; + int set_slider_value{0}; /** Update UI with latest network info from model. */ void updateNetworkState(); diff --git a/src/qt/trafficgraphwidget.cpp b/src/qt/trafficgraphwidget.cpp index 619b32da9d4..0af4a2a92d4 100644 --- a/src/qt/trafficgraphwidget.cpp +++ b/src/qt/trafficgraphwidget.cpp @@ -25,36 +25,38 @@ TrafficGraphWidget::TrafficGraphWidget(QWidget *parent) : QWidget(parent), timer(nullptr), - disp_timer(nullptr), - fMax(0.0f), vSamplesIn(), vSamplesOut(), vTimeStamp(), - nLastBytesIn(0), - nLastBytesOut(0), - nLastTime(0), + nLastBytesIn(), + nLastBytesOut(), + nLastTime(), clientModel(nullptr) { timer = new QTimer(this); - disp_timer = new QTimer(this); - connect(timer, &QTimer::timeout, this, &TrafficGraphWidget::updateRates); - connect(disp_timer, &QTimer::timeout, this, &TrafficGraphWidget::updateDisplay); - disp_timer->setInterval(100); - disp_timer->start(); + connect(timer, &QTimer::timeout, this, &TrafficGraphWidget::updateStuff); + timer->setInterval(75); + timer->start(); setMouseTracking(true); } void TrafficGraphWidget::setClientModel(ClientModel *model) { clientModel = model; + int64_t nTime = GetTimeMillis(); if(model) { - nLastBytesIn = model->node().getTotalBytesRecv(); - nLastBytesOut = model->node().getTotalBytesSent(); - nLastTime = GetTimeMillis(); + for (int i = 0; i < VALUES_SIZE; i++) { + nLastBytesIn[i] = model->node().getTotalBytesRecv(); + nLastBytesOut[i] = model->node().getTotalBytesSent(); + nLastTime[i] = std::chrono::milliseconds{nTime}; + vSamplesIn[i].push_front(nLastBytesIn[i]); + vSamplesOut[i].push_front(nLastBytesOut[i]); + vTimeStamp[i].push_front(nLastTime[i]); + } } } -std::chrono::minutes TrafficGraphWidget::getGraphRange() const { return m_range; } +bool TrafficGraphWidget::GraphRangeBump() const { return m_bump_value; } int TrafficGraphWidget::y_value(float value) { @@ -64,13 +66,13 @@ int TrafficGraphWidget::y_value(float value) void TrafficGraphWidget::paintPath(QPainterPath &path, QQueue &samples) { - int sampleCount = samples.size(); + int sampleCount = std::min(int(DESIRED_SAMPLES * m_range / values[m_value]), int(samples.size())); 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; + x = XMARGIN + w - w * i * values[m_value] / m_range / DESIRED_SAMPLES; int y = y_value(samples.at(i)); path.lineTo(x, y); } @@ -87,6 +89,7 @@ float floatmax(float a, float b) void TrafficGraphWidget::mouseMoveEvent(QMouseEvent *event) { QWidget::mouseMoveEvent(event); + if (fMax <= 0.0f) return; static int last_x = -1; static int last_y = -1; int x = event->x(); @@ -96,11 +99,11 @@ void TrafficGraphWidget::mouseMoveEvent(QMouseEvent *event) 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; - int sampleSize = vTimeStamp.size(); + int sampleSize = vTimeStamp[m_value].size(); unsigned int smallest_distance = 50; int closest_i = (i >= 0 && i < sampleSize) ? i : -1; 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)); + float val = floatmax(vSamplesIn[m_value].at(test_i), vSamplesOut[m_value].at(test_i)); int y_data = y_value(val); unsigned int distance = abs(y - y_data); if (distance < smallest_distance) { @@ -171,42 +174,46 @@ void TrafficGraphWidget::paintEvent(QPaintEvent *) painter.drawText(XMARGIN, y_value(val)-yMarginText, GUIUtil::formatBytesps(val*1000)); painter.setRenderHint(QPainter::Antialiasing); - if(!vSamplesIn.empty()) { + if(!vSamplesIn[m_value].empty()) { QPainterPath p; - paintPath(p, vSamplesIn); + paintPath(p, vSamplesIn[m_value]); painter.fillPath(p, QColor(0, 255, 0, 128)); painter.setPen(Qt::green); painter.drawPath(p); } - if(!vSamplesOut.empty()) { + if(!vSamplesOut[m_value].empty()) { QPainterPath p; - paintPath(p, vSamplesOut); + paintPath(p, vSamplesOut[m_value]); painter.fillPath(p, QColor(255, 0, 0, 128)); painter.setPen(Qt::red); painter.drawPath(p); } - int sampleCount = vTimeStamp.size(); + int sampleCount = vTimeStamp[m_value].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))); + int y = y_value(floatmax(vSamplesIn[m_value].at(ttpoint), vSamplesOut[m_value].at(ttpoint))); painter.drawEllipse(QPointF(x, y), 3, 3); QString strTime; - int64_t sampleTime = vTimeStamp.at(ttpoint); - int age = GetTime() - sampleTime/1000; + std::chrono::milliseconds sampleTime{0}; + if (ttpoint + 1 < sampleCount) + sampleTime = vTimeStamp[m_value].at(ttpoint+1); + else + sampleTime = vTimeStamp[m_value].at(ttpoint); + int age = GetTime() - sampleTime.count() / 1000; if (age < 60*60*23) - strTime = QString::fromStdString(FormatISO8601Time(sampleTime/1000)); + strTime += QString::fromStdString(FormatISO8601Time(sampleTime.count() / 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); + strTime += QString::fromStdString(FormatISO8601DateTime(sampleTime.count() / 1000)); + int nDuration = (vTimeStamp[m_value].at(ttpoint) - sampleTime).count(); + if (nDuration > 0) { + if (nDuration > 9999) + strTime += " +" + GUIUtil::formatDurationStr(std::chrono::seconds{nDuration/1000}); + else + strTime += " +" + GUIUtil::formatPingTime(std::chrono::microseconds{nDuration*1000}); + } + QString strData = tr("In") + " " + GUIUtil::formatBytesps(vSamplesIn[m_value].at(ttpoint)*1000) + "\n" + tr("Out") + " " + GUIUtil::formatBytesps(vSamplesOut[m_value].at(ttpoint)*1000); // Line below allows ToolTip to move faster than the default ToolTip timeout (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); @@ -215,11 +222,90 @@ void TrafficGraphWidget::paintEvent(QPaintEvent *) QToolTip::hideText(); } -void TrafficGraphWidget::updateDisplay() +void TrafficGraphWidget::update_fMax() +{ + float tmax = 0.0f; + for (const float f : vSamplesIn[m_new_value]) { + if(f > tmax) tmax = f; + } + for (const float f : vSamplesOut[m_new_value]) { + if(f > tmax) tmax = f; + } + new_fMax = tmax; +} + +bool update_num(float new_val, float ¤t, float &increment, int length) +{ + if (new_val == 0 || current == new_val) + return false; + + if (abs(increment) <= abs(0.8 * current) / length) { // allow equal to as current and increment could be zero + if (new_val > current) + increment = 1.0 * (current+1) / length; // +1s are to get it started even if current is zero + else + increment = -1.0 * (current+1) / length; + if (abs(increment) > abs(new_val - current)) // Only check this when creating an increment + increment = 0; // Nothing to do! + } else { + if (((increment > 0) && (current + increment * 2 > new_val)) || + ((increment < 0) && (current + increment * 2 < new_val))) { + increment = increment / 2; // Keep the momentum going even if new_val is elsewhere. + } else { + if (((increment > 0) && (current + increment * 4 < new_val)) || + ((increment < 0) && (current + increment * 4 > new_val))) { + increment = increment * 2; + } + } + } + if (abs(increment) < 0.8 * current / length) { + if ((increment >= 0 && new_val > current) || (increment <= 0 && new_val < current)) + current = new_val; + increment = 0; + } else + current += increment; + + return true; +} + +void TrafficGraphWidget::updateStuff() { - // This function refreshes or deletes the ToolTip. + if(!clientModel) return; + + static int nInterval{timer->interval()}; + int64_t nTime{GetTimeMillis()}; bool fUpdate = false; + for (int i = 0; i < VALUES_SIZE; i++) { + int64_t msecs_per_sample = int64_t(values[i]) * int64_t(60000) / DESIRED_SAMPLES; + if (nTime > (nLastTime[i].count() + msecs_per_sample - nInterval/2)) { // REBTODO - fix bad timing + updateRates(i); + if (i == m_value) { + if (ttpoint >= 0 && ttpoint < DESIRED_SAMPLES) { + ttpoint++; // Move the selected point to the left + if (ttpoint >= DESIRED_SAMPLES) ttpoint = -1; + } + fUpdate = true; + } + if (i == m_new_value) + update_fMax(); + } + } + + static float y_increment = 0; + static float x_increment = 0; + if (update_num(new_fMax, fMax, y_increment, height() - YMARGIN * 2)) + fUpdate = true; + if (update_num(values[m_new_value], m_range, x_increment, width() - XMARGIN * 2)) { + if (values[m_new_value] > m_range && values[m_value] < m_range * 0.99) { + m_value++; + } else if (m_value > 0 && values[m_new_value] <= m_range && values[m_value-1] > m_range * 0.99) + m_value--; + fUpdate = true; + } else if (m_value != m_new_value) { + fUpdate = true; + m_value = m_new_value; + } + static bool last_fToggle = fToggle; if (!QToolTip::isVisible()) { if (ttpoint >= 0) { // Remove the yellow circle if the ToolTip has gone due to mouse moving elsewhere. @@ -229,80 +315,54 @@ void TrafficGraphWidget::updateDisplay() last_fToggle = fToggle; fUpdate = true; } - } else if (ttpoint >= 0 && GetTime() >= tt_time + 9) { // ToolTip is about to expire so refresh it. - fUpdate = true; - } - if (fUpdate) { + } else if (ttpoint >= 0 && GetTime() >= tt_time + 9) // ToolTip is about to expire so refresh it. + fUpdate = true; // TODO - technically it's only the ToolTip that needs to be refreshed + + if (fUpdate) update(); - } } -void TrafficGraphWidget::updateRates() +void TrafficGraphWidget::updateRates(int i) { - if(!clientModel) return; - - int64_t nTime = GetTimeMillis(); - int nRealInterval = nTime - nLastTime; + std::chrono::milliseconds nTime{GetTimeMillis()}; quint64 bytesIn = clientModel->node().getTotalBytesRecv(), bytesOut = clientModel->node().getTotalBytesSent(); - int nInterval = timer->interval(); - if (nRealInterval < nInterval * 0.5) return; - float in_rate_kilobytes_per_sec = static_cast(bytesIn - nLastBytesIn) / nRealInterval; - float out_rate_kilobytes_per_sec = static_cast(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; - - while(vSamplesIn.size() > DESIRED_SAMPLES) { - vSamplesIn.pop_back(); - } - while(vSamplesOut.size() > DESIRED_SAMPLES) { - vSamplesOut.pop_back(); - } - while(vTimeStamp.size() > DESIRED_SAMPLES) { - vTimeStamp.pop_back(); - } + int nRealInterval = (nTime - nLastTime[i]).count(); + float in_rate_kilobytes_per_sec = static_cast(bytesIn - nLastBytesIn[i]) / nRealInterval; + float out_rate_kilobytes_per_sec = static_cast(bytesOut - nLastBytesOut[i]) / nRealInterval; + vSamplesIn[i].push_front(in_rate_kilobytes_per_sec); + vSamplesOut[i].push_front(out_rate_kilobytes_per_sec); + vTimeStamp[i].push_front(nTime); + nLastTime[i] = nTime; + nLastBytesIn[i] = bytesIn; + nLastBytesOut[i] = bytesOut; + static bool fFull[VALUES_SIZE]; + while(vTimeStamp[i].size() > DESIRED_SAMPLES) { + if (ttpoint < 0 && m_value == i && i < VALUES_SIZE - 1 && !fFull[i]) + m_bump_value = true; - float tmax = 0.0f; - for (const float f : vSamplesIn) { - if(f > tmax) tmax = f; - } - for (const float f : vSamplesOut) { - if(f > tmax) tmax = f; - } - fMax = tmax; - if (ttpoint >= 0 && ttpoint < DESIRED_SAMPLES) { - ttpoint++; // Move the selected point to the left - if (ttpoint >= DESIRED_SAMPLES) ttpoint = -1; - } - update(); -} + fFull[i] = true; -void TrafficGraphWidget::setGraphRange(std::chrono::minutes new_range) -{ - m_range = new_range; - const auto msecs_per_sample{std::chrono::duration_cast(m_range) / DESIRED_SAMPLES}; - timer->stop(); - timer->setInterval(msecs_per_sample); - timer->start(); + vSamplesIn[i].pop_back(); + vSamplesOut[i].pop_back(); + vTimeStamp[i].pop_back(); + } } -void TrafficGraphWidget::clear() +std::chrono::minutes TrafficGraphWidget::setGraphRange(unsigned int value) { - timer->stop(); - - vSamplesOut.clear(); - vSamplesIn.clear(); - vTimeStamp.clear(); - fMax = 0.0f; - - if(clientModel) { - nLastBytesIn = clientModel->node().getTotalBytesRecv(); - nLastBytesOut = clientModel->node().getTotalBytesSent(); - nLastTime = GetTimeMillis(); + // value is the array marker plus 1 (as zero is reserved for bumping up) + if (!value) { // bump + m_bump_value = false; + value = m_value + 1; + } else + value--; // get the array marker + int old_value = m_new_value; + m_new_value = std::min((int)value, VALUES_SIZE - 1); + if (m_new_value != old_value) { + update_fMax(); + update(); } - timer->start(); + + return std::chrono::minutes{values[m_new_value]}; } diff --git a/src/qt/trafficgraphwidget.h b/src/qt/trafficgraphwidget.h index e5f62a760d0..b3ea9bc4d48 100644 --- a/src/qt/trafficgraphwidget.h +++ b/src/qt/trafficgraphwidget.h @@ -17,6 +17,8 @@ class QPaintEvent; class QTimer; QT_END_NAMESPACE +#define VALUES_SIZE 12 + class TrafficGraphWidget : public QWidget { Q_OBJECT @@ -24,7 +26,7 @@ class TrafficGraphWidget : public QWidget public: explicit TrafficGraphWidget(QWidget *parent = nullptr); void setClientModel(ClientModel *model); - std::chrono::minutes getGraphRange() const; + bool GraphRangeBump() const; protected: void paintEvent(QPaintEvent *) override; @@ -38,24 +40,29 @@ class TrafficGraphWidget : public QWidget bool fToggle = true; public Q_SLOTS: - void updateRates(); - void updateDisplay(); - void setGraphRange(std::chrono::minutes new_range); - void clear(); + void updateStuff(); + std::chrono::minutes setGraphRange(unsigned int value); private: + void update_fMax(); void paintPath(QPainterPath &path, QQueue &samples); + void updateRates(int value); QTimer *timer; - QTimer *disp_timer; - float fMax; - std::chrono::minutes m_range{0}; - QQueue vSamplesIn; - QQueue vSamplesOut; - QQueue vTimeStamp; - quint64 nLastBytesIn; - quint64 nLastBytesOut; - int64_t nLastTime; + float fMax{0}; + float new_fMax{0}; + float m_range{0}; + int m_value{0}; + int m_new_value{0}; + bool m_bump_value{false}; + QQueue vSamplesIn[VALUES_SIZE]; + QQueue vSamplesOut[VALUES_SIZE]; + QQueue vTimeStamp[VALUES_SIZE]; + quint64 nLastBytesIn[VALUES_SIZE]; + quint64 nLastBytesOut[VALUES_SIZE]; + std::chrono::milliseconds nLastTime[VALUES_SIZE]; + unsigned int values[VALUES_SIZE] = {5, 10, 20, 45, 90, 3*60, 6*60, 12*60, 24*60, 3*24*60, 7*24*60, 28*24*60}; + //unsigned int values[VALUES_SIZE] = {5, 15, 60, 3*60, 12*60, 2*24*60, 7*24*60, 28*24*60}; ClientModel *clientModel; };