Sat, 06 Feb 2021 19:17:25 +0100
Added the files forgotten in the last commit.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric6/MicroPython/AddEditDevicesDialog.py Sat Feb 06 19:17:25 2021 +0100 @@ -0,0 +1,126 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2021 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing a dialog to add or edit data of unknown MicroPython +devices. +""" + +from PyQt5.QtCore import pyqtSlot, Qt, QUrl, QUrlQuery +from PyQt5.QtGui import QDesktopServices +from PyQt5.QtWidgets import QDialog, QDialogButtonBox + +from .Ui_AddEditDevicesDialog import Ui_AddEditDevicesDialog + +from .MicroPythonDevices import getSupportedDevices + +from UI.Info import BugAddress + + +class AddEditDevicesDialog(QDialog, Ui_AddEditDevicesDialog): + """ + Class implementing a dialog to add or edit data of unknown MicroPython + devices. + """ + def __init__(self, vid=0, pid=0, description=0, deviceData=None, + parent=None): + """ + Constructor + + Note: Either vid and pid and description or deviceData dictionary + must be given. + + @param vid vendor ID of the device (defaults to 0) + @type int (optional) + @param pid product ID of the device (defaults to 0) + @type int (optional) + @param description description for the device (defaults to "") + @type str (optional) + @param deviceData type of the device (defaults to None) + @type dict (optional) + @param parent reference to the parent widget (defaults to None) + @type QWidget (optional) + """ + super(AddEditDevicesDialog, self).__init__(parent) + self.setupUi(self) + + # populate the device type combo box + self.deviceTypeComboBox.addItem("", "") + for board, desc in sorted(getSupportedDevices(), key=lambda x: x[1]): + self.deviceTypeComboBox.addItem(desc, board) + + if deviceData is not None: + self.vidEdit.setText("0x{0:04x}".format(deviceData["vid"])) + self.pidEdit.setText("0x{0:04x}".format(deviceData["pid"])) + self.descriptionEdit.setText(deviceData["description"]) + self.deviceTypeComboBox.setCurrentIndex( + self.deviceTypeComboBox.findData(deviceData["type"])) + self.dataVolumeEdit.setText(deviceData["data_volume"]) + self.flashVolumeEdit.setText(deviceData["flash_volume"]) + else: + self.vidEdit.setText("0x{0:04x}".format(vid)) + self.pidEdit.setText("0x{0:04x}".format(pid)) + self.descriptionEdit.setText(description) + self.deviceTypeComboBox.setCurrentText("") + self.dataVolumeEdit.setText("") + self.flashVolumeEdit.setText("") + + self.deviceTypeComboBox.setFocus(Qt.OtherFocusReason) + + msh = self.minimumSizeHint() + self.resize(max(self.width(), msh.width()), msh.height()) + + @pyqtSlot(int) + def on_deviceTypeComboBox_currentIndexChanged(self, index): + """ + Private slot to handle the selection of a device type. + + @param index index of the current item + @type int + """ + board = self.deviceTypeComboBox.currentData() + self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(bool(board)) + self.reportButton.setEnabled(bool(board)) + + @pyqtSlot() + def on_reportButton_clicked(self): + """ + Private slot to report the entered data to the eric-bugs email address. + """ + body = "\r\n".join([ + "This is an unknow MicroPython device. Please add it.", + "", + "VID: {0}".format(self.vidEdit.text()), + "PID: {0}".format(self.pidEdit.text()), + "Description: {0}".format(self.descriptionEdit.text()), + "Device Type: {0}".format(self.deviceTypeComboBox.currentData()), + "Data Volume: {0}".format(self.dataVolumeEdit.text().strip()), + "Flash Volume: {0}".format(self.flashVolumeEdit.text().strip()), + ]) + + urlQuery = QUrlQuery() + urlQuery.addQueryItem("subject", "Unsupported MicroPython Device") + urlQuery.addQueryItem("body", body) + + url = QUrl("mailto:{0}".format(BugAddress)) + url.setQuery(urlQuery) + + QDesktopServices.openUrl(url) + + def getDeviceDict(self): + """ + Public method to get the entered data as a dictionary. + + @return dictionary containing the entered data + @rtype dict + """ + return { + "vid": int(self.vidEdit.text(), 16), + "pid": int(self.pidEdit.text(), 16), + "description": self.descriptionEdit.text(), + "type": self.deviceTypeComboBox.currentData(), + "data_volume": self.dataVolumeEdit.text().strip(), + "flash_volume": self.flashVolumeEdit.text().strip(), + }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric6/MicroPython/AddEditDevicesDialog.ui Sat Feb 06 19:17:25 2021 +0100 @@ -0,0 +1,182 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>AddEditDevicesDialog</class> + <widget class="QDialog" name="AddEditDevicesDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>500</width> + <height>270</height> + </rect> + </property> + <property name="windowTitle"> + <string>Add Unknown Device</string> + </property> + <property name="sizeGripEnabled"> + <bool>true</bool> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Vendor ID:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="vidEdit"> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Product ID:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="pidEdit"> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Description:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLineEdit" name="descriptionEdit"> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Device Type:</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QComboBox" name="deviceTypeComboBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Select the device type</string> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>Data Volume:</string> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QLineEdit" name="dataVolumeEdit"> + <property name="toolTip"> + <string>Enter the volume name used for direct acces to the device</string> + </property> + <property name="clearButtonEnabled"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="5" column="0"> + <widget class="QLabel" name="label_6"> + <property name="text"> + <string>Flash Volume:</string> + </property> + </widget> + </item> + <item row="5" column="1"> + <widget class="QLineEdit" name="flashVolumeEdit"> + <property name="toolTip"> + <string>Enter the volume name used for flashing if this device supports UF2</string> + </property> + <property name="clearButtonEnabled"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="6" column="0" colspan="2"> + <widget class="QPushButton" name="reportButton"> + <property name="toolTip"> + <string>Press to report the entered data via email</string> + </property> + <property name="text"> + <string>Report Data</string> + </property> + </widget> + </item> + <item row="7" column="0" colspan="2"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <tabstops> + <tabstop>vidEdit</tabstop> + <tabstop>pidEdit</tabstop> + <tabstop>descriptionEdit</tabstop> + <tabstop>deviceTypeComboBox</tabstop> + <tabstop>dataVolumeEdit</tabstop> + <tabstop>flashVolumeEdit</tabstop> + <tabstop>reportButton</tabstop> + </tabstops> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>AddEditDevicesDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>AddEditDevicesDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric6/MicroPython/UnknownDevicesDialog.py Sat Feb 06 19:17:25 2021 +0100 @@ -0,0 +1,256 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2021 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing a dialog to manage the list of unknown devices. +""" + +from PyQt5.QtCore import pyqtSlot, Qt, QUrl, QUrlQuery +from PyQt5.QtGui import QDesktopServices +from PyQt5.QtWidgets import QDialog, QListWidgetItem + +from E5Gui import E5MessageBox + +from .Ui_UnknownDevicesDialog import Ui_UnknownDevicesDialog + +import Preferences +from UI.Info import BugAddress + + +class UnknownDevicesDialog(QDialog, Ui_UnknownDevicesDialog): + """ + Class implementing a dialog to manage the list of unknown devices. + """ + DeviceDataRole = Qt.UserRole + ModifiedRole = Qt.UserRole + 1 + + def __init__(self, parent=None): + """ + Constructor + + @param parent reference to the parent widget (defaults to None) + @type QWidget (optional) + """ + super(UnknownDevicesDialog, self).__init__(parent) + self.setupUi(self) + + self.__loadDevices() + + def __loadDevices(self): + """ + Private method to load the list of unknown devices. + """ + self.deviceList.clear() + + devices = Preferences.getMicroPython("ManualDevices") + for device in devices: + itm = QListWidgetItem( + self.tr("{0} (0x{1:04x}/0x{2:04x})", "description, VID, PID") + .format(device["description"], device["vid"], device["pid"]), + self.deviceList) + itm.setData(self.DeviceDataRole, device) + itm.setData(self.ModifiedRole, False) + + self.__initialDeviceCount = self.deviceList.count() + + self.__checkButtons() + + def __isDirty(self): + """ + Private method to check, if the dialog contains unsaved data. + + @return flag indicating the presence of unsaved data + @rtype bool + """ + dirty = False + for row in range(self.deviceList.count()): + dirty |= self.deviceList.item(row).data(self.ModifiedRole) + dirty |= self.deviceList.count() != self.__initialDeviceCount + return dirty + + def __editItem(self, item): + """ + Private method to edit the given item. + + @param item reference to the item to be edited + @type QListWidgetItem + """ + if item is None: + # play it safe + return + + from .AddEditDevicesDialog import AddEditDevicesDialog + dlg = AddEditDevicesDialog(deviceData=item.data(self.DeviceDataRole)) + if dlg.exec() == QDialog.Accepted: + deviceDict = dlg.getDeviceDict() + item.setData(self.DeviceDataRole, deviceDict) + item.setData(self.ModifiedRole, True) + + item.setText(self.tr("{0} (*)", "list entry is modified") + .format(item.text())) + + def __saveDeviceData(self): + """ + Private method to save the device data. + """ + devices = [] + + for row in range(self.deviceList.count()): + devices.append(self.deviceList.item(row).data( + self.DeviceDataRole)) + Preferences.setMicroPython("ManualDevices", devices) + + return True + + @pyqtSlot() + def __checkButtons(self): + """ + Private slot to set the enabled state of the buttons. + """ + selectedItemsCount = len(self.deviceList.selectedItems()) + self.editButton.setEnabled(selectedItemsCount == 1) + self.deleteButton.setEnabled(selectedItemsCount >= 1) + + @pyqtSlot(QListWidgetItem) + def on_deviceList_itemActivated(self, item): + """ + Private slot to edit the data of the activated item. + + @param item reference to the activated item + @type QListWidgetItem + """ + self.__editItem(item) + + @pyqtSlot() + def on_deviceList_itemSelectionChanged(self): + """ + Private slot to handle a change of selected items. + """ + self.__checkButtons() + + @pyqtSlot() + def on_editButton_clicked(self): + """ + Private slot to edit the selected item. + """ + itm = self.deviceList.selectedItems()[0] + self.__editItem(itm) + + @pyqtSlot() + def on_deleteButton_clicked(self): + """ + Private slot to delete the selected entries. + """ + unsaved = False + for itm in self.deviceList.selectedItems(): + unsaved |= itm.data(self.ModifiedRole) + if unsaved: + ok = E5MessageBox.yesNo( + self, + self.tr("Delete Unknown Devices"), + self.tr("The selected entries contain some with modified" + " data. Shall they really be deleted?")) + if not ok: + return + + for itm in self.deviceList.selectedItems(): + self.deviceList.takeItem(self.deviceList.row(itm)) + del itm + + @pyqtSlot() + def on_deleteAllButton_clicked(self): + """ + Private slot to delete all devices. + """ + if self.__isDirty(): + ok = E5MessageBox.yesNo( + self, + self.tr("Delete Unknown Devices"), + self.tr("The list contains some devices with modified" + " data. Shall they really be deleted?")) + if not ok: + return + + self.deviceList.clear() + + @pyqtSlot() + def on_restoreButton_clicked(self): + """ + Private slot to restore the list of unknown devices. + """ + if self.__isDirty(): + ok = E5MessageBox.yesNo( + self, + self.tr("Restore Unknown Devices"), + self.tr("Restoring the list of unknown devices will overwrite" + " all changes made. Do you really want to restore the" + " list?")) + if not ok: + return + + self.__loadDevices() + + @pyqtSlot() + def on_reportButton_clicked(self): + """ + Private slot to report the data of all boards to the eric-bugs email + address. + """ + if self.deviceList.count() > 0: + bodyList = [ + "These are my MicroPython devices not yet known by eric." + " Please add them.", + "", + ] + + for row in range(self.deviceList.count()): + deviceDict = self.deviceList.item(row).data( + self.DeviceDataRole) + bodyList += [ + "Board #{0}:".format(row), + " VID: {0}".format(deviceDict["vid"]), + " PID: {0}".format(deviceDict["pid"]), + " Description: {0}".format(deviceDict["description"]), + " Device Type: {0}".format(deviceDict["type"]), + " Data Volume: {0}".format(deviceDict["data_volume"]), + " Flash Volume: {0}".format(deviceDict["flash_volume"]), + "" + ] + + urlQuery = QUrlQuery() + urlQuery.addQueryItem("subject", "Unsupported MicroPython Devices") + urlQuery.addQueryItem("body", "\r\n".join(bodyList)) + + url = QUrl("mailto:{0}".format(BugAddress)) + url.setQuery(urlQuery) + + QDesktopServices.openUrl(url) + + @pyqtSlot() + def on_buttonBox_accepted(self): + """ + Private slot to handle the OK button press. + + This action saves the edited list to the preferences store. + """ + self.__saveDeviceData() + self.accept() + + @pyqtSlot() + def on_buttonBox_rejected(self): + """ + Private slot handling the cancellation of the dialog. + """ + if self.__isDirty(): + ok = E5MessageBox.okToClearData( + self, + self.tr("Unsaved Data"), + self.tr("""The list of devices contains some with modified""" + """ data."""), + self.__saveDeviceData) + if not ok: + return + + self.reject()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric6/MicroPython/UnknownDevicesDialog.ui Sat Feb 06 19:17:25 2021 +0100 @@ -0,0 +1,129 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>UnknownDevicesDialog</class> + <widget class="QDialog" name="UnknownDevicesDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>450</width> + <height>500</height> + </rect> + </property> + <property name="windowTitle"> + <string>Unknown Devices</string> + </property> + <property name="sizeGripEnabled"> + <bool>true</bool> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QListWidget" name="deviceList"> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + <property name="selectionMode"> + <enum>QAbstractItemView::ExtendedSelection</enum> + </property> + <property name="sortingEnabled"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="0" column="1"> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QPushButton" name="editButton"> + <property name="toolTip"> + <string>Press to edit the selected entry</string> + </property> + <property name="text"> + <string>Edit...</string> + </property> + </widget> + </item> + <item> + <widget class="Line" name="line"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="deleteButton"> + <property name="toolTip"> + <string>Press to delete the selected entries</string> + </property> + <property name="text"> + <string>Delete</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="deleteAllButton"> + <property name="toolTip"> + <string>Press to delete all entries</string> + </property> + <property name="text"> + <string>Delete All</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>128</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="restoreButton"> + <property name="toolTip"> + <string>Press to restore the list of devices</string> + </property> + <property name="text"> + <string>Restore</string> + </property> + </widget> + </item> + </layout> + </item> + <item row="2" column="0" colspan="2"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + <item row="1" column="0" colspan="2"> + <widget class="QPushButton" name="reportButton"> + <property name="toolTip"> + <string>Press to report the data of all boards via email</string> + </property> + <property name="text"> + <string>Report All Data</string> + </property> + </widget> + </item> + </layout> + </widget> + <tabstops> + <tabstop>deviceList</tabstop> + <tabstop>editButton</tabstop> + <tabstop>deleteButton</tabstop> + <tabstop>deleteAllButton</tabstop> + <tabstop>restoreButton</tabstop> + <tabstop>reportButton</tabstop> + </tabstops> + <resources/> + <connections/> +</ui>