Mon, 05 May 2025 17:40:08 +0200
MicroPython
- Added support for IPv6 for WiFi and Ethernet enabled devices (MPy ≥ 1.24.0).
--- 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"