src/eric7/Project/CreateDialogCodeDialog.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9413
80c06d472826
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
10 import os 10 import os
11 import json 11 import json
12 import contextlib 12 import contextlib
13 13
14 from PyQt6.QtCore import ( 14 from PyQt6.QtCore import (
15 pyqtSlot, Qt, QMetaObject, QRegularExpression, QSortFilterProxyModel, 15 pyqtSlot,
16 QProcess, QProcessEnvironment 16 Qt,
17 QMetaObject,
18 QRegularExpression,
19 QSortFilterProxyModel,
20 QProcess,
21 QProcessEnvironment,
17 ) 22 )
18 from PyQt6.QtGui import QStandardItemModel, QStandardItem, QBrush, QColor 23 from PyQt6.QtGui import QStandardItemModel, QStandardItem, QBrush, QColor
19 from PyQt6.QtWidgets import QDialog, QDialogButtonBox 24 from PyQt6.QtWidgets import QDialog, QDialogButtonBox
20 25
21 26
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 """
612 objName = self.__objectName() 635 objName = self.__objectName()
613 if objName: 636 if objName:
614 dlg = NewDialogClassDialog(objName, file, path, self) 637 dlg = NewDialogClassDialog(objName, file, path, self)
615 if dlg.exec() == QDialog.DialogCode.Accepted: 638 if dlg.exec() == QDialog.DialogCode.Accepted:
616 className, fileName = dlg.getData() 639 className, fileName = dlg.getData()
617 640
618 self.classNameCombo.clear() 641 self.classNameCombo.clear()
619 self.classNameCombo.addItem(className) 642 self.classNameCombo.addItem(className)
620 self.srcFile = fileName 643 self.srcFile = fileName
621 self.filenameEdit.setText(self.srcFile) 644 self.filenameEdit.setText(self.srcFile)
622 self.__module = None 645 self.__module = None
623 646
624 self.okButton.setEnabled(self.classNameCombo.count() > 0) 647 self.okButton.setEnabled(self.classNameCombo.count() > 0)
625 648
626 def on_buttonBox_clicked(self, button): 649 def on_buttonBox_clicked(self, button):
627 """ 650 """
628 Private slot to handle the buttonBox clicked signal. 651 Private slot to handle the buttonBox clicked signal.
629 652
630 @param button reference to the button that was clicked 653 @param button reference to the button that was clicked
631 (QAbstractButton) 654 (QAbstractButton)
632 """ 655 """
633 if button == self.okButton: 656 if button == self.okButton:
634 self.__generateCode() 657 self.__generateCode()

eric ide

mercurial