diff options
author | Katja Marttila <katja.marttila@qt.io> | 2020-05-05 13:31:32 +0300 |
---|---|---|
committer | Katja Marttila <katja.marttila@qt.io> | 2020-05-26 15:08:51 +0300 |
commit | 17a4630886d4a91362f508f27022ae48e076d9c3 (patch) | |
tree | 6975e737ae17555de11605e515cfe79337aea760 | |
parent | 0fcfdebf88372d527008ff0a080bf0f9eb493c9a (diff) |
CLI: Add new option --file-query to auto answer QFileDialog
QFileDialog.getExistingDirectory and QFileDialog.getOpenFileName
can be called from scipt. If command line interface is used, user must
type the correct directory or file name from command line during install.
With --file-query option user can give the values when running the
installer or maintenancetool with syntax --file-query
filedialogId=C:/temp,filedialogId2=C:/temp/file.txt.
Task-number: QTIFW-1631
Change-Id: I5e58be6b509cf00de832646ef31ec4eda90773be
Reviewed-by: Arttu Tarkiainen <arttu.tarkiainen@qt.io>
-rw-r--r-- | doc/scripting-api/packagemanagercore.qdoc | 19 | ||||
-rw-r--r-- | doc/scripting-api/qfiledialog.qdoc | 7 | ||||
-rw-r--r-- | src/libs/installer/commandlineparser.cpp | 6 | ||||
-rw-r--r-- | src/libs/installer/constants.h | 1 | ||||
-rw-r--r-- | src/libs/installer/packagemanagercore.cpp | 49 | ||||
-rw-r--r-- | src/libs/installer/packagemanagercore.h | 5 | ||||
-rw-r--r-- | src/libs/installer/scriptengine.cpp | 69 | ||||
-rw-r--r-- | src/libs/installer/scriptengine_p.h | 22 | ||||
-rw-r--r-- | src/sdk/sdkapp.h | 12 | ||||
-rw-r--r-- | tests/auto/installer/cliinterface/data/filequeryrepository/A/1.0.2-1meta.7z | bin | 0 -> 907 bytes | |||
-rw-r--r-- | tests/auto/installer/cliinterface/data/filequeryrepository/Updates.xml | 13 | ||||
-rw-r--r-- | tests/auto/installer/cliinterface/settings.qrc | 2 | ||||
-rw-r--r-- | tests/auto/installer/cliinterface/tst_cliinterface.cpp | 25 |
13 files changed, 218 insertions, 12 deletions
diff --git a/doc/scripting-api/packagemanagercore.qdoc b/doc/scripting-api/packagemanagercore.qdoc index 1cff11c84..cb11cef0d 100644 --- a/doc/scripting-api/packagemanagercore.qdoc +++ b/doc/scripting-api/packagemanagercore.qdoc @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2020 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -332,6 +332,23 @@ */ /*! + \qmlmethod void installer::setFileDialogAutomaticAnswer(string identifier, string &value) + + Automatically sets the existing directory or filename \a value to QFileDialog with the ID + \a identifier. + + \sa removeFileDialogAutomaticAnswer +*/ + +/*! + \qmlmethod void installer::removeFileDialogAutomaticAnswer(string identifier) + + Removes the automatic answer from QFileDialog with the ID \a identifier. + + \sa setFileDialogAutomaticAnswer +*/ + +/*! \qmlmethod float installer::requiredDiskSpace() Returns the additional estimated amount of disk space in bytes required after installation. diff --git a/doc/scripting-api/qfiledialog.qdoc b/doc/scripting-api/qfiledialog.qdoc index 661a702f7..e6cdfdb24 100644 --- a/doc/scripting-api/qfiledialog.qdoc +++ b/doc/scripting-api/qfiledialog.qdoc @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2020 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -34,6 +34,11 @@ that displays an existing directory selected by the user. Use the QFileDialog::getOpenFileName() method to create a dialog that displays matching files in the directory selected by the user. + When a command line interface is used, a dialog is not displayed. Instead, + the user can type the directory or the file name in the console. For + automatic installations, \c{--file-query} with \c{identifier=value} pairs + can be given separated with a comma. For example, + \c{--file-query filedialog.id=C:/Temp,filedialog.id2=C:/Temp2}. */ /*! diff --git a/src/libs/installer/commandlineparser.cpp b/src/libs/installer/commandlineparser.cpp index 60b02c3ae..f3939dc36 100644 --- a/src/libs/installer/commandlineparser.cpp +++ b/src/libs/installer/commandlineparser.cpp @@ -163,6 +163,12 @@ CommandLineParser::CommandLineParser() QLatin1String("Automatically answers to message queries with their default values."))); m_parser.addOption(QCommandLineOption(QStringList() << CommandLineOptions::scAcceptLicenses, QLatin1String("Accepts all licenses without user input."))); + m_parser.addOption(QCommandLineOption(QStringList() << CommandLineOptions::scFileDialogAutomaticAnswer, + QLatin1String("Automatically sets the QFileDialog values getExistingDirectory() or getOpenFileName() " + "requested by install script. " + "Several identifier=value pairs can be given separated with comma, " + "for example --file-query filedialog.id=C:\Temp,filedialog.id2=C:\Temp2"), + QLatin1String("identifier=value"))); // Developer options m_parser.addOption(QCommandLineOption(QStringList() diff --git a/src/libs/installer/constants.h b/src/libs/installer/constants.h index fecba0e6c..9e90d9383 100644 --- a/src/libs/installer/constants.h +++ b/src/libs/installer/constants.h @@ -158,6 +158,7 @@ static const QLatin1String scRejectMessageQuery("reject-messages"); static const QLatin1String scMessageAutomaticAnswer("auto-answer"); static const QLatin1String scMessageDefaultAnswer("default-answer"); static const QLatin1String scAcceptLicenses("accept-licenses"); +static const QLatin1String scFileDialogAutomaticAnswer("file-query"); // Misc installation options static const QLatin1String scRootShort("t"); diff --git a/src/libs/installer/packagemanagercore.cpp b/src/libs/installer/packagemanagercore.cpp index 2287e5a73..0bfde19c3 100644 --- a/src/libs/installer/packagemanagercore.cpp +++ b/src/libs/installer/packagemanagercore.cpp @@ -625,6 +625,55 @@ void PackageManagerCore::setAutoAcceptLicenses() } /*! + Automatically sets the existing directory or filename \a value to QFileDialog with the ID + \a identifier. QFileDialog can be called from script. + + This can be used for unattended (automatic) installations. + + \sa {installer::setFileDialogAutomaticAnswer){installer.setFileDialogAutomaticAnswer} + \sa {QFileDialog::getExistingDirectory}{QFileDialog.getExistingDirectory} + \sa {QFileDialog::getOpenFileName}{QFileDialog.getOpenFileName} + */ +void PackageManagerCore::setFileDialogAutomaticAnswer(const QString &identifier, const QString &value) +{ + m_fileDialogAutomaticAnswers.insert(identifier, value); +} + +/*! + Removes the automatic answer from QFileDialog with the ID \a identifier. + QFileDialog can be called from script. + + \sa {installer::removeFileDialogAutomaticAnswer){installer.removeFileDialogAutomaticAnswer} + \sa {QFileDialog::getExistingDirectory}{QFileDialog.getExistingDirectory} + \sa {QFileDialog::getOpenFileName}{QFileDialog.getOpenFileName} + */ +void PackageManagerCore::removeFileDialogAutomaticAnswer(const QString &identifier) +{ + m_fileDialogAutomaticAnswers.remove(identifier); +} + +/*! + Returns \c true if QFileDialog with the ID \a identifier has an automatic answer set. + + \sa {installer.containsFileDialogAutomaticAnswer}{installer::containsFileDialogAutomaticAnswer} + \sa {installer::removeFileDialogAutomaticAnswer){installer.removeFileDialogAutomaticAnswer} + \sa {QFileDialog::getExistingDirectory}{QFileDialog.getExistingDirectory} + \sa {QFileDialog::getOpenFileName}{QFileDialog.getOpenFileName} + */ +bool PackageManagerCore::containsFileDialogAutomaticAnswer(const QString &identifier) const +{ + return m_fileDialogAutomaticAnswers.contains(identifier); +} +/*! + * Returns the hash of file dialog automatic answers + * \sa setFileDialogAutomaticAnswer() + */ +QHash<QString, QString> PackageManagerCore::fileDialogAutomaticAnswers() const +{ + return m_fileDialogAutomaticAnswers; +} + +/*! Returns the size of the component \a component as \a value. */ quint64 PackageManagerCore::size(QInstaller::Component *component, const QString &value) const diff --git a/src/libs/installer/packagemanagercore.h b/src/libs/installer/packagemanagercore.h index 67e7d6365..e188ed34d 100644 --- a/src/libs/installer/packagemanagercore.h +++ b/src/libs/installer/packagemanagercore.h @@ -187,6 +187,10 @@ public: Q_INVOKABLE void acceptMessageBoxDefaultButton(); Q_INVOKABLE void setAutoAcceptLicenses(); + Q_INVOKABLE void setFileDialogAutomaticAnswer(const QString &identifier, const QString &value); + Q_INVOKABLE void removeFileDialogAutomaticAnswer(const QString &identifier); + Q_INVOKABLE bool containsFileDialogAutomaticAnswer(const QString &identifier) const; + QHash<QString, QString> fileDialogAutomaticAnswers() const; quint64 size(QInstaller::Component *component, const QString &value) const; @@ -385,6 +389,7 @@ private: private: PackageManagerCorePrivate *const d; friend class PackageManagerCorePrivate; + QHash<QString, QString> m_fileDialogAutomaticAnswers; private: // remove once we deprecate isSelected, setSelected etc... diff --git a/src/libs/installer/scriptengine.cpp b/src/libs/installer/scriptengine.cpp index 994fa1406..accd54957 100644 --- a/src/libs/installer/scriptengine.cpp +++ b/src/libs/installer/scriptengine.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2020 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -265,6 +265,71 @@ void GuiProxy::setModified(bool value) m_gui->setModified(value); } +QFileDialogProxy::QFileDialogProxy(PackageManagerCore *core): m_core(core) +{ +} + +QString QFileDialogProxy::getExistingDirectory(const QString &caption, + const QString &dir, const QString &identifier) +{ + if (m_core->isCommandLineInstance()) { + return getExistingFileOrDirectory(caption, identifier, true); + } else { + return QFileDialog::getExistingDirectory(0, caption, dir); + } +} + +QString QFileDialogProxy::getOpenFileName(const QString &caption, const QString &dir, + const QString &filter, const QString &identifier) +{ + if (m_core->isCommandLineInstance()) { + return getExistingFileOrDirectory(caption, identifier, false); + } else { + return QFileDialog::getOpenFileName(0, caption, dir, filter); + } +} + +QString QFileDialogProxy::getExistingFileOrDirectory(const QString &caption, + const QString &identifier, bool isDirectory) +{ + QHash<QString, QString> autoAnswers = m_core->fileDialogAutomaticAnswers(); + QString selectedDirectoryOrFile; + QString errorString; + if (autoAnswers.contains(identifier)) { + selectedDirectoryOrFile = autoAnswers.value(identifier); + QFileInfo fileInfo(selectedDirectoryOrFile); + if (isDirectory ? fileInfo.isDir() : fileInfo.isFile()) { + qCDebug(QInstaller::lcInstallerInstallLog).nospace() << "Automatic answer for "<< identifier + << ": " << selectedDirectoryOrFile; + } else { + if (isDirectory) + errorString = QString::fromLatin1("Automatic answer for %1: Directory '%2' not found.") + .arg(identifier, selectedDirectoryOrFile); + else + errorString = QString::fromLatin1("Automatic answer for %1: File '%2' not found.") + .arg(identifier, selectedDirectoryOrFile); + selectedDirectoryOrFile = QString(); + } + } else { + qDebug().nospace().noquote() << identifier << ": " << caption << ": "; + QTextStream stream(stdin); + stream.readLineInto(&selectedDirectoryOrFile); + QFileInfo fileInfo(selectedDirectoryOrFile); + if (isDirectory ? !fileInfo.isDir() : !fileInfo.isFile()) { + if (isDirectory) + errorString = QString::fromLatin1("Directory '%1' not found.") + .arg(selectedDirectoryOrFile); + else + errorString = QString::fromLatin1("File '%1' not found.") + .arg(selectedDirectoryOrFile); + selectedDirectoryOrFile = QString(); + } + } + if (!errorString.isEmpty()) + qCWarning(QInstaller::lcInstallerInstallLog).nospace() << errorString; + return selectedDirectoryOrFile; +} + /*! Constructs a script engine with \a core as parent. @@ -275,7 +340,7 @@ ScriptEngine::ScriptEngine(PackageManagerCore *core) : { QJSValue global = m_engine.globalObject(); global.setProperty(QLatin1String("console"), m_engine.newQObject(new ConsoleProxy)); - global.setProperty(QLatin1String("QFileDialog"), m_engine.newQObject(new QFileDialogProxy)); + global.setProperty(QLatin1String("QFileDialog"), m_engine.newQObject(new QFileDialogProxy(core))); const QJSValue proxy = m_engine.newQObject(new InstallerProxy(this, core)); global.setProperty(QLatin1String("InstallerProxy"), proxy); global.setProperty(QLatin1String("print"), m_engine.newQObject(new ConsoleProxy) diff --git a/src/libs/installer/scriptengine_p.h b/src/libs/installer/scriptengine_p.h index 928f903cb..0fc963bf9 100644 --- a/src/libs/installer/scriptengine_p.h +++ b/src/libs/installer/scriptengine_p.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2020 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -77,15 +77,21 @@ class QFileDialogProxy : public QObject Q_DISABLE_COPY(QFileDialogProxy) public: - QFileDialogProxy() {} + QFileDialogProxy(PackageManagerCore *core); public slots : - QString getExistingDirectory(const QString &caption, const QString &dir) const { - return QFileDialog::getExistingDirectory(0, caption, dir); - } - QString getOpenFileName(const QString &caption, const QString &dir, const QString &filter) const { - return QFileDialog::getOpenFileName(0, caption, dir, filter); - } + QString getExistingDirectory(const QString &caption, const QString &dir, + const QString &identifier = QLatin1String("GetExistingDirectory")); + + QString getOpenFileName(const QString &caption, const QString &dir, const QString &filter, + const QString &identifier = QLatin1String("GetExistingFile")); + +private: + QString getExistingFileOrDirectory(const QString &caption, const QString &identifier, + bool isDirectory); + +private: + PackageManagerCore *m_core; }; class QDesktopServicesProxy : public QObject diff --git a/src/sdk/sdkapp.h b/src/sdk/sdkapp.h index ecaee3758..378555633 100644 --- a/src/sdk/sdkapp.h +++ b/src/sdk/sdkapp.h @@ -292,6 +292,18 @@ public: } } + if (m_parser.isSet(CommandLineOptions::scFileDialogAutomaticAnswer)) { + const QString positionalArguments = m_parser.value(CommandLineOptions::scFileDialogAutomaticAnswer); + const QStringList items = positionalArguments.split(QLatin1Char(','), QString::SkipEmptyParts); + + foreach (const QString &item, items) { + if (item.contains(QLatin1Char('='))) { + const QString name = item.section(QLatin1Char('='), 0, 0); + QString value = item.section(QLatin1Char('='), 1, 1); + m_core->setFileDialogAutomaticAnswer(name, value); + } + } + } if (m_parser.isSet(CommandLineOptions::scMessageDefaultAnswer)) { m_core->acceptMessageBoxDefaultButton(); } diff --git a/tests/auto/installer/cliinterface/data/filequeryrepository/A/1.0.2-1meta.7z b/tests/auto/installer/cliinterface/data/filequeryrepository/A/1.0.2-1meta.7z Binary files differnew file mode 100644 index 000000000..a006c5c96 --- /dev/null +++ b/tests/auto/installer/cliinterface/data/filequeryrepository/A/1.0.2-1meta.7z diff --git a/tests/auto/installer/cliinterface/data/filequeryrepository/Updates.xml b/tests/auto/installer/cliinterface/data/filequeryrepository/Updates.xml new file mode 100644 index 000000000..72b7938d9 --- /dev/null +++ b/tests/auto/installer/cliinterface/data/filequeryrepository/Updates.xml @@ -0,0 +1,13 @@ +<Updates> + <ApplicationName>{AnyApplication}</ApplicationName> + <ApplicationVersion>1.0.0</ApplicationVersion> + <PackageUpdate> + <Name>A</Name> + <DisplayName>A</DisplayName> + <Description>A component</Description> + <Version>1.0.2-1</Version> + <ReleaseDate>2015-01-01</ReleaseDate> + <Default>true</Default> + <Script>script.qs</Script> + </PackageUpdate> +</Updates> diff --git a/tests/auto/installer/cliinterface/settings.qrc b/tests/auto/installer/cliinterface/settings.qrc index 85e344d1b..be97adfbe 100644 --- a/tests/auto/installer/cliinterface/settings.qrc +++ b/tests/auto/installer/cliinterface/settings.qrc @@ -19,6 +19,8 @@ <file>data/installPackagesRepository/componentF.subcomponent1.subsubcomponent2/1.0.0content.7z</file> <file>data/installPackagesRepository/componentF.subcomponent2.subsubcomponent1/1.0.0content.7z</file> <file>data/installPackagesRepository/componentF.subcomponent2.subsubcomponent2/1.0.0content.7z</file> + <file>data/filequeryrepository/Updates.xml</file> + <file>data/filequeryrepository/A/1.0.2-1meta.7z</file> <file>data/componentsFromInstallPackagesRepository.xml</file> </qresource> </RCC> diff --git a/tests/auto/installer/cliinterface/tst_cliinterface.cpp b/tests/auto/installer/cliinterface/tst_cliinterface.cpp index 6040927ea..ebf63ff9d 100644 --- a/tests/auto/installer/cliinterface/tst_cliinterface.cpp +++ b/tests/auto/installer/cliinterface/tst_cliinterface.cpp @@ -351,6 +351,31 @@ private slots: << "installcontentA.txt" << "installcontentE.txt" << "installcontentG.txt" << "installcontentB.txt" << "installcontentD.txt"); } + void testFileQuery() + { + PackageManagerCore *core = PackageManager::getPackageManagerWithInit(m_installDir, + ":///data/filequeryrepository"); + core->setCommandLineInstance(true); + core->setFileDialogAutomaticAnswer("ValidDirectory", m_installDir); + + QString testFile = qApp->applicationDirPath() + QDir::toNativeSeparators("/test"); + QFile file(testFile); + QVERIFY(file.open(QIODevice::WriteOnly)); + core->setFileDialogAutomaticAnswer("ValidFile", testFile); + + //File dialog launched without ID + core->setFileDialogAutomaticAnswer("GetExistingDirectory", m_installDir); + core->setFileDialogAutomaticAnswer("GetExistingFile", testFile); + + core->installDefaultComponentsSilently(); + + QVERIFY(core->containsFileDialogAutomaticAnswer("ValidFile")); + core->removeFileDialogAutomaticAnswer("ValidFile"); + QVERIFY(!core->containsFileDialogAutomaticAnswer("ValidFile")); + + QVERIFY(file.remove()); + core->deleteLater(); + } void init() { |