1 /****************************************************************************
2 **
3 ** Copyright (C) 2019 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the tools applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28 
29 #include "pythonwriteimports.h"
30 
31 #include <customwidgetsinfo.h>
32 #include <option.h>
33 #include <uic.h>
34 
35 #include <ui4.h>
36 
37 #include <QtCore/qtextstream.h>
38 
39 QT_BEGIN_NAMESPACE
40 
41 static const char *standardImports =
42 R"I(from PySide2.QtCore import *
43 from PySide2.QtGui import *
44 from PySide2.QtWidgets import *
45 )I";
46 
47 // Change the name of a qrc file "dir/foo.qrc" file to the Python
48 // module name "foo_rc" according to project conventions.
pythonResource(QString resource)49 static QString pythonResource(QString resource)
50 {
51     const int lastSlash = resource.lastIndexOf(QLatin1Char('/'));
52     if (lastSlash != -1)
53         resource.remove(0, lastSlash + 1);
54     if (resource.endsWith(QLatin1String(".qrc"))) {
55         resource.chop(4);
56         resource.append(QLatin1String("_rc"));
57     }
58     return resource;
59 }
60 
61 namespace Python {
62 
WriteImports(Uic * uic)63 WriteImports::WriteImports(Uic *uic) : m_uic(uic)
64 {
65 }
66 
acceptUI(DomUI * node)67 void WriteImports::acceptUI(DomUI *node)
68 {
69     auto &output = m_uic->output();
70     output << standardImports << '\n';
71     if (auto customWidgets = node->elementCustomWidgets()) {
72         TreeWalker::acceptCustomWidgets(customWidgets);
73         output << '\n';
74     }
75 
76     if (auto resources = node->elementResources()) {
77         const auto includes = resources->elementInclude();
78         for (auto include : includes) {
79             if (include->hasAttributeLocation())
80                 writeImport(pythonResource(include->attributeLocation()));
81         }
82         output << '\n';
83     }
84 }
85 
writeImport(const QString & module)86 void WriteImports::writeImport(const QString &module)
87 {
88 
89     if (m_uic->option().fromImports)
90         m_uic->output() << "from  . ";
91     m_uic->output() << "import " << module << '\n';
92 }
93 
qtModuleOf(const DomCustomWidget * node) const94 QString WriteImports::qtModuleOf(const DomCustomWidget *node) const
95 {
96     if (m_uic->customWidgetsInfo()->extends(node->elementClass(), QLatin1String("QAxWidget")))
97         return QStringLiteral("QtAxContainer");
98     if (const auto headerElement = node->elementHeader()) {
99         const auto &header = headerElement->text();
100         if (header.startsWith(QLatin1String("Qt"))) {
101             const int slash = header.indexOf(QLatin1Char('/'));
102             if (slash != -1)
103                 return header.left(slash);
104         }
105     }
106     return QString();
107 }
108 
acceptCustomWidget(DomCustomWidget * node)109 void WriteImports::acceptCustomWidget(DomCustomWidget *node)
110 {
111     const auto &className = node->elementClass();
112     if (className.contains(QLatin1String("::")))
113         return; // Exclude namespaced names (just to make tests pass).
114     const QString &importModule = qtModuleOf(node);
115     auto &output = m_uic->output();
116     // For starting importing PySide2 modules
117     if (!importModule.isEmpty()) {
118         output << "from ";
119         if (importModule.startsWith(QLatin1String("Qt")))
120             output << "PySide2.";
121         output << importModule;
122         if (!className.isEmpty())
123             output << " import " << className << "\n\n";
124     } else {
125         // When the elementHeader is not set, we know it's the continuation
126         // of a PySide2 import or a normal import of another module.
127         if (!node->elementHeader() || node->elementHeader()->text().isEmpty()) {
128             output << "import " << className << '\n';
129         } else { // When we do have elementHeader, we know it's a relative import.
130             QString modulePath = node->elementHeader()->text();
131             // Replace the '/' by '.'
132             modulePath.replace(QLatin1Char('/'), QLatin1Char('.'));
133             // '.h' is added by default on headers for <customwidget>
134             if (modulePath.endsWith(QLatin1String(".h")))
135                 modulePath.chop(2);
136             output << "from " << modulePath << " import " << className << '\n';
137         }
138     }
139 }
140 
141 } // namespace Python
142 
143 QT_END_NAMESPACE
144