|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a dialog to generate a requirements file. |
|
8 """ |
|
9 |
|
10 import os |
|
11 |
|
12 import circup |
|
13 |
|
14 from PyQt6.QtCore import pyqtSlot |
|
15 from PyQt6.QtGui import QGuiApplication |
|
16 from PyQt6.QtWidgets import QAbstractButton, QDialog, QDialogButtonBox |
|
17 |
|
18 from eric7.EricWidgets import EricFileDialog, EricMessageBox |
|
19 from eric7.EricWidgets.EricApplication import ericApp |
|
20 from eric7.EricWidgets.EricPathPicker import EricPathPickerModes |
|
21 from eric7.SystemUtilities import FileSystemUtilities |
|
22 |
|
23 from .Ui_RequirementsDialog import Ui_RequirementsDialog |
|
24 |
|
25 |
|
26 class RequirementsDialog(QDialog, Ui_RequirementsDialog): |
|
27 """ |
|
28 Class implementing a dialog to generate a requirements file. |
|
29 """ |
|
30 |
|
31 def __init__(self, devicePath, parent=None): |
|
32 """ |
|
33 Constructor |
|
34 |
|
35 @param devicePath path to the connected board |
|
36 @type str |
|
37 @param parent reference to the parent widget (defaults to None) |
|
38 @type QWidget (optional) |
|
39 """ |
|
40 super().__init__(parent) |
|
41 self.setupUi(self) |
|
42 |
|
43 self.__title = self.tr("Generate Requirements") |
|
44 |
|
45 self.__refreshButton = self.buttonBox.addButton( |
|
46 self.tr("&Refresh"), QDialogButtonBox.ButtonRole.ActionRole |
|
47 ) |
|
48 |
|
49 self.requirementsFilePicker.setMode(EricPathPickerModes.SAVE_FILE_MODE) |
|
50 self.requirementsFilePicker.setFilters( |
|
51 self.tr("Text Files (*.txt);;All Files (*)") |
|
52 ) |
|
53 |
|
54 self.__devicePath = devicePath |
|
55 |
|
56 self.__requirementsEdited = False |
|
57 self.__requirementsAvailable = False |
|
58 |
|
59 self.__generateRequirements() |
|
60 |
|
61 def __updateButtons(self): |
|
62 """ |
|
63 Private method to set the state of the various buttons. |
|
64 """ |
|
65 self.saveButton.setEnabled( |
|
66 self.__requirementsAvailable |
|
67 and bool(self.requirementsFilePicker.text()) |
|
68 and os.path.isabs(self.requirementsFilePicker.text()) |
|
69 ) |
|
70 self.saveToButton.setEnabled(self.__requirementsAvailable) |
|
71 self.copyButton.setEnabled(self.__requirementsAvailable) |
|
72 |
|
73 aw = ericApp().getObject("ViewManager").activeWindow() |
|
74 if aw and self.__requirementsAvailable: |
|
75 self.insertButton.setEnabled(True) |
|
76 self.replaceAllButton.setEnabled(True) |
|
77 self.replaceSelectionButton.setEnabled(aw.hasSelectedText()) |
|
78 else: |
|
79 self.insertButton.setEnabled(False) |
|
80 self.replaceAllButton.setEnabled(False) |
|
81 self.replaceSelectionButton.setEnabled(False) |
|
82 |
|
83 @pyqtSlot(str) |
|
84 def on_requirementsFilePicker_textChanged(self, txt): |
|
85 """ |
|
86 Private slot handling a change of the requirements file name. |
|
87 |
|
88 @param txt name of the requirements file |
|
89 @type str |
|
90 """ |
|
91 self.__updateButtons() |
|
92 |
|
93 @pyqtSlot() |
|
94 def on_requirementsEdit_textChanged(self): |
|
95 """ |
|
96 Private slot handling changes of the requirements text. |
|
97 """ |
|
98 self.__requirementsEdited = True |
|
99 |
|
100 @pyqtSlot(QAbstractButton) |
|
101 def on_buttonBox_clicked(self, button): |
|
102 """ |
|
103 Private slot called by a button of the button box clicked. |
|
104 |
|
105 @param button button that was clicked |
|
106 @type QAbstractButton |
|
107 """ |
|
108 if button == self.buttonBox.button(QDialogButtonBox.StandardButton.Close): |
|
109 self.close() |
|
110 elif button == self.__refreshButton: |
|
111 self.__generateRequirements() |
|
112 |
|
113 def __generateRequirements(self): |
|
114 """ |
|
115 Private slot to generate the requirements specifiers list. |
|
116 """ |
|
117 ok = ( |
|
118 EricMessageBox.yesNo( |
|
119 self, |
|
120 self.__title, |
|
121 self.tr( |
|
122 """The requirements were changed. Do you want""" |
|
123 """ to overwrite these changes?""" |
|
124 ), |
|
125 ) |
|
126 if self.__requirementsEdited |
|
127 else True |
|
128 ) |
|
129 if ok: |
|
130 self.requirementsEdit.clear() |
|
131 self.__requirementsAvailable = False |
|
132 |
|
133 if not bool(self.requirementsFilePicker.text()): |
|
134 self.requirementsFilePicker.setText("requirements.txt") |
|
135 |
|
136 fileName = FileSystemUtilities.toNativeSeparators( |
|
137 self.requirementsFilePicker.text() |
|
138 ) |
|
139 if fileName and not os.path.isabs(fileName): |
|
140 fileName = "" |
|
141 |
|
142 modules = circup.find_modules(self.__devicePath, circup.get_bundles_list()) |
|
143 specifiers = [] |
|
144 if modules: |
|
145 for module in modules: |
|
146 specifiers.append( |
|
147 "{0}=={1}".format(module.name, module.device_version) |
|
148 ) |
|
149 |
|
150 if specifiers: |
|
151 self.requirementsEdit.setPlainText("\n".join(specifiers) + "\n") |
|
152 self.__requirementsAvailable = True |
|
153 else: |
|
154 self.requirementsEdit.setPlainText( |
|
155 self.tr("No package specifiers generated.") |
|
156 ) |
|
157 |
|
158 self.__updateButtons() |
|
159 |
|
160 self.__requirementsEdited = False |
|
161 |
|
162 def __writeToFile(self, fileName): |
|
163 """ |
|
164 Private method to write the requirements text to a file. |
|
165 |
|
166 @param fileName name of the file to write to |
|
167 @type str |
|
168 """ |
|
169 if os.path.exists(fileName): |
|
170 ok = EricMessageBox.warning( |
|
171 self, |
|
172 self.__title, |
|
173 self.tr( |
|
174 """The file <b>{0}</b> already exists. Do you want""" |
|
175 """ to overwrite it?""" |
|
176 ).format(fileName), |
|
177 ) |
|
178 if not ok: |
|
179 return |
|
180 |
|
181 txt = self.requirementsEdit.toPlainText() |
|
182 try: |
|
183 with open(fileName, "w") as f: |
|
184 f.write(txt) |
|
185 except OSError as err: |
|
186 EricMessageBox.critical( |
|
187 self, |
|
188 self.__title, |
|
189 self.tr( |
|
190 """<p>The requirements could not be written""" |
|
191 """ to <b>{0}</b>.</p><p>Reason: {1}</p>""" |
|
192 ).format(fileName, str(err)), |
|
193 ) |
|
194 |
|
195 @pyqtSlot() |
|
196 def on_saveButton_clicked(self): |
|
197 """ |
|
198 Private slot to save the requirements text to the requirements file. |
|
199 """ |
|
200 fileName = self.requirementsFilePicker.text() |
|
201 self.__writeToFile(fileName) |
|
202 |
|
203 @pyqtSlot() |
|
204 def on_saveToButton_clicked(self): |
|
205 """ |
|
206 Private slot to write the requirements text to a new file. |
|
207 """ |
|
208 fileName, selectedFilter = EricFileDialog.getSaveFileNameAndFilter( |
|
209 self, |
|
210 self.__title, |
|
211 os.path.expanduser("~"), |
|
212 self.tr("Text Files (*.txt);;All Files (*)"), |
|
213 None, |
|
214 EricFileDialog.DontConfirmOverwrite, |
|
215 ) |
|
216 if fileName: |
|
217 ext = os.path.splitext(fileName)[1] |
|
218 if not ext: |
|
219 ex = selectedFilter.split("(*")[1].split(")")[0] |
|
220 if ex: |
|
221 fileName += ex |
|
222 self.__writeToFile(fileName) |
|
223 |
|
224 @pyqtSlot() |
|
225 def on_copyButton_clicked(self): |
|
226 """ |
|
227 Private slot to copy the requirements text to the clipboard. |
|
228 """ |
|
229 txt = self.requirementsEdit.toPlainText() |
|
230 cb = QGuiApplication.clipboard() |
|
231 cb.setText(txt) |
|
232 |
|
233 @pyqtSlot() |
|
234 def on_insertButton_clicked(self): |
|
235 """ |
|
236 Private slot to insert the requirements text at the cursor position |
|
237 of the current editor. |
|
238 """ |
|
239 aw = ericApp().getObject("ViewManager").activeWindow() |
|
240 if aw: |
|
241 aw.beginUndoAction() |
|
242 aw.insert(self.requirementsEdit.toPlainText()) |
|
243 aw.endUndoAction() |
|
244 |
|
245 @pyqtSlot() |
|
246 def on_replaceSelectionButton_clicked(self): |
|
247 """ |
|
248 Private slot to replace the selected text of the current editor |
|
249 with the requirements text. |
|
250 """ |
|
251 aw = ericApp().getObject("ViewManager").activeWindow() |
|
252 if aw: |
|
253 aw.beginUndoAction() |
|
254 aw.replaceSelectedText(self.requirementsEdit.toPlainText()) |
|
255 aw.endUndoAction() |
|
256 |
|
257 @pyqtSlot() |
|
258 def on_replaceAllButton_clicked(self): |
|
259 """ |
|
260 Private slot to replace the text of the current editor with the |
|
261 requirements text. |
|
262 """ |
|
263 aw = ericApp().getObject("ViewManager").activeWindow() |
|
264 if aw: |
|
265 aw.setText(self.requirementsEdit.toPlainText()) |