Controller Scripting | Qt Installer Framework Manual

- Qt Installer Framework Manual
- Controller Scripting

Package Directory ◦ Component Scripting

# Controller Scripting

For each installer, you can specify a control script that interacts with certain parts of the installer's UI or functionality. To add the script to the installer, use the Configuration File element `ControlScript`, or from the command line enter `--script` <script_file>. The control script can add and remove pages to the wizard, change existing pages, do additional checks, and interact with the UI by simulating user clicks. This allows for example unattended installations.

The script format has to be compatible with QJSEngine.

This section describes the functions that are called to implement such a control script. It also gives an overview of installer pages and the widgets that are available on each page, such as push buttons, radio buttons, and line edits.

## Writing Control Scripts

A minimal valid script needs to contain at least a constructor, which can look like this:

     function Controller()
     {
     }

The following example presents a more advanced script that uses the gui JavaScript global object methods to set a new page title and welcome message on the introduction page and to automatically click the Next button on the target directory page:

     function Controller()
     {
     }

     Controller.prototype.IntroductionPageCallback = function()
     {
         var widget = gui.currentPageWidget(); // get the current wizard page
         if (widget != null) {
             widget.title = "New title."; // set the page title
             widget.MessageLabel.setText("New Message."); // set the welcome text
         }
     }

     Controller.prototype.TargetDirectoryPageCallback = function()
     {
         gui.clickButton(buttons.NextButton); // automatically click the Next button
     }

For more information about the JavaScript global objects that you can use in control scripts, see Scripting API.

In addition to the predefined global objects, the scripting API supports working with other objects derived from `QObject`. In the above code example the `gui.currentPageWidget()` method returns a widget of type `QWidget`.

The scripting API makes use of Qt's object trees. Widgets derived from `QObject` export their named child objects as properties for the JavaScript object, where the name of the property is the same as the child object's `QObject::objectName`. The default properties and their access functions of objects derived from `QObject` can also be used in the scripts.

For example, in the above code the `MessageLabel` object from class `QLabel` is a child of the `widget`. The `setText()` is the setter access function for its `QLabel::text` property.

In addition to properties, the signals and public slots of objects derived from `QObject` can be used in both controller and component scripts.

## Predefined Installer Pages

The QInstaller JavaScript object provides access to the following predefined installer pages:

- `Introduction`
- `TargetDirectory`
- `ComponentSelection`
- `LicenseCheck`
- `StartMenuSelection`
- `ReadyForInstallation`
- `PerformInstallation`
- `InstallationFinished`

The buttons JavaScript object provides a set of buttons that can be used on installer pages.

The following sections describe the functions that you can implement to interact with installer pages and the widgets that are available on each page.

### Introduction Page

Implement the `Controller.prototype.IntroductionPageCallback()` function to interact with widgets on the introduction page.

Wizard buttons:

- `NextButton`
- `CancelButton`

Widgets | Brief Description
--- | ---
`ErrorLabel` | Displays an error message.
`MessageLabel` | Displays a message. By default, it displays the "Welcome to the <Name> Setup" message.
`InformationLabel` | Displays progress information.

Radio Buttons | Brief Description
--- | ---
`PackageManagerRadioButton` | The package manager radio button shown on the page while running as maintenance tool.
`UpdaterRadioButton` | The updater radio button shown on the page while running as maintenance tool.
`UninstallerRadioButton` | The uninstaller radio button shown on the page while running as maintenance tool. Selected by default.

Progress Bar | Brief Description
--- | ---
`InformationProgressBar` | The progress bar shown while fetching remote packages.

Qt Core Feature | Brief Description
--- | ---
`packageManagerCoreTypeChanged()` | Connect to this signal if you want to be notified when the type of maintenance tool changes. Note: The signal is only emitted when the user has started the binary as so called maintenance tool (after the installation) and switches between the radio buttons.

