Compare commits

2 Commits

3 changed files with 51 additions and 13 deletions
+26 -7
View File
@@ -110,6 +110,12 @@ QString NetworkMonitorApplet::ipAddress() const
return m_ipAddress; return m_ipAddress;
} }
// 返回活动接口的 IPv6 全球地址,供 QML 显示
QString NetworkMonitorApplet::ipv6Address() const
{
return m_ipv6Address;
}
void NetworkMonitorApplet::refresh() void NetworkMonitorApplet::refresh()
{ {
readNetworkStats(); readNetworkStats();
@@ -279,21 +285,30 @@ qint64 NetworkMonitorApplet::getActiveTxBytes() const
return m_interfaces[m_activeInterface].txBytes; return m_interfaces[m_activeInterface].txBytes;
} }
// 检测活动接口的 IPv4 地址 // 检测活动接口的 IPv4 与 IPv6 地址
// 跳过 IPv6 和 loopback取第一个有效 IPv4 地址 // IPv4跳过 loopback,取第一个
// IP 变化时发射 ipAddressChanged 信号通知 QML 更新 // IPv6:跳过 loopback 和 link-local (fe80::),取第一个全球地址
// 设计原因:DHCP 续约、网络切换等场景下 IP 可能变化,需持续检测 // 任一地址变化时分别 emit 对应信号通知 QML 更新
// 设计原因:DHCP 续约、网络切换、IPv6 SLAAC 等场景下地址可能变化,需持续检测
void NetworkMonitorApplet::detectIpAddress() void NetworkMonitorApplet::detectIpAddress()
{ {
QString newIp; QString newIp;
QString newIpv6;
if (!m_activeInterface.isEmpty()) { if (!m_activeInterface.isEmpty()) {
QNetworkInterface iface = QNetworkInterface::interfaceFromName(m_activeInterface); QNetworkInterface iface = QNetworkInterface::interfaceFromName(m_activeInterface);
for (const QNetworkAddressEntry &entry : iface.addressEntries()) { for (const QNetworkAddressEntry &entry : iface.addressEntries()) {
// 只取 IPv4 地址,跳过 IPv6 和 loopback // IPv4:跳过 loopback,取第一个
if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol
&& !entry.ip().isLoopback()) { && !entry.ip().isLoopback()
&& newIp.isEmpty()) {
newIp = entry.ip().toString(); newIp = entry.ip().toString();
break; }
// IPv6:跳过 loopback 和 link-local (fe80::),取第一个全球地址
if (entry.ip().protocol() == QAbstractSocket::IPv6Protocol
&& !entry.ip().isLoopback()
&& !entry.ip().isLinkLocal()
&& newIpv6.isEmpty()) {
newIpv6 = entry.ip().toString();
} }
} }
} }
@@ -301,6 +316,10 @@ void NetworkMonitorApplet::detectIpAddress()
m_ipAddress = newIp; m_ipAddress = newIp;
emit ipAddressChanged(); emit ipAddressChanged();
} }
if (m_ipv6Address != newIpv6) {
m_ipv6Address = newIpv6;
emit ipv6AddressChanged();
}
} }
// 判断是否为物理网卡 // 判断是否为物理网卡
+6 -1
View File
@@ -40,6 +40,8 @@ class NetworkMonitorApplet : public DApplet
Q_PROPERTY(QString activeInterface READ activeInterface NOTIFY activeInterfaceChanged) Q_PROPERTY(QString activeInterface READ activeInterface NOTIFY activeInterfaceChanged)
// 活动接口的 IPv4 地址,供 QML 在弹出面板和 tooltip 中显示 // 活动接口的 IPv4 地址,供 QML 在弹出面板和 tooltip 中显示
Q_PROPERTY(QString ipAddress READ ipAddress NOTIFY ipAddressChanged) Q_PROPERTY(QString ipAddress READ ipAddress NOTIFY ipAddressChanged)
// 活动接口的 IPv6 全球地址(已过滤 link-local 和 loopback),供 QML 显示
Q_PROPERTY(QString ipv6Address READ ipv6Address NOTIFY ipv6AddressChanged)
public: public:
explicit NetworkMonitorApplet(QObject *parent = nullptr); explicit NetworkMonitorApplet(QObject *parent = nullptr);
@@ -57,6 +59,7 @@ public:
bool ready() const; bool ready() const;
QString activeInterface() const; QString activeInterface() const;
QString ipAddress() const; QString ipAddress() const;
QString ipv6Address() const;
Q_INVOKABLE void refresh(); Q_INVOKABLE void refresh();
Q_INVOKABLE void setActiveInterface(const QString &interface); Q_INVOKABLE void setActiveInterface(const QString &interface);
@@ -69,12 +72,13 @@ signals:
void readyChanged(); void readyChanged();
void activeInterfaceChanged(); void activeInterfaceChanged();
void ipAddressChanged(); void ipAddressChanged();
void ipv6AddressChanged();
private: private:
void readNetworkStats(); void readNetworkStats();
void calculateSpeed(); void calculateSpeed();
void detectInterfaces(); void detectInterfaces();
// 检测活动接口的 IPv4 地址,变化时发射 ipAddressChanged // 检测活动接口的 IPv4 与 IPv6 地址,变化时分别发射对应信号
void detectIpAddress(); void detectIpAddress();
// 判断是否为物理网卡(无线 wlp/wlan,有线 enp/eth), // 判断是否为物理网卡(无线 wlp/wlan,有线 enp/eth),
// 用于自动选择时优先真实网卡而非虚拟代理接口(如 Meta/tun0) // 用于自动选择时优先真实网卡而非虚拟代理接口(如 Meta/tun0)
@@ -87,6 +91,7 @@ private:
QStringList m_interfaceList; QStringList m_interfaceList;
QString m_activeInterface; QString m_activeInterface;
QString m_ipAddress; // 活动接口的 IPv4 地址 QString m_ipAddress; // 活动接口的 IPv4 地址
QString m_ipv6Address; // 活动接口的 IPv6 全球地址(过滤 link-local
// 速度计算 // 速度计算
qint64 m_lastRxBytes; qint64 m_lastRxBytes;
+19 -5
View File
@@ -31,6 +31,8 @@ AppletItem {
readonly property string activeInterface: applet ? (applet.activeInterface || "") : "" readonly property string activeInterface: applet ? (applet.activeInterface || "") : ""
// 活动接口的 IPv4 地址,由 C++ 后端 detectIpAddress 持续更新 // 活动接口的 IPv4 地址,由 C++ 后端 detectIpAddress 持续更新
readonly property string ipAddress: applet ? (applet.ipAddress || "") : "" readonly property string ipAddress: applet ? (applet.ipAddress || "") : ""
// 活动接口的 IPv6 全球地址,由 C++ 后端 detectIpAddress 持续更新
readonly property string ipv6Address: applet ? (applet.ipv6Address || "") : ""
readonly property var networkInterfaces: applet ? (applet.networkInterfaces || []) : [] readonly property var networkInterfaces: applet ? (applet.networkInterfaces || []) : []
readonly property var interfaceStats: applet ? (applet.interfaceStats || []) : [] readonly property var interfaceStats: applet ? (applet.interfaceStats || []) : []
@@ -180,12 +182,14 @@ AppletItem {
} }
} }
// 悬停提示:网卡名 + IP 地址一行展示 // 悬停提示:网卡名 + IPv4 单行,IPv6 在第二行(仅有 IPv6 时追加)
// PanelToolTip 不支持 contentItem 覆盖和文本对齐,用默认左对齐 text 属性 // 设计原因:IPv6 地址较长,单行容纳不下;无 IPv6 时保持单行与原视觉一致
PanelToolTip { PanelToolTip {
id: toolTip id: toolTip
text: root.ready text: root.ready
? (root.activeInterface + " · " + qsTr("IP地址:") + (root.ipAddress || qsTr("无"))) ? (root.activeInterface
+ " · IPv4" + (root.ipAddress || qsTr("无"))
+ (root.ipv6Address ? "\nIPv6" + root.ipv6Address : ""))
: qsTr("未检测到网络接口") : qsTr("未检测到网络接口")
toolTipX: DockPanelPositioner.x toolTipX: DockPanelPositioner.x
toolTipY: DockPanelPositioner.y toolTipY: DockPanelPositioner.y
@@ -268,7 +272,8 @@ AppletItem {
Rectangle { Rectangle {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
Layout.preferredWidth: 180 Layout.preferredWidth: 180
Layout.preferredHeight: 40 // 有 IPv6 时增高以容纳第三行;无 IPv6 时保持原 40px
Layout.preferredHeight: root.ipv6Address ? 54 : 40
color: root.cardBackground color: root.cardBackground
radius: 8 radius: 8
border.width: 1 border.width: 1
@@ -287,12 +292,21 @@ AppletItem {
} }
Text { Text {
text: root.ipAddress ? qsTr("IP地址") + root.ipAddress : "" text: root.ipAddress ? qsTr("IPv4") + root.ipAddress : ""
font.pixelSize: 13 font.pixelSize: 13
color: root.secondaryText color: root.secondaryText
visible: root.ipAddress !== "" visible: root.ipAddress !== ""
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
} }
// IPv6 全球地址行,无 IPv6 时隐藏不占位
Text {
text: root.ipv6Address ? qsTr("IPv6") + root.ipv6Address : ""
font.pixelSize: 13
color: root.secondaryText
visible: root.ipv6Address !== ""
Layout.alignment: Qt.AlignHCenter
}
} }
} }
} }