MicroPython eric7

Mon, 05 May 2025 17:40:08 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Mon, 05 May 2025 17:40:08 +0200
branch
eric7
changeset 11263
28f0ead11a82
parent 11262
07d9cc8d773c
child 11264
6feb46361342

MicroPython
- Added support for IPv6 for WiFi and Ethernet enabled devices (MPy ≥ 1.24.0).

docs/changelog.md file | annotate | diff | comparison | revisions
eric7.epj file | annotate | diff | comparison | revisions
src/eric7/MicroPython/Devices/CircuitPythonDevices.py file | annotate | diff | comparison | revisions
src/eric7/MicroPython/Devices/DeviceBase.py file | annotate | diff | comparison | revisions
src/eric7/MicroPython/Devices/EspDevices.py file | annotate | diff | comparison | revisions
src/eric7/MicroPython/Devices/RP2Devices.py file | annotate | diff | comparison | revisions
src/eric7/MicroPython/EthernetDialogs/EthernetController.py file | annotate | diff | comparison | revisions
src/eric7/MicroPython/EthernetDialogs/EthernetStatusDialog.py file | annotate | diff | comparison | revisions
src/eric7/MicroPython/EthernetDialogs/EthernetStatusDialog.ui file | annotate | diff | comparison | revisions
src/eric7/MicroPython/EthernetDialogs/Ui_EthernetStatusDialog.py file | annotate | diff | comparison | revisions
src/eric7/MicroPython/WifiDialogs/Ui_WifiStatusDialog.py file | annotate | diff | comparison | revisions
src/eric7/MicroPython/WifiDialogs/WifiController.py file | annotate | diff | comparison | revisions
src/eric7/MicroPython/WifiDialogs/WifiStatusDialog.py file | annotate | diff | comparison | revisions
src/eric7/MicroPython/WifiDialogs/WifiStatusDialog.ui file | annotate | diff | comparison | revisions
src/eric7/SystemUtilities/NetworkUtilities.py file | annotate | diff | comparison | revisions
--- a/docs/changelog.md	Mon May 05 10:17:49 2025 +0200
+++ b/docs/changelog.md	Mon May 05 17:40:08 2025 +0200
@@ -2,6 +2,8 @@
 
 ### Version 25.6
 - bug fixes
+- MicroPython
+    - Added support for IPv6 for WiFi and Ethernet enabled devices (MPy ≥ 1.24.0).
 
 ### Version 25.5
 - bug fixes
--- a/eric7.epj	Mon May 05 10:17:49 2025 +0200
+++ b/eric7.epj	Mon May 05 17:40:08 2025 +0200
@@ -2556,6 +2556,7 @@
       "src/eric7/SqlBrowser/__init__.py",
       "src/eric7/SystemUtilities/DesktopUtilities.py",
       "src/eric7/SystemUtilities/FileSystemUtilities.py",
+      "src/eric7/SystemUtilities/NetworkUtilities.py",
       "src/eric7/SystemUtilities/OSUtilities.py",
       "src/eric7/SystemUtilities/PySideImporter.py",
       "src/eric7/SystemUtilities/PythonUtilities.py",
--- a/src/eric7/MicroPython/Devices/CircuitPythonDevices.py	Mon May 05 10:17:49 2025 +0200
+++ b/src/eric7/MicroPython/Devices/CircuitPythonDevices.py	Mon May 05 17:40:08 2025 +0200
@@ -800,13 +800,14 @@
     station = {
         'active': r.enabled and r.ipv4_address is not None,
         'connected': r.ipv4_address is not None,
+        'mac': binascii.hexlify(r.mac_address, ':').decode(),
         'ifconfig': (
             str(r.ipv4_address) if r.ipv4_address else'0.0.0.0',
             str(r.ipv4_subnet) if r.ipv4_subnet else'0.0.0.0',
             str(r.ipv4_gateway) if r.ipv4_gateway else'0.0.0.0',
             str(r.ipv4_dns) if r.ipv4_dns else'0.0.0.0',
         ),
-        'mac': binascii.hexlify(r.mac_address, ':').decode(),
+        'ipv6_addr': [],
     }
     try:
         station['txpower'] = r.tx_power
@@ -834,13 +835,14 @@
     ap = {
         'active': r.enabled and r.ipv4_address_ap is not None,
         'connected': r.ipv4_address_ap is not None,
+        'mac': binascii.hexlify(r.mac_address_ap, ':').decode(),
         'ifconfig': (
             str(r.ipv4_address_ap) if r.ipv4_address_ap else'0.0.0.0',
             str(r.ipv4_subnet_ap) if r.ipv4_subnet_ap else'0.0.0.0',
             str(r.ipv4_gateway_ap) if r.ipv4_gateway_ap else'0.0.0.0',
             str(r.ipv4_dns) if r.ipv4_dns else'0.0.0.0',
         ),
-        'mac': binascii.hexlify(r.mac_address_ap, ':').decode(),
+        'ipv6_addr': [],
     }
     try:
         ap['txpower'] = r.tx_power
