mirror of
https://github.com/Mahdi-zarei/nekoray.git
synced 2025-12-25 02:50:06 +08:00
add traffic graph
This commit is contained in:
parent
7e3d546926
commit
5b6842bea7
@ -11,7 +11,7 @@ if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND WIN32)
|
|||||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON)
|
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
find_package(Qt6 REQUIRED COMPONENTS Widgets Network LinguistTools)
|
find_package(Qt6 REQUIRED COMPONENTS Widgets Network LinguistTools Charts)
|
||||||
|
|
||||||
if (NKR_CROSS)
|
if (NKR_CROSS)
|
||||||
set_property(TARGET Qt6::moc PROPERTY IMPORTED_LOCATION /usr/bin/moc)
|
set_property(TARGET Qt6::moc PROPERTY IMPORTED_LOCATION /usr/bin/moc)
|
||||||
@ -196,7 +196,6 @@ set(PROJECT_SOURCES
|
|||||||
include/ui/profile/edit_ssh.h
|
include/ui/profile/edit_ssh.h
|
||||||
include/ui/profile/edit_ssh.ui
|
include/ui/profile/edit_ssh.ui
|
||||||
include/configs/proxy/SSHBean.h
|
include/configs/proxy/SSHBean.h
|
||||||
src/sys/macos/MacOS.cpp
|
|
||||||
include/sys/macos/MacOS.h
|
include/sys/macos/MacOS.h
|
||||||
|
|
||||||
src/sys/linux/LinuxCap.cpp
|
src/sys/linux/LinuxCap.cpp
|
||||||
@ -210,6 +209,8 @@ set(PROJECT_SOURCES
|
|||||||
src/stats/connectionLister/connectionLister.cpp
|
src/stats/connectionLister/connectionLister.cpp
|
||||||
src/configs/proxy/Json2Bean.cpp
|
src/configs/proxy/Json2Bean.cpp
|
||||||
include/sys/windows/vcCheck.h
|
include/sys/windows/vcCheck.h
|
||||||
|
include/ui/utils/TrafficChart.h
|
||||||
|
include/ui/utils/CustomChartView.h
|
||||||
)
|
)
|
||||||
|
|
||||||
# Qt exe
|
# Qt exe
|
||||||
@ -261,7 +262,7 @@ target_sources(nekoray PRIVATE ${CMAKE_BINARY_DIR}/translations.qrc)
|
|||||||
# Target Link
|
# Target Link
|
||||||
|
|
||||||
target_link_libraries(nekoray PRIVATE
|
target_link_libraries(nekoray PRIVATE
|
||||||
Qt6::Widgets Qt6::Network
|
Qt6::Widgets Qt6::Network Qt6::Charts
|
||||||
Threads::Threads
|
Threads::Threads
|
||||||
${NKR_EXTERNAL_TARGETS}
|
${NKR_EXTERNAL_TARGETS}
|
||||||
${PLATFORM_LIBRARIES}
|
${PLATFORM_LIBRARIES}
|
||||||
|
|||||||
@ -63,7 +63,6 @@ namespace NekoGui {
|
|||||||
QString log_level = "info";
|
QString log_level = "info";
|
||||||
QString test_latency_url = "http://cp.cloudflare.com/";
|
QString test_latency_url = "http://cp.cloudflare.com/";
|
||||||
int test_concurrent = 10;
|
int test_concurrent = 10;
|
||||||
int traffic_loop_interval = 500;
|
|
||||||
bool disable_traffic_stats = false;
|
bool disable_traffic_stats = false;
|
||||||
int current_group = 0; // group id
|
int current_group = 0; // group id
|
||||||
QString mux_protocol = "smux";
|
QString mux_protocol = "smux";
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "include/global/NekoGui.hpp"
|
#include "include/global/NekoGui.hpp"
|
||||||
#include "include/stats/connections/connectionLister.hpp"
|
#include "include/stats/connections/connectionLister.hpp"
|
||||||
|
#include "utils/TrafficChart.h"
|
||||||
|
|
||||||
#ifndef MW_INTERFACE
|
#ifndef MW_INTERFACE
|
||||||
|
|
||||||
@ -52,6 +53,8 @@ public:
|
|||||||
|
|
||||||
void refresh_status(const QString &traffic_update = "");
|
void refresh_status(const QString &traffic_update = "");
|
||||||
|
|
||||||
|
void update_traffic_graph(int proxyDl, int proxyUp, int directDl, int directUp);
|
||||||
|
|
||||||
void neko_start(int _id = -1);
|
void neko_start(int _id = -1);
|
||||||
|
|
||||||
void neko_stop(bool crash = false, bool sem = false, bool manual = false);
|
void neko_stop(bool crash = false, bool sem = false, bool manual = false);
|
||||||
@ -182,6 +185,8 @@ private:
|
|||||||
QMutex mu_download_update;
|
QMutex mu_download_update;
|
||||||
//
|
//
|
||||||
int toolTipID;
|
int toolTipID;
|
||||||
|
//
|
||||||
|
TrafficChart* trafficGraph;
|
||||||
|
|
||||||
QList<std::shared_ptr<NekoGui::ProxyEntity>> get_now_selected_list();
|
QList<std::shared_ptr<NekoGui::ProxyEntity>> get_now_selected_list();
|
||||||
|
|
||||||
|
|||||||
@ -447,6 +447,31 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QWidget" name="graph_tab">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Traffic Graph</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_6" stretch="">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="sizeConstraint">
|
||||||
|
<enum>QLayout::SizeConstraint::SetDefaultConstraint</enum>
|
||||||
|
</property>
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
@ -505,7 +530,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>800</width>
|
<width>800</width>
|
||||||
<height>17</height>
|
<height>25</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QMenu" name="menu_program">
|
<widget class="QMenu" name="menu_program">
|
||||||
|
|||||||
@ -209,48 +209,27 @@
|
|||||||
<layout class="QHBoxLayout" name="style_h_2">
|
<layout class="QHBoxLayout" name="style_h_2">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="traffic_statistics_box">
|
<widget class="QGroupBox" name="traffic_statistics_box">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
<layout class="QGridLayout" name="gridLayout_8">
|
||||||
<item>
|
<item row="0" column="0">
|
||||||
<widget class="QLabel" name="label_16">
|
<widget class="QLabel" name="label_2">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Statistics refresh rate</string>
|
<string>Font</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item row="0" column="1">
|
||||||
<widget class="QComboBox" name="rfsh_r">
|
<widget class="QFontComboBox" name="font"/>
|
||||||
<item>
|
</item>
|
||||||
<property name="text">
|
<item row="1" column="0">
|
||||||
<string notr="true">500ms</string>
|
<widget class="QLabel" name="label_5">
|
||||||
</property>
|
<property name="text">
|
||||||
</item>
|
<string>Font Size</string>
|
||||||
<item>
|
</property>
|
||||||
<property name="text">
|
|
||||||
<string notr="true">1s</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string notr="true">2s</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string notr="true">3s</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string notr="true">5s</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>Off</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QComboBox" name="font_size"/>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -397,20 +376,6 @@
|
|||||||
</item>
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QLabel" name="label_5">
|
|
||||||
<property name="text">
|
|
||||||
<string>Font Size</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QLabel" name="label_2">
|
|
||||||
<property name="text">
|
|
||||||
<string>Font</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QLabel" name="label_15">
|
<widget class="QLabel" name="label_15">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
@ -424,12 +389,6 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="2">
|
|
||||||
<widget class="QFontComboBox" name="font"/>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="2">
|
|
||||||
<widget class="QComboBox" name="font_size"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
39
include/ui/utils/CustomChartView.h
Normal file
39
include/ui/utils/CustomChartView.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QtCharts/QChartView>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
class CustomChartView : public QChartView
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit CustomChartView(QChart *chart, QWidget *parent = nullptr)
|
||||||
|
: QChartView(chart, parent),
|
||||||
|
tooltipTimer(new QTimer(this)),
|
||||||
|
lastMousePos(QPoint(-1, -1))
|
||||||
|
{
|
||||||
|
setMouseTracking(true);
|
||||||
|
tooltipTimer->setInterval(200);
|
||||||
|
tooltipTimer->setSingleShot(true);
|
||||||
|
connect(tooltipTimer, &QTimer::timeout, this, [=]{emit mouseStopEvent(lastMousePos);});
|
||||||
|
}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void mouseStopEvent(QPoint pos);
|
||||||
|
|
||||||
|
void mouseStartMoving();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void mouseMoveEvent(QMouseEvent *event) override
|
||||||
|
{
|
||||||
|
lastMousePos = event->pos();
|
||||||
|
event->ignore();
|
||||||
|
emit mouseStartMoving();
|
||||||
|
tooltipTimer->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QTimer *tooltipTimer;
|
||||||
|
QPoint lastMousePos;
|
||||||
|
};
|
||||||
310
include/ui/utils/TrafficChart.h
Normal file
310
include/ui/utils/TrafficChart.h
Normal file
@ -0,0 +1,310 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <set>
|
||||||
|
#include <QSplineSeries>
|
||||||
|
#include <QDateTimeAxis>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QValueAxis>
|
||||||
|
#include <QToolTip>
|
||||||
|
#include <QLegendMarker>
|
||||||
|
#include <QStyleHints>
|
||||||
|
|
||||||
|
#include "CustomChartView.h"
|
||||||
|
|
||||||
|
class TrafficChart
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
QChart *chart;
|
||||||
|
CustomChartView *chartView;
|
||||||
|
|
||||||
|
QVector<int> proxyDlRaw;
|
||||||
|
QVector<int> proxyUlRaw;
|
||||||
|
QVector<int> directDlRaw;
|
||||||
|
QVector<int> directUlRaw;
|
||||||
|
|
||||||
|
QSplineSeries *proxyDlLine;
|
||||||
|
QSplineSeries *proxyUpLine;
|
||||||
|
QSplineSeries *directDlLine;
|
||||||
|
QSplineSeries *directUpLine;
|
||||||
|
std::multiset<int> dataSet;
|
||||||
|
|
||||||
|
QDateTimeAxis *timeAxis;
|
||||||
|
QValueAxis *valueAxis;
|
||||||
|
|
||||||
|
QMutex tooltipMu;
|
||||||
|
|
||||||
|
int scaleFactor = 1;
|
||||||
|
int intervalRange = 90;
|
||||||
|
|
||||||
|
void updateMagnitude()
|
||||||
|
{
|
||||||
|
auto currMax = dataSet.rbegin().operator*();
|
||||||
|
if (currMax == 0) return;
|
||||||
|
auto prevScale = scaleFactor;
|
||||||
|
// calc new magnitude
|
||||||
|
if (currMax <= 1000)
|
||||||
|
{
|
||||||
|
scaleFactor = 1;
|
||||||
|
}
|
||||||
|
else if (currMax <= 1000000)
|
||||||
|
{
|
||||||
|
scaleFactor = 1000;
|
||||||
|
}
|
||||||
|
else if (currMax <= 1000000000)
|
||||||
|
{
|
||||||
|
scaleFactor = 1000000;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
scaleFactor = 1000000000;
|
||||||
|
}
|
||||||
|
valueAxis->setMax(static_cast<double>(currMax) / static_cast<double>(scaleFactor) * 1.1);
|
||||||
|
if (prevScale == scaleFactor) return;
|
||||||
|
valueAxis->setLabelFormat(getRateLabel());
|
||||||
|
scaleData();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString getRateLabel() const
|
||||||
|
{
|
||||||
|
if (scaleFactor == 1) return "%.0f B/s";
|
||||||
|
if (scaleFactor == 1000) return "%.2f KB/s";
|
||||||
|
if (scaleFactor == 1000000) return "%.2f MB/s";
|
||||||
|
if (scaleFactor == 1000000000) return "%.2f GB/s";
|
||||||
|
return "%.0f ?/s";
|
||||||
|
}
|
||||||
|
|
||||||
|
void scaleData() const
|
||||||
|
{
|
||||||
|
auto data = proxyDlLine->points();
|
||||||
|
for (int i = 0; i < data.size(); i++)
|
||||||
|
{
|
||||||
|
data[i] = {data[i].x(), static_cast<double>(proxyDlRaw[i]) / scaleFactor};
|
||||||
|
}
|
||||||
|
proxyDlLine->replace(data);
|
||||||
|
|
||||||
|
data = proxyUpLine->points();
|
||||||
|
for (int i = 0; i < data.size(); i++)
|
||||||
|
{
|
||||||
|
data[i] = {data[i].x(), static_cast<double>(proxyUlRaw[i]) / scaleFactor};
|
||||||
|
}
|
||||||
|
proxyUpLine->replace(data);
|
||||||
|
|
||||||
|
data = directDlLine->points();
|
||||||
|
for (int i = 0; i < data.size(); i++)
|
||||||
|
{
|
||||||
|
data[i] = {data[i].x(), static_cast<double>(directDlRaw[i]) / scaleFactor};
|
||||||
|
}
|
||||||
|
directDlLine->replace(data);
|
||||||
|
|
||||||
|
data = directUpLine->points();
|
||||||
|
for (int i = 0; i < data.size(); i++)
|
||||||
|
{
|
||||||
|
data[i] = {data[i].x(), static_cast<double>(directUlRaw[i]) / scaleFactor};
|
||||||
|
}
|
||||||
|
directUpLine->replace(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit TrafficChart()
|
||||||
|
{
|
||||||
|
// init
|
||||||
|
chart = new QChart;
|
||||||
|
updateTheme();
|
||||||
|
chart->setTitle("Traffic Rate");
|
||||||
|
chart->legend()->setVisible(true);
|
||||||
|
chart->legend()->setAlignment(Qt::AlignBottom);
|
||||||
|
chart->setMargins(QMargins(0, 0, 0, 0));
|
||||||
|
|
||||||
|
proxyDlLine = new QSplineSeries;
|
||||||
|
proxyDlLine->setName("Proxy Dl");
|
||||||
|
proxyDlLine->setColor(Qt::darkBlue);
|
||||||
|
auto pen = proxyDlLine->pen();
|
||||||
|
pen.setWidth(2);
|
||||||
|
proxyDlLine->setPen(pen);
|
||||||
|
chart->addSeries(proxyDlLine);
|
||||||
|
|
||||||
|
proxyUpLine = new QSplineSeries;
|
||||||
|
proxyUpLine->setName("Proxy Ul");
|
||||||
|
proxyUpLine->setColor(Qt::darkRed);
|
||||||
|
pen = proxyUpLine->pen();
|
||||||
|
pen.setWidth(2);
|
||||||
|
proxyUpLine->setPen(pen);
|
||||||
|
chart->addSeries(proxyUpLine);
|
||||||
|
|
||||||
|
directDlLine = new QSplineSeries;
|
||||||
|
directDlLine->setName("Direct Dl");
|
||||||
|
directDlLine->setColor(Qt::darkGreen);
|
||||||
|
pen = directDlLine->pen();
|
||||||
|
pen.setWidth(2);
|
||||||
|
directDlLine->setPen(pen);
|
||||||
|
chart->addSeries(directDlLine);
|
||||||
|
|
||||||
|
directUpLine = new QSplineSeries;
|
||||||
|
directUpLine->setName("Direct Ul");
|
||||||
|
directUpLine->setColor(Qt::darkYellow);
|
||||||
|
pen = directUpLine->pen();
|
||||||
|
pen.setWidth(2);
|
||||||
|
directUpLine->setPen(pen);
|
||||||
|
chart->addSeries(directUpLine);
|
||||||
|
|
||||||
|
timeAxis = new QDateTimeAxis;
|
||||||
|
timeAxis->setFormat("hh:mm:ss");
|
||||||
|
timeAxis->setTickCount(10);
|
||||||
|
chart->addAxis(timeAxis, Qt::AlignBottom);
|
||||||
|
proxyDlLine->attachAxis(timeAxis);
|
||||||
|
proxyUpLine->attachAxis(timeAxis);
|
||||||
|
directDlLine->attachAxis(timeAxis);
|
||||||
|
directUpLine->attachAxis(timeAxis);
|
||||||
|
|
||||||
|
valueAxis = new QValueAxis;
|
||||||
|
valueAxis->setLabelFormat("%.0f B/s");
|
||||||
|
valueAxis->setMin(0);
|
||||||
|
valueAxis->setMax(1000);
|
||||||
|
chart->addAxis(valueAxis, Qt::AlignLeft);
|
||||||
|
proxyDlLine->attachAxis(valueAxis);
|
||||||
|
proxyUpLine->attachAxis(valueAxis);
|
||||||
|
directDlLine->attachAxis(valueAxis);
|
||||||
|
directUpLine->attachAxis(valueAxis);
|
||||||
|
|
||||||
|
// initial values
|
||||||
|
auto now = QDateTime::currentDateTime();
|
||||||
|
timeAxis->setRange(now.addSecs(-intervalRange + 1), now);
|
||||||
|
for (int i = 0; i < intervalRange; ++i)
|
||||||
|
{
|
||||||
|
proxyDlLine->append(now.addSecs(-intervalRange+i+1).toMSecsSinceEpoch(), 0);
|
||||||
|
proxyUpLine->append(now.addSecs(-intervalRange+i+1).toMSecsSinceEpoch(), 0);
|
||||||
|
directDlLine->append(now.addSecs(-intervalRange+i+1).toMSecsSinceEpoch(), 0);
|
||||||
|
directUpLine->append(now.addSecs(-intervalRange+i+1).toMSecsSinceEpoch(), 0);
|
||||||
|
|
||||||
|
proxyDlRaw << 0;
|
||||||
|
proxyUlRaw << 0;
|
||||||
|
directDlRaw << 0;
|
||||||
|
directUlRaw << 0;
|
||||||
|
|
||||||
|
dataSet.insert(0);
|
||||||
|
dataSet.insert(0);
|
||||||
|
dataSet.insert(0);
|
||||||
|
dataSet.insert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
chartView = new CustomChartView(chart);
|
||||||
|
chartView->setRenderHint(QPainter::Antialiasing);
|
||||||
|
|
||||||
|
QObject::connect(chartView, &CustomChartView::mouseStopEvent, chartView, [=](const QPoint pos)
|
||||||
|
{
|
||||||
|
if (!chartView->rect().contains(chartView->mapFromGlobal(QCursor::pos()))) return;
|
||||||
|
auto x = chart->mapToValue(pos).x();
|
||||||
|
int idx = -1;
|
||||||
|
int mn=5000000;
|
||||||
|
for (int i=0;i<proxyDlLine->count();i++)
|
||||||
|
{
|
||||||
|
if (auto dif = abs(proxyDlLine->at(i).x()-x); dif <= 500000)
|
||||||
|
{
|
||||||
|
if (dif < mn)
|
||||||
|
{
|
||||||
|
mn = dif;
|
||||||
|
idx = i;
|
||||||
|
}
|
||||||
|
else if (idx > 0) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (idx == -1) return;
|
||||||
|
const auto format = getRateLabel();
|
||||||
|
const auto data = QString::asprintf(
|
||||||
|
QString("Proxy Dl: " + format + "\nProxy Ul: " + format + "\nDirect Dl: " + format + "\nDirect Ul: " + format).toStdString().c_str(),
|
||||||
|
proxyDlLine->at(idx).y(), proxyUpLine->at(idx).y(), directDlLine->at(idx).y(), directUpLine->at(idx).y());
|
||||||
|
QToolTip::showText(chartView->mapToGlobal(pos), data);
|
||||||
|
});
|
||||||
|
|
||||||
|
QObject::connect(chartView, &CustomChartView::mouseStartMoving, chartView, [=]
|
||||||
|
{
|
||||||
|
if (QToolTip::isVisible())
|
||||||
|
{
|
||||||
|
QToolTip::hideText();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (QLegendMarker* marker : chart->legend()->markers())
|
||||||
|
{
|
||||||
|
QObject::connect(marker, &QLegendMarker::clicked, chartView, [=]
|
||||||
|
{
|
||||||
|
auto series = static_cast<QLineSeries*>(marker->series());
|
||||||
|
double alpha;
|
||||||
|
if (series->isVisible())
|
||||||
|
{
|
||||||
|
series->hide();
|
||||||
|
marker->setVisible(true);
|
||||||
|
alpha = 0.5;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
series->show();
|
||||||
|
alpha = 1.0;
|
||||||
|
}
|
||||||
|
QBrush brush = marker->labelBrush();
|
||||||
|
QColor color = brush.color();
|
||||||
|
color.setAlphaF(alpha);
|
||||||
|
brush.setColor(color);
|
||||||
|
marker->setLabelBrush(brush);
|
||||||
|
|
||||||
|
brush = marker->brush();
|
||||||
|
color = brush.color();
|
||||||
|
color.setAlphaF(alpha);
|
||||||
|
brush.setColor(color);
|
||||||
|
marker->setBrush(brush);
|
||||||
|
|
||||||
|
QPen markerPen = marker->pen();
|
||||||
|
color = markerPen.color();
|
||||||
|
color.setAlphaF(alpha);
|
||||||
|
markerPen.setColor(color);
|
||||||
|
marker->setPen(markerPen);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] QChartView* getChartView() const
|
||||||
|
{
|
||||||
|
return chartView;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateChart(int pDl, int pUl, int dDl, int dUl)
|
||||||
|
{
|
||||||
|
auto now = QDateTime::currentDateTime();
|
||||||
|
dataSet.insert(pDl);
|
||||||
|
dataSet.insert(pUl);
|
||||||
|
dataSet.insert(dDl);
|
||||||
|
dataSet.insert(dUl);
|
||||||
|
|
||||||
|
dataSet.erase(dataSet.find(proxyDlRaw.first()));
|
||||||
|
dataSet.erase(dataSet.find(proxyUlRaw.first()));
|
||||||
|
dataSet.erase(dataSet.find(directDlRaw.first()));
|
||||||
|
dataSet.erase(dataSet.find(directUlRaw.first()));
|
||||||
|
|
||||||
|
proxyDlLine->remove(0);
|
||||||
|
proxyUpLine->remove(0);
|
||||||
|
directDlLine->remove(0);
|
||||||
|
directUpLine->remove(0);
|
||||||
|
|
||||||
|
proxyDlRaw.removeFirst();
|
||||||
|
proxyUlRaw.removeFirst();
|
||||||
|
directDlRaw.removeFirst();
|
||||||
|
directUlRaw.removeFirst();
|
||||||
|
|
||||||
|
proxyDlLine->append(now.toMSecsSinceEpoch(), static_cast<double>(pDl) / static_cast<double>(scaleFactor));
|
||||||
|
proxyUpLine->append(now.toMSecsSinceEpoch(), static_cast<double>(pUl) / static_cast<double>(scaleFactor));
|
||||||
|
directDlLine->append(now.toMSecsSinceEpoch(), static_cast<double>(dDl) / static_cast<double>(scaleFactor));
|
||||||
|
directUpLine->append(now.toMSecsSinceEpoch(), static_cast<double>(dUl) / static_cast<double>(scaleFactor));
|
||||||
|
|
||||||
|
proxyDlRaw << pDl;
|
||||||
|
proxyUlRaw << pUl;
|
||||||
|
directDlRaw << dDl;
|
||||||
|
directUlRaw << dUl;
|
||||||
|
|
||||||
|
timeAxis->setRange(now.addSecs(-intervalRange + 1), now);
|
||||||
|
|
||||||
|
updateMagnitude();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateTheme()
|
||||||
|
{
|
||||||
|
chart->setTheme(qApp->styleHints()->colorScheme() == Qt::ColorScheme::Dark ? QChart::ChartThemeDark : QChart::ChartThemeLight);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -249,7 +249,6 @@ namespace NekoGui {
|
|||||||
_add(new configItem("mux_concurrency", &mux_concurrency, itemType::integer));
|
_add(new configItem("mux_concurrency", &mux_concurrency, itemType::integer));
|
||||||
_add(new configItem("mux_padding", &mux_padding, itemType::boolean));
|
_add(new configItem("mux_padding", &mux_padding, itemType::boolean));
|
||||||
_add(new configItem("mux_default_on", &mux_default_on, itemType::boolean));
|
_add(new configItem("mux_default_on", &mux_default_on, itemType::boolean));
|
||||||
_add(new configItem("traffic_loop_interval", &traffic_loop_interval, itemType::integer));
|
|
||||||
_add(new configItem("test_concurrent", &test_concurrent, itemType::integer));
|
_add(new configItem("test_concurrent", &test_concurrent, itemType::integer));
|
||||||
_add(new configItem("theme", &theme, itemType::string));
|
_add(new configItem("theme", &theme, itemType::string));
|
||||||
_add(new configItem("custom_inbound", &custom_inbound, itemType::string));
|
_add(new configItem("custom_inbound", &custom_inbound, itemType::string));
|
||||||
|
|||||||
@ -26,8 +26,7 @@ namespace NekoGui_traffic
|
|||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (stop) return;
|
if (stop) return;
|
||||||
auto sleep_ms = NekoGui::dataStore->traffic_loop_interval;
|
QThread::msleep(1000);
|
||||||
QThread::msleep(sleep_ms);
|
|
||||||
|
|
||||||
if (suspend || !NekoGui::dataStore->enable_stats) continue;
|
if (suspend || !NekoGui::dataStore->enable_stats) continue;
|
||||||
|
|
||||||
|
|||||||
@ -56,15 +56,13 @@ namespace NekoGui_traffic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TrafficLooper::Loop() {
|
void TrafficLooper::Loop() {
|
||||||
if (NekoGui::dataStore->disable_traffic_stats) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
elapsedTimer.start();
|
elapsedTimer.start();
|
||||||
while (true) {
|
while (true) {
|
||||||
auto sleep_ms = NekoGui::dataStore->traffic_loop_interval;
|
QThread::msleep(1000); // refresh every one second
|
||||||
if (sleep_ms < 500 || sleep_ms > 5000) sleep_ms = 1000;
|
|
||||||
QThread::msleep(sleep_ms);
|
if (NekoGui::dataStore->disable_traffic_stats) {
|
||||||
if (NekoGui::dataStore->traffic_loop_interval == 0) continue; // user disabled
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// profile start and stop
|
// profile start and stop
|
||||||
if (!loop_enabled) {
|
if (!loop_enabled) {
|
||||||
@ -76,6 +74,11 @@ namespace NekoGui_traffic {
|
|||||||
m->refresh_status("STOP");
|
m->refresh_status("STOP");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
runOnUiThread([=]
|
||||||
|
{
|
||||||
|
auto m = GetMainWindow();
|
||||||
|
m->update_traffic_graph(0, 0, 0, 0);
|
||||||
|
});
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
// 开始
|
// 开始
|
||||||
@ -96,6 +99,7 @@ namespace NekoGui_traffic {
|
|||||||
auto m = GetMainWindow();
|
auto m = GetMainWindow();
|
||||||
if (proxy != nullptr) {
|
if (proxy != nullptr) {
|
||||||
m->refresh_status(QObject::tr("Proxy: %1\nDirect: %2").arg(proxy->DisplaySpeed(), direct->DisplaySpeed()));
|
m->refresh_status(QObject::tr("Proxy: %1\nDirect: %2").arg(proxy->DisplaySpeed(), direct->DisplaySpeed()));
|
||||||
|
m->update_traffic_graph(proxy->downlink_rate, proxy->uplink_rate, direct->downlink_rate, direct->uplink_rate);
|
||||||
}
|
}
|
||||||
for (const auto &item: items) {
|
for (const auto &item: items) {
|
||||||
if (item->id < 0) continue;
|
if (item->id < 0) continue;
|
||||||
|
|||||||
@ -43,6 +43,7 @@
|
|||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QStyleHints>
|
#include <QStyleHints>
|
||||||
#include <QToolTip>
|
#include <QToolTip>
|
||||||
|
#include <QtCharts>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <3rdparty/QHotkey/qhotkey.h>
|
#include <3rdparty/QHotkey/qhotkey.h>
|
||||||
#include <include/global/HTTPRequestHelper.hpp>
|
#include <include/global/HTTPRequestHelper.hpp>
|
||||||
@ -173,6 +174,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
|||||||
connect(qApp->styleHints(), &QStyleHints::colorSchemeChanged, this, [=](const Qt::ColorScheme& scheme) {
|
connect(qApp->styleHints(), &QStyleHints::colorSchemeChanged, this, [=](const Qt::ColorScheme& scheme) {
|
||||||
new SyntaxHighlighter(scheme == Qt::ColorScheme::Dark, qvLogDocument);
|
new SyntaxHighlighter(scheme == Qt::ColorScheme::Dark, qvLogDocument);
|
||||||
themeManager->ApplyTheme(NekoGui::dataStore->theme, true);
|
themeManager->ApplyTheme(NekoGui::dataStore->theme, true);
|
||||||
|
if (trafficGraph) trafficGraph->updateTheme();
|
||||||
});
|
});
|
||||||
connect(themeManager, &ThemeManager::themeChanged, this, [=](const QString& theme){
|
connect(themeManager, &ThemeManager::themeChanged, this, [=](const QString& theme){
|
||||||
if (theme.toLower().contains("vista")) {
|
if (theme.toLower().contains("vista")) {
|
||||||
@ -247,6 +249,10 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// setup Traffic Graph
|
||||||
|
trafficGraph = new TrafficChart();
|
||||||
|
ui->graph_tab->layout()->addWidget(trafficGraph->getChartView());
|
||||||
|
|
||||||
// table UI
|
// table UI
|
||||||
ui->proxyListTable->callback_save_order = [=] {
|
ui->proxyListTable->callback_save_order = [=] {
|
||||||
auto group = NekoGui::profileManager->CurrentGroup();
|
auto group = NekoGui::profileManager->CurrentGroup();
|
||||||
@ -1128,6 +1134,11 @@ void MainWindow::refresh_status(const QString &traffic_update) {
|
|||||||
icon_status = icon_status_new;
|
icon_status = icon_status_new;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::update_traffic_graph(int proxyDl, int proxyUp, int directDl, int directUp)
|
||||||
|
{
|
||||||
|
trafficGraph->updateChart(proxyDl, proxyUp, directDl, directUp);
|
||||||
|
}
|
||||||
|
|
||||||
// table显示
|
// table显示
|
||||||
|
|
||||||
// refresh_groups -> show_group -> refresh_proxy_list
|
// refresh_groups -> show_group -> refresh_proxy_list
|
||||||
|
|||||||
@ -415,13 +415,11 @@ void MainWindow::neko_stop(bool crash, bool sem, bool manual) {
|
|||||||
NekoGui_traffic::connection_lister->suspend = true;
|
NekoGui_traffic::connection_lister->suspend = true;
|
||||||
UpdateConnectionListWithRecreate({});
|
UpdateConnectionListWithRecreate({});
|
||||||
NekoGui_traffic::trafficLooper->loop_mutex.lock();
|
NekoGui_traffic::trafficLooper->loop_mutex.lock();
|
||||||
if (NekoGui::dataStore->traffic_loop_interval != 0) {
|
NekoGui_traffic::trafficLooper->UpdateAll();
|
||||||
NekoGui_traffic::trafficLooper->UpdateAll();
|
for (const auto &item: NekoGui_traffic::trafficLooper->items) {
|
||||||
for (const auto &item: NekoGui_traffic::trafficLooper->items) {
|
if (item->id < 0) continue;
|
||||||
if (item->id < 0) continue;
|
NekoGui::profileManager->GetProfile(item->id)->Save();
|
||||||
NekoGui::profileManager->GetProfile(item->id)->Save();
|
refresh_proxy_list(item->id);
|
||||||
refresh_proxy_list(item->id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
NekoGui_traffic::trafficLooper->loop_mutex.unlock();
|
NekoGui_traffic::trafficLooper->loop_mutex.unlock();
|
||||||
|
|
||||||
|
|||||||
@ -48,20 +48,6 @@ DialogBasicSettings::DialogBasicSettings(QWidget *parent)
|
|||||||
D_LOAD_BOOL(start_minimal)
|
D_LOAD_BOOL(start_minimal)
|
||||||
D_LOAD_INT(max_log_line)
|
D_LOAD_INT(max_log_line)
|
||||||
//
|
//
|
||||||
if (NekoGui::dataStore->traffic_loop_interval == 500) {
|
|
||||||
ui->rfsh_r->setCurrentIndex(0);
|
|
||||||
} else if (NekoGui::dataStore->traffic_loop_interval == 1000) {
|
|
||||||
ui->rfsh_r->setCurrentIndex(1);
|
|
||||||
} else if (NekoGui::dataStore->traffic_loop_interval == 2000) {
|
|
||||||
ui->rfsh_r->setCurrentIndex(2);
|
|
||||||
} else if (NekoGui::dataStore->traffic_loop_interval == 3000) {
|
|
||||||
ui->rfsh_r->setCurrentIndex(3);
|
|
||||||
} else if (NekoGui::dataStore->traffic_loop_interval == 5000) {
|
|
||||||
ui->rfsh_r->setCurrentIndex(4);
|
|
||||||
} else {
|
|
||||||
ui->rfsh_r->setCurrentIndex(5);
|
|
||||||
}
|
|
||||||
//
|
|
||||||
ui->language->setCurrentIndex(NekoGui::dataStore->language);
|
ui->language->setCurrentIndex(NekoGui::dataStore->language);
|
||||||
connect(ui->language, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [=](int index) {
|
connect(ui->language, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [=](int index) {
|
||||||
CACHE.needRestart = true;
|
CACHE.needRestart = true;
|
||||||
@ -190,20 +176,6 @@ void DialogBasicSettings::accept() {
|
|||||||
NekoGui::dataStore->max_log_line = 200;
|
NekoGui::dataStore->max_log_line = 200;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ui->rfsh_r->currentIndex() == 0) {
|
|
||||||
NekoGui::dataStore->traffic_loop_interval = 500;
|
|
||||||
} else if (ui->rfsh_r->currentIndex() == 1) {
|
|
||||||
NekoGui::dataStore->traffic_loop_interval = 1000;
|
|
||||||
} else if (ui->rfsh_r->currentIndex() == 2) {
|
|
||||||
NekoGui::dataStore->traffic_loop_interval = 2000;
|
|
||||||
} else if (ui->rfsh_r->currentIndex() == 3) {
|
|
||||||
NekoGui::dataStore->traffic_loop_interval = 3000;
|
|
||||||
} else if (ui->rfsh_r->currentIndex() == 4) {
|
|
||||||
NekoGui::dataStore->traffic_loop_interval = 5000;
|
|
||||||
} else {
|
|
||||||
NekoGui::dataStore->traffic_loop_interval = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscription
|
// Subscription
|
||||||
|
|
||||||
if (ui->sub_auto_update_enable->isChecked()) {
|
if (ui->sub_auto_update_enable->isChecked()) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user