summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFriedemann Kleint <[email protected]>2023-01-13 13:43:44 +0100
committerQt Cherry-pick Bot <[email protected]>2023-02-01 22:58:08 +0000
commit113f91229a5a3e0c33328fdf48ff857b16fb2613 (patch)
tree4a874d19ca4b9ad3521b6fa6dfc2796df58b2f26
parent23c49af8a30ef4db5a1cc4c1915cbb86b71a03de (diff)
uic: Add option for absolute Python resource imports
Add option that generates an absolute Python import. import resources.rc_resources from a path like ../resources/resources.qrc assuming the project root is .. . Add an additional option to specify the import paths, from which the project root can be determined. Task-number: PYSIDE-2191 Change-Id: Ib444eb666217b8c010dba0079b0ffe9ddbaa3414 Reviewed-by: Cristian Maureira-Fredes <[email protected]> Reviewed-by: Shyamnath Premnadh <[email protected]> (cherry picked from commit 814d66d55860a1eb204b804871d579da95eabd8f) Reviewed-by: Qt Cherry-pick Bot <[email protected]>
-rw-r--r--src/tools/uic/main.cpp51
-rw-r--r--src/tools/uic/option.h11
-rw-r--r--src/tools/uic/python/pythonwriteimports.cpp70
-rw-r--r--src/tools/uic/python/pythonwriteimports.h3
4 files changed, 108 insertions, 27 deletions
diff --git a/src/tools/uic/main.cpp b/src/tools/uic/main.cpp
index c4dcec29d56..8728212f35c 100644
--- a/src/tools/uic/main.cpp
+++ b/src/tools/uic/main.cpp
@@ -13,11 +13,42 @@
#include <qcoreapplication.h>
#include <qcommandlineoption.h>
#include <qcommandlineparser.h>
+#include <qfileinfo.h>
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
+static const char pythonPathVar[] = "PYTHONPATH";
+
+// From the Python paths, find the component the UI file is under
+static QString pythonRoot(const QString &pythonPath, const QString &uiFileIn)
+{
+#ifdef Q_OS_WIN
+ static const Qt::CaseSensitivity fsSensitivity = Qt::CaseInsensitive;
+#else
+ static const Qt::CaseSensitivity fsSensitivity = Qt::CaseSensitive;
+#endif
+
+ if (pythonPath.isEmpty() || uiFileIn.isEmpty())
+ return {};
+ const QString uiFile = QFileInfo(uiFileIn).canonicalFilePath();
+ if (uiFile.isEmpty())
+ return {};
+ const auto uiFileSize = uiFile.size();
+ const auto paths = pythonPath.split(QDir::listSeparator(), Qt::SkipEmptyParts);
+ for (const auto &path : paths) {
+ const QString canonicalPath = QFileInfo(path).canonicalFilePath();
+ const auto canonicalPathSize = canonicalPath.size();
+ if (uiFileSize > canonicalPathSize
+ && uiFile.at(canonicalPathSize) == u'/'
+ && uiFile.startsWith(canonicalPath, fsSensitivity)) {
+ return canonicalPath;
+ }
+ }
+ return {};
+}
+
int runUic(int argc, char *argv[])
{
QHashSeed::setDeterministicGlobalSeed();
@@ -90,6 +121,10 @@ int runUic(int argc, char *argv[])
fromImportsOption.setDescription(u"Python: generate imports relative to '.'"_s);
parser.addOption(fromImportsOption);
+ QCommandLineOption absoluteImportsOption(u"absolute-imports"_s);
+ absoluteImportsOption.setDescription(u"Python: generate absolute imports"_s);
+ parser.addOption(absoluteImportsOption);
+
// FIXME Qt 7: Flip the default?
QCommandLineOption rcPrefixOption(u"rc-prefix"_s);
rcPrefixOption.setDescription(uR"(Python: Generate "rc_file" instead of "file_rc" import)"_s);
@@ -100,6 +135,11 @@ int runUic(int argc, char *argv[])
useStarImportsOption.setDescription(u"Python: Use * imports"_s);
parser.addOption(useStarImportsOption);
+ QCommandLineOption pythonPathOption(u"python-paths"_s);
+ pythonPathOption.setDescription(u"Python paths for --absolute-imports."_s);
+ pythonPathOption.setValueName(u"pathlist"_s);
+ parser.addOption(pythonPathOption);
+
parser.addPositionalArgument(u"[uifile]"_s, u"Input file (*.ui), otherwise stdin."_s);
parser.process(app);
@@ -130,10 +170,19 @@ int runUic(int argc, char *argv[])
}
language::setLanguage(language);
if (language == Language::Python) {
- driver.option().fromImports = parser.isSet(fromImportsOption);
+ if (parser.isSet(fromImportsOption))
+ driver.option().pythonResourceImport = Option::PythonResourceImport::FromDot;
+ else if (parser.isSet(absoluteImportsOption))
+ driver.option().pythonResourceImport = Option::PythonResourceImport::Absolute;
driver.option().useStarImports = parser.isSet(useStarImportsOption);
if (parser.isSet(rcPrefixOption))
driver.option().rcPrefix = 1;
+ QString pythonPaths;
+ if (parser.isSet(pythonPathOption))
+ pythonPaths = parser.value(pythonPathOption);
+ else if (qEnvironmentVariableIsSet(pythonPathVar))
+ pythonPaths = QString::fromUtf8(qgetenv(pythonPathVar));
+ driver.option().pythonRoot = pythonRoot(pythonPaths, inputFile);
}
if (inputFile.isEmpty()) // reading from stdin
diff --git a/src/tools/uic/option.h b/src/tools/uic/option.h
index 9b4c98344ff..78b3ff1490b 100644
--- a/src/tools/uic/option.h
+++ b/src/tools/uic/option.h
@@ -11,6 +11,12 @@ QT_BEGIN_NAMESPACE
struct Option
{
+ enum class PythonResourceImport {
+ Default, // "import rc_file"
+ FromDot, // "from . import rc_file"
+ Absolute // "import path.rc_file"
+ };
+
unsigned int headerProtection : 1;
unsigned int copyrightHeader : 1;
unsigned int generateImplemetation : 1;
@@ -20,7 +26,6 @@ struct Option
unsigned int limitXPM_LineLength : 1;
unsigned int implicitIncludes: 1;
unsigned int idBased: 1;
- unsigned int fromImports: 1;
unsigned int forceMemberFnPtrConnectionSyntax: 1;
unsigned int forceStringConnectionSyntax: 1;
unsigned int useStarImports: 1;
@@ -34,6 +39,9 @@ struct Option
QString postfix;
QString translateFunction;
QString includeFile;
+ QString pythonRoot;
+
+ PythonResourceImport pythonResourceImport = PythonResourceImport::Default;
Option()
: headerProtection(1),
@@ -45,7 +53,6 @@ struct Option
limitXPM_LineLength(0),
implicitIncludes(1),
idBased(0),
- fromImports(0),
forceMemberFnPtrConnectionSyntax(0),
forceStringConnectionSyntax(0),
useStarImports(0),
diff --git a/src/tools/uic/python/pythonwriteimports.cpp b/src/tools/uic/python/pythonwriteimports.cpp
index a268f87bb48..b122c0f895b 100644
--- a/src/tools/uic/python/pythonwriteimports.cpp
+++ b/src/tools/uic/python/pythonwriteimports.cpp
@@ -10,6 +10,8 @@
#include <ui4.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qfileinfo.h>
#include <QtCore/qtextstream.h>
#include <algorithm>
@@ -54,23 +56,6 @@ static WriteImports::ClassesPerModule defaultClasses()
};
}
-// Change the name of a qrc file "dir/foo.qrc" file to the Python
-// module name "foo_rc" according to project conventions.
-static QString pythonResource(QString resource, bool prefix)
-{
- const qsizetype lastSlash = resource.lastIndexOf(u'/');
- if (lastSlash != -1)
- resource.remove(0, lastSlash + 1);
- if (resource.endsWith(".qrc"_L1)) {
- resource.chop(4);
- if (prefix)
- resource.prepend("rc_"_L1);
- else
- resource.append("_rc"_L1);
- }
- return resource;
-}
-
// Helpers for WriteImports::ClassesPerModule maps
static void insertClass(const QString &module, const QString &className,
WriteImports::ClassesPerModule *c)
@@ -143,18 +128,57 @@ void WriteImports::acceptUI(DomUI *node)
const auto includes = resources->elementInclude();
for (auto include : includes) {
if (include->hasAttributeLocation())
- writeImport(pythonResource(include->attributeLocation(),
- uic()->option().rcPrefix));
+ writeResourceImport(include->attributeLocation());
}
output << '\n';
}
}
-void WriteImports::writeImport(const QString &module)
+QString WriteImports::resourceAbsolutePath(QString resource) const
+{
+ // If we know the project root, generate an absolute Python import
+ // to the resource. options. pythonRoot is the Python path component
+ // under which the UI file is.
+ const auto &options = uic()->option();
+ if (!options.inputFile.isEmpty() && !options.pythonRoot.isEmpty()) {
+ resource = QDir::cleanPath(QFileInfo(options.inputFile).canonicalPath() + u'/' + resource);
+ if (resource.size() > options.pythonRoot.size())
+ resource.remove(0, options.pythonRoot.size() + 1);
+ }
+ // If nothing is known, we assume the directory pointed by "../" is the root
+ while (resource.startsWith(u"../"))
+ resource.remove(0, 3);
+ resource.replace(u'/', u'.');
+ return resource;
+}
+
+void WriteImports::writeResourceImport(const QString &module)
{
- if (uic()->option().fromImports)
- uic()->output() << "from . ";
- uic()->output() << "import " << module << '\n';
+ const auto &options = uic()->option();
+ auto &str = uic()->output();
+
+ QString resource = QDir::cleanPath(module);
+ if (resource.endsWith(u".qrc"))
+ resource.chop(4);
+ const qsizetype basePos = resource.lastIndexOf(u'/') + 1;
+ // Change the name of a qrc file "dir/foo.qrc" file to the Python
+ // module name "foo_rc" according to project conventions.
+ if (options.rcPrefix)
+ resource.insert(basePos, u"rc_");
+ else
+ resource.append(u"_rc");
+
+ switch (options.pythonResourceImport) {
+ case Option::PythonResourceImport::Default:
+ str << "import " << QStringView{resource}.sliced(basePos) << '\n';
+ break;
+ case Option::PythonResourceImport::FromDot:
+ str << "from . import " << QStringView{resource}.sliced(basePos) << '\n';
+ break;
+ case Option::PythonResourceImport::Absolute:
+ str << "import " << resourceAbsolutePath(resource) << '\n';
+ break;
+ }
}
void WriteImports::doAdd(const QString &className, const DomCustomWidget *dcw)
diff --git a/src/tools/uic/python/pythonwriteimports.h b/src/tools/uic/python/pythonwriteimports.h
index 14aefd4f2a8..4497b8dc336 100644
--- a/src/tools/uic/python/pythonwriteimports.h
+++ b/src/tools/uic/python/pythonwriteimports.h
@@ -31,7 +31,8 @@ private:
void addPythonCustomWidget(const QString &className, const DomCustomWidget *dcw);
bool addQtClass(const QString &className);
void addEnumBaseClass(const QString &v);
- void writeImport(const QString &module);
+ void writeResourceImport(const QString &module);
+ QString resourceAbsolutePath(QString resource) const;
QHash<QString, QString> m_classToModule;
// Module->class (modules sorted)