Example code:

     function Controller()
     {
         var widget = gui.pageById(QInstaller.Introduction); // get the introduction wizard page
         if (widget != null)
             widget.packageManagerCoreTypeChanged.connect(onPackageManagerCoreTypeChanged);
     }

     onPackageManagerCoreTypeChanged = function()
     {
         console.log("Is Updater: " + installer.isUpdater());
         console.log("Is Uninstaller: " + installer.isUninstaller());
         console.log("Is Package Manager: " + installer.isPackageManager());
     }

### License Agreement Page

Implement the `Controller.prototype.LicenseAgreementPageCallback()` function to interact with widgets on the license agreement page.

Wizard buttons:

- `NextButton`
- `CancelButton`
- `BackButton`

Widgets | Brief Description
--- | ---
`LicenseListWidget` | Lists the available licenses.
`LicenseTextBrowser` | Shows the content of the selected license file.
`AcceptLicenseLabel` | Shows the text next to the accept license check box.
`AcceptLicenseCheckBox` | Accepts the license agreement.

### Target Directory Page

Implement the `Controller.prototype.TargetDirectoryPageCallback()` function to interact with widgets on the target directory selection page.

Wizard buttons:

- `NextButton`
- `CancelButton`
- `BackButton`

Widgets | Brief Description
--- | ---
`MessageLabel` | Displays a message.
`TargetDirectoryLineEdit` | Displays the value of the installation's target directory.
`WarningLabel` | Displays a warning.

### Component Selection Page

Implement the `Controller.prototype.ComponentSelectionPageCallback()` function to interact with widgets on the component selection page.

Wizard buttons:

- `NextButton`
- `CancelButton`
- `BackButton`

Methods | Brief Description
--- | ---
`selectAll()` | Selects all available packages if possible.
`deselectAll()` | Deselects all available packages if possible.
`selectDefault()` | Resets the checked state of available packages to their initial state.
`selectComponent(id)` | Selects the package with `id` (string).
`deselectComponent(id)` | Deselects the package with `id` (string).

Push Buttons | Brief Description
--- | ---
`SelectAllComponentsButton` | Selects all available packages if possible.
`DeselectAllComponentsButton` | Deselects all available packages if possible.
`SelectDefaultComponentsButton` | Resets the checked state of available packages to their initial state.
`ResetComponentsButton` | Resets to already installed components.
`FetchCategoryButton` | Fetch components from a category.

Widgets | Brief Description
--- | ---
`CategoryGroupBox` | Contains checkboxes for selecting repository categories.

Installer Framework 3.1 introduces repository categories as a new feature. When you use an installer that contains repository categories, you can select a category by its display name, fetch its contents, and then select the included components for installation.

You can fetch the components from a category as follows:

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

         // if CategoryGroupBox is visible, check one of the checkboxes
         // and click fetch button before selecting any components
         var groupBox = gui.findChild(page, "CategoryGroupBox");
         if (groupBox) {
             console.log("groupBox found");
             // findChild second argument is the display name of the checkbox
             var checkBox = gui.findChild(page, "Archive");
             if (checkBox) {
                 console.log("checkBox found");
                 console.log("checkBox name: " + checkBox.text);
                 if (checkBox.checked == false) {
                     checkBox.click();
                     var fetchButton = gui.findChild(page, "FetchCategoryButton");
                     if (fetchButton) {
                         console.log("fetchButton found");
                         fetchButton.click();
                     } else {
                         console.log("fetchButton NOT found");
                     }
                 }
             } else {
                 console.log("checkBox NOT found");
             }
         } else {
             console.log("groupBox NOT found");
         }
         // you can now select components from the fetched category
     }

### Start Menu Directory Page

Implement the `Controller.prototype.StartMenuDirectoryPageCallback()` function to interact with widgets on the ready for installation page.

Wizard buttons:

- `NextButton`
- `CancelButton`
- `BackButton`

Widgets | Brief Description
--- | ---
`StartMenuPathLineEdit` | Shows the directory where to create the program's shortcut.

### Ready for Installation Page

Implement the `Controller.prototype.ReadyForInstallationPageCallback()` function to interact with widgets on the ready for installation page.

Wizard buttons:

