1
# -*- coding: utf-8 -*-
3
# Copyright (c) 2007 Detlev Offenbach <detlev@die-offenbachs.de>
7
Module implementing a dialog to generate code for a Qt4 dialog.
13
from PyQt4.QtCore import *
14
from PyQt4.QtGui import *
17
from KdeQt import KQMessageBox
19
from NewDialogClassDialog import NewDialogClassDialog
20
from Ui_CreateDialogCodeDialog import Ui_CreateDialogCodeDialog
22
from Utilities import ModuleParser
26
from eric4config import getConfig
28
pyqtSignatureRole = Qt.UserRole + 1
29
pythonSignatureRole = Qt.UserRole + 2
30
rubySignatureRole = Qt.UserRole + 3
32
class CreateDialogCodeDialog(QDialog, Ui_CreateDialogCodeDialog):
34
Class implementing a dialog to generate code for a Qt4 dialog.
36
def __init__(self, formName, project, parent = None):
40
@param formName name of the file containing the form (string or QString)
41
@param project reference to the project object
42
@param parent parent widget if the dialog (QWidget)
44
QDialog.__init__(self, parent)
47
self.okButton = self.buttonBox.button(QDialogButtonBox.Ok)
49
self.project = project
51
self.formFile = unicode(formName)
52
filename, ext = os.path.splitext(self.formFile)
53
self.srcFile = '%s%s' % (filename, self.project.getDefaultSourceExtension())
55
self.slotsModel = QStandardItemModel()
56
self.proxyModel = QSortFilterProxyModel()
57
self.proxyModel.setDynamicSortFilter(True)
58
self.proxyModel.setSourceModel(self.slotsModel)
59
self.slotsView.setModel(self.proxyModel)
61
self.clearFilterButton.setIcon(UI.PixmapCache.getIcon("clearLeft.png"))
63
# initialize some member variables
66
if os.path.exists(self.srcFile):
67
self.__module = ModuleParser.readModule(self.srcFile, caching = False)
69
if self.__module is not None:
70
self.filenameEdit.setText(self.srcFile)
72
classesList = QStringList()
73
for cls in self.__module.classes.values():
74
classesList.append(cls.name)
76
self.classNameCombo.addItems(classesList)
78
self.okButton.setEnabled(self.classNameCombo.count() > 0)
80
self.__updateSlotsModel()
82
def __objectName(self):
84
Private method to get the object name of the dialog.
86
@return object name (QString)
88
dlg = uic.loadUi(self.formFile)
89
return dlg.objectName()
91
def __className(self):
93
Private method to get the class name of the dialog.
95
@return class name (QSting)
97
dlg = uic.loadUi(self.formFile)
98
return dlg.metaObject().className()
100
def __signatures(self):
101
if self.__module is None:
105
cls = self.__module.classes[unicode(self.classNameCombo.currentText())]
106
for meth in cls.methods.values():
107
if meth.name.startswith("on_"):
108
if meth.pyqtSignature is not None:
109
sig = ", ".join([str(QMetaObject.normalizedType(t)) \
110
for t in meth.pyqtSignature.split(",")])
111
signatures.append("%s(%s)" % (meth.name, sig))
113
signatures.append(meth.name)
116
def __updateSlotsModel(self):
118
Private slot to update the update the slots tree display.
120
dlg = uic.loadUi(self.formFile)
121
objects = dlg.findChildren(QWidget) + dlg.findChildren(QAction)
123
signatureList = self.__signatures()
125
self.slotsModel.clear()
126
self.slotsModel.setHorizontalHeaderLabels(QStringList(""))
128
name = obj.objectName()
132
metaObject = obj.metaObject()
133
className = metaObject.className()
134
itm = QStandardItem("%s (%s)" % (name, className))
135
self.slotsModel.appendRow(itm)
136
for index in range(metaObject.methodCount()):
137
metaMethod = metaObject.method(index)
138
if metaMethod.methodType() == QMetaMethod.Signal:
139
itm2 = QStandardItem("on_%s_%s" % (name, metaMethod.signature()))
141
if self.__module is not None:
142
method = "on_%s_%s" % \
143
(name, metaMethod.signature().split("(")[0])
144
method2 = "on_%s_%s" % \
146
QMetaObject.normalizedSignature(metaMethod.signature()))
148
if method2 in signatureList or method in signatureList:
149
itm2.setFlags(Qt.ItemFlags(Qt.ItemIsEnabled))
150
itm2.setCheckState(Qt.Checked)
151
itm2.setForeground(QBrush(Qt.blue))
155
", ".join([str(t) for t in metaMethod.parameterTypes()])
157
", ".join([str(n) for n in metaMethod.parameterNames()])
159
pythonSignature = "on_%s_%s(self, %s)" % \
161
metaMethod.signature().split("(")[0],
165
pythonSignature = "on_%s_%s(self)" % \
167
metaMethod.signature().split("(")[0]
169
itm2.setData(QVariant(pyqtSignature), pyqtSignatureRole)
170
itm2.setData(QVariant(pythonSignature), pythonSignatureRole)
172
itm2.setFlags(Qt.ItemFlags(\
173
Qt.ItemIsUserCheckable | \
177
itm2.setCheckState(Qt.Unchecked)
179
self.slotsView.setExpanded(\
180
self.proxyModel.mapFromSource(self.slotsModel.indexFromItem(itm)),
183
self.slotsView.sortByColumn(0, Qt.AscendingOrder)
185
def __generateCode(self):
187
Private slot to generate the code as requested by the user.
189
# first decide on extension
190
if self.filenameEdit.text().endsWith(".py") or \
191
self.filenameEdit.text().endsWith(".pyw"):
192
self.__generatePythonCode()
193
elif self.filenameEdit.text().endsWith(".rb"):
195
# second decide on project language
196
elif self.project.getProjectLanguage() == "Python":
197
self.__generatePythonCode()
198
elif self.project.getProjectLanguage() == "Ruby":
201
# assume Python (our global default)
202
self.__generatePythonCode()
204
def __generatePythonCode(self):
206
Private slot to generate Python code as requested by the user.
208
# init some variables
214
if self.__module is None:
217
tmplName = os.path.join(getConfig('ericCodeTemplatesDir'), "impl.py.tmpl")
218
tmplFile = open(tmplName, 'rb')
219
template = tmplFile.read()
222
KQMessageBox.critical(self,
223
self.trUtf8("Code Generation"),
224
self.trUtf8("""<p>Could not open the code template file "%1".</p>"""
225
"""<p>Reason: %2</p>""")\
228
QMessageBox.StandardButtons(\
233
.replace("$FORMFILE$",
234
os.path.splitext(os.path.basename(self.formFile))[0])\
235
.replace("$FORMCLASS$", unicode(self.__objectName()))\
236
.replace("$CLASSNAME$", unicode(self.classNameCombo.currentText()))\
237
.replace("$SUPERCLASS$", unicode(self.__className()))
239
sourceImpl = template.splitlines(True)
242
# determine indent string
243
for line in sourceImpl:
244
if line.lstrip().startswith("def __init__"):
245
indentStr = line.replace(line.lstrip(), "")
248
# extend existing file
250
srcFile = open(self.srcFile, 'rb')
251
sourceImpl = srcFile.readlines()
253
if not sourceImpl[-1].endswith(os.linesep):
254
sourceImpl[-1] = "%s%s" % (sourceImpl[-1], os.linesep)
256
KQMessageBox.critical(self,
257
self.trUtf8("Code Generation"),
258
self.trUtf8("""<p>Could not open the source file "%1".</p>"""
259
"""<p>Reason: %2</p>""")\
262
QMessageBox.StandardButtons(\
266
cls = self.__module.classes[unicode(self.classNameCombo.currentText())]
267
if cls.endlineno == len(sourceImpl) or cls.endlineno == -1:
269
# delete empty lines at end
270
while not sourceImpl[-1].strip():
273
appendAtIndex = cls.endlineno - 1
275
# determine indent string
276
for line in sourceImpl[cls.lineno:cls.endlineno+1]:
277
if line.lstrip().startswith("def __init__"):
278
indentStr = line.replace(line.lstrip(), "")
281
# do the coding stuff
282
for row in range(self.slotsModel.rowCount()):
283
topItem = self.slotsModel.item(row)
284
for childRow in range(topItem.rowCount()):
285
child = topItem.child(childRow)
286
if child.checkState() and \
287
child.flags() & Qt.ItemFlags(Qt.ItemIsUserCheckable):
288
slotsCode.append('%s\n' % indentStr)
289
slotsCode.append('%s@pyqtSignature("%s")\n' % \
290
(indentStr, child.data(pyqtSignatureRole).toString()))
291
slotsCode.append('%sdef %s:\n' % \
292
(indentStr, child.data(pythonSignatureRole).toString()))
293
slotsCode.append('%s"""\n' % (indentStr * 2,))
294
slotsCode.append('%sSlot documentation goes here.\n' % \
296
slotsCode.append('%s"""\n' % (indentStr * 2,))
297
slotsCode.append('%s# %s: not implemented yet\n' % \
298
(indentStr * 2, "TODO"))
299
slotsCode.append('%sraise "Not implemented yet"\n' % (indentStr * 2,))
301
if appendAtIndex == -1:
302
sourceImpl.extend(slotsCode)
304
sourceImpl[appendAtIndex:appendAtIndex] = slotsCode
308
srcFile = open(unicode(self.filenameEdit.text()), 'wb')
309
srcFile.write("".join(sourceImpl))
312
KQMessageBox.critical(self,
313
self.trUtf8("Code Generation"),
314
self.trUtf8("""<p>Could not write the source file "%1".</p>"""
315
"""<p>Reason: %2</p>""")\
316
.arg(self.filenameEdit.text())\
318
QMessageBox.StandardButtons(\
322
self.project.appendFile(unicode(self.filenameEdit.text()))
324
@pyqtSignature("int")
325
def on_classNameCombo_activated(self, index):
327
Private slot to handle the activated signal of the classname combo.
329
@param index index of the activated item (integer)
331
self.__updateSlotsModel()
333
def on_filterEdit_textChanged(self, text):
335
Private slot called, when thext of the filter edit has changed.
337
@param text changed text (QString)
339
re = QRegExp(text, Qt.CaseInsensitive, QRegExp.RegExp2)
340
self.proxyModel.setFilterRegExp(re)
343
def on_clearFilterButton_clicked(self):
345
Private slot called by a click of the clear filter button.
347
self.filterEdit.clear()
350
def on_newButton_clicked(self):
352
Private slot called to enter the data for a new dialog class.
354
path, file = os.path.split(self.srcFile)
355
dlg = NewDialogClassDialog(self.__objectName(), file, path, self)
356
if dlg.exec_() == QDialog.Accepted:
357
className, fileName = dlg.getData()
359
self.classNameCombo.clear()
360
self.classNameCombo.addItem(className)
361
self.srcFile = fileName
362
self.filenameEdit.setText(self.srcFile)
365
self.okButton.setEnabled(self.classNameCombo.count > 0)
367
def on_buttonBox_clicked(self, button):
369
Private slot to handle the buttonBox clicked signal.
371
@param button reference to the button that was clicked (QAbstractButton)
373
if button == self.okButton:
374
self.__generateCode()