Compare commits
7 Commits
64efe509fb
..
v1.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 43cb5238c8 | |||
| ea2081f0a6 | |||
| ab1a5227fa | |||
| 8f5e30532d | |||
| 9b9c26d5d1 | |||
| 6142dc4769 | |||
| 009dd9578f |
@@ -23,3 +23,6 @@ cmake-build-*/
|
|||||||
# System files
|
# System files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
|
# AI 工作流内部文档(不纳入版本控制)
|
||||||
|
docs/superpowers/
|
||||||
@@ -21,6 +21,8 @@ for the UI. The C++ backend reads `/proc/net/dev` to monitor network traffic.
|
|||||||
- Taskbar icon displays live speed (changes color at high speeds)
|
- Taskbar icon displays live speed (changes color at high speeds)
|
||||||
- Hover tooltip shows speed summary
|
- Hover tooltip shows speed summary
|
||||||
- Click to open detailed popup
|
- Click to open detailed popup
|
||||||
|
- Popup shows active interface IP address, per-interface speed comparison and stats
|
||||||
|
- One-click plugin uninstall from the popup
|
||||||
|
|
||||||
## Build & install
|
## Build & install
|
||||||
|
|
||||||
@@ -42,6 +44,14 @@ bash install.sh
|
|||||||
`cmake --install` needs `sudo` because the default `DDE_SHELL_PACKAGE_INSTALL_DIR`
|
`cmake --install` needs `sudo` because the default `DDE_SHELL_PACKAGE_INSTALL_DIR`
|
||||||
is `/usr/share/dde-shell` (a CMake CACHE variable on this system).
|
is `/usr/share/dde-shell` (a CMake CACHE variable on this system).
|
||||||
|
|
||||||
|
### Deb packaging
|
||||||
|
|
||||||
|
`build-deb.sh` builds a `.deb` from the version declared in `CMakeLists.txt`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
bash build-deb.sh # -> jnetapplet_<version>_<arch>.deb
|
||||||
|
```
|
||||||
|
|
||||||
After installation, restart dde-shell:
|
After installation, restart dde-shell:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -57,11 +67,13 @@ then restart `dde-shell` (it discovers applets by scanning the install dir for
|
|||||||
```
|
```
|
||||||
├── CMakeLists.txt # Build configuration
|
├── CMakeLists.txt # Build configuration
|
||||||
├── install.sh # Install script
|
├── install.sh # Install script
|
||||||
|
├── build-deb.sh # Deb packaging script
|
||||||
├── networkmonitorapplet.h # C++ backend header
|
├── networkmonitorapplet.h # C++ backend header
|
||||||
├── networkmonitorapplet.cpp # C++ backend implementation
|
├── networkmonitorapplet.cpp # C++ backend implementation
|
||||||
├── package/
|
├── package/
|
||||||
│ ├── metadata.json # Plugin metadata
|
│ ├── metadata.json # Plugin metadata
|
||||||
│ └── networkview.qml # QML UI
|
│ └── networkview.qml # QML UI
|
||||||
|
├── docs/ # Design docs and implementation plans
|
||||||
└── AGENTS.md # This file
|
└── AGENTS.md # This file
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -90,6 +102,7 @@ Inherits from `DApplet`, provides:
|
|||||||
- `networkInterfaces`: List of available interfaces
|
- `networkInterfaces`: List of available interfaces
|
||||||
- `interfaceStats`: Per-interface statistics
|
- `interfaceStats`: Per-interface statistics
|
||||||
- `activeInterface`: Currently selected interface
|
- `activeInterface`: Currently selected interface
|
||||||
|
- `ipAddress`: IP address of the active interface
|
||||||
- `refresh()`: Manually trigger stats update
|
- `refresh()`: Manually trigger stats update
|
||||||
- `setActiveInterface(name)`: Switch active interface
|
- `setActiveInterface(name)`: Switch active interface
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -6,7 +6,7 @@ set(CMAKE_CXX_STANDARD 17)
|
|||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
set(CMAKE_AUTOMOC ON)
|
set(CMAKE_AUTOMOC ON)
|
||||||
|
|
||||||
find_package(Qt6 REQUIRED COMPONENTS Core Quick DBus)
|
find_package(Qt6 REQUIRED COMPONENTS Core Quick DBus Network)
|
||||||
find_package(Dtk6 REQUIRED COMPONENTS Core)
|
find_package(Dtk6 REQUIRED COMPONENTS Core)
|
||||||
find_package(DDEShell REQUIRED)
|
find_package(DDEShell REQUIRED)
|
||||||
|
|
||||||
@@ -29,6 +29,7 @@ target_link_libraries(space.jokul.JNetApplet PRIVATE
|
|||||||
Qt6::Core
|
Qt6::Core
|
||||||
Qt6::Quick
|
Qt6::Quick
|
||||||
Qt6::DBus
|
Qt6::DBus
|
||||||
|
Qt6::Network
|
||||||
Dtk6::Core
|
Dtk6::Core
|
||||||
Dde::Shell
|
Dde::Shell
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,2 +1,76 @@
|
|||||||
# JNetApplet
|
# JNetApplet
|
||||||
|
|
||||||
|
基于 dde-shell 的**深度系统任务栏网络速率监控插件**,使用 C++ 后端采集数据、
|
||||||
|
QML 前端渲染界面,实时显示当前网卡的上行/下行速率与累计流量。
|
||||||
|
|
||||||
|
- 插件 ID:`space.jokul.JNetApplet`
|
||||||
|
- 适用环境:deepin 桌面环境(dde-shell 面板)
|
||||||
|
- 数据源:读取 `/proc/net/dev` 计算网络流量
|
||||||
|
|
||||||
|
## 功能特性
|
||||||
|
|
||||||
|
- 实时上行/下行速率监控(1 秒刷新)
|
||||||
|
- 累计上传/下载流量统计
|
||||||
|
- 多网卡支持,可在弹窗中切换活动网卡
|
||||||
|
- 任务栏图标实时显示速率,高速时变色提示
|
||||||
|
- 悬停显示速率摘要,点击展开详情弹窗
|
||||||
|
- 详情弹窗展示活动网卡 IP 地址、各网卡速率对比与统计图表
|
||||||
|
- 支持从弹窗一键卸载插件
|
||||||
|
|
||||||
|
## 构建与安装
|
||||||
|
|
||||||
|
需要配置期存在 `DDEShell` CMake 包(来自 `dde-shell` 开发包)。
|
||||||
|
|
||||||
|
### 方式一:从源码安装
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cmake -B build
|
||||||
|
cmake --build build
|
||||||
|
sudo cmake --install build # -> /usr/share/dde-shell/space.jokul.JNetApplet/
|
||||||
|
```
|
||||||
|
|
||||||
|
或使用安装脚本:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
bash install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
`cmake --install` 需要 `sudo`,因为默认 `DDE_SHELL_PACKAGE_INSTALL_DIR`
|
||||||
|
为 `/usr/share/dde-shell`(本机 CMake CACHE 变量)。
|
||||||
|
|
||||||
|
安装后重启 dde-shell 使插件生效:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
systemctl --user restart dde-shell@DDE
|
||||||
|
```
|
||||||
|
|
||||||
|
### 方式二:打包为 deb 安装
|
||||||
|
|
||||||
|
仓库提供 `build-deb.sh` 一键打包脚本,会从 `CMakeLists.txt` 读取版本号并构建 deb 包:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
bash build-deb.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
产物为 `jnetapplet_<version>_<arch>.deb`,可直接用 `dpkg -i` 安装。
|
||||||
|
|
||||||
|
## 验证
|
||||||
|
|
||||||
|
项目无测试 / lint / typecheck / CI 流程,验证方式为手动:安装后重启 `dde-shell`
|
||||||
|
(其通过扫描安装目录中的 `metadata.json` 发现插件)。
|
||||||
|
|
||||||
|
## 项目结构
|
||||||
|
|
||||||
|
```
|
||||||
|
├── CMakeLists.txt # 构建配置
|
||||||
|
├── install.sh # 安装脚本
|
||||||
|
├── build-deb.sh # deb 一键打包脚本
|
||||||
|
├── networkmonitorapplet.h # C++ 后端头文件
|
||||||
|
├── networkmonitorapplet.cpp # C++ 后端实现
|
||||||
|
├── package/
|
||||||
|
│ ├── metadata.json # 插件元数据
|
||||||
|
│ └── networkview.qml # QML 界面
|
||||||
|
├── docs/ # 设计文档与实现计划
|
||||||
|
├── README.md # 本文件
|
||||||
|
└── AGENTS.md # AI 代理工作指引
|
||||||
|
```
|
||||||
|
|||||||
Executable
+128
@@ -0,0 +1,128 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# JNetApplet deb 一键打包脚本
|
||||||
|
# 用法:bash build-deb.sh
|
||||||
|
# 产物:jnetapplet_<version>_<arch>.deb
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# 项目根目录
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
|
||||||
|
# 从 CMakeLists.txt 读取版本号
|
||||||
|
VERSION=$(grep -oP 'project\([^)]*VERSION\s+\K[0-9]+\.[0-9]+\.[0-9]+' CMakeLists.txt)
|
||||||
|
if [ -z "$VERSION" ]; then
|
||||||
|
echo "错误:无法从 CMakeLists.txt 读取版本号"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 架构判断
|
||||||
|
ARCH=$(dpkg --print-architecture)
|
||||||
|
if [ -z "$ARCH" ]; then
|
||||||
|
ARCH="amd64"
|
||||||
|
fi
|
||||||
|
|
||||||
|
PKG_NAME="jnetapplet"
|
||||||
|
DEB_FILE="${PKG_NAME}_${VERSION}_${ARCH}.deb"
|
||||||
|
|
||||||
|
echo "=========================================="
|
||||||
|
echo " JNetApplet deb 打包"
|
||||||
|
echo " 版本: $VERSION"
|
||||||
|
echo " 架构: $ARCH"
|
||||||
|
echo " 产物: $DEB_FILE"
|
||||||
|
echo "=========================================="
|
||||||
|
|
||||||
|
# 检查依赖工具
|
||||||
|
for cmd in cmake dpkg-deb; do
|
||||||
|
if ! command -v "$cmd" &> /dev/null; then
|
||||||
|
echo "错误:缺少 $cmd,请先安装"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# 清理并创建构建目录
|
||||||
|
echo "[1/5] 构建 CMake 项目..."
|
||||||
|
rm -rf build
|
||||||
|
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release 2>&1 | tail -3
|
||||||
|
cmake --build build -j"$(nproc)" 2>&1 | tail -3
|
||||||
|
|
||||||
|
# 创建 staging 目录(模拟安装根目录)
|
||||||
|
STAGE_DIR="$(mktemp -d)"
|
||||||
|
trap "rm -rf '$STAGE_DIR'" EXIT
|
||||||
|
|
||||||
|
echo "[2/5] 安装到 staging 目录..."
|
||||||
|
# DESTDIR 指定安装根目录前缀,cmake 会将 /usr/... 安装到 $STAGE_DIR/usr/...
|
||||||
|
DESTDIR="$STAGE_DIR" cmake --install build 2>&1 | tail -3
|
||||||
|
|
||||||
|
# 生成 DEBIAN/control
|
||||||
|
echo "[3/5] 生成 control 文件..."
|
||||||
|
mkdir -p "$STAGE_DIR/DEBIAN"
|
||||||
|
|
||||||
|
# 计算安装后大小(KB)
|
||||||
|
INSTALLED_SIZE=$(du -sk "$STAGE_DIR/usr" | cut -f1)
|
||||||
|
|
||||||
|
cat > "$STAGE_DIR/DEBIAN/control" << EOF
|
||||||
|
Package: ${PKG_NAME}
|
||||||
|
Version: ${VERSION}
|
||||||
|
Architecture: ${ARCH}
|
||||||
|
Maintainer: Jokul <jokul@git.jokul.space>
|
||||||
|
Installed-Size: ${INSTALLED_SIZE}
|
||||||
|
Depends: dde-shell, libc6, libqt6core6, libqt6gui6, libqt6quick6, libqt6network6, libdtk6core
|
||||||
|
Section: utils
|
||||||
|
Priority: optional
|
||||||
|
Description: 网络速度监控 dde-shell 任务栏插件
|
||||||
|
实时监控网络下载/上传速度,支持多网卡切换、IP 地址显示、
|
||||||
|
总流量统计。适用于 deepin 桌面环境的 dde-shell 任务栏。
|
||||||
|
Homepage: https://git.jokul.space/Jokul/JNetApplet
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# 生成 postinst 脚本:安装后重启桌面用户的 dde-shell
|
||||||
|
# 注意:dpkg 以 root 运行此脚本,systemctl --user 需切换到桌面用户身份
|
||||||
|
cat > "$STAGE_DIR/DEBIAN/postinst" << 'EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
# 找到活跃桌面用户(UID >= 1000),以其身份重启 dde-shell 用户服务
|
||||||
|
for user in $(who | awk '{print $1}' | sort -u); do
|
||||||
|
[ "$user" = "root" ] && continue
|
||||||
|
uid=$(id -u "$user" 2>/dev/null) || continue
|
||||||
|
[ "$uid" -lt 1000 ] && continue
|
||||||
|
su "$user" -c "systemctl --user restart dde-shell@DDE" 2>/dev/null || true
|
||||||
|
done
|
||||||
|
exit 0
|
||||||
|
EOF
|
||||||
|
chmod 755 "$STAGE_DIR/DEBIAN/postinst"
|
||||||
|
|
||||||
|
# 生成 prerm 脚本:卸载前重启桌面用户的 dde-shell 移除插件
|
||||||
|
cat > "$STAGE_DIR/DEBIAN/prerm" << 'EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
# 以桌面用户身份重启 dde-shell 以卸载插件
|
||||||
|
for user in $(who | awk '{print $1}' | sort -u); do
|
||||||
|
[ "$user" = "root" ] && continue
|
||||||
|
uid=$(id -u "$user" 2>/dev/null) || continue
|
||||||
|
[ "$uid" -lt 1000 ] && continue
|
||||||
|
su "$user" -c "systemctl --user restart dde-shell@DDE" 2>/dev/null || true
|
||||||
|
done
|
||||||
|
exit 0
|
||||||
|
EOF
|
||||||
|
chmod 755 "$STAGE_DIR/DEBIAN/prerm"
|
||||||
|
|
||||||
|
# 修正文件权限(so 文件需要 755,qml/json 需要 644)
|
||||||
|
echo "[4/5] 修正文件权限..."
|
||||||
|
find "$STAGE_DIR/usr" -type f -name "*.so" -exec chmod 755 {} \;
|
||||||
|
find "$STAGE_DIR/usr" -type f \( -name "*.qml" -o -name "*.json" \) -exec chmod 644 {} \;
|
||||||
|
|
||||||
|
# 构建 deb
|
||||||
|
echo "[5/5] 构建 deb 包..."
|
||||||
|
rm -f "$DEB_FILE"
|
||||||
|
dpkg-deb --build --root-owner-group "$STAGE_DIR" "$DEB_FILE"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=========================================="
|
||||||
|
echo " 打包完成!"
|
||||||
|
echo " 产物: $DEB_FILE ($(du -h "$DEB_FILE" | cut -f1))"
|
||||||
|
echo ""
|
||||||
|
echo " 安装: sudo dpkg -i $DEB_FILE"
|
||||||
|
echo " 卸载: sudo dpkg -r $PKG_NAME"
|
||||||
|
echo "=========================================="
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
#include <QNetworkInterface>
|
||||||
|
|
||||||
DS_BEGIN_NAMESPACE
|
DS_BEGIN_NAMESPACE
|
||||||
|
|
||||||
@@ -103,6 +104,12 @@ QString NetworkMonitorApplet::activeInterface() const
|
|||||||
return m_activeInterface;
|
return m_activeInterface;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 返回活动接口的 IPv4 地址,供 QML 显示
|
||||||
|
QString NetworkMonitorApplet::ipAddress() const
|
||||||
|
{
|
||||||
|
return m_ipAddress;
|
||||||
|
}
|
||||||
|
|
||||||
void NetworkMonitorApplet::refresh()
|
void NetworkMonitorApplet::refresh()
|
||||||
{
|
{
|
||||||
readNetworkStats();
|
readNetworkStats();
|
||||||
@@ -114,6 +121,8 @@ void NetworkMonitorApplet::setActiveInterface(const QString &interface)
|
|||||||
m_activeInterface = interface;
|
m_activeInterface = interface;
|
||||||
m_firstUpdate = true;
|
m_firstUpdate = true;
|
||||||
emit activeInterfaceChanged();
|
emit activeInterfaceChanged();
|
||||||
|
// 接口切换后立即检测新接口的 IP 地址
|
||||||
|
detectIpAddress();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,7 +176,18 @@ void NetworkMonitorApplet::readNetworkStats()
|
|||||||
|
|
||||||
// 自动选择活动接口
|
// 自动选择活动接口
|
||||||
if (m_activeInterface.isEmpty() && !m_interfaceList.isEmpty()) {
|
if (m_activeInterface.isEmpty() && !m_interfaceList.isEmpty()) {
|
||||||
// 优先选择有流量的接口
|
// 优先选择物理网卡(wlp/wlan/enp/eth)中有流量的接口,
|
||||||
|
// 避免选中 Meta/tun0 等虚拟代理接口作为默认显示
|
||||||
|
for (const QString &name : m_interfaceList) {
|
||||||
|
if (!isPhysicalInterface(name)) continue;
|
||||||
|
const NetworkInterface &iface = m_interfaces[name];
|
||||||
|
if (iface.rxBytes > 0 || iface.txBytes > 0) {
|
||||||
|
m_activeInterface = name;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 其次选择任意有流量的接口(物理网卡都无流量时的兜底)
|
||||||
|
if (m_activeInterface.isEmpty()) {
|
||||||
for (const QString &name : m_interfaceList) {
|
for (const QString &name : m_interfaceList) {
|
||||||
const NetworkInterface &iface = m_interfaces[name];
|
const NetworkInterface &iface = m_interfaces[name];
|
||||||
if (iface.rxBytes > 0 || iface.txBytes > 0) {
|
if (iface.rxBytes > 0 || iface.txBytes > 0) {
|
||||||
@@ -175,6 +195,8 @@ void NetworkMonitorApplet::readNetworkStats()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// 最后选第一个(无任何流量时)
|
||||||
if (m_activeInterface.isEmpty()) {
|
if (m_activeInterface.isEmpty()) {
|
||||||
m_activeInterface = m_interfaceList.first();
|
m_activeInterface = m_interfaceList.first();
|
||||||
}
|
}
|
||||||
@@ -190,6 +212,8 @@ void NetworkMonitorApplet::readNetworkStats()
|
|||||||
|
|
||||||
calculateSpeed();
|
calculateSpeed();
|
||||||
emit statsChanged();
|
emit statsChanged();
|
||||||
|
// 每次刷新都检测 IP,以应对 DHCP 续约等 IP 变更场景
|
||||||
|
detectIpAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkMonitorApplet::calculateSpeed()
|
void NetworkMonitorApplet::calculateSpeed()
|
||||||
@@ -255,6 +279,40 @@ qint64 NetworkMonitorApplet::getActiveTxBytes() const
|
|||||||
return m_interfaces[m_activeInterface].txBytes;
|
return m_interfaces[m_activeInterface].txBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检测活动接口的 IPv4 地址
|
||||||
|
// 跳过 IPv6 和 loopback,只取第一个有效 IPv4 地址
|
||||||
|
// IP 变化时发射 ipAddressChanged 信号通知 QML 更新
|
||||||
|
// 设计原因:DHCP 续约、网络切换等场景下 IP 可能变化,需持续检测
|
||||||
|
void NetworkMonitorApplet::detectIpAddress()
|
||||||
|
{
|
||||||
|
QString newIp;
|
||||||
|
if (!m_activeInterface.isEmpty()) {
|
||||||
|
QNetworkInterface iface = QNetworkInterface::interfaceFromName(m_activeInterface);
|
||||||
|
for (const QNetworkAddressEntry &entry : iface.addressEntries()) {
|
||||||
|
// 只取 IPv4 地址,跳过 IPv6 和 loopback
|
||||||
|
if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol
|
||||||
|
&& !entry.ip().isLoopback()) {
|
||||||
|
newIp = entry.ip().toString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m_ipAddress != newIp) {
|
||||||
|
m_ipAddress = newIp;
|
||||||
|
emit ipAddressChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否为物理网卡
|
||||||
|
// 物理网卡前缀:无线 wlp/wlan/wifi,有线 enp/eth
|
||||||
|
// 虚拟接口(Meta/tun/tap/docker/veth/br-)返回 false,
|
||||||
|
// 用于自动选择时优先真实网卡而非代理 TUN 接口
|
||||||
|
bool NetworkMonitorApplet::isPhysicalInterface(const QString &name) const
|
||||||
|
{
|
||||||
|
return name.startsWith("wlp") || name.startsWith("wlan")
|
||||||
|
|| name.startsWith("enp") || name.startsWith("eth");
|
||||||
|
}
|
||||||
|
|
||||||
D_APPLET_CLASS(NetworkMonitorApplet)
|
D_APPLET_CLASS(NetworkMonitorApplet)
|
||||||
|
|
||||||
DS_END_NAMESPACE
|
DS_END_NAMESPACE
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ class NetworkMonitorApplet : public DApplet
|
|||||||
Q_PROPERTY(QStringList interfaceStats READ interfaceStats NOTIFY statsChanged)
|
Q_PROPERTY(QStringList interfaceStats READ interfaceStats NOTIFY statsChanged)
|
||||||
Q_PROPERTY(bool ready READ ready NOTIFY readyChanged)
|
Q_PROPERTY(bool ready READ ready NOTIFY readyChanged)
|
||||||
Q_PROPERTY(QString activeInterface READ activeInterface NOTIFY activeInterfaceChanged)
|
Q_PROPERTY(QString activeInterface READ activeInterface NOTIFY activeInterfaceChanged)
|
||||||
|
// 活动接口的 IPv4 地址,供 QML 在弹出面板和 tooltip 中显示
|
||||||
|
Q_PROPERTY(QString ipAddress READ ipAddress NOTIFY ipAddressChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit NetworkMonitorApplet(QObject *parent = nullptr);
|
explicit NetworkMonitorApplet(QObject *parent = nullptr);
|
||||||
@@ -54,6 +56,7 @@ public:
|
|||||||
QStringList interfaceStats() const;
|
QStringList interfaceStats() const;
|
||||||
bool ready() const;
|
bool ready() const;
|
||||||
QString activeInterface() const;
|
QString activeInterface() const;
|
||||||
|
QString ipAddress() const;
|
||||||
|
|
||||||
Q_INVOKABLE void refresh();
|
Q_INVOKABLE void refresh();
|
||||||
Q_INVOKABLE void setActiveInterface(const QString &interface);
|
Q_INVOKABLE void setActiveInterface(const QString &interface);
|
||||||
@@ -65,11 +68,17 @@ signals:
|
|||||||
void statsChanged();
|
void statsChanged();
|
||||||
void readyChanged();
|
void readyChanged();
|
||||||
void activeInterfaceChanged();
|
void activeInterfaceChanged();
|
||||||
|
void ipAddressChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void readNetworkStats();
|
void readNetworkStats();
|
||||||
void calculateSpeed();
|
void calculateSpeed();
|
||||||
void detectInterfaces();
|
void detectInterfaces();
|
||||||
|
// 检测活动接口的 IPv4 地址,变化时发射 ipAddressChanged
|
||||||
|
void detectIpAddress();
|
||||||
|
// 判断是否为物理网卡(无线 wlp/wlan,有线 enp/eth),
|
||||||
|
// 用于自动选择时优先真实网卡而非虚拟代理接口(如 Meta/tun0)
|
||||||
|
bool isPhysicalInterface(const QString &name) const;
|
||||||
qint64 getActiveRxBytes() const;
|
qint64 getActiveRxBytes() const;
|
||||||
qint64 getActiveTxBytes() const;
|
qint64 getActiveTxBytes() const;
|
||||||
|
|
||||||
@@ -77,6 +86,7 @@ private:
|
|||||||
QMap<QString, NetworkInterface> m_interfaces;
|
QMap<QString, NetworkInterface> m_interfaces;
|
||||||
QStringList m_interfaceList;
|
QStringList m_interfaceList;
|
||||||
QString m_activeInterface;
|
QString m_activeInterface;
|
||||||
|
QString m_ipAddress; // 活动接口的 IPv4 地址
|
||||||
|
|
||||||
// 速度计算
|
// 速度计算
|
||||||
qint64 m_lastRxBytes;
|
qint64 m_lastRxBytes;
|
||||||
|
|||||||
+912
-278
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user