@@ -852,6 +854,7 @@
         'active': r.enabled,
         'hostname': r.hostname,
     }
+    overall['prefer'] = 4
     print(json.dumps(overall))
 
 wifi_status()
@@ -1388,8 +1391,9 @@
         Public method to get Ethernet status data of the connected board.
 
         @return list of tuples containing the translated status data label and
-            the associated value
-        @rtype list of tuples of (str, str)
+            the associated value and a dictionary with keys 'ipv4' and 'ipv6'
+            containing the respective address information
+        @rtype tuple of list of tuples of (str, str) and dict
         @exception OSError raised to indicate an issue with the device
         """
         command = """{0}
@@ -1402,7 +1406,7 @@
     res = {{
         'active': nic.link_status != 0,
         'connected': nic.link_status == 1 and nic.ifconfig[0] != b'\x00\x00\x00\x00',
-        'ifconfig': (
+        'ipv4_addr': (
             nic.pretty_ip(nic.ifconfig[0]),
             nic.pretty_ip(nic.ifconfig[1]),
             nic.pretty_ip(nic.ifconfig[2]),
@@ -1424,19 +1428,20 @@
         if err:
             raise OSError(self._shortError(err))
 
-        status = []
         ethStatus = json.loads(out.decode("utf-8"))
-        status.append((self.tr("Active"), self.bool2str(ethStatus["active"])))
-        status.append((self.tr("Connected"), self.bool2str(ethStatus["connected"])))
-        status.append((self.tr("IPv4 Address"), ethStatus["ifconfig"][0]))
-        status.append((self.tr("Netmask"), ethStatus["ifconfig"][1]))
-        status.append((self.tr("Gateway"), ethStatus["ifconfig"][2]))
-        status.append((self.tr("DNS"), ethStatus["ifconfig"][3]))
-        status.append((self.tr("MAC-Address"), ethStatus["mac"]))
-        status.append((self.tr("Chip Type"), ethStatus["chip"]))
-        status.append((self.tr("max. Sockets"), ethStatus["max_sockets"]))
+        status = [
+            (self.tr("Active"), self.bool2str(ethStatus["active"])),
+            (self.tr("Connected"), self.bool2str(ethStatus["connected"])),
+            (self.tr("MAC-Address"), ethStatus["mac"]),
+            (self.tr("Chip Type"), ethStatus["chip"]),
+            (self.tr("max. Sockets"), ethStatus["max_sockets"]),
+        ]
+        addressInfo = {
+            "ipv4": ethStatus["ipv4_addr"],
+            "ipv6": [],
+        }
 
-        return status
+        return status, addressInfo
 
     def connectToLan(self, config, hostname):
         """
--- a/src/eric7/MicroPython/Devices/DeviceBase.py	Mon May 05 10:17:49 2025 +0200
+++ b/src/eric7/MicroPython/Devices/DeviceBase.py	Mon May 05 17:40:08 2025 +0200
@@ -2018,10 +2018,11 @@
         Public method to get Ethernet status data of the connected board.
 
         @return list of tuples containing the translated status data label and
-            the associated value
-        @rtype list of tuples of (str, str)
+            the associated value and a dictionary with keys 'ipv4' and 'ipv6'
+            containing the respective address information
+        @rtype tuple of list of tuples of (str, str) and dict
         """
-        return []
+        return [], {"ipv4": [], "ipv6": []}
 
     def connectToLan(self, config, hostname):  # noqa: U-100
         """
--- a/src/eric7/MicroPython/Devices/EspDevices.py	Mon May 05 10:17:49 2025 +0200
+++ b/src/eric7/MicroPython/Devices/EspDevices.py	Mon May 05 17:40:08 2025 +0200
@@ -747,9 +747,20 @@
         'active': wifi.active(),
         'connected': wifi.isconnected(),
         'status': wifi.status(),
-        'ifconfig': wifi.ifconfig(),
         'mac': ubinascii.hexlify(wifi.config('mac'), ':').decode(),
+        'channel': wifi.config('channel'),
+        'essid': wifi.config('essid'),
     }
+    try:
+        station['ifconfig'] = (
+            wifi.ipconfig('addr4') + (wifi.ipconfig('gw4'), network.ipconfig('dns'))
+        )
+    except AttributeError:
+        station['ifconfig'] = wifi.ifconfig()
+    try:
+        station['ipv6_addr'] = [a[0] for a in wifi.ipconfig('addr6')]
+    except ValueError:
+        station['ipv6_addr'] = []
     if wifi.active():
         try:
             station['txpower'] = wifi.config('txpower')
@@ -762,12 +773,21 @@
         'active': wifi.active(),
         'connected': wifi.isconnected(),
         'status': wifi.status(),
-        'ifconfig': wifi.ifconfig(),
         'mac': ubinascii.hexlify(wifi.config('mac'), ':').decode(),
         'channel': wifi.config('channel'),
         'essid': wifi.config('essid'),
         'ap_security': security_str(wifi.config('security')),
     }
+    try:
+        ap['ifconfig'] = (
+            wifi.ipconfig('addr4') + (wifi.ipconfig('gw4'), network.ipconfig('dns'))
+        )
+    except AttributeError:
+        ap['ifconfig'] = wifi.ifconfig()
+    try:
+        ap['ipv6_addr'] = [a[0] for a in wifi.ipconfig('addr6')]
+    except ValueError:
+        ap['ipv6_addr'] = []
     if wifi.active():
         try:
             ap['txpower'] = wifi.config('txpower')
@@ -786,6 +806,10 @@
         overall['country'] = network.country()
     except AttributeError:
         pass
+    try:
+        overall['prefer'] = network.ipconfig('prefer')
+    except ValueError:
+        overall['prefer'] = 4
     print(ujson.dumps(overall))
 
 wifi_status()
--- a/src/eric7/MicroPython/Devices/RP2Devices.py	Mon May 05 10:17:49 2025 +0200
+++ b/src/eric7/MicroPython/Devices/RP2Devices.py	Mon May 05 17:40:08 2025 +0200
@@ -611,11 +611,21 @@
         'active': wifi.active(),
         'connected': wifi.isconnected(),
         'status': wifi.status(),
-        'ifconfig': wifi.ifconfig(),
         'mac': ubinascii.hexlify(wifi.config('mac'), ':').decode(),
         'channel': wifi.config('channel'),
+        'essid': wifi.config('essid'),
         'txpower': wifi.config('txpower'),
     }
+    try:
+        station['ifconfig'] = (
+            wifi.ipconfig('addr4') + (wifi.ipconfig('gw4'), network.ipconfig('dns'))
+        )
+    except AttributeError:
+        station['ifconfig'] = wifi.ifconfig()
+    try:
+        station['ipv6_addr'] = [a[0] for a in wifi.ipconfig('addr6')]
+    except ValueError:
+        station['ipv6_addr'] = []
     print(ujson.dumps(station))
 
     wifi = network.WLAN(network.AP_IF)
@@ -623,13 +633,22 @@
         'active': wifi.active(),
         'connected': wifi.isconnected(),
         'status': wifi.status(),
-        'ifconfig': wifi.ifconfig(),
         'mac': ubinascii.hexlify(wifi.config('mac'), ':').decode(),
         'channel': wifi.config('channel'),
         'txpower': wifi.config('txpower'),
         'essid': wifi.config('essid'),
         'ap_security': security_str(wifi.config('security')),
     }
+    try:
+        ap['ifconfig'] = (
+            wifi.ipconfig('addr4') + (wifi.ipconfig('gw4'), network.ipconfig('dns'))
+        )
+    except AttributeError:
+        ap['ifconfig'] = wifi.ifconfig()
+    try:
+        ap['ipv6_addr'] = [a[0] for a in wifi.ipconfig('addr6')]
+    except ValueError:
+        ap['ipv6_addr'] = []
     print(ujson.dumps(ap))
 
     overall = {
@@ -643,6 +662,10 @@
         overall['hostname'] = network.hostname()
     except AttributeError:
         pass
+    try:
+        overall['prefer'] = network.ipconfig('prefer')
+    except ValueError:
+        overall['prefer'] = 4
     print(ujson.dumps(overall))
 
 wifi_status()
@@ -662,13 +685,14 @@
         'active': pw.get_connection_status() not in (0, 7, 8, 9),
         'connected': pw.get_connection_status() == 3,
         'status': pw.get_connection_status(),
+        'mac': ubinascii.hexlify(pw.get_mac_address(), ':').decode(),
         'ifconfig': (
             ip_str(pw.get_ip_address()),
             ip_str(pw.get_subnet_mask()),
             ip_str(pw.get_gateway_ip()),
             '0.0.0.0'
         ),
-        'mac': ubinascii.hexlify(pw.get_mac_address(), ':').decode(),
+        'ipv6_addr': [],
     }
     if station['connected']:
         station.update({
@@ -685,6 +709,7 @@
         'status': pw.get_connection_status(),
         'mac': ubinascii.hexlify(pw.get_mac_address(), ':').decode(),
         'ap_security': pw.get_current_encryption_type(),
+        'ipv6_addr': [],
     }
     if ap['active']:
         ap['essid'] = pw.get_current_ssid()
@@ -697,7 +722,8 @@
     print(ujson.dumps(ap))
 
     overall = {
-        'active': pw.get_connection_status() != 0
+        'active': pw.get_connection_status() != 0,
+        'prefer': 4,
     }
     print(ujson.dumps(overall))
 
@@ -1858,8 +1884,9 @@
         Public method to get Ethernet status data of the connected board.
 
         @return list of tuples containing the translated status data label and
-            the associated value
-        @rtype list of tuples of (str, str)
+            the associated value and a dictionary with keys 'ipv4' and 'ipv6'
+            containing the respective address information
+        @rtype tuple of list of tuples of (str, str) and dict
         @exception OSError raised to indicate an issue with the device
         """
         command = """{0}
@@ -1874,13 +1901,26 @@
         'active': nic.active(),
         'connected': nic.isconnected(),
         'status': nic.status(),
-        'ifconfig': nic.ifconfig(),
         'mac': ubinascii.hexlify(nic.config('mac'), ':').decode(),
     }}
     try:
+        res['ipv4_addr'] = (
+            nic.ipconfig('addr4') + (nic.ipconfig('gw4'), network.ipconfig('dns'))
+        )
+    except AttributeError:
+        res['ipv4_addr'] = nic.ifconfig()
+    try:
+        res['ipv6_addr'] = [a[0] for a in nic.ipconfig('addr6')]
+    except ValueError:
+        res['ipv6_addr'] = []
+    try:
         res['hostname'] = network.hostname()
     except AttributeError:
         res['hostname'] = ''
+    try:
+        res['prefer'] = network.ipconfig('prefer')
+    except ValueError:
+        res['prefer'] = 4
     print(ujson.dumps(res))
 
 ethernet_status()
@@ -1893,29 +1933,26 @@
         if err:
             raise OSError(self._shortError(err))
 
-        status = []
         ethStatus = json.loads(out.decode("utf-8"))
-        status.append((self.tr("Active"), self.bool2str(ethStatus["active"])))
-        status.append((self.tr("Connected"), self.bool2str(ethStatus["connected"])))
-        status.append(
+        status = [
+            (self.tr("Active"), self.bool2str(ethStatus["active"])),
+            (self.tr("Connected"), self.bool2str(ethStatus["connected"])),
             (
                 self.tr("Status"),
                 self.__statusTranslations["picowiz"][ethStatus["status"]],
-            )
-        )
-        status.append(
+            ),
             (
                 self.tr("Hostname"),
                 ethStatus["hostname"] if ethStatus["hostname"] else self.tr("unknown"),
-            )
-        )
-        status.append((self.tr("IPv4 Address"), ethStatus["ifconfig"][0]))
-        status.append((self.tr("Netmask"), ethStatus["ifconfig"][1]))
-        status.append((self.tr("Gateway"), ethStatus["ifconfig"][2]))
-        status.append((self.tr("DNS"), ethStatus["ifconfig"][3]))
-        status.append((self.tr("MAC-Address"), ethStatus["mac"]))
+            ),
+            (self.tr("MAC-Address"), ethStatus["mac"]),
+        ]
+        addressInfo = {
+            "ipv4": ethStatus["ipv4_addr"],
+            "ipv6": ethStatus["ipv6_addr"],
+        }
 
-        return status
+        return status, addressInfo
 
     def connectToLan(self, config, hostname):
         """
--- a/src/eric7/MicroPython/EthernetDialogs/EthernetController.py	Mon May 05 10:17:49 2025 +0200
+++ b/src/eric7/MicroPython/EthernetDialogs/EthernetController.py	Mon May 05 17:40:08 2025 +0200
@@ -32,6 +32,8 @@
 
         self.__mpy = microPython
 
+        self.__ethernetStatusDialog = None
+
     def createMenu(self, menu):
         """
         Public method to create the Ethernet submenu.
@@ -81,15 +83,14 @@
         """
         from .EthernetStatusDialog import EthernetStatusDialog
 
-        try:
-            with EricOverrideCursor():
-                status = self.__mpy.getDevice().getEthernetStatus()
-                # status is a list of user labels and associated values
+        if self.__ethernetStatusDialog is not None:
+            self.__ethernetStatusDialog.deleteLater()
+            self.__ethernetStatusDialog = None
 
-            dlg = EthernetStatusDialog(status, parent=self.__mpy)
-            dlg.exec()
-        except Exception as exc:
-            self.__mpy.showError("getEthernetStatus()", str(exc))
+        self.__ethernetStatusDialog = EthernetStatusDialog(
+            microPython=self.__mpy, parent=self.__mpy
+        )
+        self.__ethernetStatusDialog.show()
 
     @pyqtSlot()
     def __connectLanDhcp(self):
--- a/src/eric7/MicroPython/EthernetDialogs/EthernetStatusDialog.py	Mon May 05 10:17:49 2025 +0200
+++ b/src/eric7/MicroPython/EthernetDialogs/EthernetStatusDialog.py	Mon May 05 17:40:08 2025 +0200
@@ -7,9 +7,12 @@
 Module implementing a dialog to show Ethernet related status information.
 """
 
-from PyQt6.QtCore import Qt
+from PyQt6.QtCore import Qt, pyqtSlot
 from PyQt6.QtWidgets import QDialog, QDialogButtonBox, QTreeWidgetItem
 
+from eric7.EricGui import EricPixmapCache
+from eric7.SystemUtilities.NetworkUtilities import ipv6AddressScope
+
 from .Ui_EthernetStatusDialog import Ui_EthernetStatusDialog
 
 
@@ -18,25 +21,87 @@
     Class implementing a dialog to show Ethernet related status information.
     """
 
-    def __init__(self, status, parent=None):
+    def __init__(self, microPython, parent=None):
         """
         Constructor
 
-        @param status status data to be show
-        @type list of tuples of (str, str)
+        @param microPython reference to the MicroPython widget
+        @type MicroPythonWidget
         @param parent reference to the parent widget (defaults to None)
         @type QWidget (optional)
         """
         super().__init__(parent)
         self.setupUi(self)
+        self.setWindowFlags(Qt.WindowType.Window)
 
         self.statusTree.setColumnCount(2)
 
+        self.refreshButton.setIcon(EricPixmapCache.getIcon("reload"))
+        self.refreshButton.clicked.connect(self.__showStatus)
+
+        self.__mpy = microPython
+
+        self.__showStatus()
+
+    @pyqtSlot()
+    def __showStatus(self):
+        """
+        Private slot to show the current WiFi status.
+        """
+        # clear old data
+        self.statusTree.clear()
+
+        # get the status
+        try:
+            status, addressInfo = self.__mpy.getDevice().getEthernetStatus()
+        except Exception as exc:
+            self.__mpy.showError("getEthernetStatus()", str(exc))
+            return
+
         for topic, value in status:
             QTreeWidgetItem(self.statusTree, [topic, str(value)])
 
+        if addressInfo["ipv4"]:
+            header = self.__createHeader(self.statusTree, self.tr("IPv4"))
+            QTreeWidgetItem(header, [self.tr("Address"), addressInfo["ipv4"][0]])
+            QTreeWidgetItem(header, [self.tr("Netmask"), addressInfo["ipv4"][1]])
+            QTreeWidgetItem(header, [self.tr("Gateway"), addressInfo["ipv4"][2]])
+            QTreeWidgetItem(header, [self.tr("DNS"), addressInfo["ipv4"][3]])
+
+        if addressInfo["ipv6"]:
+            header = self.__createHeader(self.statusTree, self.tr("IPv6"))
+            addrHeader = self.__createHeader(
+                header, self.tr("Addresses"), underlined=False
+            )
+            for addr in sorted(addressInfo["ipv6"]):
+                QTreeWidgetItem(addrHeader, [addr.lower(), ipv6AddressScope(addr)])
+
         for col in range(self.statusTree.columnCount()):
             self.statusTree.resizeColumnToContents(col)
 
         self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setDefault(True)
         self.buttonBox.setFocus(Qt.FocusReason.OtherFocusReason)
+
+    def __createHeader(self, parent, text, underlined=True):
+        """
+        Private method to create a subheader item.
+
+        @param parent reference to the parent item
+        @type QTreeWidgetItem
+        @param text text for the header item
+        @type str
+        @param underlined flag indicating an underlined header (defaults to True)
+        @type bool (optional)
+        @return reference to the created header item
+        @rtype QTreeWidgetItem
+        """
+        headerItem = QTreeWidgetItem(parent, [text])
+        headerItem.setExpanded(True)
+        headerItem.setFirstColumnSpanned(True)
+
+        if underlined:
+            font = headerItem.font(0)
+            font.setUnderline(True)
+            headerItem.setFont(0, font)
+
+        return headerItem
--- a/src/eric7/MicroPython/EthernetDialogs/EthernetStatusDialog.ui	Mon May 05 10:17:49 2025 +0200
+++ b/src/eric7/MicroPython/EthernetDialogs/EthernetStatusDialog.ui	Mon May 05 17:40:08 2025 +0200
@@ -29,12 +29,15 @@
       <string>Ethernet Status</string>
      </property>
      <property name="alignment">
-      <set>Qt::AlignCenter</set>
+      <set>Qt::AlignmentFlag::AlignCenter</set>
      </property>
     </widget>
    </item>
    <item>
     <widget class="QTreeWidget" name="statusTree">
+     <property name="alternatingRowColors">
+      <bool>true</bool>
+     </property>
      <property name="rootIsDecorated">
       <bool>false</bool>
      </property>
@@ -52,17 +55,35 @@
     </widget>
    </item>
    <item>
-    <widget class="QDialogButtonBox" name="buttonBox">
-     <property name="orientation">
-      <enum>Qt::Horizontal</enum>
-     </property>
-     <property name="standardButtons">
-      <set>QDialogButtonBox::Close</set>
-     </property>
-    </widget>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QPushButton" name="refreshButton">
+       <property name="toolTip">
+        <string>Press to refresh the status display.</string>
+       </property>
+       <property name="text">
+        <string>Refresh</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QDialogButtonBox" name="buttonBox">
+       <property name="orientation">
+        <enum>Qt::Orientation::Horizontal</enum>
+       </property>
+       <property name="standardButtons">
+        <set>QDialogButtonBox::StandardButton::Close</set>
+       </property>
+      </widget>
+     </item>
+    </layout>
    </item>
   </layout>
  </widget>
+ <tabstops>
+  <tabstop>statusTree</tabstop>
+  <tabstop>refreshButton</tabstop>
+ </tabstops>
  <resources/>
  <connections>
   <connection>
--- a/src/eric7/MicroPython/EthernetDialogs/Ui_EthernetStatusDialog.py	Mon May 05 10:17:49 2025 +0200
+++ b/src/eric7/MicroPython/EthernetDialogs/Ui_EthernetStatusDialog.py	Mon May 05 17:40:08 2025 +0200
@@ -1,6 +1,6 @@
 # Form implementation generated from reading ui file 'src/eric7/MicroPython/EthernetDialogs/EthernetStatusDialog.ui'
 #
-# Created by: PyQt6 UI code generator 6.7.0
+# Created by: PyQt6 UI code generator 6.9.0
 #
 # WARNING: Any manual changes made to this file will be lost when pyuic6 is
 # run again.  Do not edit this file unless you know what you are doing.
@@ -25,24 +25,34 @@
         self.label.setObjectName("label")
         self.verticalLayout.addWidget(self.label)
         self.statusTree = QtWidgets.QTreeWidget(parent=EthernetStatusDialog)
+        self.statusTree.setAlternatingRowColors(True)
         self.statusTree.setRootIsDecorated(False)
         self.statusTree.setItemsExpandable(False)
         self.statusTree.setHeaderHidden(True)
         self.statusTree.setObjectName("statusTree")
         self.statusTree.headerItem().setText(0, "1")
         self.verticalLayout.addWidget(self.statusTree)
+        self.horizontalLayout = QtWidgets.QHBoxLayout()
+        self.horizontalLayout.setObjectName("horizontalLayout")
+        self.refreshButton = QtWidgets.QPushButton(parent=EthernetStatusDialog)
+        self.refreshButton.setObjectName("refreshButton")
+        self.horizontalLayout.addWidget(self.refreshButton)
         self.buttonBox = QtWidgets.QDialogButtonBox(parent=EthernetStatusDialog)
         self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal)
         self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Close)
         self.buttonBox.setObjectName("buttonBox")
-        self.verticalLayout.addWidget(self.buttonBox)
+        self.horizontalLayout.addWidget(self.buttonBox)
+        self.verticalLayout.addLayout(self.horizontalLayout)
 
         self.retranslateUi(EthernetStatusDialog)
         self.buttonBox.accepted.connect(EthernetStatusDialog.accept) # type: ignore
         self.buttonBox.rejected.connect(EthernetStatusDialog.reject) # type: ignore
         QtCore.QMetaObject.connectSlotsByName(EthernetStatusDialog)
+        EthernetStatusDialog.setTabOrder(self.statusTree, self.refreshButton)
 
     def retranslateUi(self, EthernetStatusDialog):
         _translate = QtCore.QCoreApplication.translate
         EthernetStatusDialog.setWindowTitle(_translate("EthernetStatusDialog", "Ethernet Status"))
         self.label.setText(_translate("EthernetStatusDialog", "Ethernet Status"))
+        self.refreshButton.setToolTip(_translate("EthernetStatusDialog", "Press to refresh the status display."))
+        self.refreshButton.setText(_translate("EthernetStatusDialog", "Refresh"))
--- a/src/eric7/MicroPython/WifiDialogs/Ui_WifiStatusDialog.py	Mon May 05 10:17:49 2025 +0200
+++ b/src/eric7/MicroPython/WifiDialogs/Ui_WifiStatusDialog.py	Mon May 05 17:40:08 2025 +0200
@@ -1,6 +1,6 @@
 # Form implementation generated from reading ui file 'src/eric7/MicroPython/WifiDialogs/WifiStatusDialog.ui'
 #
-# Created by: PyQt6 UI code generator 6.8.1
+# Created by: PyQt6 UI code generator 6.9.0
 #
 # WARNING: Any manual changes made to this file will be lost when pyuic6 is
 # run again.  Do not edit this file unless you know what you are doing.
@@ -25,6 +25,7 @@
         self.label.setObjectName("label")
         self.verticalLayout.addWidget(self.label)
         self.statusTree = QtWidgets.QTreeWidget(parent=WifiStatusDialog)
+        self.statusTree.setAlternatingRowColors(True)
         self.statusTree.setHeaderHidden(True)
         self.statusTree.setObjectName("statusTree")
         self.statusTree.headerItem().setText(0, "1")
--- a/src/eric7/MicroPython/WifiDialogs/WifiController.py	Mon May 05 10:17:49 2025 +0200
+++ b/src/eric7/MicroPython/WifiDialogs/WifiController.py	Mon May 05 17:40:08 2025 +0200
@@ -106,6 +106,7 @@
         """
         Private slot to connect the current device to a WiFi network.
         """
+        # TODO: add country to WiFi connection
         from .WifiConnectionDialog import WifiConnectionDialog
 
         dlg = WifiConnectionDialog(parent=self.__mpy)
--- a/src/eric7/MicroPython/WifiDialogs/WifiStatusDialog.py	Mon May 05 10:17:49 2025 +0200
+++ b/src/eric7/MicroPython/WifiDialogs/WifiStatusDialog.py	Mon May 05 17:40:08 2025 +0200
@@ -13,6 +13,7 @@
 from PyQt6.QtWidgets import QDialog, QDialogButtonBox, QTreeWidgetItem
 
 from eric7.EricGui import EricPixmapCache
+from eric7.SystemUtilities.NetworkUtilities import ipv6AddressScope
 
 from .Ui_WifiStatusDialog import Ui_WifiStatusDialog
 
@@ -75,6 +76,11 @@
             QTreeWidgetItem(
                 self.statusTree, [self.tr("Country"), overallStatus["country"]]
             )
+        with contextlib.suppress(KeyError):
+            QTreeWidgetItem(
+                self.statusTree,
+                [self.tr("Preferred IP Version"), str(overallStatus["prefer"])],
+            )
 
         # populate status of client interface
         if clientStatus:
@@ -96,17 +102,8 @@
                 )
                 with contextlib.suppress(KeyError):
                     QTreeWidgetItem(header, [self.tr("Status"), clientStatus["status"]])
-                QTreeWidgetItem(
-                    header, [self.tr("IPv4 Address"), clientStatus["ifconfig"][0]]
-                )
-                QTreeWidgetItem(
-                    header, [self.tr("Netmask"), clientStatus["ifconfig"][1]]
-                )
-                QTreeWidgetItem(
-                    header, [self.tr("Gateway"), clientStatus["ifconfig"][2]]
-                )
-                QTreeWidgetItem(header, [self.tr("DNS"), clientStatus["ifconfig"][3]])
-                QTreeWidgetItem(header, [self.tr("MAC-Address"), clientStatus["mac"]])
+                with contextlib.suppress(KeyError):
+                    QTreeWidgetItem(header, [self.tr("SSID"), clientStatus["essid"]])
                 with contextlib.suppress(KeyError):
                     QTreeWidgetItem(
                         header, [self.tr("Channel"), str(clientStatus["channel"])]
@@ -123,7 +120,35 @@
                             self.tr("{0} dBm").format(clientStatus["txpower"]),
                         ],
                     )
