diff options
author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2018-11-20 08:38:16 +0100 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2019-04-05 08:29:11 +0000 |
commit | bc8633036cb38de3dd8c27eb1e1f6a3aa39f1190 (patch) | |
tree | 1b817ca5c21d85b4de4cfe1d86692dcf763dfebf /src | |
parent | 89120c4a76fed3c62c019514c12e7fb7c063b4b7 (diff) |
uic: Add python
- Add command line option
- Add import (include) handling
- Add language helpers like streamable classes for Function definition,
object instantiation
- Implement header comment formatting
Task-number: PYSIDE-797
Change-Id: I15041ab16504ea159f6665781a829cd548585af1
Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
Diffstat (limited to 'src')
-rw-r--r-- | src/tools/uic/cpp/cppwriteincludes.h | 4 | ||||
-rw-r--r-- | src/tools/uic/cpp/cppwriteinitialization.cpp | 74 | ||||
-rw-r--r-- | src/tools/uic/main.cpp | 10 | ||||
-rw-r--r-- | src/tools/uic/python/python.pri | 8 | ||||
-rw-r--r-- | src/tools/uic/python/pythonwritedeclaration.cpp | 73 | ||||
-rw-r--r-- | src/tools/uic/python/pythonwritedeclaration.h | 62 | ||||
-rw-r--r-- | src/tools/uic/python/pythonwriteimports.cpp | 92 | ||||
-rw-r--r-- | src/tools/uic/python/pythonwriteimports.h | 58 | ||||
-rw-r--r-- | src/tools/uic/shared/language.cpp | 120 | ||||
-rw-r--r-- | src/tools/uic/shared/language.h | 82 | ||||
-rw-r--r-- | src/tools/uic/uic.cpp | 91 | ||||
-rw-r--r-- | src/tools/uic/uic.h | 3 | ||||
-rw-r--r-- | src/tools/uic/uic.pro | 1 |
13 files changed, 630 insertions, 48 deletions
diff --git a/src/tools/uic/cpp/cppwriteincludes.h b/src/tools/uic/cpp/cppwriteincludes.h index e9247f4cc4..79cbd41014 100644 --- a/src/tools/uic/cpp/cppwriteincludes.h +++ b/src/tools/uic/cpp/cppwriteincludes.h @@ -40,6 +40,7 @@ QT_BEGIN_NAMESPACE class QTextStream; +class CustomWidgetsInfo; class Driver; class Uic; @@ -74,6 +75,9 @@ struct WriteIncludes : public TreeWalker void acceptIncludes(DomIncludes *node) override; void acceptInclude(DomInclude *node) override; +protected: + QTextStream &output() const { return m_output; } + private: void add(const QString &className, bool determineHeader = true, const QString &header = QString(), bool global = false); diff --git a/src/tools/uic/cpp/cppwriteinitialization.cpp b/src/tools/uic/cpp/cppwriteinitialization.cpp index 938d47f62d..f5ac59df08 100644 --- a/src/tools/uic/cpp/cppwriteinitialization.cpp +++ b/src/tools/uic/cpp/cppwriteinitialization.cpp @@ -71,7 +71,7 @@ namespace { // Write a statement to create a spacer item. void writeSpacerItem(const DomSpacer *node, QTextStream &output) { const QHash<QString, DomProperty *> properties = propertyMap(node->elementProperty()); - output << "new QSpacerItem("; + output << language::operatorNew << "QSpacerItem("; int w = 0; int h = 0; @@ -495,8 +495,9 @@ void WriteInitialization::acceptUI(DomUI *node) const QString widgetClassName = node->elementWidget()->attributeClass(); - m_output << m_option.indent << "void " << "setupUi(" << widgetClassName << " *" << varName << ")\n" - << m_option.indent << "{\n"; + const QString parameterType = widgetClassName + QLatin1String(" *"); + m_output << m_option.indent + << language::startFunctionDefinition1("setupUi", parameterType, varName, m_option.indent); const QStringList connections = m_uic->databaseInfo()->connections(); for (int i=0; i<connections.size(); ++i) { @@ -550,7 +551,7 @@ void WriteInitialization::acceptUI(DomUI *node) if (m_option.autoConnection) m_output << "\n" << m_indent << "QMetaObject::connectSlotsByName(" << varName << ");\n"; - m_output << m_option.indent << "} // setupUi\n\n"; + m_output << m_option.indent << language::endFunctionDefinition("setupUi"); if (!m_mainFormUsedInRetranslateUi) { m_refreshInitialization += m_indent; @@ -559,10 +560,10 @@ void WriteInitialization::acceptUI(DomUI *node) m_refreshInitialization += QLatin1String(");\n"); } - m_output << m_option.indent << "void " << "retranslateUi(" << widgetClassName << " *" << varName << ")\n" - << m_option.indent << "{\n" + m_output << m_option.indent + << language::startFunctionDefinition1("retranslateUi", parameterType, varName, m_option.indent) << m_refreshInitialization - << m_option.indent << "} // retranslateUi\n\n"; + << m_option.indent << language::endFunctionDefinition("retranslateUi"); m_layoutChain.pop(); m_widgetChain.pop(); @@ -612,8 +613,9 @@ void WriteInitialization::acceptWidget(DomWidget *node) const auto *cwi = m_uic->customWidgetsInfo(); if (m_widgetChain.size() != 1) { - m_output << m_indent << varName << " = new " << cwi->realClassName(className) - << '(' << parentWidget << ");\n"; + m_output << m_indent << varName << " = " << language::operatorNew + << language::fixClassName(cwi->realClassName(className)) + << '(' << parentWidget << ')' << language::eol; } parentWidget = savedParentWidget; @@ -846,7 +848,8 @@ void WriteInitialization::addButtonGroup(const DomWidget *buttonNode, const QStr m_output << m_indent; if (createGroupOnTheFly) m_output << className << " *"; - m_output << groupName << " = new " << className << '(' << m_mainFormVarName << ");\n"; + m_output << groupName << " = " << language::operatorNew + << className << '(' << m_mainFormVarName << ')' << language::eol; m_buttonGroups.insert(groupName); writeProperties(groupName, className, group->elementProperty()); } @@ -863,7 +866,7 @@ void WriteInitialization::acceptLayout(DomLayout *node) bool isGroupBox = false; - m_output << m_indent << varName << " = new " << className << '('; + m_output << m_indent << varName << " = " << language::operatorNew << className << '('; if (!m_layoutChain.top() && !isGroupBox) m_output << m_driver->findOrInsertWidget(m_widgetChain.top()); @@ -1037,7 +1040,8 @@ void WriteInitialization::acceptActionGroup(DomActionGroup *node) if (m_actionGroupChain.top()) varName = m_driver->findOrInsertActionGroup(m_actionGroupChain.top()); - m_output << m_indent << actionName << " = new QActionGroup(" << varName << ");\n"; + m_output << m_indent << actionName << " = " << language::operatorNew + << "QActionGroup(" << varName << ");\n"; writeProperties(actionName, QLatin1String("QActionGroup"), node->elementProperty()); m_actionGroupChain.push(node); @@ -1057,7 +1061,8 @@ void WriteInitialization::acceptAction(DomAction *node) if (m_actionGroupChain.top()) varName = m_driver->findOrInsertActionGroup(m_actionGroupChain.top()); - m_output << m_indent << actionName << " = new QAction(" << varName << ");\n"; + m_output << m_indent << actionName << " = " << language::operatorNew + << "QAction(" << varName << ')' << language::eol; writeProperties(actionName, QLatin1String("QAction"), node->elementProperty()); } @@ -1322,8 +1327,8 @@ void WriteInitialization::writeProperties(const QString &varName, case DomProperty::Palette: { const DomPalette *pal = p->elementPalette(); const QString paletteName = m_driver->unique(QLatin1String("palette")); - m_output << m_indent << "QPalette " << paletteName << ";\n"; - + m_output << m_indent << language::stackVariable("QPalette", paletteName) + << language::eol; writeColorGroup(pal->elementActive(), QLatin1String("QPalette::Active"), paletteName); writeColorGroup(pal->elementInactive(), QLatin1String("QPalette::Inactive"), paletteName); writeColorGroup(pal->elementDisabled(), QLatin1String("QPalette::Disabled"), paletteName); @@ -1512,20 +1517,15 @@ QString WriteInitialization::writeSizePolicy(const DomSizePolicy *sp) const QString spName = m_driver->unique(QLatin1String("sizePolicy")); m_sizePolicyNameMap.insert(sizePolicyHandle, spName); - m_output << m_indent << "QSizePolicy " << spName; - do { - if (sp->hasElementHSizeType() && sp->hasElementVSizeType()) { - m_output << "(QSizePolicy::" << language::sizePolicy(sp->elementHSizeType()) - << ", QSizePolicy::" << language::sizePolicy(sp->elementVSizeType()) << ");\n"; - break; - } - if (sp->hasAttributeHSizeType() && sp->hasAttributeVSizeType()) { - m_output << "(QSizePolicy::" << sp->attributeHSizeType() << ", QSizePolicy::" - << sp->attributeVSizeType() << ");\n"; - break; - } - m_output << ";\n"; - } while (false); + m_output << m_indent << language::stackVariableWithInitParameters("QSizePolicy", spName); + if (sp->hasElementHSizeType() && sp->hasElementVSizeType()) { + m_output << "QSizePolicy" << language::qualifier << language::sizePolicy(sp->elementHSizeType()) + << ", QSizePolicy" << language::qualifier << language::sizePolicy(sp->elementVSizeType()); + } else if (sp->hasAttributeHSizeType() && sp->hasAttributeVSizeType()) { + m_output << "QSizePolicy::" << sp->attributeHSizeType() << ", QSizePolicy::" + << sp->attributeVSizeType(); + } + m_output << ')' << language::eol; m_output << m_indent << spName << ".setHorizontalStretch(" << sp->elementHorStretch() << ");\n"; @@ -1549,7 +1549,8 @@ QString WriteInitialization::writeFontProperties(const DomFont *f) const QString fontName = m_driver->unique(QLatin1String("font")); m_fontPropertiesNameMap.insert(FontHandle(f), fontName); - m_output << m_indent << "QFont " << fontName << ";\n"; + m_output << m_indent << language::stackVariable("QFont", fontName) + << language::eol; if (f->hasElementFamily() && !f->elementFamily().isEmpty()) { m_output << m_indent << fontName << ".setFamily(" << language::qstring(f->elementFamily(), m_dindent) << ");\n"; @@ -2512,7 +2513,8 @@ QString WriteInitialization::Item::writeSetupUi(const QString &parent, Item::Emp bool generateMultiDirective = false; if (emptyItemPolicy == Item::ConstructItemOnly && m_children.isEmpty()) { if (m_setupUiData.policy == ItemData::DontGenerate) { - m_setupUiStream << m_indent << "new " << m_itemClassName << '(' << parent << ");\n"; + m_setupUiStream << m_indent << language::operatorNew << m_itemClassName + << '(' << parent << ')' << language::eol; return QString(); } if (m_setupUiData.policy == ItemData::GenerateWithMultiDirective) @@ -2523,11 +2525,17 @@ QString WriteInitialization::Item::writeSetupUi(const QString &parent, Item::Emp generateMultiDirectiveBegin(m_setupUiStream, m_setupUiData.directives); const QString uniqueName = m_driver->unique(QLatin1String("__") + m_itemClassName.toLower()); - m_setupUiStream << m_indent << m_itemClassName << " *" << uniqueName << " = new " << m_itemClassName << '(' << parent << ");\n"; + m_setupUiStream << m_indent; + if (language::language() == Language::Cpp) + m_setupUiStream << m_itemClassName << " *"; + m_setupUiStream << uniqueName + << " = " << language::operatorNew << m_itemClassName << '(' << parent + << ')' << language::eol; if (generateMultiDirective) { m_setupUiStream << "#else\n"; - m_setupUiStream << m_indent << "new " << m_itemClassName << '(' << parent << ");\n"; + m_setupUiStream << m_indent << language::operatorNew << m_itemClassName + << '(' << parent << ')' << language::eol; generateMultiDirectiveEnd(m_setupUiStream, m_setupUiData.directives); } diff --git a/src/tools/uic/main.cpp b/src/tools/uic/main.cpp index 0516b854ff..166fe78ff7 100644 --- a/src/tools/uic/main.cpp +++ b/src/tools/uic/main.cpp @@ -29,6 +29,7 @@ #include "uic.h" #include "option.h" #include "driver.h" +#include <language.h> #include <qfile.h> #include <qdir.h> @@ -96,7 +97,7 @@ int runUic(int argc, char *argv[]) QCommandLineOption generatorOption(QStringList() << QStringLiteral("g") << QStringLiteral("generator")); generatorOption.setDescription(QStringLiteral("Select generator.")); - generatorOption.setValueName(QStringLiteral("java|cpp")); + generatorOption.setValueName(QStringLiteral("python|cpp")); parser.addOption(generatorOption); QCommandLineOption idBasedOption(QStringLiteral("idbased")); @@ -116,6 +117,13 @@ int runUic(int argc, char *argv[]) driver.option().translateFunction = parser.value(translateOption); driver.option().includeFile = parser.value(includeOption); + Language language = Language::Cpp; + if (parser.isSet(generatorOption)) { + if (parser.value(generatorOption).compare(QLatin1String("python")) == 0) + language = Language::Python; + } + language::setLanguage(language); + if (parser.isSet(noStringLiteralOption)) fprintf(stderr, "The -s, --no-stringliteral option is deprecated and it won't take any effect.\n"); diff --git a/src/tools/uic/python/python.pri b/src/tools/uic/python/python.pri new file mode 100644 index 0000000000..4dd31160a1 --- /dev/null +++ b/src/tools/uic/python/python.pri @@ -0,0 +1,8 @@ +INCLUDEPATH += $$PWD + +# Input +HEADERS += $$PWD/pythonwritedeclaration.h \ + $$PWD/pythonwriteimports.h + +SOURCES += $$PWD/pythonwritedeclaration.cpp \ + $$PWD/pythonwriteimports.cpp diff --git a/src/tools/uic/python/pythonwritedeclaration.cpp b/src/tools/uic/python/pythonwritedeclaration.cpp new file mode 100644 index 0000000000..5122b18e23 --- /dev/null +++ b/src/tools/uic/python/pythonwritedeclaration.cpp @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "pythonwritedeclaration.h" +#include <cppwriteinitialization.h> +#include <language.h> +#include <driver.h> +#include <ui4.h> +#include <uic.h> + +#include <QtCore/qtextstream.h> +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +namespace Python { + +WriteDeclaration::WriteDeclaration(Uic *uic) : + m_uic(uic), + m_driver(uic->driver()), + m_output(uic->output()), + m_option(uic->option()) +{ +} + +void WriteDeclaration::acceptUI(DomUI *node) +{ + // remove any left-over C++ namespaces + const QString qualifiedClassName = QLatin1String("Ui_") + node->elementClass() + + m_option.postfix; + m_output << "class " << language::fixClassName(qualifiedClassName) << "(object):\n"; + + TreeWalker::acceptWidget(node->elementWidget()); + if (const DomButtonGroups *domButtonGroups = node->elementButtonGroups()) + acceptButtonGroups(domButtonGroups); + CPP::WriteInitialization(m_uic).acceptUI(node); +} + +// Register button groups to prevent the on-the-fly creation legacy +// feature from triggering +void WriteDeclaration::acceptButtonGroup(const DomButtonGroup *buttonGroup) +{ + m_driver->findOrInsertButtonGroup(buttonGroup); +} + +} // namespace Python + +QT_END_NAMESPACE diff --git a/src/tools/uic/python/pythonwritedeclaration.h b/src/tools/uic/python/pythonwritedeclaration.h new file mode 100644 index 0000000000..a8d50b6fbf --- /dev/null +++ b/src/tools/uic/python/pythonwritedeclaration.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PYTHONWRITEDECLARATION_H +#define PYTHONWRITEDECLARATION_H + +#include <treewalker.h> + +QT_BEGIN_NAMESPACE + +class QTextStream; +class Driver; +class Uic; + +struct Option; + +namespace Python { + +struct WriteDeclaration : public TreeWalker +{ + WriteDeclaration(Uic *uic); + + void acceptUI(DomUI *node) override; + void acceptButtonGroup(const DomButtonGroup *buttonGroup) override; + +private: + Uic *m_uic; + Driver *m_driver; + QTextStream &m_output; + const Option &m_option; +}; + +} // namespace Python + +QT_END_NAMESPACE + +#endif // PYTHONWRITEDECLARATION_H diff --git a/src/tools/uic/python/pythonwriteimports.cpp b/src/tools/uic/python/pythonwriteimports.cpp new file mode 100644 index 0000000000..8e11981f37 --- /dev/null +++ b/src/tools/uic/python/pythonwriteimports.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "pythonwriteimports.h" + +#include <customwidgetsinfo.h> +#include <uic.h> + +#include <ui4.h> + +#include <QtCore/qtextstream.h> + +QT_BEGIN_NAMESPACE + +static const char *standardImports = +R"I(from PySide2.QtCore import (QCoreApplication, QMetaObject, QObject, QPoint, + QRect, QSize, QUrl, Qt) +from PySide2.QtGui import (QColor, QFont, QIcon, QPixmap) +from PySide2.QtWidgets import * +)I"; + +namespace Python { + +WriteImports::WriteImports(Uic *uic) : m_uic(uic) +{ +} + +void WriteImports::acceptUI(DomUI *node) +{ + auto &output = m_uic->output(); + output << standardImports << '\n'; + if (auto customWidgets = node->elementCustomWidgets()) { + TreeWalker::acceptCustomWidgets(customWidgets); + output << '\n'; + } +} + +QString WriteImports::qtModuleOf(const DomCustomWidget *node) const +{ + if (m_uic->customWidgetsInfo()->extends(node->elementClass(), QLatin1String("QAxWidget"))) + return QStringLiteral("QtAxContainer"); + if (const auto headerElement = node->elementHeader()) { + const auto &header = headerElement->text(); + if (header.startsWith(QLatin1String("Qt"))) { + const int slash = header.indexOf(QLatin1Char('/')); + if (slash != -1) + return header.left(slash); + } + } + return QString(); +} + +void WriteImports::acceptCustomWidget(DomCustomWidget *node) +{ + const auto &className = node->elementClass(); + if (className.contains(QLatin1String("::"))) + return; // Exclude namespaced names (just to make tests pass). + const QString &qtModule = qtModuleOf(node); + auto &output = m_uic->output(); + if (!qtModule.isEmpty()) + output << "from PySide2." << qtModule << ' '; + output << "import " << className << '\n'; +} + +} // namespace Python + +QT_END_NAMESPACE diff --git a/src/tools/uic/python/pythonwriteimports.h b/src/tools/uic/python/pythonwriteimports.h new file mode 100644 index 0000000000..427cbb48b1 --- /dev/null +++ b/src/tools/uic/python/pythonwriteimports.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PYTHONWRITEIMPORTS_H +#define PYTHONWRITEIMPORTS_H + +#include <treewalker.h> + +QT_BEGIN_NAMESPACE + +class Uic; + +namespace Python { + +struct WriteImports : public TreeWalker +{ +public: + explicit WriteImports(Uic *uic); + + void acceptUI(DomUI *node) override; + void acceptCustomWidget(DomCustomWidget *node) override; + +private: + QString qtModuleOf(const DomCustomWidget *node) const; + + Uic *const m_uic; +}; + +} // namespace Python + +QT_END_NAMESPACE + +#endif // PYTHONWRITEIMPORTS_H diff --git a/src/tools/uic/shared/language.cpp b/src/tools/uic/shared/language.cpp index d0491b74e3..d05f3c935f 100644 --- a/src/tools/uic/shared/language.cpp +++ b/src/tools/uic/shared/language.cpp @@ -33,6 +33,48 @@ namespace language { static Encoding encoding = Encoding::Utf8; +static Language _language = Language::Cpp; + +Language language() { return _language; } + +void setLanguage(Language l) +{ + _language = l; + switch (_language) { + case Language::Cpp: + derefPointer = QLatin1String("->"); + nullPtr = QLatin1String("nullptr"); + operatorNew = QLatin1String("new "); + qtQualifier = QLatin1String("Qt::"); + qualifier = QLatin1String("::"); + self = QLatin1String(""); // for testing: change to "this->"; + eol = QLatin1String(";\n"); + encoding = Encoding::Utf8; + break; + case Language::Python: + derefPointer = QLatin1String("."); + nullPtr = QLatin1String("None"); + operatorNew = QLatin1String(""); + qtQualifier = QLatin1String("Qt."); + qualifier = QLatin1String("."); + self = QLatin1String("self."); + eol = QLatin1String("\n"); + encoding = Encoding::Unicode; + break; + } +} + +QString derefPointer; +QString nullPtr; +QString operatorNew; +QString qtQualifier; +QString qualifier; +QString self; +QString eol; + +QString cppQualifier = QLatin1String("::"); +QString cppTrue = QLatin1String("true"); +QString cppFalse = QLatin1String("false"); QTextStream &operator<<(QTextStream &str, const qtConfig &c) { @@ -71,6 +113,13 @@ const char *lookupEnum(const EnumLookup(&array)[N], int value, int defaultIndex return defaultValue; } +QString fixClassName(QString className) +{ + if (language() == Language::Python) + className.replace(cppQualifier, QLatin1String("_")); + return className; +} + const char *toolbarArea(int v) { static const EnumLookup toolBarAreas[] = @@ -250,12 +299,12 @@ void _formatString(QTextStream &str, const QString &value, const QString &indent switch (encoding) { // Special characters as 3 digit octal escapes (u8"\303\234mlaut") case Encoding::Utf8: { - if (qString) + if (qString && _language == Language::Cpp) str << "QString::fromUtf8("; const QByteArray utf8 = value.toUtf8(); formatStringSequence<Encoding::Utf8>(str, utf8.cbegin(), utf8.cend(), indent, 8, 3); - if (qString) + if (qString && _language == Language::Cpp) str << ')'; } break; @@ -268,4 +317,71 @@ void _formatString(QTextStream &str, const QString &value, const QString &indent } } +QTextStream &operator<<(QTextStream &str, const repeat &r) +{ + for (int i = 0; i < r.m_count; ++i) + str << r.m_char; + return str; +} + +startFunctionDefinition1::startFunctionDefinition1(const char *name, const QString ¶meterType, + const QString ¶meterName, + const QString &indent, + const char *returnType) : + m_name(name), m_parameterType(parameterType), m_parameterName(parameterName), + m_indent(indent), m_return(returnType) +{ +} + +QTextStream &operator<<(QTextStream &str, const startFunctionDefinition1 &f) +{ + switch (language()) { + case Language::Cpp: + str << (f.m_return ? f.m_return : "void") << ' ' << f.m_name << '(' + << f.m_parameterType; + if (f.m_parameterType.cend()->isLetter()) + str << ' '; + str << f.m_parameterName << ')' << '\n' << f.m_indent << "{\n"; + break; + case Language::Python: + str << "def " << f.m_name << "(self, " << f.m_parameterName << "):\n"; + break; + } + return str; +} + +endFunctionDefinition::endFunctionDefinition(const char *name) : m_name(name) +{ +} + +QTextStream &operator<<(QTextStream &str, const endFunctionDefinition &f) +{ + switch (language()) { + case Language::Cpp: + str << "} // " << f.m_name << "\n\n"; + break; + case Language::Python: + str << "# " << f.m_name << "\n\n"; + break; + } + return str; +} + +void _formatStackVariable(QTextStream &str, const char *className, QStringView varName, + bool withInitParameters) +{ + switch (language()) { + case Language::Cpp: + str << className << ' ' << varName; + if (withInitParameters) + str << '('; + break; + case Language::Python: + str << varName << " = " << className << '('; + if (!withInitParameters) + str << ')'; + break; + } +} + } // namespace language diff --git a/src/tools/uic/shared/language.h b/src/tools/uic/shared/language.h index 04a3763e54..a1c871a549 100644 --- a/src/tools/uic/shared/language.h +++ b/src/tools/uic/shared/language.h @@ -34,8 +34,25 @@ QT_FORWARD_DECLARE_CLASS(QTextStream) +enum class Language { Cpp, Python }; + namespace language { +Language language(); +void setLanguage(Language); + +extern QString derefPointer; +extern QString nullPtr; +extern QString operatorNew; +extern QString qtQualifier; +extern QString qualifier; +extern QString self; +extern QString eol; + +extern QString cppQualifier; +extern QString cppTrue; +extern QString cppFalse; + // Base class for streamable objects with one QStringView parameter class StringViewStreamable { @@ -72,6 +89,8 @@ public: QTextStream &operator<<(QTextStream &, const closeQtConfig &c); +QString fixClassName(QString className); + const char *toolbarArea(int v); const char *sizePolicy(int v); const char *dockWidgetArea(int v); @@ -107,6 +126,69 @@ inline QTextStream &operator<<(QTextStream &str, const language::_string<AsQStri using charliteral = _string<false>; using qstring = _string<true>; +class repeat { +public: + explicit repeat(int count, char c) : m_count(count), m_char(c) {} + + friend QTextStream &operator<<(QTextStream &str, const repeat &r); + +private: + const int m_count; + const char m_char; +}; + +class startFunctionDefinition1 { +public: + explicit startFunctionDefinition1(const char *name, const QString ¶meterType, + const QString ¶meterName, + const QString &indent, + const char *returnType = nullptr); + + friend QTextStream &operator<<(QTextStream &str, const startFunctionDefinition1 &f); +private: + const char *m_name; + const QString &m_parameterType; + const QString &m_parameterName; + const QString &m_indent; + const char *m_return; +}; + +class endFunctionDefinition { +public: + explicit endFunctionDefinition(const char *name); + + friend QTextStream &operator<<(QTextStream &str, const endFunctionDefinition &f); +private: + const char *m_name; +}; + +void _formatStackVariable(QTextStream &str, const char *className, QStringView varName, bool withInitParameters); + +template <bool withInitParameters> +class _stackVariable { +public: + explicit _stackVariable(const char *className, QStringView varName) : + m_className(className), m_varName(varName) {} + + void format(QTextStream &str) const + { _formatStackVariable(str, m_className, m_varName, withInitParameters); } + +private: + const char *m_className; + QStringView m_varName; + QStringView m_parameters; +}; + +template <bool withInitParameters> +inline QTextStream &operator<<(QTextStream &str, const _stackVariable<withInitParameters> &s) +{ + s.format(str); + return str; +} + +using stackVariable = _stackVariable<false>; +using stackVariableWithInitParameters = _stackVariable<true>; + } // namespace language #endif // LANGUAGE_H diff --git a/src/tools/uic/uic.cpp b/src/tools/uic/uic.cpp index 225dc6aeb2..b95f1a784b 100644 --- a/src/tools/uic/uic.cpp +++ b/src/tools/uic/uic.cpp @@ -35,6 +35,10 @@ #include "cppwriteincludes.h" #include "cppwritedeclaration.h" +#include <pythonwritedeclaration.h> +#include <pythonwriteimports.h> + +#include <language.h> #include <qxmlstream.h> #include <qfileinfo.h> @@ -103,7 +107,7 @@ bool Uic::printDependencies() return true; } -void Uic::writeCopyrightHeader(DomUI *ui) +void Uic::writeCopyrightHeaderCpp(const DomUI *ui) const { QString comment = ui->elementComment(); if (!comment.isEmpty()) @@ -118,6 +122,48 @@ void Uic::writeCopyrightHeader(DomUI *ui) out << "********************************************************************************/\n\n"; } +// Format existing UI file comments for Python with some smartness : Replace all +// leading C++ comment characters by '#' or prepend '#' if needed. + +static inline bool isCppCommentChar(QChar c) +{ + return c == QLatin1Char('/') || c == QLatin1Char('*'); +} + +static int leadingCppCommentCharCount(const QStringRef &s) +{ + int i = 0; + for (const int size = s.size(); i < size && isCppCommentChar(s.at(i)); ++i) { + } + return i; +} + +void Uic::writeCopyrightHeaderPython(const DomUI *ui) const +{ + QString comment = ui->elementComment(); + if (!comment.isEmpty()) { + const auto lines = comment.splitRef(QLatin1Char('\n')); + for (const auto &line : lines) { + if (const int leadingCommentChars = leadingCppCommentCharCount(line)) { + out << language::repeat(leadingCommentChars, '#') + << line.right(line.size() - leadingCommentChars); + } else { + if (!line.startsWith(QLatin1Char('#'))) + out << "# "; + out << line; + } + out << '\n'; + } + out << '\n'; + } + + out << language::repeat(80, '#') << "\n## Form generated from reading UI file '" + << QFileInfo(opt.inputFile).fileName() + << "'\n##\n## Created by: Qt User Interface Compiler version " << QT_VERSION_STR + << "\n##\n## WARNING! All changes made in this file will be lost when recompiling UI file!\n" + << language::repeat(80, '#') << "\n\n"; +} + // Check the version with a stream reader at the <ui> element. static double versionFromUiAttribute(QXmlStreamReader &reader) @@ -195,15 +241,26 @@ bool Uic::write(QIODevice *in) bool Uic::write(DomUI *ui) { - using namespace CPP; - if (!ui || !ui->elementWidget()) return false; - if (opt.copyrightHeader) - writeCopyrightHeader(ui); + const auto lang = language::language(); + + if (lang == Language::Python) + out << "# -*- coding: utf-8 -*-\n\n"; - if (opt.headerProtection) { + if (opt.copyrightHeader) { + switch (language::language()) { + case Language::Cpp: + writeCopyrightHeaderCpp(ui); + break; + case Language::Python: + writeCopyrightHeaderPython(ui); + break; + } + } + + if (opt.headerProtection && lang == Language::Cpp) { writeHeaderProtectionStart(); out << "\n"; } @@ -218,13 +275,25 @@ bool Uic::write(DomUI *ui) info.acceptUI(ui); cWidgetsInfo.acceptUI(ui); - WriteIncludes writeIncludes(this); - writeIncludes.acceptUI(ui); - Validator(this).acceptUI(ui); - WriteDeclaration(this).acceptUI(ui); + switch (language::language()) { + case Language::Cpp: { + CPP::WriteIncludes writeIncludes(this); + writeIncludes.acceptUI(ui); + Validator(this).acceptUI(ui); + CPP::WriteDeclaration(this).acceptUI(ui); + } + break; + case Language::Python: { + Python::WriteImports writeImports(this); + writeImports.acceptUI(ui); + Validator(this).acceptUI(ui); + Python::WriteDeclaration(this).acceptUI(ui); + } + break; + } - if (opt.headerProtection) + if (opt.headerProtection && lang == Language::Cpp) writeHeaderProtectionEnd(); return true; diff --git a/src/tools/uic/uic.h b/src/tools/uic/uic.h index af5f42c6db..f3dfd49149 100644 --- a/src/tools/uic/uic.h +++ b/src/tools/uic/uic.h @@ -91,7 +91,8 @@ public: private: // copyright header - void writeCopyrightHeader(DomUI *ui); + void writeCopyrightHeaderCpp(const DomUI *ui) const; + void writeCopyrightHeaderPython(const DomUI *ui) const; DomUI *parseUiFile(QXmlStreamReader &reader); // header protection diff --git a/src/tools/uic/uic.pro b/src/tools/uic/uic.pro index 4469ce50e5..1bf8ef8ad3 100644 --- a/src/tools/uic/uic.pro +++ b/src/tools/uic/uic.pro @@ -7,6 +7,7 @@ DEFINES += QT_UIC QT_NO_CAST_FROM_ASCII QT_NO_FOREACH include(uic.pri) include(shared/shared.pri) include(cpp/cpp.pri) +include(python/python.pri) HEADERS += uic.h |