- `CommitButton`
- `CancelButton`
- `BackButton`

Widgets | Brief Description
--- | ---
`MessageLabel` | Displays a message.
`TaskDetailsBrowser` | Displays some more detailed information about the installation.

### Perform Installation Page

Implement the `Controller.prototype.PerformInstallationPageCallback()` function to interact with widgets on the perform installation page.

Wizard buttons:

- `CommitButton`
- `CancelButton`

### Finished Page

Implement the `Controller.prototype.FinishedPageCallback()` function to interact with widgets on the installation finished page.

Wizard buttons:

- `CommitButton`
- `CancelButton`
- `FinishButton`

Widgets | Brief Description
--- | ---
`MessageLabel` | Displays a message.
`RunItCheckBox` | Text field that informs users that they can start an application after the installation process has finished.

## Custom Pages

Custom pages are registered as `Dynamic${ObjectName}`, where `${ObjectName}` is the object name set in the UI file. Thus, the `Dynamic${ObjectName}Callback()` function is called. Widgets can be addressed using their object names (from the UI file).

Example code:

     function Component()
     {
         // add page with widget \c SomePageWidget before the target directory page
         installer.addWizardPage(component, "SomePageWidget", QInstaller.TargetDirectory)
     }

     Component.prototype.DynamicSomePageWidgetCallback = function()
     {
         var page = gui.pageWidgetByObjectName("DynamicSomePageWidget");
         page.myButton.click, //direct child of the UI file's widget
         page.someFancyWidget.subWidget.setText("foobar") // nested widget
     }

## Message Boxes

While executing the installer application, for example, the application might show some message boxes about an error that occurred. This is fine while running the application on the end user's system, but it might break automated test suites. To overcome this issue, all message boxes shown by the Qt Installer Framework are addressable by a specific identifier.

Identifier | Possible Answers | Description
--- | --- | ---
`OverwriteTargetDirectory` | Yes, No | Confirmation for using an already existing directory as the target directory for installation.
`installationError` | OK, Retry, Ignore | A fatal error occurred while performing the installation.
`installationErrorWithRetry` | Retry, Ignore, Cancel | An error occurred while performing the installation. End users can select Retry to try again.
`AuthorizationError` | Abort, OK | Elevated permissions could not be acquired.
`OperationDoesNotExistError` | Abort, Ignore | An error occurred while trying to perform an operation, but the operation did not exist.
`isAutoDependOnError` | OK | An error occurred while calling a package script. It was not possible to evaluate if the package has a auto dependency on other packages.
`isDefaultError` | OK | An error occurred while calling a package script. It was not possible to evaluate if the package will be installed by default.
`DownloadError` | Retry, Cancel | An error occurred while downloading an archive hash from a remote repository. End users can select Retry to try again.
`archiveDownloadError` | Retry, Cancel | An error occurred while downloading a archive from a remote repository. End users can select Retry to try again.
`WriteError` | OK | An error occurred while writing the maintenance tool.
`ElevationError` | OK | Elevated permissions could not be acquired.
`unknown` | OK | An unknown error occurred while removing a certain package.
`Error` | OK | Generic error.
`stopProcessesForUpdates` | Retry, Ignore, Cancel | An error occurred while updating a package. Some running application or process needs to be quit before the update can be performed. End users can select Retry to try again once they have been stopped.
`Installer_Needs_To_Be_Local_Error` | OK | The installer binary was started from a network location, but the installation over network is not supported.
`TargetDirectoryInUse` | No | The installation's target directory already contains an installation.
`WrongTargetDirectory` | OK | The installation's target directory is a file or symlink.
`AlreadyRunning` | OK | Another application instance is already running.

Example code:

     function Controller()
     {
         installer.autoRejectMessageBoxes;
         installer.setMessageBoxAutomaticAnswer("OverwriteTargetDirectory", QMessageBox.Yes);
         installer.setMessageBoxAutomaticAnswer("stopProcessesForUpdates", QMessageBox.Ignore);
     }

Package Directory ◦ Component Scripting