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. Pick-to: 6.5 Task-number: PYSIDE-2191 Change-Id: Ib444eb666217b8c010dba0079b0ffe9ddbaa3414 Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io> Reviewed-by: Shyamnath Premnadh <Shyamnath.Premnadh@qt.io>
This commit is contained in:
parent
d4f72b4de6
commit
814d66d558
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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 (uic()->option().fromImports)
|
||||
uic()->output() << "from . ";
|
||||
uic()->output() << "import " << module << '\n';
|
||||
// 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)
|
||||
{
|
||||
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)
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user