~ubuntu-branches/ubuntu/karmic/eric/karmic

« back to all changes in this revision

Viewing changes to eric/Project/CreateDialogCodeDialog.py

  • Committer: Bazaar Package Importer
  • Author(s): Scott Kitterman
  • Date: 2008-01-28 18:02:25 UTC
  • mfrom: (1.1.4 upstream)
  • Revision ID: james.westby@ubuntu.com-20080128180225-6nrox6yrworh2c4v
Tags: 4.0.4-1ubuntu1
* Add python-qt3 to build-depends becuase that's where Ubuntu puts 
  pyqtconfig
* Change maintainer to MOTU

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
 
 
3
# Copyright (c) 2007 Detlev Offenbach <detlev@die-offenbachs.de>
 
4
#
 
5
 
 
6
"""
 
7
Module implementing a dialog to generate code for a Qt4 dialog.
 
8
"""
 
9
 
 
10
import os
 
11
import sys
 
12
 
 
13
from PyQt4.QtCore import *
 
14
from PyQt4.QtGui import *
 
15
from PyQt4 import uic
 
16
 
 
17
from KdeQt import KQMessageBox
 
18
 
 
19
from NewDialogClassDialog import NewDialogClassDialog
 
20
from Ui_CreateDialogCodeDialog import Ui_CreateDialogCodeDialog
 
21
 
 
22
from Utilities import ModuleParser
 
23
 
 
24
import UI.PixmapCache
 
25
 
 
26
from eric4config import getConfig
 
27
 
 
28
pyqtSignatureRole   = Qt.UserRole + 1
 
29
pythonSignatureRole = Qt.UserRole + 2
 
30
rubySignatureRole   = Qt.UserRole + 3
 
31
 
 
32
class CreateDialogCodeDialog(QDialog, Ui_CreateDialogCodeDialog):
 
33
    """
 
34
    Class implementing a dialog to generate code for a Qt4 dialog.
 
35
    """
 
36
    def __init__(self, formName, project, parent = None):
 
37
        """
 
38
        Constructor
 
39
        
 
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)
 
43
        """
 
44
        QDialog.__init__(self, parent)
 
45
        self.setupUi(self)
 
46
        
 
47
        self.okButton = self.buttonBox.button(QDialogButtonBox.Ok)
 
48
        
 
49
        self.project = project
 
50
        
 
51
        self.formFile = unicode(formName)
 
52
        filename, ext = os.path.splitext(self.formFile)
 
53
        self.srcFile = '%s%s' % (filename, self.project.getDefaultSourceExtension())
 
54
        
 
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)
 
60
        
 
61
        self.clearFilterButton.setIcon(UI.PixmapCache.getIcon("clearLeft.png"))
 
62
        
 
63
        # initialize some member variables
 
64
        self.__module = None
 
65
        
 
66
        if os.path.exists(self.srcFile):
 
67
            self.__module = ModuleParser.readModule(self.srcFile, caching = False)
 
68
        
 
69
        if self.__module is not None:
 
70
            self.filenameEdit.setText(self.srcFile)
 
71
            
 
72
            classesList = QStringList()
 
73
            for cls in self.__module.classes.values():
 
74
                classesList.append(cls.name)
 
75
            classesList.sort()
 
76
            self.classNameCombo.addItems(classesList)
 
77
        
 
78
        self.okButton.setEnabled(self.classNameCombo.count() > 0)
 
79
        
 
80
        self.__updateSlotsModel()
 
81
        
 
82
    def __objectName(self):
 
83
        """
 
84
        Private method to get the object name of the dialog.
 
85
        
 
86
        @return object name (QString)
 
87
        """
 
88
        dlg = uic.loadUi(self.formFile)
 
89
        return dlg.objectName()
 
90
        
 
91
    def __className(self):
 
92
        """
 
93
        Private method to get the class name of the dialog.
 
94
        
 
95
        @return class name (QSting)
 
96
        """
 
97
        dlg = uic.loadUi(self.formFile)
 
98
        return dlg.metaObject().className()
 
99
        
 
100
    def __signatures(self):
 
101
        if self.__module is None:
 
102
            return []
 
103
            
 
104
        signatures = []
 
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))
 
112
                else:
 
