在Ubuntu服务器上静默安装Qt运行安装程序

52

我想知道在Ubuntu Server上是否有一种方法可以静默安装Qt运行安装程序?
我的意思是绕过安装程序的选项并进行默认安装?


你不能直接从仓库安装Qt库吗?为什么需要在服务器上运行Qt安装程序? - Pavel Strakhov
由于运行安装程序提供了二进制文件,因此Qt已经准备好用于生产。 - Antoine
1
@Pavel Strakhov,在Docker容器中安装qt库并构建源代码是否可行?我尝试在Docker中运行QT安装程序,但失败了。错误信息如下:网络错误:[QNetworkReply :: NetworkError(UnknownNetworkError)]“无法连接到服务器。请检查您的网络连接并重试。” - user2301
Qt安装程序的4.0版本现在已经拥有了一流的支持。更多信息请参见此答案 - Joshua Wade
1
目前为止,这里的每个答案都已过时。请使用以下命令:https://doc.qt.io/qtinstallerframework/ifw-use-cases-cli.html#unattended-usage - likeicare
12个回答

64

更新答案:

新版的Qt安装程序拥有完整的命令行界面,可以让你像下面这样操作:

qt-unified-windows-x86-4.2.0-online.exe ^
  --accept-licenses ^
  --default-answer ^
  --confirm-command install ^
  qt.qt5.5158.win64_msvc2019_64 ^
  qt.qt5.5158.qtcharts ^
  qt.qt5.5158.debug_info ^
  qt.qt5.5158.src ^
  qt.tools.qtcreator

查看所有选项请使用--help。要弄清包名称,请进行图形安装,但停止在列出所有选择的包名称的最终确认屏幕。

原回答

Qt工具包使用Qt安装程序框架(QtIFW)进行打包。 QtIFW安装程序支持--script选项,允许您通过控制器脚本API以编程方式控制安装。以下是用于非交互式安装Qt 5的qt-installer-noninteractive.qs文件:

// Emacs mode hint: -*- mode: JavaScript -*-

function Controller() {
    installer.autoRejectMessageBoxes();
    installer.installationFinished.connect(function() {
        gui.clickButton(buttons.NextButton);
    })
}

Controller.prototype.WelcomePageCallback = function() {
    // click delay here because the next button is initially disabled for ~1 second
    gui.clickButton(buttons.NextButton, 3000);
}

Controller.prototype.CredentialsPageCallback = function() {
    gui.clickButton(buttons.NextButton);
}

Controller.prototype.IntroductionPageCallback = function() {
    gui.clickButton(buttons.NextButton);
}

Controller.prototype.TargetDirectoryPageCallback = function()
{
    gui.currentPageWidget().TargetDirectoryLineEdit.setText(installer.value("HomeDir") + "/Qt");
    gui.clickButton(buttons.NextButton);
}

Controller.prototype.ComponentSelectionPageCallback = function() {
    var widget = gui.currentPageWidget();

    widget.deselectAll();
    widget.selectComponent("qt.55.gcc_64");
    widget.selectComponent("qt.55.qtquickcontrols");

    // widget.deselectComponent("qt.tools.qtcreator");
    // widget.deselectComponent("qt.55.qt3d");
    // widget.deselectComponent("qt.55.qtcanvas3d");
    // widget.deselectComponent("qt.55.qtlocation");
    // widget.deselectComponent("qt.55.qtquick1");
    // widget.deselectComponent("qt.55.qtscript");
    // widget.deselectComponent("qt.55.qtwebengine");
    // widget.deselectComponent("qt.extras");
    // widget.deselectComponent("qt.tools.doc");
    // widget.deselectComponent("qt.tools.examples");

    gui.clickButton(buttons.NextButton);
}

Controller.prototype.LicenseAgreementPageCallback = function() {
    gui.currentPageWidget().AcceptLicenseRadioButton.setChecked(true);
    gui.clickButton(buttons.NextButton);
}

Controller.prototype.StartMenuDirectoryPageCallback = function() {
    gui.clickButton(buttons.NextButton);
}

Controller.prototype.ReadyForInstallationPageCallback = function()
{
    gui.clickButton(buttons.NextButton);
}

Controller.prototype.FinishedPageCallback = function() {
var checkBoxForm = gui.currentPageWidget().LaunchQtCreatorCheckBoxForm;
if (checkBoxForm && checkBoxForm.launchQtCreatorCheckBox) {
    checkBoxForm.launchQtCreatorCheckBox.checked = false;
}
    gui.clickButton(buttons.FinishButton);
}