+                QTreeWidgetItem(header, [self.tr("MAC-Address"), clientStatus["mac"]])
 
+                # IPv4 specific data
+                ip4Header = self.__createSubheader(header, self.tr("IPv4"))
+                QTreeWidgetItem(
+                    ip4Header, [self.tr("Address"), clientStatus["ifconfig"][0]]
+                )
+                QTreeWidgetItem(
+                    ip4Header, [self.tr("Netmask"), clientStatus["ifconfig"][1]]
+                )
+                QTreeWidgetItem(
+                    ip4Header, [self.tr("Gateway"), clientStatus["ifconfig"][2]]
+                )
+                QTreeWidgetItem(
+                    ip4Header, [self.tr("DNS"), clientStatus["ifconfig"][3]]
+                )
+
+                # IPv6 specific data
+                if clientStatus["ipv6_addr"]:
+                    ip6Header = self.__createSubheader(header, self.tr("IPv6"))
+                    ip6AddrHeader = self.__createSubheader(
+                        ip6Header, self.tr("Addresses"), underlined=False
+                    )
+                    for addr in sorted(clientStatus["ipv6_addr"]):
+                        QTreeWidgetItem(
+                            ip6AddrHeader, [addr.lower(), ipv6AddressScope(addr)]
+                        )
+
+                # data about the connected access point
                 if "ap_ssid" in clientStatus:
                     apHeader = self.__createSubheader(
                         header, self.tr("Connected Access Point")
@@ -171,23 +196,11 @@
                 with contextlib.suppress(KeyError):
                     QTreeWidgetItem(header, [self.tr("Status"), apStatus["status"]])
                 with contextlib.suppress(KeyError):
-                    QTreeWidgetItem(
-                        header, [self.tr("IPv4 Address"), apStatus["ifconfig"][0]]
-                    )
-                    QTreeWidgetItem(
-                        header, [self.tr("Netmask"), apStatus["ifconfig"][1]]
-                    )
-                    QTreeWidgetItem(
-                        header, [self.tr("Gateway"), apStatus["ifconfig"][2]]
-                    )
-                    QTreeWidgetItem(header, [self.tr("DNS"), apStatus["ifconfig"][3]])
-                with contextlib.suppress(KeyError):
                     QTreeWidgetItem(header, [self.tr("SSID"), apStatus["essid"]])
                 with contextlib.suppress(KeyError):
                     QTreeWidgetItem(
                         header, [self.tr("Security"), apStatus["ap_security"]]
                     )
-                QTreeWidgetItem(header, [self.tr("MAC-Address"), apStatus["mac"]])
                 with contextlib.suppress(KeyError):
                     QTreeWidgetItem(
                         header, [self.tr("Channel"), str(apStatus["channel"])]
@@ -202,6 +215,34 @@
                             self.tr("{0} dBm").format(apStatus["txpower"]),
                         ],
                     )
