src/eric7/MicroPython/ConvertToUF2Dialog.py

branch
eric7
changeset 9915
fd5ff16632cb
child 10069
435cc5875135
equal deleted inserted replaced
9913:702ad0ef6b3f 9915:fd5ff16632cb
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a dialog to convert a .hex or .bin firmware file to .uf2.
8 """
9
10 import json
11 import os
12
13 from PyQt6.QtCore import QProcess, QRegularExpression, pyqtSlot
14 from PyQt6.QtGui import QRegularExpressionValidator
15 from PyQt6.QtWidgets import QDialog
16
17 from eric7 import Preferences
18 from eric7.EricWidgets.EricPathPicker import EricPathPickerModes
19 from eric7.SystemUtilities.PythonUtilities import getPythonExecutable
20
21 from .Ui_ConvertToUF2Dialog import Ui_ConvertToUF2Dialog
22
23
24 class ConvertToUF2Dialog(QDialog, Ui_ConvertToUF2Dialog):
25 """
26 Class implementing a dialog to convert a .hex or .bin firmware file to .uf2.
27 """
28
29 FamiliesFile = os.path.join(os.path.dirname(__file__), "Tools", "uf2families.json")
30 ConvertScript = os.path.join(os.path.dirname(__file__), "Tools", "uf2conv.py")
31
32 def __init__(self, parent=None):
33 """
34 Constructor
35
36 @param parent reference to the parent widget (defaults to None)
37 @type QWidget (optional)
38 """
39 super().__init__(parent)
40 self.setupUi(self)
41
42 self.firmwarePicker.setMode(EricPathPickerModes.OPEN_FILE_MODE)
43 self.firmwarePicker.setFilters(
44 self.tr("MicroPython Firmware Files (*.hex *.bin);;All Files (*)")
45 )
46
47 self.__validator = QRegularExpressionValidator(
48 QRegularExpression(r"[0-9a-fA-F]{0,7}")
49 )
50 self.addressEdit.setValidator(self.__validator)
51 self.addressEdit.setEnabled(False)
52
53 self.__populateFamilyComboBox()
54
55 self.__process = QProcess(self)
56 self.__process.readyReadStandardOutput.connect(self.__readOutput)
57 self.__process.readyReadStandardError.connect(self.__readError)
58 self.__process.finished.connect(self.__conversionFinished)
59
60 self.__updateConvertButton()
61
62 def __populateFamilyComboBox(self):
63 """
64 Private method to populate the chip family combo box with values read from
65 'uf2families.json' file.
66 """
67 with open(ConvertToUF2Dialog.FamiliesFile, "r") as f:
68 families = json.load(f)
69
70 self.familiesComboBox.addItem("", "")
71 for family in families:
72 self.familiesComboBox.addItem(family["description"], family["id"])
73 self.familiesComboBox.model().sort(0)
74
75 def __updateConvertButton(self):
76 """
77 Private method to set the enabled status of the 'Convert' button.
78 """
79 self.convertButton.setEnabled(
80 bool(self.firmwarePicker.text())
81 and bool(self.familiesComboBox.currentText())
82 )
83
84 @pyqtSlot(str)
85 def on_firmwarePicker_textChanged(self, firmware):
86 """
87 Private slot handling a change of the firmware file name.
88
89 @param firmware name of the firmware file
90 @type str
91 """
92 self.addressEdit.setEnabled(firmware.lower().endswith(".bin"))
93 self.__updateConvertButton()
94
95 @pyqtSlot(str)
96 def on_familiesComboBox_currentTextChanged(self, family):
97 """
98 Private slot handling the selection of a chip family.
99
100 @param family name of the selected chip family
101 @type str
102 """
103 self.__updateConvertButton()
104
105 @pyqtSlot()
106 def on_convertButton_clicked(self):
107 """
108 Private slot activating the conversion process.
109 """
110 self.outputEdit.clear()
111
112 inputFile = self.firmwarePicker.text()
113 outputFile = os.path.splitext(inputFile)[0] + ".uf2"
114 args = [
115 ConvertToUF2Dialog.ConvertScript,
116 "--convert",
117 "--family",
118 self.familiesComboBox.currentData(),
119 "--output",
120 outputFile,
121 ]
122 if inputFile.lower().endswith(".bin"):
123 address = self.addressEdit.text()
124 if address:
125 args.extend(["--base", "0x{0}".format(address)])
126 args.append(inputFile)
127 python = getPythonExecutable()
128
129 # output the generated command
130 self.outputEdit.insertPlainText(
131 "{0} {1}\n{2}\n\n".format(python, " ".join(args), "=" * 40)
132 )
133 self.outputEdit.ensureCursorVisible()
134
135 # start the conversion process
136 self.convertButton.setEnabled(False)
137 self.__process.start(python, args)
138
139 @pyqtSlot()
140 def __readOutput(self):
141 """
142 Private slot to read the standard output channel of the conversion process.
143 """
144 out = str(
145 self.__process.readAllStandardOutput(),
146 Preferences.getSystem("IOEncoding"),
147 "replace",
148 )
149 self.outputEdit.insertPlainText(out)
150 self.outputEdit.ensureCursorVisible()
151
152 @pyqtSlot()
153 def __readError(self):
154 """
155 Private slot to read the standard error channel of the conversion process.
156 """
157 out = str(
158 self.__process.readAllStandardError(),
159 Preferences.getSystem("IOEncoding"),
160 "replace",
161 )
162 self.outputEdit.insertPlainText(self.tr("--- ERROR ---\n"))
163 self.outputEdit.insertPlainText(out)
164 self.outputEdit.ensureCursorVisible()
165
166 def __conversionFinished(self, exitCode, exitStatus):
167 """
168 Private slot handling the end of the conversion process.
169
170 @param exitCode exit code of the process
171 @type int
172 @param exitStatus exit status of the process
173 @type QProcess.ExitStatus
174 """
175 self.convertButton.setEnabled(True)

eric ide

mercurial