此脚本演示如何选择/取消选择特定组件。根据您的需求进行自定义,或者完全删除这些行以进行默认安装。同样,您可能想要自定义或删除TargetDirectoryLineEdit 行。运行Qt安装程序如下:

qt-opensource-linux-x64-5.5.1.run --script qt-installer-noninteractive.qs

为了进行无头安装,请添加-platform minimal选项。基于QtIFW更新版本的未来安装程序应该可以使用--silent选项代替(请参见QTIFW-166)。

如果需要获取组件名称、向导页面名称等更详细的控制台输出,请添加--verbose选项。此链接也有助于确定组件名称。


2
平台最小化与qt-opensource-linux-x64-5.6.0.run不兼容。 - RDT2
11
使用 qt-opensource-linux-x64-android-5.7.0.run-platform minimal 会出现 Unknown option: p, l, a, t, f, o, r, m 的错误提示,--platform minimal 会出现 Unknown option: platform 的错误提示,而 --silent 则会出现 Unknown option: silent 的错误提示。在没有 UI 系统的情况下,还会出现 QXcbConnection: Could not connect to displayAborted (core dumped) 的错误提示。 - ssc
3
“Unknown option: p, l, a, t, f, o, r, m”这个提示信息的输出有误(是一个 bug),但命令仍然成功执行。 - nocnokneo
4
如果你想知道组件名称的来源,它似乎来自这里:https://github.com/qtproject/qtsdk/tree/master/packaging-tools/configurations/pkg_templates - Sohail
1
@3bdalla,答案中描述的“-platform minimal”选项对你无效吗? - nocnokneo
显示剩余10条评论

14

从安装程序4.X开始,您不再需要操作JS文件。Qt在线安装程序版本4.0已经支持无头安装。有关更多信息,请参见此答案

从安装程序3.0.2-online 29-11-2017开始,您必须在JS脚本中添加延迟,因为“欢迎”页面上的“下一步”按钮会被禁用一秒钟左右。

Controller.prototype.WelcomePageCallback = function() {
    gui.clickButton(buttons.NextButton, 3000);
}

他们在3.2.1-online 2020年1月29日的发布页面上首次解释如何跳过新表单。

Controller.prototype.ObligationsPageCallback = function() {
    var page = gui.pageWidgetByObjectName("ObligationsPage");
    page.obligationsAgreement.setChecked(true);
    page.completeChanged();
    gui.clickButton(buttons.NextButton);
}

有些人可能想知道如何创建神秘的qtaccount.ini文件(从3.2.1-2-online开始需要)。 Qt只告诉我们应该将其放置在〜/ .local / share / Qt /中。我找不到任何其他信息。安装程序会在您首次输入凭据时自行创建此文件。因此,在填写账户表格后进行手动安装并退出即可。生成的文件看起来像这样

[General]
email=my.email@domain.ca
pass=mypass

[QtAccount]
email=my.email@domain.ca
jwt=a long hash
u=a small hash