+                QTreeWidgetItem(header, [self.tr("MAC-Address"), apStatus["mac"]])
+
+                # IPv4 specific data
+                ip4Header = self.__createSubheader(header, self.tr("IPv4"))
+                with contextlib.suppress(KeyError):
+                    QTreeWidgetItem(
+                        ip4Header, [self.tr("Address"), apStatus["ifconfig"][0]]
+                    )
+                    QTreeWidgetItem(
+                        ip4Header, [self.tr("Netmask"), apStatus["ifconfig"][1]]
+                    )
+                    QTreeWidgetItem(
+                        ip4Header, [self.tr("Gateway"), apStatus["ifconfig"][2]]
+                    )
+                    QTreeWidgetItem(
+                        ip4Header, [self.tr("DNS"), apStatus["ifconfig"][3]]
+                    )
+
+                # IPv6 specific data
+                if clientStatus["ipv6_addr"]:
+                    ip6Header = self.__createSubheader(header, self.tr("IPv6"))
+                    ip6AddrHeader = self.__createSubheader(
+                        ip6Header, self.tr("Addresses"), underlined=False
+                    )
+                    for addr in sorted(apStatus["ipv6_addr"]):
+                        QTreeWidgetItem(
+                            ip6AddrHeader, [addr.lower(), ipv6AddressScope(addr)]
+                        )
 
         for col in range(self.statusTree.columnCount()):
             self.statusTree.resizeColumnToContents(col)