113
                    signatures.append(meth.name)
 
114
        return signatures
 
115
        
 
116
    def __updateSlotsModel(self):
 
117
        """
 
118
        Private slot to update the update the slots tree display.
 
119
        """
 
120
        dlg = uic.loadUi(self.formFile)
 
121
        objects = dlg.findChildren(QWidget) + dlg.findChildren(QAction)
 
122
        
 
123
        signatureList = self.__signatures()
 
124
        
 
125
        self.slotsModel.clear()
 
126
        self.slotsModel.setHorizontalHeaderLabels(QStringList(""))
 
127
        for obj in objects:
 
128
            name = obj.objectName()
 
129
            if name.isEmpty():
 
130
                continue
 
131
            
 
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()))
 
140
                    itm.appendRow(itm2)
 
141
                    if self.__module is not None:
 
142
                        method = "on_%s_%s" % \
 
143
                            (name, metaMethod.signature().split("(")[0])
 
144
                        method2 = "on_%s_%s" % \
 
145
                            (name, 
 
146
                             QMetaObject.normalizedSignature(metaMethod.signature()))
 
147
                        
 
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))
 
152
                            continue
 
153
                        
 
154
                    pyqtSignature = \
 
155
                        ", ".join([str(t) for t in metaMethod.parameterTypes()])
 
156
                    methNamesSig = \
 
157
                        ", ".join([str(n) for n in metaMethod.parameterNames()])
 
158
                    if methNamesSig:
 
159
                        pythonSignature = "on_%s_%s(self, %s)" % \
 
160
                            (name, 
 
161
                             metaMethod.signature().split("(")[0], 
 
162
                             methNamesSig
 
163
                            )
 
164
                    else:
 
165
                        pythonSignature = "on_%s_%s(self)" % \
 
166
                            (name, 
 
167
                             metaMethod.signature().split("(")[0]
 
168
                            )
 
169
                    itm2.setData(QVariant(pyqtSignature), pyqtSignatureRole)
 
170
                    itm2.setData(QVariant(pythonSignature), pythonSignatureRole)
 
171
                    
 
172
                    itm2.setFlags(Qt.ItemFlags(\
 
173
                        Qt.ItemIsUserCheckable | \
 
174
                        Qt.ItemIsEnabled | \
 
175
                        Qt.ItemIsSelectable)
 
176
                    )
 
177
                    itm2.setCheckState(Qt.Unchecked)
 
178
            
 
179
            self.slotsView.setExpanded(\
 
180
                self.proxyModel.mapFromSource(self.slotsModel.indexFromItem(itm)), 
 
181
                True)
 
182
        
 
183
        self.slotsView.sortByColumn(0, Qt.AscendingOrder)
 
184
        
 
185
    def __generateCode(self):
 
186
        """
 
187
        Private slot to generate the code as requested by the user.
 
188
        """
 
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"):
 
194
            pass
 
195
        # second decide on project language
 
196
        elif self.project.getProjectLanguage() == "Python":
 
197
            self.__generatePythonCode()
 
198
        elif self.project.getProjectLanguage() == "Ruby":
 
199
            pass
 
200
        else:
 
201
            # assume Python (our global default)
 
202
            self.__generatePythonCode()
 
203
        
 
204
    def __generatePythonCode(self):
 
205
        """
 
206
        Private slot to generate Python code as requested by the user.
 
207
        """
 
208
        # init some variables
 
209
        sourceImpl = []
 
210
        appendAtIndex = -1
 
211
        indentStr = "    "
 
212
        slotsCode = []
 
213
        
 
214
        if self.__module is None:
 
215
            # new file
 
216
            try:
 
217
                tmplName = os.path.join(getConfig('ericCodeTemplatesDir'), "impl.py.tmpl")
 
218
                tmplFile = open(tmplName, 'rb')
 
219
                template = tmplFile.read()
 
220
                tmplFile.close()
 
