6 Commits
Author SHA1 Message Date
Jokul 30a186b6dc style: 包统计行布局改为与流量总计行一致
标签+↓蓝粗箭头+数值+↑绿粗箭头+数值拆分为独立 Text 元素,
与上方流量总计行视觉对称
2026-08-08 23:51:44 +08:00
Jokul 6ef9a23a24 fix: 弹窗高度增加容纳包统计行 + 文案调整
PanelPopup 高度 250->280 容纳新增的包统计行;
翻译文案:Total->流量总计、Packets->包统计
2026-08-08 23:48:23 +08:00
Jokul a5cfffc2f1 fix: 包统计行从接口信息区移到累计流量区下方
包统计与流量统计同属累计数据,放一起信息归属清晰;移除接口信息区的
包统计行,在总计行下方新增居中显示;标签加 qsTr 支持国际化
2026-08-08 23:32:53 +08:00
Jokul ee885e0dbb fix: 关于窗口无法拖动
drag.target 对 Window 无效,改用 startSystemMove() 系统级窗口拖动,
与 SettingsWindow/TrafficChartWindow 一致
2026-08-08 23:24:45 +08:00
Jokul d18314bdec feat: 弹窗展示包统计/错误/丢包数据
C++ 后端暴露活动接口的收发包/错误/丢包累计统计(rxPackets/txPackets/
rxErrors/txErrors/rxDropped/txDropped,quint64 + packetStatsChanged 信号),
弹窗接口信息区新增一行紧凑包统计,包总数始终显示,
错误/丢包仅在存在时显示;箭头遵循应用惯例 ↓=接收、↑=发送
2026-08-08 23:23:38 +08:00
Jokul 4aca583d2d perf: C++ 后端性能优化
- 正则预编译:readNetworkStats 的正则改为 static const,仅首次编译一次
- 信号脏标记:速度/总量仅在值实际变化时才发射 speedChanged/totalChanged,
  避免网络空闲时每秒触发 QML 无效重绘
- IP 检测降频:detectIpAddress 由每秒改为每 5 秒检测一次
- 历史缓存:speedHistoryDownload/Upload 增加懒重建缓存,避免 QML
  每次读取都重新构造 300 个 QPointF
