Project/CreateDialogCodeDialog.py

changeset 2995
63d874899b8b
parent 2741
d0d7836d0c01
child 3020
542e97d4ecb3
child 3057
10516539f238
equal deleted inserted replaced
2994:5ae1349b8fb4 2995:63d874899b8b
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
196 205
197 # 2. check fpr * 206 # 2. check fpr *
198 mapped = mapped.replace("*", "") 207 mapped = mapped.replace("*", "")
199 208
200 # 3. replace QString and QStringList 209 # 3. replace QString and QStringList
201 mapped = mapped.replace("QStringList", "list").replace("QString", "str") 210 mapped = mapped.replace("QStringList", "list")\
211 .replace("QString", "str")
202 212
203 # 4. replace double by float 213 # 4. replace double by float
204 mapped = mapped.replace("double", "float") 214 mapped = mapped.replace("double", "float")
205 215
206 return mapped 216 return mapped
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))
486 """ 515 """
487 Private slot to handle the activated signal of the classname combo. 516 Private slot to handle the activated signal of the classname combo.
488 517
489 @param index index of the activated item (integer) 518 @param index index of the activated item (integer)
490 """ 519 """
491 if self.classNameCombo.currentText() == CreateDialogCodeDialog.Separator: 520 if (self.classNameCombo.currentText() ==
521 CreateDialogCodeDialog.Separator):
492 self.okButton.setEnabled(False) 522 self.okButton.setEnabled(False)
493 self.filterEdit.clear() 523 self.filterEdit.clear()
494 self.slotsModel.clear() 524 self.slotsModel.clear()
495 self.slotsModel.setHorizontalHeaderLabels([""]) 525 self.slotsModel.setHorizontalHeaderLabels([""])
496 else: 526 else:
529 559
530 def on_buttonBox_clicked(self, button): 560 def on_buttonBox_clicked(self, button):
531 """ 561 """
532 Private slot to handle the buttonBox clicked signal. 562 Private slot to handle the buttonBox clicked signal.
533 563
534 @param button reference to the button that was clicked (QAbstractButton) 564 @param button reference to the button that was clicked
565 (QAbstractButton)
535 """ 566 """
536 if button == self.okButton: 567 if button == self.okButton:
537 self.__generateCode() 568 self.__generateCode()
538 self.accept() 569 self.accept()

eric ide

mercurial