diff options
author | Friedemann Kleint <[email protected]> | 2023-01-13 13:43:44 +0100 |
---|---|---|
committer | Qt Cherry-pick Bot <[email protected]> | 2023-02-01 22:58:08 +0000 |
commit | 113f91229a5a3e0c33328fdf48ff857b16fb2613 (patch) | |
tree | 4a874d19ca4b9ad3521b6fa6dfc2796df58b2f26 | |
parent | 23c49af8a30ef4db5a1cc4c1915cbb86b71a03de (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.cpp | 51 | ||||
-rw-r--r-- | src/tools/uic/option.h | 11 | ||||
-rw-r--r-- | src/tools/uic/python/pythonwriteimports.cpp | 70 | ||||
-rw-r--r-- | src/tools/uic/python/pythonwriteimports.h | 3 |
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) |