41 |
46 |
42 class CreateDialogCodeDialog(QDialog, Ui_CreateDialogCodeDialog): |
47 class CreateDialogCodeDialog(QDialog, Ui_CreateDialogCodeDialog): |
43 """ |
48 """ |
44 Class implementing a dialog to generate code for a Qt5 dialog. |
49 Class implementing a dialog to generate code for a Qt5 dialog. |
45 """ |
50 """ |
|
51 |
46 DialogClasses = { |
52 DialogClasses = { |
47 "QDialog", "QWidget", "QMainWindow", "QWizard", "QWizardPage", |
53 "QDialog", |
48 "QDockWidget", "QFrame", "QGroupBox", "QScrollArea", "QMdiArea", |
54 "QWidget", |
49 "QTabWidget", "QToolBox", "QStackedWidget" |
55 "QMainWindow", |
|
56 "QWizard", |
|
57 "QWizardPage", |
|
58 "QDockWidget", |
|
59 "QFrame", |
|
60 "QGroupBox", |
|
61 "QScrollArea", |
|
62 "QMdiArea", |
|
63 "QTabWidget", |
|
64 "QToolBox", |
|
65 "QStackedWidget", |
50 } |
66 } |
51 Separator = 25 * "=" |
67 Separator = 25 * "=" |
52 |
68 |
53 def __init__(self, formName, project, parent=None): |
69 def __init__(self, formName, project, parent=None): |
54 """ |
70 """ |
55 Constructor |
71 Constructor |
56 |
72 |
57 @param formName name of the file containing the form (string) |
73 @param formName name of the file containing the form (string) |
58 @param project reference to the project object |
74 @param project reference to the project object |
59 @param parent parent widget if the dialog (QWidget) |
75 @param parent parent widget if the dialog (QWidget) |
60 """ |
76 """ |
61 super().__init__(parent) |
77 super().__init__(parent) |
62 self.setupUi(self) |
78 self.setupUi(self) |
63 |
79 |
64 self.okButton = self.buttonBox.button( |
80 self.okButton = self.buttonBox.button(QDialogButtonBox.StandardButton.Ok) |
65 QDialogButtonBox.StandardButton.Ok) |
81 |
66 |
|
67 self.slotsView.header().hide() |
82 self.slotsView.header().hide() |
68 |
83 |
69 self.project = project |
84 self.project = project |
70 |
85 |
71 self.formFile = formName |
86 self.formFile = formName |
72 filename, ext = os.path.splitext(self.formFile) |
87 filename, ext = os.path.splitext(self.formFile) |
73 self.srcFile = '{0}{1}'.format( |
88 self.srcFile = "{0}{1}".format( |
74 filename, self.project.getDefaultSourceExtension()) |
89 filename, self.project.getDefaultSourceExtension() |
75 |
90 ) |
|
91 |
76 self.slotsModel = QStandardItemModel() |
92 self.slotsModel = QStandardItemModel() |
77 self.proxyModel = QSortFilterProxyModel() |
93 self.proxyModel = QSortFilterProxyModel() |
78 self.proxyModel.setDynamicSortFilter(True) |
94 self.proxyModel.setDynamicSortFilter(True) |
79 self.proxyModel.setSourceModel(self.slotsModel) |
95 self.proxyModel.setSourceModel(self.slotsModel) |
80 self.slotsView.setModel(self.proxyModel) |
96 self.slotsView.setModel(self.proxyModel) |
81 |
97 |
82 # initialize some member variables |
98 # initialize some member variables |
83 self.__initError = False |
99 self.__initError = False |
84 self.__module = None |
100 self.__module = None |
85 |
101 |
86 packagesRoot = self.project.getUicParameter("PackagesRoot") |
102 packagesRoot = self.project.getUicParameter("PackagesRoot") |
87 if packagesRoot: |
103 if packagesRoot: |
88 self.packagesPath = os.path.join(self.project.getProjectPath(), |
104 self.packagesPath = os.path.join( |
89 packagesRoot) |
105 self.project.getProjectPath(), packagesRoot |
|
106 ) |
90 else: |
107 else: |
91 self.packagesPath = self.project.getProjectPath() |
108 self.packagesPath = self.project.getProjectPath() |
92 |
109 |
93 if os.path.exists(self.srcFile): |
110 if os.path.exists(self.srcFile): |
94 vm = ericApp().getObject("ViewManager") |
111 vm = ericApp().getObject("ViewManager") |
95 ed = vm.getOpenEditor(self.srcFile) |
112 ed = vm.getOpenEditor(self.srcFile) |
96 if ed and not vm.checkDirty(ed): |
113 if ed and not vm.checkDirty(ed): |
97 self.__initError = True |
114 self.__initError = True |
98 return |
115 return |
99 |
116 |
100 with contextlib.suppress(ImportError): |
117 with contextlib.suppress(ImportError): |
101 splitExt = os.path.splitext(self.srcFile) |
118 splitExt = os.path.splitext(self.srcFile) |
102 exts = [splitExt[1]] if len(splitExt) == 2 else None |
119 exts = [splitExt[1]] if len(splitExt) == 2 else None |
103 from Utilities import ModuleParser |
120 from Utilities import ModuleParser |
|
121 |
104 self.__module = ModuleParser.readModule( |
122 self.__module = ModuleParser.readModule( |
105 self.srcFile, extensions=exts, caching=False) |
123 self.srcFile, extensions=exts, caching=False |
106 |
124 ) |
|
125 |
107 if self.__module is not None: |
126 if self.__module is not None: |
108 self.filenameEdit.setText(self.srcFile) |
127 self.filenameEdit.setText(self.srcFile) |
109 |
128 |
110 classesList = [] |
129 classesList = [] |
111 vagueClassesList = [] |
130 vagueClassesList = [] |
112 for cls in list(self.__module.classes.values()): |
131 for cls in list(self.__module.classes.values()): |
113 if not set(cls.super).isdisjoint( |
132 if not set(cls.super).isdisjoint(CreateDialogCodeDialog.DialogClasses): |
114 CreateDialogCodeDialog.DialogClasses): |
|
115 classesList.append(cls.name) |
133 classesList.append(cls.name) |
116 else: |
134 else: |
117 vagueClassesList.append(cls.name) |
135 vagueClassesList.append(cls.name) |
118 classesList.sort() |
136 classesList.sort() |
119 self.classNameCombo.addItems(classesList) |
137 self.classNameCombo.addItems(classesList) |
120 if vagueClassesList: |
138 if vagueClassesList: |
121 if classesList: |
139 if classesList: |
122 self.classNameCombo.addItem( |
140 self.classNameCombo.addItem(CreateDialogCodeDialog.Separator) |
123 CreateDialogCodeDialog.Separator) |
|
124 self.classNameCombo.addItems(sorted(vagueClassesList)) |
141 self.classNameCombo.addItems(sorted(vagueClassesList)) |
125 |
142 |
126 if ( |
143 if ( |
127 os.path.exists(self.srcFile) and |
144 os.path.exists(self.srcFile) |
128 self.__module is not None and |
145 and self.__module is not None |
129 self.classNameCombo.count() == 0 |
146 and self.classNameCombo.count() == 0 |
130 ): |
147 ): |
131 self.__initError = True |
148 self.__initError = True |
132 EricMessageBox.critical( |
149 EricMessageBox.critical( |
133 self, |
150 self, |
134 self.tr("Create Dialog Code"), |
151 self.tr("Create Dialog Code"), |
135 self.tr( |
152 self.tr( |
136 """The file <b>{0}</b> exists but does not contain""" |
153 """The file <b>{0}</b> exists but does not contain""" |
137 """ any classes.""").format(self.srcFile)) |
154 """ any classes.""" |
138 |
155 ).format(self.srcFile), |
|
156 ) |
|
157 |
139 self.okButton.setEnabled(self.classNameCombo.count() > 0) |
158 self.okButton.setEnabled(self.classNameCombo.count() > 0) |
140 |
159 |
141 self.__updateSlotsModel() |
160 self.__updateSlotsModel() |
142 |
161 |
143 def initError(self): |
162 def initError(self): |
144 """ |
163 """ |
145 Public method to determine, if there was an initialzation error. |
164 Public method to determine, if there was an initialzation error. |
146 |
165 |
147 @return flag indicating an initialzation error (boolean) |
166 @return flag indicating an initialzation error (boolean) |
148 """ |
167 """ |
149 return self.__initError |
168 return self.__initError |
150 |
169 |
151 def __runUicLoadUi(self, command): |
170 def __runUicLoadUi(self, command): |
152 """ |
171 """ |
153 Private method to run the UicLoadUi.py script with the given command |
172 Private method to run the UicLoadUi.py script with the given command |
154 and return the output. |
173 and return the output. |
155 |
174 |
156 @param command uic command to be run |
175 @param command uic command to be run |
157 @type str |
176 @type str |
158 @return tuple of process output and error flag |
177 @return tuple of process output and error flag |
159 @rtype tuple of (str, bool) |
178 @rtype tuple of (str, bool) |
160 """ |
179 """ |
161 venvManager = ericApp().getObject("VirtualEnvManager") |
180 venvManager = ericApp().getObject("VirtualEnvManager") |
162 projectType = self.project.getProjectType() |
181 projectType = self.project.getProjectType() |
163 |
182 |
164 venvName = self.project.getProjectVenv(resolveDebugger=False) |
183 venvName = self.project.getProjectVenv(resolveDebugger=False) |
165 if not venvName: |
184 if not venvName: |
166 # no project specific environment, try a type specific one |
185 # no project specific environment, try a type specific one |
167 if projectType in ("PyQt5", "PySide2"): |
186 if projectType in ("PyQt5", "PySide2"): |
168 venvName = Preferences.getQt("PyQtVenvName") |
187 venvName = Preferences.getQt("PyQtVenvName") |
169 elif projectType in ("PyQt6", "E7Plugin", "PySide6"): |
188 elif projectType in ("PyQt6", "E7Plugin", "PySide6"): |
170 venvName = Preferences.getQt("PyQt6VenvName") |
189 venvName = Preferences.getQt("PyQt6VenvName") |
171 interpreter = venvManager.getVirtualenvInterpreter(venvName) |
190 interpreter = venvManager.getVirtualenvInterpreter(venvName) |
172 execPath = venvManager.getVirtualenvExecPath(venvName) |
191 execPath = venvManager.getVirtualenvExecPath(venvName) |
173 |
192 |
174 if not interpreter: |
193 if not interpreter: |
175 interpreter = Globals.getPythonExecutable() |
194 interpreter = Globals.getPythonExecutable() |
176 |
195 |
177 env = QProcessEnvironment.systemEnvironment() |
196 env = QProcessEnvironment.systemEnvironment() |
178 if execPath: |
197 if execPath: |
179 if env.contains("PATH"): |
198 if env.contains("PATH"): |
180 env.insert( |
199 env.insert("PATH", os.pathsep.join([execPath, env.value("PATH")])) |
181 "PATH", os.pathsep.join([execPath, env.value("PATH")]) |
|
182 ) |
|
183 else: |
200 else: |
184 env.insert("PATH", execPath) |
201 env.insert("PATH", execPath) |
185 |
202 |
186 if projectType in ("PyQt5", "PySide2"): |
203 if projectType in ("PyQt5", "PySide2"): |
187 loadUi = os.path.join(os.path.dirname(__file__), "UicLoadUi5.py") |
204 loadUi = os.path.join(os.path.dirname(__file__), "UicLoadUi5.py") |
188 elif projectType in ("PyQt6", "E7Plugin", "PySide6"): |
205 elif projectType in ("PyQt6", "E7Plugin", "PySide6"): |
189 loadUi = os.path.join(os.path.dirname(__file__), "UicLoadUi6.py") |
206 loadUi = os.path.join(os.path.dirname(__file__), "UicLoadUi6.py") |
190 args = [ |
207 args = [ |
191 loadUi, |
208 loadUi, |
192 command, |
209 command, |
193 self.formFile, |
210 self.formFile, |
194 self.packagesPath, |
211 self.packagesPath, |
195 ] |
212 ] |
196 |
213 |
197 uicText = "" |
214 uicText = "" |
198 ok = False |
215 ok = False |
199 |
216 |
200 proc = QProcess() |
217 proc = QProcess() |
201 proc.setWorkingDirectory(self.packagesPath) |
218 proc.setWorkingDirectory(self.packagesPath) |
202 proc.setProcessEnvironment(env) |
219 proc.setProcessEnvironment(env) |
203 proc.start(interpreter, args) |
220 proc.start(interpreter, args) |
204 started = proc.waitForStarted(5000) |
221 started = proc.waitForStarted(5000) |
213 EricMessageBox.critical( |
230 EricMessageBox.critical( |
214 self, |
231 self, |
215 self.tr("uic error"), |
232 self.tr("uic error"), |
216 self.tr( |
233 self.tr( |
217 """<p>There was an error loading the form <b>{0}</b>""" |
234 """<p>There was an error loading the form <b>{0}</b>""" |
218 """.</p><p>{1}</p>""").format( |
235 """.</p><p>{1}</p>""" |
219 self.formFile, outText) |
236 ).format(self.formFile, outText), |
220 ) |
237 ) |
221 else: |
238 else: |
222 EricMessageBox.critical( |
239 EricMessageBox.critical( |
223 self, |
240 self, |
224 self.tr("uic error"), |
241 self.tr("uic error"), |
225 self.tr( |
242 self.tr( |
226 """<p>The project specific Python interpreter <b>{0}</b>""" |
243 """<p>The project specific Python interpreter <b>{0}</b>""" |
227 """ could not be started or did not finish within 30""" |
244 """ could not be started or did not finish within 30""" |
228 """ seconds.</p>""").format(interpreter) |
245 """ seconds.</p>""" |
|
246 ).format(interpreter), |
229 ) |
247 ) |
230 |
248 |
231 return uicText, ok |
249 return uicText, ok |
232 |
250 |
233 def __objectName(self): |
251 def __objectName(self): |
234 """ |
252 """ |
235 Private method to get the object name of a form. |
253 Private method to get the object name of a form. |
236 |
254 |
237 @return object name |
255 @return object name |
238 @rtype str |
256 @rtype str |
239 """ |
257 """ |
240 objectName = "" |
258 objectName = "" |
241 |
259 |
242 output, ok = self.__runUicLoadUi("object_name") |
260 output, ok = self.__runUicLoadUi("object_name") |
243 if ok and output: |
261 if ok and output: |
244 objectName = output |
262 objectName = output |
245 |
263 |
246 return objectName |
264 return objectName |
247 |
265 |
248 def __className(self): |
266 def __className(self): |
249 """ |
267 """ |
250 Private method to get the class name of a form. |
268 Private method to get the class name of a form. |
251 |
269 |
252 @return class name |
270 @return class name |
253 @rtype str |
271 @rtype str |
254 """ |
272 """ |
255 className = "" |
273 className = "" |
256 |
274 |
257 output, ok = self.__runUicLoadUi("class_name") |
275 output, ok = self.__runUicLoadUi("class_name") |
258 if ok and output: |
276 if ok and output: |
259 className = output |
277 className = output |
260 |
278 |
261 return className |
279 return className |
262 |
280 |
263 def __signatures(self): |
281 def __signatures(self): |
264 """ |
282 """ |
265 Private slot to get the signatures. |
283 Private slot to get the signatures. |
266 |
284 |
267 @return list of signatures (list of strings) |
285 @return list of signatures (list of strings) |
268 """ |
286 """ |
269 if self.__module is None: |
287 if self.__module is None: |
270 return [] |
288 return [] |
271 |
289 |
272 signatures = [] |
290 signatures = [] |
273 clsName = self.classNameCombo.currentText() |
291 clsName = self.classNameCombo.currentText() |
274 if clsName: |
292 if clsName: |
275 cls = self.__module.classes[clsName] |
293 cls = self.__module.classes[clsName] |
276 for meth in list(cls.methods.values()): |
294 for meth in list(cls.methods.values()): |
277 if meth.name.startswith("on_"): |
295 if meth.name.startswith("on_"): |
278 if meth.pyqtSignature is not None: |
296 if meth.pyqtSignature is not None: |
279 sig = ", ".join( |
297 sig = ", ".join( |
280 [bytes(QMetaObject.normalizedType(t)).decode() |
298 [ |
281 for t in meth.pyqtSignature.split(",")]) |
299 bytes(QMetaObject.normalizedType(t)).decode() |
|
300 for t in meth.pyqtSignature.split(",") |
|
301 ] |
|
302 ) |
282 signatures.append("{0}({1})".format(meth.name, sig)) |
303 signatures.append("{0}({1})".format(meth.name, sig)) |
283 else: |
304 else: |
284 signatures.append(meth.name) |
305 signatures.append(meth.name) |
285 return signatures |
306 return signatures |
286 |
307 |
287 def __mapType(self, type_): |
308 def __mapType(self, type_): |
288 """ |
309 """ |
289 Private method to map a type as reported by Qt's meta object to the |
310 Private method to map a type as reported by Qt's meta object to the |
290 correct Python type. |
311 correct Python type. |
291 |
312 |
292 @param type_ type as reported by Qt (QByteArray) |
313 @param type_ type as reported by Qt (QByteArray) |
293 @return mapped Python type (string) |
314 @return mapped Python type (string) |
294 """ |
315 """ |
295 mapped = bytes(type_).decode() |
316 mapped = bytes(type_).decode() |
296 |
317 |
297 # I. always check for * |
318 # I. always check for * |
298 mapped = mapped.replace("*", "") |
319 mapped = mapped.replace("*", "") |
299 |
320 |
300 # 1. check for const |
321 # 1. check for const |
301 mapped = mapped.replace("const ", "") |
322 mapped = mapped.replace("const ", "") |
302 |
323 |
303 # 2. replace QString and QStringList |
324 # 2. replace QString and QStringList |
304 mapped = ( |
325 mapped = mapped.replace("QStringList", "list").replace("QString", "str") |
305 mapped |
326 |
306 .replace("QStringList", "list") |
|
307 .replace("QString", "str") |
|
308 ) |
|
309 |
|
310 # 3. replace double by float |
327 # 3. replace double by float |
311 mapped = mapped.replace("double", "float") |
328 mapped = mapped.replace("double", "float") |
312 |
329 |
313 return mapped |
330 return mapped |
314 |
331 |
315 def __updateSlotsModel(self): |
332 def __updateSlotsModel(self): |
316 """ |
333 """ |
317 Private slot to update the slots tree display. |
334 Private slot to update the slots tree display. |
318 """ |
335 """ |
319 self.filterEdit.clear() |
336 self.filterEdit.clear() |
320 |
337 |
321 output, ok = self.__runUicLoadUi("signatures") |
338 output, ok = self.__runUicLoadUi("signatures") |
322 if ok and output: |
339 if ok and output: |
323 objectsList = json.loads(output.strip()) |
340 objectsList = json.loads(output.strip()) |
324 |
341 |
325 signatureList = self.__signatures() |
342 signatureList = self.__signatures() |
326 |
343 |
327 self.slotsModel.clear() |
344 self.slotsModel.clear() |
328 self.slotsModel.setHorizontalHeaderLabels([""]) |
345 self.slotsModel.setHorizontalHeaderLabels([""]) |
329 for objectDict in objectsList: |
346 for objectDict in objectsList: |
330 itm = QStandardItem("{0} ({1})".format( |
347 itm = QStandardItem( |
331 objectDict["name"], |
348 "{0} ({1})".format(objectDict["name"], objectDict["class_name"]) |
332 objectDict["class_name"])) |
349 ) |
333 self.slotsModel.appendRow(itm) |
350 self.slotsModel.appendRow(itm) |
334 for methodDict in objectDict["methods"]: |
351 for methodDict in objectDict["methods"]: |
335 itm2 = QStandardItem(methodDict["signature"]) |
352 itm2 = QStandardItem(methodDict["signature"]) |
336 itm.appendRow(itm2) |
353 itm.appendRow(itm2) |
337 |
354 |
338 if ( |
355 if self.__module is not None and ( |
339 self.__module is not None and |
356 methodDict["methods"][0] in signatureList |
340 (methodDict["methods"][0] in signatureList or |
357 or methodDict["methods"][1] in signatureList |
341 methodDict["methods"][1] in signatureList) |
|
342 ): |
358 ): |
343 itm2.setFlags(Qt.ItemFlag.ItemIsEnabled) |
359 itm2.setFlags(Qt.ItemFlag.ItemIsEnabled) |
344 itm2.setCheckState(Qt.CheckState.Checked) |
360 itm2.setCheckState(Qt.CheckState.Checked) |
345 if ericApp().usesDarkPalette(): |
361 if ericApp().usesDarkPalette(): |
346 itm2.setForeground(QBrush(QColor("#75bfff"))) |
362 itm2.setForeground(QBrush(QColor("#75bfff"))) |
347 else: |
363 else: |
348 itm2.setForeground(QBrush(Qt.GlobalColor.blue)) |
364 itm2.setForeground(QBrush(Qt.GlobalColor.blue)) |
349 continue |
365 continue |
350 |
366 |
351 itm2.setData(methodDict["pyqt_signature"], |
367 itm2.setData(methodDict["pyqt_signature"], pyqtSignatureRole) |
352 pyqtSignatureRole) |
368 itm2.setData(methodDict["python_signature"], pythonSignatureRole) |
353 itm2.setData(methodDict["python_signature"], |
369 itm2.setData(methodDict["return_type"], returnTypeRole) |
354 pythonSignatureRole) |
370 itm2.setData(methodDict["parameter_types"], parameterTypesListRole) |
355 itm2.setData(methodDict["return_type"], |
371 itm2.setData(methodDict["parameter_names"], parameterNamesListRole) |
356 returnTypeRole) |
372 |
357 itm2.setData(methodDict["parameter_types"], |
|
358 parameterTypesListRole) |
|
359 itm2.setData(methodDict["parameter_names"], |
|
360 parameterNamesListRole) |
|
361 |
|
362 itm2.setFlags( |
373 itm2.setFlags( |
363 Qt.ItemFlag.ItemIsUserCheckable | |
374 Qt.ItemFlag.ItemIsUserCheckable |
364 Qt.ItemFlag.ItemIsEnabled | |
375 | Qt.ItemFlag.ItemIsEnabled |
365 Qt.ItemFlag.ItemIsSelectable |
376 | Qt.ItemFlag.ItemIsSelectable |
366 ) |
377 ) |
367 itm2.setCheckState(Qt.CheckState.Unchecked) |
378 itm2.setCheckState(Qt.CheckState.Unchecked) |
368 |
379 |
369 self.slotsView.sortByColumn(0, Qt.SortOrder.AscendingOrder) |
380 self.slotsView.sortByColumn(0, Qt.SortOrder.AscendingOrder) |
370 |
381 |
371 def __generateCode(self): |
382 def __generateCode(self): |
372 """ |
383 """ |
373 Private slot to generate the code as requested by the user. |
384 Private slot to generate the code as requested by the user. |
374 """ |
385 """ |
375 if ( |
386 if ( |
376 self.filenameEdit.text().endswith(".rb") or |
387 self.filenameEdit.text().endswith(".rb") |
377 self.project.getProjectLanguage() == "Ruby" |
388 or self.project.getProjectLanguage() == "Ruby" |
378 ): |
389 ): |
379 # Ruby code generation is not supported |
390 # Ruby code generation is not supported |
380 pass |
391 pass |
381 else: |
392 else: |
382 # assume Python (our global default) |
393 # assume Python (our global default) |
383 self.__generatePythonCode() |
394 self.__generatePythonCode() |
384 |
395 |
385 def __generatePythonCode(self): |
396 def __generatePythonCode(self): |
386 """ |
397 """ |
387 Private slot to generate Python code as requested by the user. |
398 Private slot to generate Python code as requested by the user. |
388 """ |
399 """ |
389 if self.project.getProjectLanguage() != "Python3": |
400 if self.project.getProjectLanguage() != "Python3": |
390 EricMessageBox.critical( |
401 EricMessageBox.critical( |
391 self, |
402 self, |
392 self.tr("Code Generation"), |
403 self.tr("Code Generation"), |
393 self.tr( |
404 self.tr( |
394 """<p>Code generation for project language""" |
405 """<p>Code generation for project language""" |
395 """ "{0}" is not supported.</p>""") |
406 """ "{0}" is not supported.</p>""" |
396 .format(self.project.getProjectLanguage())) |
407 ).format(self.project.getProjectLanguage()), |
|
408 ) |
397 return |
409 return |
398 |
410 |
399 # init some variables |
411 # init some variables |
400 sourceImpl = [] |
412 sourceImpl = [] |
401 appendAtIndex = -1 |
413 appendAtIndex = -1 |
402 indentStr = " " |
414 indentStr = " " |
403 slotsCode = [] |
415 slotsCode = [] |
404 |
416 |
405 if self.__module is None: |
417 if self.__module is None: |
406 # new file |
418 # new file |
407 try: |
419 try: |
408 if self.project.getProjectType() == "PySide2": |
420 if self.project.getProjectType() == "PySide2": |
409 tmplName = os.path.join( |
421 tmplName = os.path.join( |
410 getConfig('ericCodeTemplatesDir'), |
422 getConfig("ericCodeTemplatesDir"), "impl_pyside2.py.tmpl" |
411 "impl_pyside2.py.tmpl") |
423 ) |
412 elif self.project.getProjectType() == "PySide6": |
424 elif self.project.getProjectType() == "PySide6": |
413 tmplName = os.path.join( |
425 tmplName = os.path.join( |
414 getConfig('ericCodeTemplatesDir'), |
426 getConfig("ericCodeTemplatesDir"), "impl_pyside6.py.tmpl" |
415 "impl_pyside6.py.tmpl") |
427 ) |
416 elif self.project.getProjectType() == "PyQt5": |
428 elif self.project.getProjectType() == "PyQt5": |
417 tmplName = os.path.join( |
429 tmplName = os.path.join( |
418 getConfig('ericCodeTemplatesDir'), |
430 getConfig("ericCodeTemplatesDir"), "impl_pyqt5.py.tmpl" |
419 "impl_pyqt5.py.tmpl") |
431 ) |
420 elif self.project.getProjectType() in [ |
432 elif self.project.getProjectType() in ["PyQt6", "E7Plugin"]: |
421 "PyQt6", "E7Plugin" |
|
422 ]: |
|
423 tmplName = os.path.join( |
433 tmplName = os.path.join( |
424 getConfig('ericCodeTemplatesDir'), |
434 getConfig("ericCodeTemplatesDir"), "impl_pyqt6.py.tmpl" |
425 "impl_pyqt6.py.tmpl") |
435 ) |
426 else: |
436 else: |
427 EricMessageBox.critical( |
437 EricMessageBox.critical( |
428 self, |
438 self, |
429 self.tr("Code Generation"), |
439 self.tr("Code Generation"), |
430 self.tr( |
440 self.tr( |
431 """<p>No code template file available for""" |
441 """<p>No code template file available for""" |
432 """ project type "{0}".</p>""") |
442 """ project type "{0}".</p>""" |
433 .format(self.project.getProjectType())) |
443 ).format(self.project.getProjectType()), |
|
444 ) |
434 return |
445 return |
435 with open(tmplName, 'r', encoding="utf-8") as tmplFile: |
446 with open(tmplName, "r", encoding="utf-8") as tmplFile: |
436 template = tmplFile.read() |
447 template = tmplFile.read() |
437 except OSError as why: |
448 except OSError as why: |
438 EricMessageBox.critical( |
449 EricMessageBox.critical( |
439 self, |
450 self, |
440 self.tr("Code Generation"), |
451 self.tr("Code Generation"), |
441 self.tr( |
452 self.tr( |
442 """<p>Could not open the code template file""" |
453 """<p>Could not open the code template file""" |
443 """ "{0}".</p><p>Reason: {1}</p>""") |
454 """ "{0}".</p><p>Reason: {1}</p>""" |
444 .format(tmplName, str(why))) |
455 ).format(tmplName, str(why)), |
|
456 ) |
445 return |
457 return |
446 |
458 |
447 objName = self.__objectName() |
459 objName = self.__objectName() |
448 if objName: |
460 if objName: |
449 template = ( |
461 template = ( |
450 template |
462 template.replace( |
451 .replace( |
|
452 "$FORMFILE$", |
463 "$FORMFILE$", |
453 os.path.splitext(os.path.basename(self.formFile))[0]) |
464 os.path.splitext(os.path.basename(self.formFile))[0], |
|
465 ) |
454 .replace("$FORMCLASS$", objName) |
466 .replace("$FORMCLASS$", objName) |
455 .replace("$CLASSNAME$", self.classNameCombo.currentText()) |
467 .replace("$CLASSNAME$", self.classNameCombo.currentText()) |
456 .replace("$SUPERCLASS$", self.__className()) |
468 .replace("$SUPERCLASS$", self.__className()) |
457 ) |
469 ) |
458 |
470 |
459 sourceImpl = template.splitlines(True) |
471 sourceImpl = template.splitlines(True) |
460 appendAtIndex = -1 |
472 appendAtIndex = -1 |
461 |
473 |
462 # determine indent string |
474 # determine indent string |
463 for line in sourceImpl: |
475 for line in sourceImpl: |
464 if line.lstrip().startswith("def __init__"): |
476 if line.lstrip().startswith("def __init__"): |
465 indentStr = line.replace(line.lstrip(), "") |
477 indentStr = line.replace(line.lstrip(), "") |
466 break |
478 break |
467 else: |
479 else: |
468 # extend existing file |
480 # extend existing file |
469 try: |
481 try: |
470 with open(self.srcFile, 'r', encoding="utf-8") as srcFile: |
482 with open(self.srcFile, "r", encoding="utf-8") as srcFile: |
471 sourceImpl = srcFile.readlines() |
483 sourceImpl = srcFile.readlines() |
472 if not sourceImpl[-1].endswith("\n"): |
484 if not sourceImpl[-1].endswith("\n"): |
473 sourceImpl[-1] = "{0}{1}".format(sourceImpl[-1], "\n") |
485 sourceImpl[-1] = "{0}{1}".format(sourceImpl[-1], "\n") |
474 except OSError as why: |
486 except OSError as why: |
475 EricMessageBox.critical( |
487 EricMessageBox.critical( |
476 self, |
488 self, |
477 self.tr("Code Generation"), |
489 self.tr("Code Generation"), |
478 self.tr( |
490 self.tr( |
479 """<p>Could not open the source file "{0}".</p>""" |
491 """<p>Could not open the source file "{0}".</p>""" |
480 """<p>Reason: {1}</p>""") |
492 """<p>Reason: {1}</p>""" |
481 .format(self.srcFile, str(why))) |
493 ).format(self.srcFile, str(why)), |
|
494 ) |
482 return |
495 return |
483 |
496 |
484 cls = self.__module.classes[self.classNameCombo.currentText()] |
497 cls = self.__module.classes[self.classNameCombo.currentText()] |
485 if cls.endlineno == len(sourceImpl) or cls.endlineno == -1: |
498 if cls.endlineno == len(sourceImpl) or cls.endlineno == -1: |
486 appendAtIndex = -1 |
499 appendAtIndex = -1 |
487 # delete empty lines at end |
500 # delete empty lines at end |
488 while not sourceImpl[-1].strip(): |
501 while not sourceImpl[-1].strip(): |
490 else: |
503 else: |
491 appendAtIndex = cls.endlineno - 1 |
504 appendAtIndex = cls.endlineno - 1 |
492 while not sourceImpl[appendAtIndex].strip(): |
505 while not sourceImpl[appendAtIndex].strip(): |
493 appendAtIndex -= 1 |
506 appendAtIndex -= 1 |
494 appendAtIndex += 1 |
507 appendAtIndex += 1 |
495 |
508 |
496 # determine indent string |
509 # determine indent string |
497 for line in sourceImpl[cls.lineno:cls.endlineno + 1]: |
510 for line in sourceImpl[cls.lineno : cls.endlineno + 1]: |
498 if line.lstrip().startswith("def __init__"): |
511 if line.lstrip().startswith("def __init__"): |
499 indentStr = line.replace(line.lstrip(), "") |
512 indentStr = line.replace(line.lstrip(), "") |
500 break |
513 break |
501 |
514 |
502 # do the coding stuff |
515 # do the coding stuff |
503 pyqtSignatureFormat = ( |
516 pyqtSignatureFormat = ( |
504 '@Slot({0})' |
517 "@Slot({0})" |
505 if self.project.getProjectType() in ("PySide2", "PySide6") else |
518 if self.project.getProjectType() in ("PySide2", "PySide6") |
506 '@pyqtSlot({0})' |
519 else "@pyqtSlot({0})" |
507 ) |
520 ) |
508 for row in range(self.slotsModel.rowCount()): |
521 for row in range(self.slotsModel.rowCount()): |
509 topItem = self.slotsModel.item(row) |
522 topItem = self.slotsModel.item(row) |
510 for childRow in range(topItem.rowCount()): |
523 for childRow in range(topItem.rowCount()): |
511 child = topItem.child(childRow) |
524 child = topItem.child(childRow) |
512 if ( |
525 if child.checkState() == Qt.CheckState.Checked and ( |
513 child.checkState() == Qt.CheckState.Checked and |
526 child.flags() & Qt.ItemFlag.ItemIsUserCheckable |
514 (child.flags() & Qt.ItemFlag.ItemIsUserCheckable == |
527 == Qt.ItemFlag.ItemIsUserCheckable |
515 Qt.ItemFlag.ItemIsUserCheckable) |
|
516 ): |
528 ): |
517 slotsCode.append('{0}\n'.format(indentStr)) |
529 slotsCode.append("{0}\n".format(indentStr)) |
518 slotsCode.append('{0}{1}\n'.format( |
530 slotsCode.append( |
519 indentStr, |
531 "{0}{1}\n".format( |
520 pyqtSignatureFormat.format( |
532 indentStr, |
521 child.data(pyqtSignatureRole)))) |
533 pyqtSignatureFormat.format(child.data(pyqtSignatureRole)), |
522 slotsCode.append('{0}def {1}:\n'.format( |
534 ) |
523 indentStr, child.data(pythonSignatureRole))) |
535 ) |
|
536 slotsCode.append( |
|
537 "{0}def {1}:\n".format( |
|
538 indentStr, child.data(pythonSignatureRole) |
|
539 ) |
|
540 ) |
524 indentStr2 = indentStr * 2 |
541 indentStr2 = indentStr * 2 |
525 slotsCode.append('{0}"""\n'.format(indentStr2)) |
542 slotsCode.append('{0}"""\n'.format(indentStr2)) |
526 slotsCode.append( |
543 slotsCode.append( |
527 '{0}Slot documentation goes here.\n'.format( |
544 "{0}Slot documentation goes here.\n".format(indentStr2) |
528 indentStr2)) |
545 ) |
529 if ( |
546 if child.data(returnTypeRole) or child.data(parameterTypesListRole): |
530 child.data(returnTypeRole) or |
547 slotsCode.append("{0}\n".format(indentStr2)) |
531 child.data(parameterTypesListRole) |
|
532 ): |
|
533 slotsCode.append('{0}\n'.format(indentStr2)) |
|
534 if child.data(parameterTypesListRole): |
548 if child.data(parameterTypesListRole): |
535 for name, type_ in zip( |
549 for name, type_ in zip( |
536 child.data(parameterNamesListRole), |
550 child.data(parameterNamesListRole), |
537 child.data(parameterTypesListRole)): |
551 child.data(parameterTypesListRole), |
|
552 ): |
538 slotsCode.append( |
553 slotsCode.append( |
539 '{0}@param {1} DESCRIPTION\n'.format( |
554 "{0}@param {1} DESCRIPTION\n".format( |
540 indentStr2, name)) |
555 indentStr2, name |
541 slotsCode.append('{0}@type {1}\n'.format( |
556 ) |
542 indentStr2, type_)) |
557 ) |
|
558 slotsCode.append( |
|
559 "{0}@type {1}\n".format(indentStr2, type_) |
|
560 ) |
543 if child.data(returnTypeRole): |
561 if child.data(returnTypeRole): |
544 slotsCode.append( |
562 slotsCode.append( |
545 '{0}@returns DESCRIPTION\n'.format( |
563 "{0}@returns DESCRIPTION\n".format(indentStr2) |
546 indentStr2)) |
564 ) |
547 slotsCode.append('{0}@rtype {1}\n'.format( |
565 slotsCode.append( |
548 indentStr2, child.data(returnTypeRole))) |
566 "{0}@rtype {1}\n".format( |
|
567 indentStr2, child.data(returnTypeRole) |
|
568 ) |
|
569 ) |
549 slotsCode.append('{0}"""\n'.format(indentStr2)) |
570 slotsCode.append('{0}"""\n'.format(indentStr2)) |
550 slotsCode.append('{0}# {1}: not implemented yet\n'.format( |
571 slotsCode.append( |
551 indentStr2, "TODO")) |
572 "{0}# {1}: not implemented yet\n".format(indentStr2, "TODO") |
552 slotsCode.append('{0}raise NotImplementedError\n'.format( |
573 ) |
553 indentStr2)) |
574 slotsCode.append( |
554 |
575 "{0}raise NotImplementedError\n".format(indentStr2) |
|
576 ) |
|
577 |
555 if appendAtIndex == -1: |
578 if appendAtIndex == -1: |
556 sourceImpl.extend(slotsCode) |
579 sourceImpl.extend(slotsCode) |
557 else: |
580 else: |
558 sourceImpl[appendAtIndex:appendAtIndex] = slotsCode |
581 sourceImpl[appendAtIndex:appendAtIndex] = slotsCode |
559 |
582 |
560 # write the new code |
583 # write the new code |
561 newline = (None if self.project.useSystemEol() |
584 newline = None if self.project.useSystemEol() else self.project.getEolString() |
562 else self.project.getEolString()) |
|
563 fn = self.filenameEdit.text() |
585 fn = self.filenameEdit.text() |
564 try: |
586 try: |
565 with open(fn, 'w', encoding="utf-8", newline=newline) as srcFile: |
587 with open(fn, "w", encoding="utf-8", newline=newline) as srcFile: |
566 srcFile.write("".join(sourceImpl)) |
588 srcFile.write("".join(sourceImpl)) |
567 except OSError as why: |
589 except OSError as why: |
568 EricMessageBox.critical( |
590 EricMessageBox.critical( |
569 self, |
591 self, |
570 self.tr("Code Generation"), |
592 self.tr("Code Generation"), |
571 self.tr("""<p>Could not write the source file "{0}".</p>""" |
593 self.tr( |
572 """<p>Reason: {1}</p>""") |
594 """<p>Could not write the source file "{0}".</p>""" |
573 .format(fn, str(why))) |
595 """<p>Reason: {1}</p>""" |
|
596 ).format(fn, str(why)), |
|
597 ) |
574 return |
598 return |
575 |
599 |
576 self.project.appendFile(fn) |
600 self.project.appendFile(fn) |
577 |
601 |
578 @pyqtSlot(int) |
602 @pyqtSlot(int) |
579 def on_classNameCombo_activated(self, index): |
603 def on_classNameCombo_activated(self, index): |
580 """ |
604 """ |
581 Private slot to handle the activated signal of the classname combo. |
605 Private slot to handle the activated signal of the classname combo. |
582 |
606 |
583 @param index index of the activated item (integer) |
607 @param index index of the activated item (integer) |
584 """ |
608 """ |
585 if (self.classNameCombo.currentText() == |
609 if self.classNameCombo.currentText() == CreateDialogCodeDialog.Separator: |
586 CreateDialogCodeDialog.Separator): |
|
587 self.okButton.setEnabled(False) |
610 self.okButton.setEnabled(False) |
588 self.filterEdit.clear() |
611 self.filterEdit.clear() |
589 self.slotsModel.clear() |
612 self.slotsModel.clear() |
590 self.slotsModel.setHorizontalHeaderLabels([""]) |
613 self.slotsModel.setHorizontalHeaderLabels([""]) |
591 else: |
614 else: |
592 self.okButton.setEnabled(True) |
615 self.okButton.setEnabled(True) |
593 self.__updateSlotsModel() |
616 self.__updateSlotsModel() |
594 |
617 |
595 def on_filterEdit_textChanged(self, text): |
618 def on_filterEdit_textChanged(self, text): |
596 """ |
619 """ |
597 Private slot called, when thext of the filter edit has changed. |
620 Private slot called, when thext of the filter edit has changed. |
598 |
621 |
599 @param text changed text (string) |
622 @param text changed text (string) |
600 """ |
623 """ |
601 rx = QRegularExpression( |
624 rx = QRegularExpression( |
602 text, |
625 text, QRegularExpression.PatternOption.CaseInsensitiveOption |
603 QRegularExpression.PatternOption.CaseInsensitiveOption) |
626 ) |
604 self.proxyModel.setFilterRegularExpression(rx) |
627 self.proxyModel.setFilterRegularExpression(rx) |
605 |
628 |
606 @pyqtSlot() |
629 @pyqtSlot() |
607 def on_newButton_clicked(self): |
630 def on_newButton_clicked(self): |
608 """ |
631 """ |
609 Private slot called to enter the data for a new dialog class. |
632 Private slot called to enter the data for a new dialog class. |
610 """ |
633 """ |