52

I wanted to know if there is a way to do a silent install of the Qt run installer on Ubuntu Server?
I mean by-pass the options of the installer and do a default install?

Antoine
  • 910
  • 1
  • 9
  • 26
  • Can't you just install Qt libraries from the repository? Why do you need to run Qt installer on a server? – Pavel Strakhov Aug 03 '14 at 18:37
  • Because the run installer provide the binaries, Qt is ready for production like that. – Antoine Aug 04 '14 at 13:53
  • 1
    @Pavel Strakhov Is it possible to install qt libraries inside docker container and build the source code? I tried to run QT installer in docker but did not work. ```Network error: [ QNetworkReply::NetworkError(UnknownNetworkError) ] "Failed to connect to server. Check your network connection and try again."``` – user2301 Aug 20 '18 at 09:12
  • Version `4.0` of the Qt installer has first-class support for this now. See [this answer](https://stackoverflow.com/a/63817824/8166701) for more info. – Joshua Wade Oct 12 '20 at 14:29
  • 1
    As of now, every answer here is outdated. Use this ```installer.exe --root "C:\MyInstallation" --accept-licenses --default-answer install componentA ``` https://doc.qt.io/qtinstallerframework/ifw-use-cases-cli.html#unattended-usage – likeicare Nov 26 '20 at 08:36

12 Answers12

63

Updated Answer:

Newer Qt installers have a proper CLI that allows you to do something like:

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

See --help for all options. To figure out package names, go through a graphical install but stop at the final confirmation screen that lists the package names for all your selections.

Previous Answer

The Qt toolkit is packaged using the Qt Installer Framework (QtIFW). QtIFW installers support a --script option that allows you to programatically control the installation via the Controller Scripting API. Here's qt-installer-noninteractive.qs file to install Qt 5 non-interactively:

// 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);
}

This script demonstrates how to select/deselect certain components. Customize for your needs or just remove the lines entirely for a default installation. Likewise, you may want to customize or remove the TargetDirectoryLineEdit line. Run the Qt installer like:

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

Add -platform minimal for a headless installation. Future installers based on newer versions of QtIFW should be able to use a --silent option instead (see QTIFW-166).

Add --verbose for more verbose console output (helpful for gleaning component names, wizard page names, etc). This link is also helpful for figuring out component names.

