From 7702f5fed79767bfeddfb1d6a072304f10d146b2 Mon Sep 17 00:00:00 2001 From: James O'SHANNESSY <12959316+joshanne@users.noreply.github.com> Date: Thu, 26 Mar 2026 09:57:21 +1100 Subject: [PATCH] ui: remove dependency on qtwidgets for PasswordEdit box Qt6 isn't supported by the qtwidgets package. We only use qtwidgets for the PasswordEdit widget, so let's create our own internal version that makes use of qtawesome and exposes the same functionality. --- dronecan_gui_tool/setup_window.py | 3 +- dronecan_gui_tool/widgets/__init__.py | 44 ++++++++++++++++++++++++++- setup.py | 1 - 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/dronecan_gui_tool/setup_window.py b/dronecan_gui_tool/setup_window.py index 9bb8783..abd541b 100644 --- a/dronecan_gui_tool/setup_window.py +++ b/dronecan_gui_tool/setup_window.py @@ -14,7 +14,6 @@ from .widgets import show_error, get_monospace_font, directory_selection from PyQt5.QtWidgets import QComboBox, QCompleter, QDialog, QDirModel, QFileDialog, QGroupBox, QHBoxLayout, QLabel, \ QLineEdit, QPushButton, QSpinBox, QVBoxLayout, QGridLayout, QCheckBox, QWidget -from qtwidgets import PasswordEdit from PyQt5.QtCore import Qt, QTimer from PyQt5.QtGui import QIntValidator from logging import getLogger @@ -22,6 +21,8 @@ from itertools import count import re +from .widgets import PasswordEdit + STANDARD_BAUD_RATES = 9600, 115200, 460800, 921600, 1000000, 3000000 DEFAULT_BAUD_RATE = 115200 diff --git a/dronecan_gui_tool/widgets/__init__.py b/dronecan_gui_tool/widgets/__init__.py index 572af66..bc319a4 100644 --- a/dronecan_gui_tool/widgets/__init__.py +++ b/dronecan_gui_tool/widgets/__init__.py @@ -11,7 +11,7 @@ import queue import importlib.resources from PyQt5.QtWidgets import QTableWidget, QTableWidgetItem, QAbstractItemView, QHeaderView, QApplication, QWidget, \ - QComboBox, QCompleter, QPushButton, QHBoxLayout, QVBoxLayout, QMessageBox + QComboBox, QCompleter, QPushButton, QHBoxLayout, QVBoxLayout, QMessageBox, QLineEdit from PyQt5.QtCore import Qt, QTimer, QStringListModel from PyQt5.QtGui import QColor, QKeySequence, QFont, QFontInfo, QIcon from logging import getLogger @@ -586,6 +586,48 @@ def started(self): def custom_area_layout(self): return self._custom_area_layout +class PasswordEdit(QLineEdit): + """Standalone Password Entry Widget with an optional visibility toggle""" + + def __init__(self, parent=None, show_visibility_toggle=True): + super().__init__(parent) + + # Set Icons + self._icon_password_show = QIcon(get_icon("fa6.eye")) + self._icon_password_hide = QIcon(get_icon("fa6.eye-slash")) + + # Set Initial State + self.setEchoMode(QLineEdit.EchoMode.Password) + self._visible = False + + # Setup Toggle Action + if show_visibility_toggle: + self.toggle_action = self.addAction( + self._icon_password_show, + QLineEdit.ActionPosition.TrailingPosition + ) + self.toggle_action.triggered.connect(self.on_toggle_password_Action) + + def _update_icon(self): + """Update the icon based on the visibility state""" + # Password is hidden by default, so default the password to the show action + icon = self._icon_password_show + + if self._visible: + # If the password is visible, set the icon to the hide icon + icon = self._icon_password_hide + + # Set the icon + self.toggle_action.setIcon(icon) + + def on_toggle_password_Action(self): + """Action for toggling the visibility state""" + self._visible = not self._visible + if self._visible: + self.setEchoMode(QLineEdit.EchoMode.Normal) + else: + self.setEchoMode(QLineEdit.EchoMode.Password) + self._update_icon() def get_icon(name): return qtawesome.icon(name) diff --git a/setup.py b/setup.py index 51a4ba6..7e0f7b7 100755 --- a/setup.py +++ b/setup.py @@ -56,7 +56,6 @@ 'pygments', 'qtpy', 'pyqtgraph', - 'qtwidgets', 'intelhex' ], # We can't use "scripts" here, because generated shims don't work with multiprocessing pickler.