你让我开心极了! - Matthias Kuhn
是的,以前有一个跳过按钮,但现在不再有了(https://www.qt.io/blog/qt-offering-changes-2020)。安装Qt二进制文件将需要一个Qt账户。解决方案是创建一个帐户或不使用Qt。 - Nil
有没有办法找到填写帐户凭据的Controller.ABC...看起来像什么?因为现在凭据是必需的?我不能只复制自己的ini文件,因为我需要创建一个通用脚本。 - Kriegalex
@Kriegalex 是的,它被称为 Controller.prototype.CredentialsPageCallback。查看其他答案以了解如何使用它。您的用户名和密码将自动填充为 qtaccount.ini - Nil
@Nil 正如我所说,我不能使用复制的ini文件,因为我的脚本应该在无头环境下为多个用户工作(就像在这些凭据变得强制性之前一样)。我需要一种方法让CredentialsPageCallback从电子邮件/密码输入自动填写信息。此页面上的CredentialsPageCallback都只是“下一步”操作。 - Kriegalex

9

对于不同版本的Qt,向导问题的回答可能会有一些细微的差异。为了让它更简单,我打包了一个通用脚本,从离线/在线安装程序中提取Qt。

该脚本: qtci/extract-qt-installer at master · benlau/qtci

使用示例:

extract-qt-installer qt-opensource-linux-x64-android-5.5.1.run ~/Qt

环境变量

VERBOSE [Optional] Set to "true" will enable VERBOSE output
QT_CI_PAGEAGES [Optional] Select the components to be installed instead of using default (eg. QT_CI_PAGEAGES="qt.59.gcc_64")

此外,它有少量脚本可下载和安装不同版本的Qt。 qtci/recipes at master · benlau/qtci

9

2020更新:现在有更好的方法。

Qt在线安装程序版本4.0现在支持无人值守安装。

有关更多信息,请参见下面的答案


原始答案:

绕过“用户数据收集”屏幕

自2019年10月8日起,在Windows上增加了一屏幕,这将导致安装挂起。您可以通过以下内容向.qs文件添加点击来通过此屏幕:

Controller.prototype.DynamicTelemetryPluginFormCallback = function() {
    var widget = gui.currentPageWidget();
    widget.TelemetryPluginForm.statisticGroupBox.disableStatisticRadioButton.checked = true;
    gui.clickButton(buttons.NextButton);
}

选择“归档”和“最新版本发布”

另一个最近的更改是对软件包类别的调整。现在,默认情况下只选择LTS版本,这意味着您不能安装最新的Qt版本,除非先在软件包类别中选择“最新版本发布”。

以下是如何进行操作:

Controller.prototype.ComponentSelectionPageCallback = function() {
    var page = gui.pageWidgetByObjectName("ComponentSelectionPage");

    var archiveCheckBox = gui.findChild(page, "Archive");
    var latestCheckBox = gui.findChild(page, "Latest releases");
    var fetchButton = gui.findChild(page, "FetchCategoryButton");

    archiveCheckBox.click();
    latestCheckBox.click();
    fetchButton.click();

    // ...
}

查看 此处 获取更完整的 Windows 示例。


你是如何找出按钮的名称和路径的呢?TelemetryPluginForm.statisticGroupBox.disableStatisticRadioButton。 - CsTamas
1
gui.currentPageWidget() 似乎返回一个 JS 对象。凭借纯粹的运气,我在获取小部件后能够执行以下操作:console.log(JSON.stringify(widget));。我还使用了 --verbose,但我认为这在这种情况下没有影响。 - Joshua Wade

8
上面的脚本已经过时了。以下代码应该可以正常运行(我还添加了重试下载错误的功能)。
function Controller() {
    installer.autoRejectMessageBoxes();
    installer.setMessageBoxAutomaticAnswer("installationError", QMessageBox.Retry);
    installer.setMessageBoxAutomaticAnswer("installationErrorWithRetry", QMessageBox.Retry);
    installer.setMessageBoxAutomaticAnswer("DownloadError", QMessageBox.Retry);
    installer.setMessageBoxAutomaticAnswer("archiveDownloadError", QMessageBox.Retry);
    installer.installationFinished.connect(function() {
        gui.clickButton(buttons.NextButton);
    })
}

Controller.prototype.WelcomePageCallback = function() {
    // click delay here because the next button is initially disabled for ~1 second
    gui.clickButton(buttons.NextButton, 3000);
}

Controller.prototype.CredentialsPageCallback = function() {
    gui.clickButton(buttons.NextButton);
}

Controller.prototype.IntroductionPageCallback = function() {
    gui.clickButton(buttons.NextButton);
}

Controller.prototype.TargetDirectoryPageCallback = function()
{
    //dev is the user in our docker image
    gui.currentPageWidget().TargetDirectoryLineEdit.setText(installer.value("HomeDir") + "/Qt");
    gui.clickButton(buttons.NextButton);
}

Controller.prototype.PerformInstallationPageCallback = function() {
    gui.clickButton(buttons.CommitButton);
}

Controller.prototype.ComponentSelectionPageCallback = function() {
    function list_packages() {
      var components = installer.components();
      console.log("Available components: " + components.length);
      var packages = ["Packages: "];
      for (var i = 0 ; i < components.length ;i++) {
          packages.push(components[i].name);
      }
      console.log(packages.join(" "));
    }

    list_packages();

    var widget = gui.currentPageWidget();

    console.log(widget);

    widget.deselectAll();
    widget.selectComponent("qt.qt5.5130");
    widget.selectComponent("qt.qt5.5130.gcc_64");
    // widget.deselectComponent("");

    gui.clickButton(buttons.NextButton);
}

Controller.prototype.LicenseAgreementPageCallback = function() {
    gui.currentPageWidget().AcceptLicenseRadioButton.setChecked(true);
    gui.clickButton(buttons.NextButton);
}

Controller.prototype.StartMenuDirectoryPageCallback = function() {
    gui.clickButton(buttons.NextButton);
}

Controller.prototype.ReadyForInstallationPageCallback = function()
{
    gui.clickButton(buttons.NextButton);
}

Controller.prototype.FinishedPageCallback = function() {
    var checkBoxForm = gui.currentPageWidget().LaunchQtCreatorCheckBoxForm;
    if (checkBoxForm && checkBoxForm.launchQtCreatorCheckBox) {
        checkBoxForm.launchQtCreatorCheckBox.checked = false;
    }
    gui.clickButton(buttons.FinishButton);
}

使用详细模式运行,这样您就不必等待太长时间而没有输出。

qt.run -platform minimal --verbose --script ./qt-installer-noninteractive.qs 

在Windows上,您可能会收到一个错误提示,说Visual C++分发安装程序失败了。如果VC++已经安装,这种情况可能会发生,可以通过将installationError / installationErrorWithRetry 的自动答案设置为QMessageBox.Ignore来解决,而不是QMessageBox.Retry - Joshua Wade
如果您的脚本适用于旧版本,则应将组件选择保留为:widget.selectComponent("qt.VERSION.gcc_64"); widget.selectComponent("qt.qt5.VERSION.gcc_64"); - Kriegalex

4

1
这个。经过尝试两种方法,我可以说使用aqtinstall比维护QtIFW脚本更容易且不会引起太多挫败感。 - Joshua Wade
还是更喜欢这种方法。其他方法需要Qt账户。 - Sergei Lebedev

3
Qt安装程序v4.0已经发布,现在可以无头运行。
qt-unified-windows.exe install --root C:\Qt\InstallFolder

您可以使用--help获取可用命令列表:
qt-unified-windows.exe --help

命令行安装程序需要用户交互,但可以通过标志绕过。请参见此Wiki页面了解完整列表。
您可以从这里下载安装程序。

1
你知道是否有不安装示例和文档的方法吗?它们被视为依赖项,因此会自动添加。我尝试了 --nf--nd,但它们仍然被安装了。 - Nil

3

从3.2.1版本开始,凭据现在是必需的,如上面稍微解释过的。一个 qtaccount.ini 文件被创建在 ~/.local/share/qt 中。如果您无法保存并重用此文件,因为您的脚本需要为多个用户工作,您可以使用这个新的 CredentialsPageCallback

Controller.prototype.CredentialsPageCallback = function() {
  var page = gui.pageWidgetByObjectName("CredentialsPage");
  page.loginWidget.EmailLineEdit.setText("MYEMAIL");
  page.loginWidget.PasswordLineEdit.setText("MYPASSWORD");
  gui.clickButton(buttons.NextButton);
}

一如既往地,请小心处理密码,特别是明文传输的情况。


2
我遇到了同样的问题,并编写了一个简单的 Python 脚本,基本上与官方的 Qt 安装程序做的事情一样。你可以在这里找到它。
以下是如何使用它:
sudo apt install python3-requests p7zip-full wget

wget https://git.kaidan.im/lnj/qli-installer/raw/master/qli-installer.py
chmod +x qli-installer.py

./qli-installer.py 5.11.3 linux desktop

然后在这种情况下,Qt安装可以在./5.11.3/gcc_64/中找到。当然,在其他系统/目标(例如mac ioslinux android android_armv7)中会有所不同。

https://lnj.gitlab.io/post/qli-installer/


今天看起来https://git.kaidan.im已经挂了。 - Aritz
抱歉,有一个名为https://git.sr.ht/~lnj/qli-installer的工具,但最好使用aqtinstall分支:https://github.com/miurahr/aqtinstall。 - LNJ

1
注意:下面描述的脚本已经过时。关于原因和一些替代脚本,请参见项目的主页。也许它的分支中有一些是可用的。
几个月前,我制作了一个可配置的Qt安装自动化辅助脚本。虽然它不是真正的无头安装程序,因为它只是遍历安装向导屏幕,但它可以在持续集成服务器上使用,并且实际上已经测试过了。
注释和特点:
- 适用于Linux、MacOS和Windows。 - Qt版本、组件和安装路径可配置。 - 总是使用最新的在线安装程序。 - 在Travis CI中测试过。 - 需要Bash,但通常在持续集成服务器上(至少在Travis上)预装。 - 公共领域(通过CC0豁免)。

首页:https://github.com/skalee/non-interactive-qt-installer


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接