Compare commits
2 Commits
v1.0.0
...
2beafd4cf1
| Author | SHA1 | Date | |
|---|---|---|---|
| 2beafd4cf1 | |||
| f7281a5f0c |
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断是否为物理网卡
|
// 判断是否为物理网卡
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user