2026-08-08 23:22:12 +08:00
6 changed files with 235 additions and 26 deletions
+4 -1
View File
@@ -74,9 +74,12 @@ Window {
color: "transparent" color: "transparent"
// 拖动层:声明在关闭按钮之前(位于其下方),避免遮挡按钮点击与 hover // 拖动层:声明在关闭按钮之前(位于其下方),避免遮挡按钮点击与 hover
// 用 startSystemMove() 系统级窗口拖动(drag.target 对 Window 无效,
// 与 SettingsWindow/TrafficChartWindow 一致)
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
drag.target: root cursorShape: Qt.OpenHandCursor
onPressed: root.startSystemMove()
} }
Text { Text {
+73
View File
@@ -43,6 +43,14 @@ Control {
readonly property string ipv6Address: applet ? (applet.ipv6Address || "") : "" readonly property string ipv6Address: applet ? (applet.ipv6Address || "") : ""
readonly property var networkInterfaces: applet ? (applet.networkInterfaces || []) : [] readonly property var networkInterfaces: applet ? (applet.networkInterfaces || []) : []
// 活动接口的收发包/错误/丢包累计统计(自开机起),由 C++ 后端持续更新
readonly property real rxPackets: applet ? (applet.rxPackets || 0) : 0
readonly property real txPackets: applet ? (applet.txPackets || 0) : 0
readonly property real rxErrors: applet ? (applet.rxErrors || 0) : 0
readonly property real txErrors: applet ? (applet.txErrors || 0) : 0
readonly property real rxDropped: applet ? (applet.rxDropped || 0) : 0
readonly property real txDropped: applet ? (applet.txDropped || 0) : 0
// 用户自定义字体色:非空时覆盖 primaryText,空串回退 DTK 调色板派生 // 用户自定义字体色:非空时覆盖 primaryText,空串回退 DTK 调色板派生
// 仅作用于主要文字(接口名、速度数值、累计数值、chip 未选中文字) // 仅作用于主要文字(接口名、速度数值、累计数值、chip 未选中文字)
// secondaryText/tertiaryText/强调色不跟随,保持系统主题层次不崩 // secondaryText/tertiaryText/强调色不跟随,保持系统主题层次不崩
@@ -458,6 +466,71 @@ Control {
Item { Layout.fillWidth: true } Item { Layout.fillWidth: true }
} }
// 包统计:收发包 + 错误/丢包(累计值,自开机起),紧接累计流量下方
// 布局与流量总计行一致:标签 + ↓蓝粗 + 数值 + ↑绿粗 + 数值,拆分为独立 Text
// 设计原因:包统计与流量统计同属"累计数据",放一起信息归属清晰且视觉对称;
// 包总数始终显示;错误/丢包仅在存在时显示,避免正常网卡信息过载
RowLayout {
Layout.fillWidth: true
spacing: 8
visible: popup.ready
Item { Layout.fillWidth: true }
// 包统计标签(与"流量总计"标签对齐)
Text {
text: qsTr("Packets")
font.pixelSize: Math.round(12 * popup.fontScale)
color: popup.tertiaryText
}
// 接收包数:蓝色下箭头(与流量总计行下载箭头一致)
Text {
text: "↓"
font.pixelSize: Math.round(12 * popup.fontScale)
font.weight: Font.Bold
color: popup.accentBlue
}
Text {
text: popup.rxPackets.toFixed(0)
font.pixelSize: Math.round(13 * popup.fontScale)
color: popup.primaryText
}
// 发送包数:绿色上箭头(与流量总计行上传箭头一致)
Text {
text: "↑"
font.pixelSize: Math.round(12 * popup.fontScale)
font.weight: Font.Bold
color: popup.accentGreen
}
Text {
text: popup.txPackets.toFixed(0)
font.pixelSize: Math.round(13 * popup.fontScale)
color: popup.primaryText
}
// 错误数:仅在存在错误或丢包时显示,格式 "错误 {rxE}/{txE}"
Text {
text: qsTr("Errors") + " " + popup.rxErrors.toFixed(0) + "/" + popup.txErrors.toFixed(0)
font.pixelSize: Math.round(12 * popup.fontScale)
color: popup.tertiaryText
visible: popup.rxErrors > 0 || popup.txErrors > 0 || popup.rxDropped > 0 || popup.txDropped > 0
}
// 丢包数:仅在存在错误或丢包时显示,格式 "丢包 {rxD}/{txD}"
Text {
text: qsTr("Dropped") + " " + popup.rxDropped.toFixed(0) + "/" + popup.txDropped.toFixed(0)
font.pixelSize: Math.round(12 * popup.fontScale)
color: popup.tertiaryText
visible: popup.rxErrors > 0 || popup.txErrors > 0 || popup.rxDropped > 0 || popup.txDropped > 0
}
Item { Layout.fillWidth: true }
}
// 分隔线3 // 分隔线3
Rectangle { Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
+1 -1
View File
@@ -320,7 +320,7 @@ AppletItem {
PanelPopup { PanelPopup {
id: networkPopup id: networkPopup
width: 330 width: 330
height: 250 height: 280
popupX: DockPanelPositioner.x popupX: DockPanelPositioner.x
popupY: DockPanelPositioner.y popupY: DockPanelPositioner.y
+101 -23
View File
@@ -30,9 +30,17 @@ NetworkMonitorApplet::NetworkMonitorApplet(QObject *parent)
, m_uploadSpeed(0) , m_uploadSpeed(0)
, m_totalDownload(0) , m_totalDownload(0)
, m_totalUpload(0) , m_totalUpload(0)
, m_rxPackets(0)
, m_txPackets(0)
, m_rxErrors(0)
, m_txErrors(0)
, m_rxDropped(0)
, m_txDropped(0)
, m_ipDetectCounter(4)
, m_ready(false) , m_ready(false)
, m_firstUpdate(true) , m_firstUpdate(true)
, m_textColor() , m_textColor()
, m_historyDirty(true)
{ {
// 从独立配置文件读取持久化的字体颜色 // 从独立配置文件读取持久化的字体颜色
// 设计原因:使用独立配置文件避免污染 dde-shell 的共享配置, // 设计原因:使用独立配置文件避免污染 dde-shell 的共享配置,
@@ -139,6 +147,15 @@ QString NetworkMonitorApplet::ipv6Address() const
return m_ipv6Address; return m_ipv6Address;
} }
// ---- 活动接口包统计 getter(累计值,自开机起)----
// 供弹窗接口信息区展示收发包/错误/丢包数
quint64 NetworkMonitorApplet::rxPackets() const { return m_rxPackets; }
quint64 NetworkMonitorApplet::txPackets() const { return m_txPackets; }
quint64 NetworkMonitorApplet::rxErrors() const { return m_rxErrors; }
quint64 NetworkMonitorApplet::txErrors() const { return m_txErrors; }
quint64 NetworkMonitorApplet::rxDropped() const { return m_rxDropped; }
quint64 NetworkMonitorApplet::txDropped() const { return m_txDropped; }
// 返回插件版本号,从 dde-shell 插件元数据(metadata.json)读取 // 返回插件版本号,从 dde-shell 插件元数据(metadata.json)读取
// 版本唯一源为 CMakeLists.txt 的 project(VERSION),经 configure_file 写入 metadata.json, // 版本唯一源为 CMakeLists.txt 的 project(VERSION),经 configure_file 写入 metadata.json,
// dde-shell 加载时存入 DPluginMetaData,此处通过继承自 DApplet 的 pluginMetaData() 读取 // dde-shell 加载时存入 DPluginMetaData,此处通过继承自 DApplet 的 pluginMetaData() 读取
@@ -191,34 +208,42 @@ void NetworkMonitorApplet::setTextColor(const QString &color)
} }
// 返回当前活动接口的下行速度历史,转换为 QVariantList of QPointF 供 QML 使用 // 返回当前活动接口的下行速度历史,转换为 QVariantList of QPointF 供 QML 使用
// 设计原因:QPointF 仅携带 x/y 两个值,上传与下载分别对应两个列表 // 设计原因:QPointF 仅携带 x/y 两个值,上传与下载分别对应两个列表;
// 结果缓存于成员变量,仅当 m_historyDirty 时重建,避免 QML 每次读取都重新构造 300 个点
QVariantList NetworkMonitorApplet::speedHistoryDownload() const QVariantList NetworkMonitorApplet::speedHistoryDownload() const
{ {
QVariantList list; if (m_historyDirty) {
if (m_activeInterface.isEmpty() || !m_speedHistory.contains(m_activeInterface)) { rebuildHistoryCache();
return list;
} }
const QVector<SpeedSample> &samples = m_speedHistory[m_activeInterface]; return m_cachedHistoryDl;
list.reserve(samples.size());
for (const SpeedSample &s : samples) {
list.append(QPointF(s.timestamp, s.downloadSpeed));
}
return list;
} }
// 返回当前活动接口的上行速度历史,结构同 speedHistoryDownload // 返回当前活动接口的上行速度历史,结构同 speedHistoryDownload
QVariantList NetworkMonitorApplet::speedHistoryUpload() const QVariantList NetworkMonitorApplet::speedHistoryUpload() const
{ {
QVariantList list; if (m_historyDirty) {
if (m_activeInterface.isEmpty() || !m_speedHistory.contains(m_activeInterface)) { rebuildHistoryCache();
return list;
} }
const QVector<SpeedSample> &samples = m_speedHistory[m_activeInterface]; return m_cachedHistoryUpload;
list.reserve(samples.size()); }
for (const SpeedSample &s : samples) {
list.append(QPointF(s.timestamp, s.uploadSpeed)); // 重建上下行历史缓存(一次遍历同时填充两个列表)
// 仅当 dirty 时由两个 getter 调用,避免重复构建;
// 单 dirty 标记 + 一次重建双缓存,保证先后读取两个列表时数据一致
void NetworkMonitorApplet::rebuildHistoryCache() const
{
m_cachedHistoryDl.clear();
m_cachedHistoryUpload.clear();
if (!m_activeInterface.isEmpty() && m_speedHistory.contains(m_activeInterface)) {
const QVector<SpeedSample> &samples = m_speedHistory[m_activeInterface];
m_cachedHistoryDl.reserve(samples.size());
m_cachedHistoryUpload.reserve(samples.size());
for (const SpeedSample &s : samples) {
m_cachedHistoryDl.append(QPointF(s.timestamp, s.downloadSpeed));
m_cachedHistoryUpload.append(QPointF(s.timestamp, s.uploadSpeed));
}
} }
return list; m_historyDirty = false;
} }
void NetworkMonitorApplet::refresh() void NetworkMonitorApplet::refresh()
@@ -240,6 +265,19 @@ void NetworkMonitorApplet::setActiveInterface(const QString &interface)
m_activeInterface = interface; m_activeInterface = interface;
m_firstUpdate = true; m_firstUpdate = true;
// 接口切换后历史数据变化,标记脏缓存让趋势图立即重建
m_historyDirty = true;
// 接口切换后包统计指向新接口,旧值不再有效,重置并通知 QML
// 设计原因:包统计为各接口独立的累计值,切换后应显示新接口的数据
m_rxPackets = 0;
m_txPackets = 0;
m_rxErrors = 0;
m_txErrors = 0;
m_rxDropped = 0;
m_txDropped = 0;
emit packetStatsChanged();
// 持久化到配置文件,下次启动自动加载用户选择的网卡 // 持久化到配置文件,下次启动自动加载用户选择的网卡
// 设计原因:弹窗 chip 和设置窗口都调用此方法,统一持久化保证两处选择一致 // 设计原因:弹窗 chip 和设置窗口都调用此方法,统一持久化保证两处选择一致
const QString configPath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) const QString configPath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation)
@@ -276,7 +314,8 @@ void NetworkMonitorApplet::readNetworkStats()
if (line.isEmpty()) continue; if (line.isEmpty()) continue;
// 格式: "interface: rx_bytes rx_packets rx_errors rx_dropped ... tx_bytes tx_packets tx_errors tx_dropped ..." // 格式: "interface: rx_bytes rx_packets rx_errors rx_dropped ... tx_bytes tx_packets tx_errors tx_dropped ..."
QRegularExpression re("^([^:]+):\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+\\d+\\s+\\d+\\s+\\d+\\s+\\d+\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)"); // static const 仅首次调用时编译正则一次,后续复用,避免每秒对每行重复编译
static const QRegularExpression re("^([^:]+):\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+\\d+\\s+\\d+\\s+\\d+\\s+\\d+\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)");
QRegularExpressionMatch match = re.match(line); QRegularExpressionMatch match = re.match(line);
if (match.hasMatch()) { if (match.hasMatch()) {
@@ -357,8 +396,13 @@ void NetworkMonitorApplet::readNetworkStats()
} }
calculateSpeed(); calculateSpeed();
// 每次刷新都检测 IP,以应对 DHCP 续约等 IP 变更场景 // IP 地址变化频率极低(DHCP 续约/网络切换等),降频至每 5 秒检测一次,
detectIpAddress(); // 减少每秒 QNetworkInterface::interfaceFromName 的系统查询开销
// 计数器每次 refresh 递增,达到 5 时归零并执行检测
if (++m_ipDetectCounter >= 5) {
m_ipDetectCounter = 0;
detectIpAddress();
}
} }
void NetworkMonitorApplet::calculateSpeed() void NetworkMonitorApplet::calculateSpeed()
@@ -369,6 +413,13 @@ void NetworkMonitorApplet::calculateSpeed()
const double elapsedSec = (nowMs - m_lastTimestampMs) / 1000.0; const double elapsedSec = (nowMs - m_lastTimestampMs) / 1000.0;
m_lastTimestampMs = nowMs; m_lastTimestampMs = nowMs;
// 记录旧值用于 dirty 比较:网络空闲时速度/总量可能不变,
// 仅在真正变化时才发射信号,避免 QML 每秒无效重绘
const double oldDownload = m_downloadSpeed;
const double oldUpload = m_uploadSpeed;
const qint64 oldTotalDownload = m_totalDownload;
const qint64 oldTotalUpload = m_totalUpload;
// ---- 活动接口速度计算(用于任务栏显示和总量累加)---- // ---- 活动接口速度计算(用于任务栏显示和总量累加)----
if (m_firstUpdate) { if (m_firstUpdate) {
// 首次更新或接口切换后:仅记录基线,不计算速度 // 首次更新或接口切换后:仅记录基线,不计算速度
@@ -393,6 +444,26 @@ void NetworkMonitorApplet::calculateSpeed()
m_lastTxBytes = currentTxBytes; m_lastTxBytes = currentTxBytes;
} }
// ---- 活动接口包统计(累计值,自开机起)----
// 直接从当前活动接口读取收发包/错误/丢包计数,任一值变化时发射 packetStatsChanged
if (!m_activeInterface.isEmpty() && m_interfaces.contains(m_activeInterface)) {
const NetworkInterface &iface = m_interfaces[m_activeInterface];
if (m_rxPackets != static_cast<quint64>(iface.rxPackets)
|| m_txPackets != static_cast<quint64>(iface.txPackets)
|| m_rxErrors != static_cast<quint64>(iface.rxErrors)
|| m_txErrors != static_cast<quint64>(iface.txErrors)
|| m_rxDropped != static_cast<quint64>(iface.rxDropped)
|| m_txDropped != static_cast<quint64>(iface.txDropped)) {
m_rxPackets = static_cast<quint64>(iface.rxPackets);
m_txPackets = static_cast<quint64>(iface.txPackets);
m_rxErrors = static_cast<quint64>(iface.rxErrors);
m_txErrors = static_cast<quint64>(iface.txErrors);
m_rxDropped = static_cast<quint64>(iface.rxDropped);
m_txDropped = static_cast<quint64>(iface.txDropped);
emit packetStatsChanged();
}
}
// ---- 为所有接口采集速度历史(非仅活动接口)---- // ---- 为所有接口采集速度历史(非仅活动接口)----
// 设计原因:原仅采集活动接口,切换到非活动接口时趋势图为空需等待数分钟。 // 设计原因:原仅采集活动接口,切换到非活动接口时趋势图为空需等待数分钟。
// 改为遍历所有接口计算各自速度并追加历史,切换接口时趋势图立即有数据。 // 改为遍历所有接口计算各自速度并追加历史,切换接口时趋势图立即有数据。
@@ -426,11 +497,18 @@ void NetworkMonitorApplet::calculateSpeed()
m_lastRxBytesByIface[name] = iface.rxBytes; m_lastRxBytesByIface[name] = iface.rxBytes;
m_lastTxBytesByIface[name] = iface.txBytes; m_lastTxBytesByIface[name] = iface.txBytes;
} }
// 历史采样点已追加,标记脏缓存,QML 下次读取时重建
m_historyDirty = true;
emit speedHistoryChanged(); emit speedHistoryChanged();
} }
emit speedChanged(); // 仅在速度/总量实际变化时发射信号,避免空闲时每秒触发 QML 无效重绘
emit totalChanged(); if (m_downloadSpeed != oldDownload || m_uploadSpeed != oldUpload) {
emit speedChanged();
}
if (m_totalDownload != oldTotalDownload || m_totalUpload != oldTotalUpload) {
emit totalChanged();
}
} }
void NetworkMonitorApplet::detectInterfaces() void NetworkMonitorApplet::detectInterfaces()
+40
View File
@@ -57,6 +57,14 @@ class NetworkMonitorApplet : public DApplet
Q_PROPERTY(QString ipAddress READ ipAddress NOTIFY ipAddressChanged) Q_PROPERTY(QString ipAddress READ ipAddress NOTIFY ipAddressChanged)
// 活动接口的 IPv6 全球地址(已过滤 link-local 和 loopback),供 QML 显示 // 活动接口的 IPv6 全球地址(已过滤 link-local 和 loopback),供 QML 显示
Q_PROPERTY(QString ipv6Address READ ipv6Address NOTIFY ipv6AddressChanged) Q_PROPERTY(QString ipv6Address READ ipv6Address NOTIFY ipv6AddressChanged)
// 活动接口的收发包/错误/丢包累计统计(自开机起累计值),供弹窗接口信息区展示
// 全部用 quint64 类型,QML 侧可直接读取;任一值变化时统一发射 packetStatsChanged
Q_PROPERTY(quint64 rxPackets READ rxPackets NOTIFY packetStatsChanged)
Q_PROPERTY(quint64 txPackets READ txPackets NOTIFY packetStatsChanged)
Q_PROPERTY(quint64 rxErrors READ rxErrors NOTIFY packetStatsChanged)
Q_PROPERTY(quint64 txErrors READ txErrors NOTIFY packetStatsChanged)
Q_PROPERTY(quint64 rxDropped READ rxDropped NOTIFY packetStatsChanged)
Q_PROPERTY(quint64 txDropped READ txDropped NOTIFY packetStatsChanged)
// 插件版本号,从 dde-shell 元数据读取,版本唯一源为 CMakeLists.txt project(VERSION) // 插件版本号,从 dde-shell 元数据读取,版本唯一源为 CMakeLists.txt project(VERSION)
Q_PROPERTY(QString version READ version CONSTANT) Q_PROPERTY(QString version READ version CONSTANT)
// 任务栏网速数值字体颜色,空串表示跟随系统主题(由 QML 回退到 primaryText), // 任务栏网速数值字体颜色,空串表示跟随系统主题(由 QML 回退到 primaryText),
@@ -84,6 +92,13 @@ public:
QString activeInterface() const; QString activeInterface() const;
QString ipAddress() const; QString ipAddress() const;
QString ipv6Address() const; QString ipv6Address() const;
// 返回活动接口的收发包/错误/丢包累计值(自开机起),供弹窗接口信息区展示
quint64 rxPackets() const;
quint64 txPackets() const;
quint64 rxErrors() const;
quint64 txErrors() const;
quint64 rxDropped() const;
quint64 txDropped() const;
// 返回当前活动接口的下行速度历史(QPointF 列表),供 QML 趋势图绘制 // 返回当前活动接口的下行速度历史(QPointF 列表),供 QML 趋势图绘制
QVariantList speedHistoryDownload() const; QVariantList speedHistoryDownload() const;
// 返回当前活动接口的上行速度历史(QPointF 列表),供 QML 趋势图绘制 // 返回当前活动接口的上行速度历史(QPointF 列表),供 QML 趋势图绘制
@@ -108,6 +123,8 @@ signals:
void activeInterfaceChanged(); void activeInterfaceChanged();
void ipAddressChanged(); void ipAddressChanged();
void ipv6AddressChanged(); void ipv6AddressChanged();
// 活动接口包统计(收发包/错误/丢包)变化时通知 QML 更新
void packetStatsChanged();
void speedHistoryChanged(); void speedHistoryChanged();
// 任务栏网速字体颜色变化时通知 QML 更新 // 任务栏网速字体颜色变化时通知 QML 更新
void textColorChanged(); void textColorChanged();
@@ -148,6 +165,19 @@ private:
qint64 m_totalDownload; qint64 m_totalDownload;
qint64 m_totalUpload; qint64 m_totalUpload;
// 活动接口包统计缓存(自开机起累计值),供弹窗接口信息区展示
// 设计原因:/proc/net/dev 已解析包计数但此前直接丢弃,现缓存并暴露给 QML
quint64 m_rxPackets;
quint64 m_txPackets;
quint64 m_rxErrors;
quint64 m_txErrors;
quint64 m_rxDropped;
quint64 m_txDropped;
// IP 地址检测降频计数器:IP 变化频率极低,每 5 次 refresh 才检测一次
// 初始化 4 使首次 refresh 立即检测,避免启动后 IP 显示延迟
int m_ipDetectCounter;
bool m_ready; bool m_ready;
bool m_firstUpdate; bool m_firstUpdate;
// 任务栏网速字体颜色,空串表示跟随系统主题 // 任务栏网速字体颜色,空串表示跟随系统主题
@@ -160,6 +190,16 @@ private:
QHash<QString, QVector<SpeedSample>> m_speedHistory; QHash<QString, QVector<SpeedSample>> m_speedHistory;
// 每个接口最多保留的采样点数:5 分钟 × 60 秒 = 300 // 每个接口最多保留的采样点数:5 分钟 × 60 秒 = 300
static constexpr int MAX_HISTORY_SAMPLES = 300; static constexpr int MAX_HISTORY_SAMPLES = 300;
// 历史 QVariantList 缓存:避免 QML 每次读取都重新构造 300 个 QPointF
// 设计原因:Canvas 每秒读取一次 + hover 时也读取,逐个 append 开销可观;
// 用 dirty 标记懒重建,仅当追加采样点或切换接口后重建一次
// 注意:getter 为 const,故缓存与 dirty 标记声明为 mutable
mutable QVariantList m_cachedHistoryDl;
mutable QVariantList m_cachedHistoryUpload;
mutable bool m_historyDirty;
// 重建历史缓存:仅当 m_historyDirty 时由两个 getter 调用
void rebuildHistoryCache() const;
}; };
DS_END_NAMESPACE DS_END_NAMESPACE
+16 -1
View File
@@ -79,7 +79,22 @@
<message> <message>
<location filename="../package/components/NetworkPopup.qml" line="427"/> <location filename="../package/components/NetworkPopup.qml" line="427"/>
<source>Total</source> <source>Total</source>
<translation>总计</translation> <translation>流量总计</translation>
</message>
<message>
<location filename="../package/components/NetworkPopup.qml" line="474"/>
<source>Packets</source>
<translation>包统计</translation>
</message>
<message>
<location filename="../package/components/NetworkPopup.qml" line="483"/>
<source>Errors</source>
<translation>错误</translation>
</message>
<message>
<location filename="../package/components/NetworkPopup.qml" line="494"/>
<source>Dropped</source>
<translation>丢包</translation>
</message> </message>
</context> </context>
<context> <context>