@@ -224,12 +265,11 @@
 
         font = headerItem.font(0)
         font.setBold(True)
-
         headerItem.setFont(0, font)
 
         return headerItem
 
-    def __createSubheader(self, parent, text):
+    def __createSubheader(self, parent, text, underlined=True):
         """
         Private method to create a subheader item.
 
@@ -237,6 +277,8 @@
         @type QTreeWidgetItem
         @param text text for the header item
         @type str
+        @param underlined flag indicating an underlined header (defaults to True)
+        @type bool (optional)
         @return reference to the created header item
         @rtype QTreeWidgetItem
         """
@@ -244,8 +286,9 @@
         headerItem.setExpanded(True)
         headerItem.setFirstColumnSpanned(True)
 
-        font = headerItem.font(0)
-        font.setUnderline(True)
-        headerItem.setFont(0, font)
+        if underlined:
+            font = headerItem.font(0)
+            font.setUnderline(True)
+            headerItem.setFont(0, font)
 
         return headerItem
--- a/src/eric7/MicroPython/WifiDialogs/WifiStatusDialog.ui	Mon May 05 10:17:49 2025 +0200
+++ b/src/eric7/MicroPython/WifiDialogs/WifiStatusDialog.ui	Mon May 05 17:40:08 2025 +0200
@@ -35,6 +35,9 @@
    </item>
    <item>
     <widget class="QTreeWidget" name="statusTree">
+     <property name="alternatingRowColors">
+      <bool>true</bool>
+     </property>
      <property name="headerHidden">
       <bool>true</bool>
      </property>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/eric7/SystemUtilities/NetworkUtilities.py	Mon May 05 17:40:08 2025 +0200
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2025 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing network related utility functions.
+"""
+
+
+def ipv6AddressScope(address):
+    """
+    Function to determine the scope of an IPv6 address.
+
+    @param address IPv6 address
+    @type str
+    @return address scope
+    @rtype str
+    """
+    address = address.lower()
+    if address.startswith("fe80"):
+        return "Link-Local Scope"
+    elif address.startswith("fec"):
+        return "Site-Local Scope"
+    elif address.startswith("ff"):
+        return "Multicast Scope"
+    elif address.startswith(("fc", "fd")):
+        return "Unique-Local Scope"
+    else:
+        return "Global Scope"

eric ide

mercurial