nocnokneo
  • 1,857
  • 20
  • 24
  • Thanks for this answer. I'm new to the IFW scripting and it helped me fill in the gaps that are in the Qt docs. – timmyonline Jan 12 '16 at 22:03
  • 2
    -platform minimal does not work with qt-opensource-linux-x64-5.6.0.run – RDT2 Apr 28 '16 at 16:06
  • 11
    With `qt-opensource-linux-x64-android-5.7.0.run`, `-platform minimal` fails with `Unknown option: p, l, a, t, f, o, r, m`, `--platform minimal` fails with `Unknown option: platform` and `--silent` fails with `Unknown option: silent` plus `QXcbConnection: Could not connect to display` and `Aborted (core dumped)` on systems without UI. – ssc Nov 30 '16 at 15:54
  • 3
    The `Unknown option: p, l, a, t, f, o, r, m` message is incorrectly printed (it's a bug). The command still succeeds. – nocnokneo Dec 05 '16 at 03:50
  • 1
    Managed to get the silent install to complete but with a slight change in the `qs` file to use a catch-all `widget.selectAll();`. – Saïd Jan 10 '17 at 13:57
  • 4
    In case you were wondering where the component name comes from, it appears to come from here: https://github.com/qtproject/qtsdk/tree/master/packaging-tools/configurations/pkg_templates – Sohail Jul 10 '17 at 12:32
  • @nocnokneo How to uncheck the "Open Qt x.x.x ReadMe" checkbox on the last page of the installer? I can see you uncheck `launchQtCreatorCheckBox`, but I'm not sure how to determine what the readme checkbox variable name should be. – paleozogt Dec 29 '17 at 22:51
  • 1
    Where do we need to pass QT accoun details. This is the first step it will ask while installing. In non-interactive do we need to provide username and password in the controller script? – user2301 Aug 15 '18 at 11:25
  • @user2301 Are you using a commercial license? If not, there is no need to enter a username and password. – nocnokneo Aug 15 '18 at 18:25
  • @ nocnokneo: No I am using open license. But when I use the above script with docker, I get the this network error. ```Network error: [ QNetworkReply::NetworkError(UnknownNetworkError) ] "Failed to connect to server. Check your network connection and try again."``` When I am doing offline installation, why do I get network error? – user2301 Aug 17 '18 at 12:06
  • @cheez great comment! this should definately be in the answer. took me a while to find where to find the names to select from. – jaaq Oct 23 '18 at 07:54
  • This works fine when you ssh to a server but it needs X11. Is there anyway to make it not require X11? – 3bdalla Jan 28 '19 at 09:34
  • 1
    @3bdalla Did the `-platform minimal` option described in the answer not work for you? – nocnokneo Jan 28 '19 at 13:04
  • @nocnokneo my bad didn't see it. It worked perfectly thanks. – 3bdalla Jan 28 '19 at 13:44
  • Hi @nocnokneo, would you mind updating this answer with the info contained in [this answer below](https://stackoverflow.com/a/58293775/8166701)? The script as you have it is broken as of about a week ago due to changes in the installer. – Joshua Wade Oct 16 '19 at 20:39
14

As of installer 4.X, you don't need to play with JS files anymore. Version 4.0 of the Qt online installer has first-class support for headless installation. See this answer for more information.

As of installer 3.0.2-online 29-11-2017, you must add a delay in your JS script because the "Next" button in the "Welcome" page is disabled for one second or so.

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

For once, they explain on the 3.2.1-online 29-01-2020 release page how to skip the new form

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

Some may wonder how to create the mysterious qtaccount.ini file (required since 3.2.1-2-online). Qt only tells us that it should be placed in ~/.local/share/Qt/. I was unable to find any other information. The installer creates this file itself when you enter your credentials for the first time. So simply do a manual installation and quit after the accound form. The generated file looks like

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

[QtAccount]
email=my.email@domain.ca
jwt=a long hash
u=a small hash
Nil
  • 2,345
  • 1
  • 26
  • 33
  • You made my day! – Matthias Kuhn Feb 28 '18 at 13:12
  • Yes, there was a skip button, but there won't be [anymore](https://www.qt.io/blog/qt-offering-changes-2020). `Installation of Qt binaries will require a Qt Account ` The solution is to create an account or to not use Qt. – Nil Apr 20 '20 at 13:41
  • Any way to find what the Controller.ABC... that fills the account credential would look like ? Since the credentials are now mandatory ? I can't just copy my own ini file, as I need to create a generic script. – Kriegalex Jun 04 '20 at 10:40
  • @Kriegalex Yes it's called `Controller.prototype.CredentialsPageCallback`. Look at other answers to know how to use it. Your username and password will be automatically filled with `qtaccount.ini`. – Nil Jun 04 '20 at 14:20
  • @Nil as I said, I can't use a copied ini file as my script should work for multiple users in a headless environment (as it used to work before this credentials became mandatory). I need a way for CredentialsPageCallback to fill the info automatically from an input of e-mail / password. The CredentialsPageCallback on this page are all just the "next" action. – Kriegalex Jun 05 '20 at 08:06
9

There have few minor different in answering the questions of wizard for a different kind of version of Qt. To make it more simple, I have packed a generic script to extract Qt from an offline/online installer.

The script: qtci/extract-qt-installer at master · benlau/qtci

Example Usage:

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

Environment Variables"

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")

Moreover, it has few scripts to download and install Qt with different versions.

qtci/recipes at master · benlau/qtci

benlau
  • 301
  • 3
  • 9
9

2020 update: There's a better way now.

Version 4.0 of the Qt online installer has first-class support for headless installation.

See this answer below for more info.


Original answer:

Bypassing "User Data Collection" screen

As of October 8, 2019, an extra screen has been added on Windows which will cause the installation to hang. You can click through it with the following addition to your .qs file:

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

Selecting "Archive" and "Latest releases"

Another recent change is to package categories. LTS is now the only one selected by default, meaning that you cannot install the latest Qt version without first selecting "Latest releases" in the package categories.

Here's how to do that:

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();

    // ...
}

See here for a more complete example for Windows.

Joshua Wade
  • 4,755
  • 2
  • 24
  • 44
  • how did you find out the name and path to the button? TelemetryPluginForm.statisticGroupBox.disableStatisticRadioButton – CsTamas Oct 10 '19 at 14:14
  • 1
    `gui.currentPageWidget()` seems to give back a JS object. By what feels like pure luck, I was able to do this after fetching the widget: `console.log(JSON.stringify(widget));`. I'm also using `--verbose`, but I don't think that makes a difference in this case. – Joshua Wade Oct 10 '19 at 14:29
8

The script above is old. This should work (and I added retry for download errors)

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);
}