221
            except IOError, why:
 
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>""")\
 
226
                        .arg(tmplName)\
 
227
                        .arg(str(why)),
 
228
                    QMessageBox.StandardButtons(\
 
229
                        QMessageBox.Ok))
 
230
                return
 
231
            
 
232
            template = template\
 
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()))
 
238
            
 
239
            sourceImpl = template.splitlines(True)
 
240
            appendAtIndex = -1
 
241
            
 
242
            # determine indent string
 
243
            for line in sourceImpl:
 
244
                if line.lstrip().startswith("def __init__"):
 
245
                    indentStr = line.replace(line.lstrip(), "")
 
246
                    break
 
247
        else:
 
248
            # extend existing file
 
249
            try:
 
250
                srcFile = open(self.srcFile, 'rb')
 
251
                sourceImpl = srcFile.readlines()
 
252
                srcFile.close()
 
253
                if not sourceImpl[-1].endswith(os.linesep):
 
254
                    sourceImpl[-1] = "%s%s" % (sourceImpl[-1], os.linesep)
 
255
            except IOError, why:
 
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>""")\
 
260
                        .arg(self.srcFile)\
 
261
                        .arg(str(why)),
 
262
                    QMessageBox.StandardButtons(\
 
263
                        QMessageBox.Ok))
 
264
                return
 
265
            
 
266
            cls = self.__module.classes[unicode(self.classNameCombo.currentText())]
 
267
            if cls.endlineno == len(sourceImpl) or cls.endlineno == -1:
 
268
                appendAtIndex = -1
 
269
                # delete empty lines at end
 
270
                while not sourceImpl[-1].strip():
 
271
                    del sourceImpl[-1]
 
272
            else:
 
273
                appendAtIndex = cls.endlineno - 1
 
274
            
 
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(), "")
 
279
                    break
 
280
        
 
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' % \
 
295
                        (indentStr * 2,))
 
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,))
 
300
        
 
301
        if appendAtIndex == -1:
 
302
            sourceImpl.extend(slotsCode)
 
303
        else:
 
304
            sourceImpl[appendAtIndex:appendAtIndex] = slotsCode
 
305
        
 
306
        # write the new code
 
307
        try:
 
308
            srcFile = open(unicode(self.filenameEdit.text()), 'wb')
 
309
            srcFile.write("".join(sourceImpl))
 
310
            srcFile.close()
 
311
        except IOError, why:
 
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())\
 
317
                    .arg(str(why)),
 
318
                QMessageBox.StandardButtons(\
 
319
                    QMessageBox.Ok))
 
320
            return
 
321
        
 
322
        self.project.appendFile(unicode(self.filenameEdit.text()))
 
323
        
 
324
    @pyqtSignature("int")
 
325
    def on_classNameCombo_activated(self, index):
 
326
        """
 
327
        Private slot to handle the activated signal of the classname combo.
 
328
        
 
329
        @param index index of the activated item (integer)
 
330
        """
 
331
        self.__updateSlotsModel()
 
332
        
 
333
    def on_filterEdit_textChanged(self, text):
 
334
        """
 
335
        Private slot called, when thext of the filter edit has changed.
 
336
        
 
337
        @param text changed text (QString)
 
338
        """
 
339
        re = QRegExp(text, Qt.CaseInsensitive, QRegExp.RegExp2)
 
340
        self.proxyModel.setFilterRegExp(re)
 
341
        
 
342
    @pyqtSignature("")
 
343
    def on_clearFilterButton_clicked(self):
 
344
        """
 
345
        Private slot called by a click of the clear filter button.
 
346
        """
 
347
        self.filterEdit.clear()
 
348
        
 
349
    @pyqtSignature("")
 
350
    def on_newButton_clicked(self):
 
351
        """
 
352
        Private slot called to enter the data for a new dialog class.
 
353
        """
 
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()
 
358
            
 
359
            self.classNameCombo.clear()
 
360
            self.classNameCombo.addItem(className)
 
361
            self.srcFile = fileName
 
362
            self.filenameEdit.setText(self.srcFile)
 
363
            self.__module = None
 
364
            
 
365
        self.okButton.setEnabled(self.classNameCombo.count > 0)
 
366
        
 
367
    def on_buttonBox_clicked(self, button):
 
368
        """
 
369
        Private slot to handle the buttonBox clicked signal.
 
370
        
 
371
        @param button reference to the button that was clicked (QAbstractButton)
 
372
        """
 
373
        if button == self.okButton:
 
374
            self.__generateCode()
 
375
            self.accept()