diff --git a/src/nqrduck_autotm/controller.py b/src/nqrduck_autotm/controller.py index 81a1e88..c2a1324 100644 --- a/src/nqrduck_autotm/controller.py +++ b/src/nqrduck_autotm/controller.py @@ -1,13 +1,14 @@ import logging +import serial from serial.tools.list_ports import comports from nqrduck.module.module_controller import ModuleController logger = logging.getLogger(__name__) class AutoTMController(ModuleController): - + BAUDRATE = 115200 - def find_devices(self): + def find_devices(self) -> None: """Scan for available serial devices and add them to the model as available devices. """ logger.debug("Scanning for available serial devices") ports = comports() @@ -15,3 +16,13 @@ class AutoTMController(ModuleController): logger.debug("Found %s devices", len(self.module.model.available_devices)) for device in self.module.model.available_devices: logger.debug("Found device: %s", device) + + def connect(self, device : str) -> None: + """Connect to the specified device. """ + logger.debug("Connecting to device %s", device) + try: + self.module.model.serial = serial.Serial(device, self.BAUDRATE, timeout=0.1) + logger.debug("Connected to device %s", device) + except serial.SerialException as e: + logger.error("Failed to connect to device %s", device) + logger.error(e) diff --git a/src/nqrduck_autotm/model.py b/src/nqrduck_autotm/model.py index 8db4226..50d485c 100644 --- a/src/nqrduck_autotm/model.py +++ b/src/nqrduck_autotm/model.py @@ -1,8 +1,10 @@ +import serial from PyQt6.QtCore import pyqtSignal from nqrduck.module.module_model import ModuleModel class AutoTMModel(ModuleModel): available_devices_changed = pyqtSignal(list) + serial_changed = pyqtSignal(serial.Serial) @property def available_devices(self): @@ -12,3 +14,12 @@ class AutoTMModel(ModuleModel): def available_devices(self, value): self._available_devices = value self.available_devices_changed.emit(value) + + @property + def serial(self): + return self._serial + + @serial.setter + def serial(self, value): + self._serial = value + self.serial_changed.emit(value) diff --git a/src/nqrduck_autotm/resources/autotm_widget.ui b/src/nqrduck_autotm/resources/autotm_widget.ui index e24edbe..44fe3b0 100644 --- a/src/nqrduck_autotm/resources/autotm_widget.ui +++ b/src/nqrduck_autotm/resources/autotm_widget.ui @@ -37,6 +37,9 @@ + + + @@ -44,11 +47,8 @@ - - - - + Connect @@ -61,6 +61,20 @@ + + + + Connected to: + + + + + + + + + + @@ -168,7 +182,7 @@ 0 0 273 - 255 + 231 diff --git a/src/nqrduck_autotm/view.py b/src/nqrduck_autotm/view.py index a513975..6d439ac 100644 --- a/src/nqrduck_autotm/view.py +++ b/src/nqrduck_autotm/view.py @@ -1,6 +1,8 @@ import logging -from PyQt6.QtWidgets import QWidget -from PyQt6.QtCore import pyqtSlot +import serial +from datetime import datetime +from PyQt6.QtWidgets import QWidget, QLabel, QVBoxLayout +from PyQt6.QtCore import pyqtSlot, Qt from nqrduck.module.module_view import ModuleView from .widget import Ui_Form @@ -16,16 +18,71 @@ class AutoTMView(ModuleView): self._ui_form.setupUi(self) self.widget = widget + # Disable the connectButton while no devices are selected + self._ui_form.connectButton.setDisabled(True) + # On clicking of the refresh button scan for available usb devices self._ui_form.refreshButton.clicked.connect(self.module.controller.find_devices) # Connect the available devices changed signal to the on_available_devices_changed slot self.module.model.available_devices_changed.connect(self.on_available_devices_changed) + # Connect the serial changed signal to the on_serial_changed slot + self.module.model.serial_changed.connect(self.on_serial_changed) + + # On clicking of the connect button call the connect method + self._ui_form.connectButton.clicked.connect(self.on_connect_button_clicked) + + # Add a vertical layout to the info box + self._ui_form.scrollAreaWidgetContents.setLayout(QVBoxLayout()) + self._ui_form.scrollAreaWidgetContents.layout().setAlignment(Qt.AlignmentFlag.AlignTop) + @pyqtSlot(list) - def on_available_devices_changed(self, available_devices): + def on_available_devices_changed(self, available_devices : list) -> None: """Update the available devices list in the view. """ logger.debug("Updating available devices list") self._ui_form.portBox.clear() self._ui_form.portBox.addItems(available_devices) - logger.debug("Updated available devices list") \ No newline at end of file + # Enable the connectButton if there are available devices + if available_devices: + self._ui_form.connectButton.setEnabled(True) + else: + self._ui_form.connectButton.setEnabled(False) + logger.debug("Updated available devices list") + + @pyqtSlot() + def on_connect_button_clicked(self) -> None: + """This method is called when the connect button is clicked. + It calls the connect method of the controller with the currently selected device. + """ + logger.debug("Connect button clicked") + selected_device = self._ui_form.portBox.currentText() + self.module.controller.connect(selected_device) + + @pyqtSlot(serial.Serial) + def on_serial_changed(self, serial : serial.Serial) -> None: + """Update the serial 'connectionLabel' according to the current serial connection. + + Args: + serial (serial.Serial): The current serial connection.""" + logger.debug("Updating serial connection label") + if serial.is_open: + self._ui_form.connectionLabel.setText(serial.port) + self.add_info_text("Connected to device %s" % serial.port) + else: + self._ui_form.connectionLabel.setText("Disconnected") + logger.debug("Updated serial connection label") + + def add_info_text(self, text : str) -> None: + """ Adds text to the info text box. + + Args: + text (str): Text to add to the info text box. + """ + # Add a timestamp to the text + timestamp = datetime.now().strftime("%H:%M:%S") + text = "[%s] %s" % (timestamp, text) + text_label = QLabel(text) + text_label.setStyleSheet("font-size: 25px;") + self._ui_form.scrollAreaWidgetContents.layout().addWidget(text_label) + self._ui_form.scrollArea.verticalScrollBar().setValue(self._ui_form.scrollArea.verticalScrollBar().maximum()) \ No newline at end of file diff --git a/src/nqrduck_autotm/widget.py b/src/nqrduck_autotm/widget.py index 8027ca3..986183c 100644 --- a/src/nqrduck_autotm/widget.py +++ b/src/nqrduck_autotm/widget.py @@ -1,6 +1,6 @@ # Form implementation generated from reading ui file '../Modules/nqrduck-autotm/src/nqrduck_autotm/resources/autotm_widget.ui' # -# Created by: PyQt6 UI code generator 6.5.1 +# Created by: PyQt6 UI code generator 6.5.2 # # 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. @@ -31,18 +31,25 @@ class Ui_Form(object): self.verticalLayout_2.addWidget(self.label_2) self.gridLayout_2 = QtWidgets.QGridLayout() self.gridLayout_2.setObjectName("gridLayout_2") - self.label = QtWidgets.QLabel(parent=Form) - self.label.setObjectName("label") - self.gridLayout_2.addWidget(self.label, 0, 0, 1, 1) self.portBox = QtWidgets.QComboBox(parent=Form) self.portBox.setObjectName("portBox") self.gridLayout_2.addWidget(self.portBox, 0, 1, 1, 1) - self.pushButton = QtWidgets.QPushButton(parent=Form) - self.pushButton.setObjectName("pushButton") - self.gridLayout_2.addWidget(self.pushButton, 1, 1, 1, 1) + self.label = QtWidgets.QLabel(parent=Form) + self.label.setObjectName("label") + self.gridLayout_2.addWidget(self.label, 0, 0, 1, 1) + self.connectButton = QtWidgets.QPushButton(parent=Form) + self.connectButton.setObjectName("connectButton") + self.gridLayout_2.addWidget(self.connectButton, 1, 1, 1, 1) self.refreshButton = QtWidgets.QPushButton(parent=Form) self.refreshButton.setObjectName("refreshButton") self.gridLayout_2.addWidget(self.refreshButton, 1, 0, 1, 1) + self.label_10 = QtWidgets.QLabel(parent=Form) + self.label_10.setObjectName("label_10") + self.gridLayout_2.addWidget(self.label_10, 2, 0, 1, 1) + self.connectionLabel = QtWidgets.QLabel(parent=Form) + self.connectionLabel.setText("") + self.connectionLabel.setObjectName("connectionLabel") + self.gridLayout_2.addWidget(self.connectionLabel, 2, 1, 1, 1) self.verticalLayout_2.addLayout(self.gridLayout_2) self.label_3 = QtWidgets.QLabel(parent=Form) font = QtGui.QFont() @@ -99,7 +106,7 @@ class Ui_Form(object): self.scrollArea.setWidgetResizable(True) self.scrollArea.setObjectName("scrollArea") self.scrollAreaWidgetContents = QtWidgets.QWidget() - self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 273, 255)) + self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 273, 231)) self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents") self.scrollArea.setWidget(self.scrollAreaWidgetContents) self.verticalLayout_2.addWidget(self.scrollArea) @@ -125,8 +132,9 @@ class Ui_Form(object): Form.setWindowTitle(_translate("Form", "Form")) self.label_2.setText(_translate("Form", "Connection Settings:")) self.label.setText(_translate("Form", "Port:")) - self.pushButton.setText(_translate("Form", "Connect")) + self.connectButton.setText(_translate("Form", "Connect")) self.refreshButton.setText(_translate("Form", "Refresh")) + self.label_10.setText(_translate("Form", "Connected to:")) self.label_3.setText(_translate("Form", "T&M Type:")) self.label_4.setText(_translate("Form", "Frequency Sweep:")) self.label_8.setText(_translate("Form", "MHz"))