run with verbose so you don't have to wait too much time without output

qt.run -platform minimal --verbose --script ./qt-installer-noninteractive.qs 
PPP
  • 1,279
  • 1
  • 28
  • 71
  • On Windows, you may get an error saying that the Visual C++ Redistributable installer failed. This can happen if VC++ is already installed, and can be fixed by setting the automatic answer for `installationError` / `installationErrorWithRetry` to `QMessageBox.Ignore` instead of `QMessageBox.Retry`. – Joshua Wade Oct 08 '19 at 18:49
  • If your script works with older version, one should leave the component selection like that : ```widget.selectComponent("qt.VERSION.gcc_64");``` ```widget.selectComponent("qt.qt5.VERSION.gcc_64");``` – Kriegalex Aug 14 '20 at 11:23
4

Take a look at this awesome project: aqtinstall https://github.com/miurahr/aqtinstall/

It can install Qt on Linux, Mac and Windows machines without any interaction.

There's also a Github action that uses this tool: https://github.com/jurplel/install-qt-action

Joshua Wade
  • 4,755
  • 2
  • 24
  • 44
Andrey Semakin
  • 2,032
  • 1
  • 23
  • 50
3

Credentials are now mandatory since 3.2.1, as explained a bit above. A qtaccount.ini file is created in ~/.local/share/qt. If you can't save & reuse this file, because your script needs to work for multiple users for example, you can use this new CredentialsPageCallback:

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

As always, please be careful when moving passwords around, especially in clear text.

Kriegalex
  • 423
  • 6
  • 17
3

The Qt installer v4.0 has been released, and it can run headless now:

qt-unified-windows.exe install --root C:\Qt\InstallFolder

You can use --help for a list of available commands:

qt-unified-windows.exe --help

The command-line installer requires user interaction, but this can be bypassed with flags. See this wiki page for a full list.

You can download the installer here.

Joshua Wade
  • 4,755
  • 2
  • 24
  • 44
  • 1
    Do you know if there's a way to NOT install the examples and documentation? They are automatically added because they are considered a dependency. I tried `--nf` and `--nd` but they are still installed. – Nil Jan 08 '21 at 20:16
2

I had the same problem and came up with a simple python script that basically does the same what the official Qt installer does. You can find it here.

And that's how to use it:

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

Then the Qt installation can be found at ./5.11.3/gcc_64/ in this case. With other systems/targets (i.e. mac ios or linux android android_armv7) this will differ of course.

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

LNJ
  • 304
  • 2
  • 13
  • `https://git.kaidan.im` seems to be down as of today. – Aritz Aug 03 '20 at 09:52
  • Yes sorry for that. There is https://git.sr.ht/~lnj/qli-installer, but it's probably the best if you use the aqtinstall fork of it: https://github.com/miurahr/aqtinstall – LNJ Aug 05 '20 at 11:09
1

This works for me!

 export DISPLAY=:1
 Xvfb :1 -screen 0 1024x768x16 &
 fluxbox &
 x11vnc -display :1 &

Connect to server using any vnc client

1

Note: the script which is described below has been deprecated in the meantime. Rationale and some alternative scripts are listed on the project's home page. Perhaps some of its forks do work, however.

Couple of months ago, I have crafted a configurable helper script for Qt installation automation. Although it is not a truly headless installer, as it simply iterates through install wizard screens, it can be used in continuous integration servers, and in fact it is tested this way.

Notes & features:

  • Works on Linux, MacOS, and Windows.
  • Qt version, components, and install path are configurable.
  • Always uses the most recent online installer.
  • Tested in Travis CI.
  • Bash is required, but it is typically present on continuous integration servers (at least on Travis).
  • Public domain (via CC0 waiver).

Home page: https://github.com/skalee/non-interactive-qt-installer

skalee
  • 12,331
  • 6
  • 55
  • 57
1

It's necessary to write in credentials in order to pass through credentials menu. In order to avoid that it's necessary to run docker build command with following network argument:

docker build --network none -t <img_name> <Dockerfile_path>

It was something that was missing in order to succesfully run qt interactive installation in docker.