7 Module implementing a dialog to generate code for a Qt4/Qt5 dialog. |
7 Module implementing a dialog to generate code for a Qt4/Qt5 dialog. |
8 """ |
8 """ |
9 |
9 |
10 import os |
10 import os |
11 |
11 |
12 from PyQt4.QtCore import QMetaObject, QByteArray, QRegExp, Qt, pyqtSlot, QMetaMethod, \ |
12 from PyQt4.QtCore import QMetaObject, QByteArray, QRegExp, Qt, pyqtSlot, \ |
13 qVersion |
13 QMetaMethod, qVersion |
14 from PyQt4.QtGui import QWidget, QSortFilterProxyModel, QStandardItemModel, QDialog, \ |
14 from PyQt4.QtGui import QWidget, QSortFilterProxyModel, QStandardItemModel, \ |
15 QBrush, QStandardItem, QDialogButtonBox, QAction |
15 QDialog, QBrush, QStandardItem, QDialogButtonBox, QAction |
16 from PyQt4 import uic |
16 from PyQt4 import uic |
17 |
17 |
18 from E5Gui.E5Application import e5App |
18 from E5Gui.E5Application import e5App |
19 from E5Gui import E5MessageBox |
19 from E5Gui import E5MessageBox |
20 |
20 |
29 |
29 |
30 class CreateDialogCodeDialog(QDialog, Ui_CreateDialogCodeDialog): |
30 class CreateDialogCodeDialog(QDialog, Ui_CreateDialogCodeDialog): |
31 """ |
31 """ |
32 Class implementing a dialog to generate code for a Qt4/Qt5 dialog. |
32 Class implementing a dialog to generate code for a Qt4/Qt5 dialog. |
33 """ |
33 """ |
34 DialogClasses = {"QDialog", "QWidget", "QMainWindow", "QWizard", "QWizardPage", |
34 DialogClasses = { |
|
35 "QDialog", "QWidget", "QMainWindow", "QWizard", "QWizardPage", |
35 "QDockWidget", "QFrame", "QGroupBox", "QScrollArea", "QMdiArea", |
36 "QDockWidget", "QFrame", "QGroupBox", "QScrollArea", "QMdiArea", |
36 "QTabWidget", "QToolBox", "QStackedWidget"} |
37 "QTabWidget", "QToolBox", "QStackedWidget" |
|
38 } |
37 Separator = 25 * "=" |
39 Separator = 25 * "=" |
38 |
40 |
39 def __init__(self, formName, project, parent=None): |
41 def __init__(self, formName, project, parent=None): |
40 """ |
42 """ |
41 Constructor |
43 Constructor |
53 |
55 |
54 self.project = project |
56 self.project = project |
55 |
57 |
56 self.formFile = formName |
58 self.formFile = formName |
57 filename, ext = os.path.splitext(self.formFile) |
59 filename, ext = os.path.splitext(self.formFile) |
58 self.srcFile = '{0}{1}'.format(filename, self.project.getDefaultSourceExtension()) |
60 self.srcFile = '{0}{1}'.format( |
|
61 filename, self.project.getDefaultSourceExtension()) |
59 |
62 |
60 self.slotsModel = QStandardItemModel() |
63 self.slotsModel = QStandardItemModel() |
61 self.proxyModel = QSortFilterProxyModel() |
64 self.proxyModel = QSortFilterProxyModel() |
62 self.proxyModel.setDynamicSortFilter(True) |
65 self.proxyModel.setDynamicSortFilter(True) |
63 self.proxyModel.setSourceModel(self.slotsModel) |
66 self.proxyModel.setSourceModel(self.slotsModel) |
79 if len(splitExt) == 2: |
82 if len(splitExt) == 2: |
80 exts = [splitExt[1]] |
83 exts = [splitExt[1]] |
81 else: |
84 else: |
82 exts = None |
85 exts = None |
83 from Utilities import ModuleParser |
86 from Utilities import ModuleParser |
84 self.__module = ModuleParser.readModule(self.srcFile, extensions=exts, |
87 self.__module = ModuleParser.readModule( |
85 caching=False) |
88 self.srcFile, extensions=exts, caching=False) |
86 except ImportError: |
89 except ImportError: |
87 pass |
90 pass |
88 |
91 |
89 if self.__module is not None: |
92 if self.__module is not None: |
90 self.filenameEdit.setText(self.srcFile) |
93 self.filenameEdit.setText(self.srcFile) |
91 |
94 |
92 classesList = [] |
95 classesList = [] |
93 vagueClassesList = [] |
96 vagueClassesList = [] |
94 for cls in list(self.__module.classes.values()): |
97 for cls in list(self.__module.classes.values()): |
95 if not set(cls.super).isdisjoint(CreateDialogCodeDialog.DialogClasses): |
98 if not set(cls.super).isdisjoint( |
|
99 CreateDialogCodeDialog.DialogClasses): |
96 classesList.append(cls.name) |
100 classesList.append(cls.name) |
97 else: |
101 else: |
98 vagueClassesList.append(cls.name) |
102 vagueClassesList.append(cls.name) |
99 classesList.sort() |
103 classesList.sort() |
100 self.classNameCombo.addItems(classesList) |
104 self.classNameCombo.addItems(classesList) |
101 if vagueClassesList: |
105 if vagueClassesList: |
102 if classesList: |
106 if classesList: |
103 self.classNameCombo.addItem(CreateDialogCodeDialog.Separator) |
107 self.classNameCombo.addItem( |
|
108 CreateDialogCodeDialog.Separator) |
104 self.classNameCombo.addItems(sorted(vagueClassesList)) |
109 self.classNameCombo.addItems(sorted(vagueClassesList)) |
105 |
110 |
106 if os.path.exists(self.srcFile) and \ |
111 if os.path.exists(self.srcFile) and \ |
107 self.__module is not None and \ |
112 self.__module is not None and \ |
108 self.classNameCombo.count() == 0: |
113 self.classNameCombo.count() == 0: |
109 self.__initError = True |
114 self.__initError = True |
110 E5MessageBox.critical(self, |
115 E5MessageBox.critical(self, |
111 self.trUtf8("Create Dialog Code"), |
116 self.trUtf8("Create Dialog Code"), |
112 self.trUtf8("""The file <b>{0}</b> exists but does not contain""" |
117 self.trUtf8( |
113 """ any classes.""").format(self.srcFile)) |
118 """The file <b>{0}</b> exists but does not contain""" |
|
119 """ any classes.""").format(self.srcFile)) |
114 |
120 |
115 self.okButton.setEnabled(self.classNameCombo.count() > 0) |
121 self.okButton.setEnabled(self.classNameCombo.count() > 0) |
116 |
122 |
117 self.__updateSlotsModel() |
123 self.__updateSlotsModel() |
118 |
124 |
134 dlg = uic.loadUi(self.formFile) |
140 dlg = uic.loadUi(self.formFile) |
135 return dlg.objectName() |
141 return dlg.objectName() |
136 except (AttributeError, ImportError) as err: |
142 except (AttributeError, ImportError) as err: |
137 E5MessageBox.critical(self, |
143 E5MessageBox.critical(self, |
138 self.trUtf8("uic error"), |
144 self.trUtf8("uic error"), |
139 self.trUtf8("""<p>There was an error loading the form <b>{0}</b>.</p>""" |
145 self.trUtf8( |
140 """<p>{1}</p>""").format(self.formFile, str(err))) |
146 """<p>There was an error loading the form <b>{0}</b>""" |
|
147 """.</p><p>{1}</p>""").format(self.formFile, str(err))) |
141 return "" |
148 return "" |
142 |
149 |
143 def __className(self): |
150 def __className(self): |
144 """ |
151 """ |
145 Private method to get the class name of the dialog. |
152 Private method to get the class name of the dialog. |
150 dlg = uic.loadUi(self.formFile) |
157 dlg = uic.loadUi(self.formFile) |
151 return dlg.metaObject().className() |
158 return dlg.metaObject().className() |
152 except (AttributeError, ImportError) as err: |
159 except (AttributeError, ImportError) as err: |
153 E5MessageBox.critical(self, |
160 E5MessageBox.critical(self, |
154 self.trUtf8("uic error"), |
161 self.trUtf8("uic error"), |
155 self.trUtf8("""<p>There was an error loading the form <b>{0}</b>.</p>""" |
162 self.trUtf8( |
156 """<p>{1}</p>""").format(self.formFile, str(err))) |
163 """<p>There was an error loading the form <b>{0}</b>""" |
|
164 """.</p><p>{1}</p>""").format(self.formFile, str(err))) |
157 return "" |
165 return "" |
158 |
166 |
159 def __signatures(self): |
167 def __signatures(self): |
160 """ |
168 """ |
161 Private slot to get the signatures. |
169 Private slot to get the signatures. |
170 if clsName: |
178 if clsName: |
171 cls = self.__module.classes[clsName] |
179 cls = self.__module.classes[clsName] |
172 for meth in list(cls.methods.values()): |
180 for meth in list(cls.methods.values()): |
173 if meth.name.startswith("on_"): |
181 if meth.name.startswith("on_"): |
174 if meth.pyqtSignature is not None: |
182 if meth.pyqtSignature is not None: |
175 sig = ", ".join([bytes(QMetaObject.normalizedType(t)).decode() \ |
183 sig = ", ".join( |
176 for t in meth.pyqtSignature.split(",")]) |
184 [bytes(QMetaObject.normalizedType(t)).decode() |
|
185 for t in meth.pyqtSignature.split(",")]) |
177 signatures.append("{0}({1})".format(meth.name, sig)) |
186 signatures.append("{0}({1})".format(meth.name, sig)) |
178 else: |
187 else: |
179 signatures.append(meth.name) |
188 signatures.append(meth.name) |
180 return signatures |
189 return signatures |
181 |
190 |
231 for index in range(metaObject.methodCount()): |
241 for index in range(metaObject.methodCount()): |
232 metaMethod = metaObject.method(index) |
242 metaMethod = metaObject.method(index) |
233 if metaMethod.methodType() == QMetaMethod.Signal: |
243 if metaMethod.methodType() == QMetaMethod.Signal: |
234 if qVersion() >= "5.0.0": |
244 if qVersion() >= "5.0.0": |
235 itm2 = QStandardItem("on_{0}_{1}".format( |
245 itm2 = QStandardItem("on_{0}_{1}".format( |
236 name, bytes(metaMethod.methodSignature()).decode())) |
246 name, |
|
247 bytes(metaMethod.methodSignature()).decode())) |
237 else: |
248 else: |
238 itm2 = QStandardItem("on_{0}_{1}".format( |
249 itm2 = QStandardItem("on_{0}_{1}".format( |
239 name, metaMethod.signature())) |
250 name, metaMethod.signature())) |
240 itm.appendRow(itm2) |
251 itm.appendRow(itm2) |
241 if self.__module is not None: |
252 if self.__module is not None: |
242 if qVersion() >= "5.0.0": |
253 if qVersion() >= "5.0.0": |
243 method = "on_{0}_{1}".format( |
254 method = "on_{0}_{1}".format( |
244 name, bytes(metaMethod.methodSignature()).decode()\ |
255 name, |
245 .split("(")[0]) |
256 bytes(metaMethod.methodSignature())\ |
|
257 .decode().split("(")[0]) |
246 else: |
258 else: |
247 method = "on_{0}_{1}".format( |
259 method = "on_{0}_{1}".format( |
248 name, metaMethod.signature().split("(")[0]) |
260 name, metaMethod.signature().split("(")[0]) |
249 method2 = "{0}({1})".format(method, |
261 method2 = "{0}({1})".format(method, |
250 ", ".join([self.__mapType(t) |
262 ", ".join( |
251 for t in metaMethod.parameterTypes()])) |
263 [self.__mapType(t) |
|
264 for t in metaMethod.parameterTypes()])) |
252 |
265 |
253 if method2 in signatureList or method in signatureList: |
266 if method2 in signatureList or \ |
|
267 method in signatureList: |
254 itm2.setFlags(Qt.ItemFlags(Qt.ItemIsEnabled)) |
268 itm2.setFlags(Qt.ItemFlags(Qt.ItemIsEnabled)) |
255 itm2.setCheckState(Qt.Checked) |
269 itm2.setCheckState(Qt.Checked) |
256 itm2.setForeground(QBrush(Qt.blue)) |
270 itm2.setForeground(QBrush(Qt.blue)) |
257 continue |
271 continue |
258 |
272 |
265 for index in range(len(parameterNames)): |
279 for index in range(len(parameterNames)): |
266 if not parameterNames[index]: |
280 if not parameterNames[index]: |
267 parameterNames[index] = \ |
281 parameterNames[index] = \ |
268 QByteArray("p{0:d}".format(index)) |
282 QByteArray("p{0:d}".format(index)) |
269 methNamesSig = \ |
283 methNamesSig = \ |
270 ", ".join([bytes(n).decode() for n in parameterNames]) |
284 ", ".join( |
|
285 [bytes(n).decode() for n in parameterNames]) |
271 |
286 |
272 if methNamesSig: |
287 if methNamesSig: |
273 if qVersion() >= "5.0.0": |
288 if qVersion() >= "5.0.0": |
274 pythonSignature = "on_{0}_{1}(self, {2})".format( |
289 pythonSignature = \ |
275 name, |
290 "on_{0}_{1}(self, {2})".format( |
276 bytes(metaMethod.methodSignature()).decode()\ |
291 name, |
277 .split("(")[0], |
292 bytes(metaMethod.methodSignature())\ |
278 methNamesSig) |
293 .decode().split("(")[0], |
|
294 methNamesSig) |
279 else: |
295 else: |
280 pythonSignature = "on_{0}_{1}(self, {2})".format( |
296 pythonSignature = \ |
281 name, |
297 "on_{0}_{1}(self, {2})".format( |
282 metaMethod.signature().split("(")[0], |
298 name, |
283 methNamesSig) |
299 metaMethod.signature().split("(")[0], |
|
300 methNamesSig) |
284 else: |
301 else: |
285 if qVersion() >= "5.0.0": |
302 if qVersion() >= "5.0.0": |
286 pythonSignature = "on_{0}_{1}(self)".format( |
303 pythonSignature = "on_{0}_{1}(self)".format( |
287 name, |
304 name, |
288 bytes(metaMethod.methodSignature()).decode()\ |
305 bytes(metaMethod.methodSignature())\ |
289 .split("(")[0]) |
306 .decode().split("(")[0]) |
290 else: |
307 else: |
291 pythonSignature = "on_{0}_{1}(self)".format( |
308 pythonSignature = "on_{0}_{1}(self)".format( |
292 name, |
309 name, |
293 metaMethod.signature().split("(")[0]) |
310 metaMethod.signature().split("(")[0]) |
294 itm2.setData(pyqtSignature, pyqtSignatureRole) |
311 itm2.setData(pyqtSignature, pyqtSignatureRole) |
303 |
320 |
304 self.slotsView.sortByColumn(0, Qt.AscendingOrder) |
321 self.slotsView.sortByColumn(0, Qt.AscendingOrder) |
305 except (AttributeError, ImportError) as err: |
322 except (AttributeError, ImportError) as err: |
306 E5MessageBox.critical(self, |
323 E5MessageBox.critical(self, |
307 self.trUtf8("uic error"), |
324 self.trUtf8("uic error"), |
308 self.trUtf8("""<p>There was an error loading the form <b>{0}</b>.</p>""" |
325 self.trUtf8( |
309 """<p>{1}</p>""").format(self.formFile, str(err))) |
326 """<p>There was an error loading the form <b>{0}</b>""" |
|
327 """.</p><p>{1}</p>""").format(self.formFile, str(err))) |
310 |
328 |
311 def __generateCode(self): |
329 def __generateCode(self): |
312 """ |
330 """ |
313 Private slot to generate the code as requested by the user. |
331 Private slot to generate the code as requested by the user. |
314 """ |
332 """ |
340 if self.__module is None: |
358 if self.__module is None: |
341 # new file |
359 # new file |
342 try: |
360 try: |
343 if self.project.getProjectLanguage() == "Python2": |
361 if self.project.getProjectLanguage() == "Python2": |
344 if self.project.getProjectType() == "PySide": |
362 if self.project.getProjectType() == "PySide": |
345 tmplName = os.path.join(getConfig('ericCodeTemplatesDir'), |
363 tmplName = os.path.join( |
346 "impl_pyside.py2.tmpl") |
364 getConfig('ericCodeTemplatesDir'), |
|
365 "impl_pyside.py2.tmpl") |
347 elif self.project.getProjectType() == "PyQt5": |
366 elif self.project.getProjectType() == "PyQt5": |
348 tmplName = os.path.join(getConfig('ericCodeTemplatesDir'), |
367 tmplName = os.path.join( |
349 "impl_pyqt5.py2.tmpl") |
368 getConfig('ericCodeTemplatesDir'), |
|
369 "impl_pyqt5.py2.tmpl") |
350 else: |
370 else: |
351 tmplName = os.path.join(getConfig('ericCodeTemplatesDir'), |
371 tmplName = os.path.join( |
352 "impl_pyqt.py2.tmpl") |
372 getConfig('ericCodeTemplatesDir'), |
|
373 "impl_pyqt.py2.tmpl") |
353 else: |
374 else: |
354 if self.project.getProjectType() == "PySide": |
375 if self.project.getProjectType() == "PySide": |
355 tmplName = os.path.join(getConfig('ericCodeTemplatesDir'), |
376 tmplName = os.path.join( |
356 "impl_pyside.py.tmpl") |
377 getConfig('ericCodeTemplatesDir'), |
|
378 "impl_pyside.py.tmpl") |
357 elif self.project.getProjectType() == "PyQt5": |
379 elif self.project.getProjectType() == "PyQt5": |
358 tmplName = os.path.join(getConfig('ericCodeTemplatesDir'), |
380 tmplName = os.path.join( |
359 "impl_pyqt5.py.tmpl") |
381 getConfig('ericCodeTemplatesDir'), |
|
382 "impl_pyqt5.py.tmpl") |
360 else: |
383 else: |
361 tmplName = os.path.join(getConfig('ericCodeTemplatesDir'), |
384 tmplName = os.path.join( |
362 "impl_pyqt.py.tmpl") |
385 getConfig('ericCodeTemplatesDir'), |
|
386 "impl_pyqt.py.tmpl") |
363 tmplFile = open(tmplName, 'r', encoding="utf-8") |
387 tmplFile = open(tmplName, 'r', encoding="utf-8") |
364 template = tmplFile.read() |
388 template = tmplFile.read() |
365 tmplFile.close() |
389 tmplFile.close() |
366 except IOError as why: |
390 except IOError as why: |
367 E5MessageBox.critical(self, |
391 E5MessageBox.critical(self, |
368 self.trUtf8("Code Generation"), |
392 self.trUtf8("Code Generation"), |
369 self.trUtf8("""<p>Could not open the code template file "{0}".</p>""" |
393 self.trUtf8( |
370 """<p>Reason: {1}</p>""")\ |
394 """<p>Could not open the code template file""" |
|
395 """ "{0}".</p><p>Reason: {1}</p>""")\ |
371 .format(tmplName, str(why))) |
396 .format(tmplName, str(why))) |
372 return |
397 return |
373 |
398 |
374 objName = self.__objectName() |
399 objName = self.__objectName() |
375 if objName: |
400 if objName: |
376 template = template\ |
401 template = template\ |
377 .replace("$FORMFILE$", |
402 .replace( |
378 os.path.splitext(os.path.basename(self.formFile))[0])\ |
403 "$FORMFILE$", |
|
404 os.path.splitext(os.path.basename(self.formFile))[0])\ |
379 .replace("$FORMCLASS$", objName)\ |
405 .replace("$FORMCLASS$", objName)\ |
380 .replace("$CLASSNAME$", self.classNameCombo.currentText())\ |
406 .replace("$CLASSNAME$", self.classNameCombo.currentText())\ |
381 .replace("$SUPERCLASS$", self.__className()) |
407 .replace("$SUPERCLASS$", self.__className()) |
382 |
408 |
383 sourceImpl = template.splitlines(True) |
409 sourceImpl = template.splitlines(True) |
397 if not sourceImpl[-1].endswith("\n"): |
423 if not sourceImpl[-1].endswith("\n"): |
398 sourceImpl[-1] = "{0}{1}".format(sourceImpl[-1], "\n") |
424 sourceImpl[-1] = "{0}{1}".format(sourceImpl[-1], "\n") |
399 except IOError as why: |
425 except IOError as why: |
400 E5MessageBox.critical(self, |
426 E5MessageBox.critical(self, |
401 self.trUtf8("Code Generation"), |
427 self.trUtf8("Code Generation"), |
402 self.trUtf8("""<p>Could not open the source file "{0}".</p>""" |
428 self.trUtf8( |
403 """<p>Reason: {1}</p>""")\ |
429 """<p>Could not open the source file "{0}".</p>""" |
|
430 """<p>Reason: {1}</p>""")\ |
404 .format(self.srcFile, str(why))) |
431 .format(self.srcFile, str(why))) |
405 return |
432 return |
406 |
433 |
407 cls = self.__module.classes[self.classNameCombo.currentText()] |
434 cls = self.__module.classes[self.classNameCombo.currentText()] |
408 if cls.endlineno == len(sourceImpl) or cls.endlineno == -1: |
435 if cls.endlineno == len(sourceImpl) or cls.endlineno == -1: |
442 if child.checkState() and \ |
469 if child.checkState() and \ |
443 child.flags() & Qt.ItemFlags(Qt.ItemIsUserCheckable): |
470 child.flags() & Qt.ItemFlags(Qt.ItemIsUserCheckable): |
444 slotsCode.append('{0}\n'.format(indentStr)) |
471 slotsCode.append('{0}\n'.format(indentStr)) |
445 slotsCode.append('{0}{1}\n'.format( |
472 slotsCode.append('{0}{1}\n'.format( |
446 indentStr, |
473 indentStr, |
447 pyqtSignatureFormat.format(child.data(pyqtSignatureRole)))) |
474 pyqtSignatureFormat.format( |
|
475 child.data(pyqtSignatureRole)))) |
448 slotsCode.append('{0}def {1}:\n'.format( |
476 slotsCode.append('{0}def {1}:\n'.format( |
449 indentStr, child.data(pythonSignatureRole))) |
477 indentStr, child.data(pythonSignatureRole))) |
450 slotsCode.append('{0}"""\n'.format(indentStr * 2)) |
478 slotsCode.append('{0}"""\n'.format(indentStr * 2)) |
451 slotsCode.append('{0}Slot documentation goes here.\n'.format( |
479 slotsCode.append( |
452 indentStr * 2)) |
480 '{0}Slot documentation goes here.\n'.format( |
|
481 indentStr * 2)) |
453 slotsCode.append('{0}"""\n'.format(indentStr * 2)) |
482 slotsCode.append('{0}"""\n'.format(indentStr * 2)) |
454 slotsCode.append('{0}# {1}: not implemented yet\n'.format( |
483 slotsCode.append('{0}# {1}: not implemented yet\n'.format( |
455 indentStr * 2, "TODO")) |
484 indentStr * 2, "TODO")) |
456 slotsCode.append('{0}raise NotImplementedError\n'.format( |
485 slotsCode.append('{0}raise NotImplementedError\n'.format( |
457 indentStr * 2)) |
486 indentStr * 2)) |