feat: 新增流量波动图窗口,展示活动接口最近 5 分钟网速趋势
- C++ 后端增加 SpeedSample 采样结构与每接口环形缓冲(MAX_HISTORY_SAMPLES=300, 即 5 分钟/1Hz),通过 speedHistoryDownload/speedHistoryUpload 暴露为 QVariantList of QPointF 供 QML 绘制;接口切换/消失时同步清理历史避免泄漏 - 新增 package/components/TrafficChartWindow.qml:纯 QML Canvas 绘制下载/上传 双折线,无外部依赖;支持 hover 辅助线、置顶图钉、空状态兜底 - networkview.qml 右键菜单增加"流量波动图"入口,弹出屏幕居中独立窗口
This commit is contained in:
@@ -127,6 +127,37 @@ QString NetworkMonitorApplet::version() const
|
||||
return pluginMetaData().value("Version").toString();
|
||||
}
|
||||
|
||||
// 返回当前活动接口的下行速度历史,转换为 QVariantList of QPointF 供 QML 使用
|
||||
// 设计原因:QPointF 仅携带 x/y 两个值,上传与下载分别对应两个列表
|
||||
QVariantList NetworkMonitorApplet::speedHistoryDownload() const
|
||||
{
|
||||
QVariantList list;
|
||||
if (m_activeInterface.isEmpty() || !m_speedHistory.contains(m_activeInterface)) {
|
||||
return list;
|
||||
}
|
||||
const QVector<SpeedSample> &samples = m_speedHistory[m_activeInterface];
|
||||
list.reserve(samples.size());
|
||||
for (const SpeedSample &s : samples) {
|
||||
list.append(QPointF(s.timestamp, s.downloadSpeed));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
// 返回当前活动接口的上行速度历史,结构同 speedHistoryDownload
|
||||
QVariantList NetworkMonitorApplet::speedHistoryUpload() const
|
||||
{
|
||||
QVariantList list;
|
||||
if (m_activeInterface.isEmpty() || !m_speedHistory.contains(m_activeInterface)) {
|
||||
return list;
|
||||
}
|
||||
const QVector<SpeedSample> &samples = m_speedHistory[m_activeInterface];
|
||||
list.reserve(samples.size());
|
||||
for (const SpeedSample &s : samples) {
|
||||
list.append(QPointF(s.timestamp, s.uploadSpeed));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
void NetworkMonitorApplet::refresh()
|
||||
{
|
||||
readNetworkStats();
|
||||
@@ -138,6 +169,8 @@ void NetworkMonitorApplet::setActiveInterface(const QString &interface)
|
||||
m_activeInterface = interface;
|
||||
m_firstUpdate = true;
|
||||
emit activeInterfaceChanged();
|
||||
// 接口切换后通知 QML 趋势图切换显示新接口的历史数据
|
||||
emit speedHistoryChanged();
|
||||
// 接口切换后立即检测新接口的 IP 地址
|
||||
detectIpAddress();
|
||||
}
|
||||
@@ -190,7 +223,16 @@ void NetworkMonitorApplet::readNetworkStats()
|
||||
if (newInterfaces.keys() != m_interfaces.keys()) {
|
||||
m_interfaceList = newInterfaces.keys();
|
||||
m_interfaces = newInterfaces;
|
||||
|
||||
|
||||
// 清理已消失接口的历史缓冲,避免内存泄漏
|
||||
// 设计原因:USB 网卡、VPN 隧道等接口可能随时消失,其历史数据不再需要
|
||||
const QStringList oldIfaces = m_speedHistory.keys();
|
||||
for (const QString &name : oldIfaces) {
|
||||
if (!newInterfaces.contains(name)) {
|
||||
m_speedHistory.remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
// 自动选择活动接口
|
||||
if (m_activeInterface.isEmpty() && !m_interfaceList.isEmpty()) {
|
||||
// 优先选择物理网卡(wlp/wlan/enp/eth)中有流量的接口,
|
||||
@@ -255,7 +297,26 @@ void NetworkMonitorApplet::calculateSpeed()
|
||||
|
||||
m_lastRxBytes = currentRxBytes;
|
||||
m_lastTxBytes = currentTxBytes;
|
||||
|
||||
|
||||
// 追加采样点到当前活动接口的环形缓冲
|
||||
// 设计原因:applet 启动即持续采集,用户随时打开趋势图都能看到完整 5 分钟数据
|
||||
if (!m_activeInterface.isEmpty()) {
|
||||
SpeedSample sample;
|
||||
sample.timestamp = QDateTime::currentSecsSinceEpoch();
|
||||
sample.downloadSpeed = static_cast<double>(m_downloadSpeed);
|
||||
sample.uploadSpeed = static_cast<double>(m_uploadSpeed);
|
||||
m_speedHistory[m_activeInterface].append(sample);
|
||||
|
||||
// 滑动窗口裁剪:超过 300 点时丢弃最旧的
|
||||
// removeFirst 是 O(n),但 300 点每秒一次开销可忽略,无需复杂环形索引
|
||||
QVector<SpeedSample> &samples = m_speedHistory[m_activeInterface];
|
||||
while (samples.size() > MAX_HISTORY_SAMPLES) {
|
||||
samples.removeFirst();
|
||||
}
|
||||
|
||||
emit speedHistoryChanged();
|
||||
}
|
||||
|
||||
emit speedChanged();
|
||||
emit totalChanged();
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
#include <QTimer>
|
||||
#include <QVariantList>
|
||||
#include <QMap>
|
||||
#include <QHash>
|
||||
#include <QPointF>
|
||||
#include <QDateTime>
|
||||
|
||||
DS_BEGIN_NAMESPACE
|
||||
|
||||
@@ -26,6 +29,15 @@ struct NetworkInterface {
|
||||
qint64 txDropped; // 发送丢弃数
|
||||
};
|
||||
|
||||
// 单个速度采样点:用于趋势图的环形缓冲
|
||||
// timestamp: 自 epoch 的秒级时间戳,作为 X 轴定位
|
||||
// downloadSpeed / uploadSpeed: 当时的瞬时速度(字节/秒),作为两条折线的 Y 值
|
||||
struct SpeedSample {
|
||||
qint64 timestamp;
|
||||
double downloadSpeed;
|
||||
double uploadSpeed;
|
||||
};
|
||||
|
||||
class NetworkMonitorApplet : public DApplet
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -44,6 +56,11 @@ class NetworkMonitorApplet : public DApplet
|
||||
Q_PROPERTY(QString ipv6Address READ ipv6Address NOTIFY ipv6AddressChanged)
|
||||
// 插件版本号,从 dde-shell 元数据读取,版本唯一源为 CMakeLists.txt project(VERSION)
|
||||
Q_PROPERTY(QString version READ version CONSTANT)
|
||||
// 当前活动接口的下行速度历史采样点(QPointF 列表:x=时间戳秒, y=速度 bytes/sec)
|
||||
// 用于趋势图绘制下载折线,每次 calculateSpeed 追加一个点并发射 speedHistoryChanged
|
||||
Q_PROPERTY(QVariantList speedHistoryDownload READ speedHistoryDownload NOTIFY speedHistoryChanged)
|
||||
// 当前活动接口的上行速度历史采样点,结构同 speedHistoryDownload
|
||||
Q_PROPERTY(QVariantList speedHistoryUpload READ speedHistoryUpload NOTIFY speedHistoryChanged)
|
||||
|
||||
public:
|
||||
explicit NetworkMonitorApplet(QObject *parent = nullptr);
|
||||
@@ -62,6 +79,10 @@ public:
|
||||
QString activeInterface() const;
|
||||
QString ipAddress() const;
|
||||
QString ipv6Address() const;
|
||||
// 返回当前活动接口的下行速度历史(QPointF 列表),供 QML 趋势图绘制
|
||||
QVariantList speedHistoryDownload() const;
|
||||
// 返回当前活动接口的上行速度历史(QPointF 列表),供 QML 趋势图绘制
|
||||
QVariantList speedHistoryUpload() const;
|
||||
// 返回插件版本号,从 dde-shell 插件元数据(metadata.json)读取
|
||||
QString version() const;
|
||||
|
||||
@@ -77,6 +98,7 @@ signals:
|
||||
void activeInterfaceChanged();
|
||||
void ipAddressChanged();
|
||||
void ipv6AddressChanged();
|
||||
void speedHistoryChanged();
|
||||
|
||||
private:
|
||||
void readNetworkStats();
|
||||
@@ -109,6 +131,12 @@ private:
|
||||
|
||||
bool m_ready;
|
||||
bool m_firstUpdate;
|
||||
|
||||
// 速度历史环形缓冲:每个接口独立维护一份,key 为接口名
|
||||
// 设计原因:接口切换时趋势图不出现跳变,切回时仍能看到该接口历史
|
||||
QHash<QString, QVector<SpeedSample>> m_speedHistory;
|
||||
// 每个接口最多保留的采样点数:5 分钟 × 60 秒 = 300
|
||||
static constexpr int MAX_HISTORY_SAMPLES = 300;
|
||||
};
|
||||
|
||||
DS_END_NAMESPACE
|
||||
Reference in New Issue
Block a user