1#!/usr/bin/env python
2
3
4#############################################################################
5##
6## Copyright (C) 2013 Riverbank Computing Limited.
7## Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
8## All rights reserved.
9##
10## This file is part of the examples of PyQt.
11##
12## $QT_BEGIN_LICENSE:BSD$
13## You may use this file under the terms of the BSD license as follows:
14##
15## "Redistribution and use in source and binary forms, with or without
16## modification, are permitted provided that the following conditions are
17## met:
18##   * Redistributions of source code must retain the above copyright
19##     notice, this list of conditions and the following disclaimer.
20##   * Redistributions in binary form must reproduce the above copyright
21##     notice, this list of conditions and the following disclaimer in
22##     the documentation and/or other materials provided with the
23##     distribution.
24##   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
25##     the names of its contributors may be used to endorse or promote
26##     products derived from this software without specific prior written
27##     permission.
28##
29## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
40## $QT_END_LICENSE$
41##
42#############################################################################
43
44
45from PyQt5.QtCore import QDir, QFile, QRegExp
46from PyQt5.QtGui import QPixmap
47from PyQt5.QtWidgets import (QApplication, QCheckBox, QGridLayout, QGroupBox,
48        QLabel, QLineEdit, QMessageBox, QRadioButton, QVBoxLayout, QWizard,
49        QWizardPage)
50
51import classwizard_rc
52
53
54class ClassWizard(QWizard):
55    def __init__(self, parent=None):
56        super(ClassWizard, self).__init__(parent)
57
58        self.addPage(IntroPage())
59        self.addPage(ClassInfoPage())
60        self.addPage(CodeStylePage())
61        self.addPage(OutputFilesPage())
62        self.addPage(ConclusionPage())
63
64        self.setPixmap(QWizard.BannerPixmap, QPixmap(':/images/banner.png'))
65        self.setPixmap(QWizard.BackgroundPixmap,
66                QPixmap(':/images/background.png'))
67
68        self.setWindowTitle("Class Wizard")
69
70    def accept(self):
71        className = self.field('className')
72        baseClass = self.field('baseClass')
73        macroName = self.field('macroName')
74        baseInclude = self.field('baseInclude')
75
76        outputDir = self.field('outputDir')
77        header = self.field('header')
78        implementation = self.field('implementation')
79
80        block = ''
81
82        if self.field('comment'):
83            block += '/*\n'
84            block += '    ' + header + '\n'
85            block += '*/\n'
86            block += '\n'
87
88        if self.field('protect'):
89            block += '#ifndef ' + macroName + '\n'
90            block += '#define ' + macroName + '\n'
91            block += '\n'
92
93        if self.field('includeBase'):
94            block += '#include ' + baseInclude + '\n'
95            block += '\n'
96
97        block += 'class ' + className
98        if baseClass:
99            block += ' : public ' + baseClass
100
101        block += '\n'
102        block += '{\n'
103
104        if self.field('qobjectMacro'):
105            block += '    Q_OBJECT\n'
106            block += '\n'
107
108        block += 'public:\n'
109
110        if self.field('qobjectCtor'):
111            block += '    ' + className + '(QObject *parent = 0);\n'
112        elif self.field('qwidgetCtor'):
113            block += '    ' + className + '(QWidget *parent = 0);\n'
114        elif self.field('defaultCtor'):
115            block += '    ' + className + '();\n'
116
117            if self.field('copyCtor'):
118                block += '    ' + className + '(const ' + className + ' &other);\n'
119                block += '\n'
120                block += '    ' + className + ' &operator=' + '(const ' + className + ' &other);\n'
121
122        block += '};\n'
123
124        if self.field('protect'):
125            block += '\n'
126            block += '#endif\n'
127
128        headerFile = QFile(outputDir + '/' + header)
129
130        if not headerFile.open(QFile.WriteOnly | QFile.Text):
131            QMessageBox.warning(None, "Class Wizard",
132                    "Cannot write file %s:\n%s" % (headerFile.fileName(), headerFile.errorString()))
133            return
134
135        headerFile.write(block)
136
137        block = ''
138
139        if self.field('comment'):
140            block += '/*\n'
141            block += '    ' + implementation + '\n'
142            block += '*/\n'
143            block += '\n'
144
145        block += '#include "' + header + '"\n'
146        block += '\n'
147
148        if self.field('qobjectCtor'):
149            block += className + '::' + className + '(QObject *parent)\n'
150            block += '    : ' + baseClass + '(parent)\n'
151            block += '{\n'
152            block += '}\n'
153        elif self.field('qwidgetCtor'):
154            block += className + '::' + className + '(QWidget *parent)\n'
155            block += '    : ' + baseClass + '(parent)\n'
156            block += '{\n'
157            block += '}\n'
158        elif self.field('defaultCtor'):
159            block += className + '::' + className + '()\n'
160            block += '{\n'
161            block += '    // missing code\n'
162            block += '}\n'
163
164            if self.field('copyCtor'):
165                block += '\n'
166                block += className + '::' + className + '(const ' + className + ' &other)\n'
167                block += '{\n'
168                block += '    *this = other;\n'
169                block += '}\n'
170                block += '\n'
171                block += className + ' &' + className + '::operator=(const ' + className + ' &other)\n'
172                block += '{\n'
173
174                if baseClass:
175                    block += '    ' + baseClass + '::operator=(other);\n'
176
177                block += '    // missing code\n'
178                block += '    return *this;\n'
179                block += '}\n'
180
181        implementationFile = QFile(outputDir + '/' + implementation)
182
183        if not implementationFile.open(QFile.WriteOnly | QFile.Text):
184            QMessageBox.warning(None, "Class Wizard",
185                    "Cannot write file %s:\n%s" % (implementationFile.fileName(), implementationFile.errorString()))
186            return
187
188        implementationFile.write(block)
189
190        super(ClassWizard, self).accept()
191
192
193class IntroPage(QWizardPage):
194    def __init__(self, parent=None):
195        super(IntroPage, self).__init__(parent)
196
197        self.setTitle("Introduction")
198        self.setPixmap(QWizard.WatermarkPixmap,
199                QPixmap(':/images/watermark1.png'))
200
201        label = QLabel("This wizard will generate a skeleton C++ class "
202                "definition, including a few functions. You simply need to "
203                "specify the class name and set a few options to produce a "
204                "header file and an implementation file for your new C++ "
205                "class.")
206        label.setWordWrap(True)
207
208        layout = QVBoxLayout()
209        layout.addWidget(label)
210        self.setLayout(layout)
211
212
213class ClassInfoPage(QWizardPage):
214    def __init__(self, parent=None):
215        super(ClassInfoPage, self).__init__(parent)
216
217        self.setTitle("Class Information")
218        self.setSubTitle("Specify basic information about the class for "
219                "which you want to generate skeleton source code files.")
220        self.setPixmap(QWizard.LogoPixmap, QPixmap(':/images/logo1.png'))
221
222        classNameLabel = QLabel("&Class name:")
223        classNameLineEdit = QLineEdit()
224        classNameLabel.setBuddy(classNameLineEdit)
225
226        baseClassLabel = QLabel("B&ase class:")
227        baseClassLineEdit = QLineEdit()
228        baseClassLabel.setBuddy(baseClassLineEdit)
229
230        qobjectMacroCheckBox = QCheckBox("Generate Q_OBJECT &macro")
231
232        groupBox = QGroupBox("C&onstructor")
233
234        qobjectCtorRadioButton = QRadioButton("&QObject-style constructor")
235        qwidgetCtorRadioButton = QRadioButton("Q&Widget-style constructor")
236        defaultCtorRadioButton = QRadioButton("&Default constructor")
237        copyCtorCheckBox = QCheckBox("&Generate copy constructor and operator=")
238
239        defaultCtorRadioButton.setChecked(True)
240
241        defaultCtorRadioButton.toggled.connect(copyCtorCheckBox.setEnabled)
242
243        self.registerField('className*', classNameLineEdit)
244        self.registerField('baseClass', baseClassLineEdit)
245        self.registerField('qobjectMacro', qobjectMacroCheckBox)
246        self.registerField('qobjectCtor', qobjectCtorRadioButton)
247        self.registerField('qwidgetCtor', qwidgetCtorRadioButton)
248        self.registerField('defaultCtor', defaultCtorRadioButton)
249        self.registerField('copyCtor', copyCtorCheckBox)
250
251        groupBoxLayout = QVBoxLayout()
252        groupBoxLayout.addWidget(qobjectCtorRadioButton)
253        groupBoxLayout.addWidget(qwidgetCtorRadioButton)
254        groupBoxLayout.addWidget(defaultCtorRadioButton)
255        groupBoxLayout.addWidget(copyCtorCheckBox)
256        groupBox.setLayout(groupBoxLayout)
257
258        layout = QGridLayout()
259        layout.addWidget(classNameLabel, 0, 0)
260        layout.addWidget(classNameLineEdit, 0, 1)
261        layout.addWidget(baseClassLabel, 1, 0)
262        layout.addWidget(baseClassLineEdit, 1, 1)
263        layout.addWidget(qobjectMacroCheckBox, 2, 0, 1, 2)
264        layout.addWidget(groupBox, 3, 0, 1, 2)
265        self.setLayout(layout)
266
267
268class CodeStylePage(QWizardPage):
269    def __init__(self, parent=None):
270        super(CodeStylePage, self).__init__(parent)
271
272        self.setTitle("Code Style Options")
273        self.setSubTitle("Choose the formatting of the generated code.")
274        self.setPixmap(QWizard.LogoPixmap, QPixmap(':/images/logo2.png'))
275
276        commentCheckBox = QCheckBox("&Start generated files with a comment")
277        commentCheckBox.setChecked(True)
278
279        protectCheckBox = QCheckBox("&Protect header file against multiple "
280                "inclusions")
281        protectCheckBox.setChecked(True)
282
283        macroNameLabel = QLabel("&Macro name:")
284        self.macroNameLineEdit = QLineEdit()
285        macroNameLabel.setBuddy(self.macroNameLineEdit)
286
287        self.includeBaseCheckBox = QCheckBox("&Include base class definition")
288        self.baseIncludeLabel = QLabel("Base class include:")
289        self.baseIncludeLineEdit = QLineEdit()
290        self.baseIncludeLabel.setBuddy(self.baseIncludeLineEdit)
291
292        protectCheckBox.toggled.connect(macroNameLabel.setEnabled)
293        protectCheckBox.toggled.connect(self.macroNameLineEdit.setEnabled)
294        self.includeBaseCheckBox.toggled.connect(self.baseIncludeLabel.setEnabled)
295        self.includeBaseCheckBox.toggled.connect(self.baseIncludeLineEdit.setEnabled)
296
297        self.registerField('comment', commentCheckBox)
298        self.registerField('protect', protectCheckBox)
299        self.registerField('macroName', self.macroNameLineEdit)
300        self.registerField('includeBase', self.includeBaseCheckBox)
301        self.registerField('baseInclude', self.baseIncludeLineEdit)
302
303        layout = QGridLayout()
304        layout.setColumnMinimumWidth(0, 20)
305        layout.addWidget(commentCheckBox, 0, 0, 1, 3)
306        layout.addWidget(protectCheckBox, 1, 0, 1, 3)
307        layout.addWidget(macroNameLabel, 2, 1)
308        layout.addWidget(self.macroNameLineEdit, 2, 2)
309        layout.addWidget(self.includeBaseCheckBox, 3, 0, 1, 3)
310        layout.addWidget(self.baseIncludeLabel, 4, 1)
311        layout.addWidget(self.baseIncludeLineEdit, 4, 2)
312        self.setLayout(layout)
313
314    def initializePage(self):
315        className = self.field('className')
316        self.macroNameLineEdit.setText(className.upper() + "_H")
317
318        baseClass = self.field('baseClass')
319        is_baseClass = bool(baseClass)
320
321        self.includeBaseCheckBox.setChecked(is_baseClass)
322        self.includeBaseCheckBox.setEnabled(is_baseClass)
323        self.baseIncludeLabel.setEnabled(is_baseClass)
324        self.baseIncludeLineEdit.setEnabled(is_baseClass)
325
326        if not is_baseClass:
327            self.baseIncludeLineEdit.clear()
328        elif QRegExp('Q[A-Z].*').exactMatch(baseClass):
329            self.baseIncludeLineEdit.setText('<' + baseClass + '>')
330        else:
331            self.baseIncludeLineEdit.setText('"' + baseClass.lower() + '.h"')
332
333
334class OutputFilesPage(QWizardPage):
335    def __init__(self, parent=None):
336        super(OutputFilesPage, self).__init__(parent)
337
338        self.setTitle("Output Files")
339        self.setSubTitle("Specify where you want the wizard to put the "
340                "generated skeleton code.")
341        self.setPixmap(QWizard.LogoPixmap, QPixmap(':/images/logo3.png'))
342
343        outputDirLabel = QLabel("&Output directory:")
344        self.outputDirLineEdit = QLineEdit()
345        outputDirLabel.setBuddy(self.outputDirLineEdit)
346
347        headerLabel = QLabel("&Header file name:")
348        self.headerLineEdit = QLineEdit()
349        headerLabel.setBuddy(self.headerLineEdit)
350
351        implementationLabel = QLabel("&Implementation file name:")
352        self.implementationLineEdit = QLineEdit()
353        implementationLabel.setBuddy(self.implementationLineEdit)
354
355        self.registerField('outputDir*', self.outputDirLineEdit)
356        self.registerField('header*', self.headerLineEdit)
357        self.registerField('implementation*', self.implementationLineEdit)
358
359        layout = QGridLayout()
360        layout.addWidget(outputDirLabel, 0, 0)
361        layout.addWidget(self.outputDirLineEdit, 0, 1)
362        layout.addWidget(headerLabel, 1, 0)
363        layout.addWidget(self.headerLineEdit, 1, 1)
364        layout.addWidget(implementationLabel, 2, 0)
365        layout.addWidget(self.implementationLineEdit, 2, 1)
366        self.setLayout(layout)
367
368    def initializePage(self):
369        className = self.field('className')
370        self.headerLineEdit.setText(className.lower() + '.h')
371        self.implementationLineEdit.setText(className.lower() + '.cpp')
372        self.outputDirLineEdit.setText(QDir.toNativeSeparators(QDir.tempPath()))
373
374
375class ConclusionPage(QWizardPage):
376    def __init__(self, parent=None):
377        super(ConclusionPage, self).__init__(parent)
378
379        self.setTitle("Conclusion")
380        self.setPixmap(QWizard.WatermarkPixmap,
381                QPixmap(':/images/watermark2.png'))
382
383        self.label = QLabel()
384        self.label.setWordWrap(True)
385
386        layout = QVBoxLayout()
387        layout.addWidget(self.label)
388        self.setLayout(layout)
389
390    def initializePage(self):
391        finishText = self.wizard().buttonText(QWizard.FinishButton)
392        finishText.replace('&', '')
393        self.label.setText("Click %s to generate the class skeleton." % finishText)
394
395
396if __name__ == '__main__':
397
398    import sys
399
400    app = QApplication(sys.argv)
401    wizard = ClassWizard()
402    wizard.show()
403    sys.exit(app.exec_())
404