Question

I've been able to create an installation for my software. However, I can't figure out how to create another installer which could update previous installation.

I have updated component versions, software version and release dates, but whenever I run second installation over the folder with pre-installed software - I'm getting The folder you selected already exists and contains an installation. Chose different target for installation.

Any hint on how to update an existing installation using Qt installer Framework would be very welcome!

No correct solution

OTHER TIPS

I faced the same problem too. So I have downloaded the latest snapshot and studied samples. One in particular is very helpful : dynamicpage

I didn't succeed to show a popup warning to the user when he's choosing an existing location, so I have found a work around: instead, a red label is shown under the selected directory.

This is not really a solution for updating component by component, but you will be able to continue installation process.

First of all, we need to replace the default page "TargetDirectory".

In file installerscript.qs, this is what you need to add:

// Constructor
function Component()
{
    component.loaded.connect(this, Component.prototype.installerLoaded);
    installer.setDefaultPageVisible(QInstaller.TargetDirectory, false);
}

// Utility function like QString QDir::toNativeSeparators(const QString & pathName) [static]
var Dir = new function () {
    this.toNativeSparator = function (path) {
        if (installer.value("os") == "win")
            return path.replace(/\//g, '\\');
        return path;
    }
};

// Called as soon as the component was loaded
Component.prototype.installerLoaded = function()
{
    if (installer.addWizardPage(component, "TargetWidget", QInstaller.TargetDirectory)) {
        var widget = gui.pageWidgetByObjectName("DynamicTargetWidget");
        if (widget != null) {
            widget.targetDirectory.textChanged.connect(this, Component.prototype.targetChanged);
            widget.targetChooser.clicked.connect(this, Component.prototype.chooseTarget);

            widget.windowTitle = "Installation Folder";
            widget.targetDirectory.text = Dir.toNativeSparator(installer.value("TargetDir"));
        }
    }
}

// Callback when one is clicking on the button to select where to install your application
Component.prototype.chooseTarget = function () {
    var widget = gui.pageWidgetByObjectName("DynamicTargetWidget");
    if (widget != null) {
        var newTarget = QFileDialog.getExistingDirectory("Choose your target directory.", widget.targetDirectory.text);
        if (newTarget != "") {
            widget.targetDirectory.text = Dir.toNativeSparator(newTarget);
        }
    }
}

Component.prototype.targetChanged = function (text) {
    var widget = gui.pageWidgetByObjectName("DynamicTargetWidget");
    if (widget != null) {
        if (text != "") {
            widget.complete = true;
            installer.setValue("TargetDir", text);
            if (installer.fileExists(text + "/components.xml")) {
                var warning = "<font color='red'>" + qsTr("A previous installation exists in this folder. If you wish to continue, everything will be overwritten.") + "</font>";
                widget.labelOverwrite.text = warning;
            } else {
                widget.labelOverwrite.text = "";
            }
            return;
        }
        widget.complete = false;
    }
}

In file targetwidget.ui

This is actually not the real .ui file, just for explanation purpose. In the end of this file, I have added an empty QLabel called labelOverwrite. The text is filled with red message in targetChanged callback.

<widget class="QWidget" name="TargetWidget">
  <layout class="QVBoxLayout" name="verticalLayout">
    <item>
      <widget class="QLabel" name="label">
       <property name="text">
        <string>Please specify the folder where Miam-Player will be installed.</string>
       </property>
      </widget>
    </item>
    <item>
      <layout class="QHBoxLayout" name="horizontalLayout">
       <item>
        <widget class="QLineEdit" name="targetDirectory"/>
       </item>
       <item>
        <widget class="QToolButton" name="targetChooser"/>
       </item>
      </layout>
    </item>
    <item>
      <widget class="QLabel" name="labelOverwrite"/>
    </item>
  </layout>
</widget>

Finally, don't forget to modify your existing file package.xml

<?xml version="1.0" encoding="UTF-8"?>
<Package>
    <DisplayName>Miam-Player</DisplayName>
    <Description>Miam-Player is the main program. It is required and cannot be unselected.</Description>
    <Name>org.miamplayer.core</Name>
    <Script>installscript.qs</Script>
    ...
    <UserInterfaces>
        <UserInterface>targetwidget.ui</UserInterface>
    </UserInterfaces>
</Package>

Matthieu answer is correct, in addition, if you want a question dialog use this funcion in installerscript.qs :

Component.prototype.targetChanged = function (text)
{
    var widget = gui.currentPageWidget(); // get the current wizard page
    var install = false;

    if (widget != null)
    {
        if (text != "")
        {
            if (installer.fileExists(text + "/components.xml"))
            {
                var result = QMessageBox.question("quit.question", "Installer", "Do you want to overwrite previous installation?",
                    QMessageBox.Yes | QMessageBox.No);
                if (result == QMessageBox.Yes)
                {
                   install = true;
                }
            }
            else
                install = true;
        }
        else
            install = false;
    }

    widget.complete = install;

    if(install)
        installer.setValue("TargetDir", text);      
}
Controller.prototype.TargetDirectoryPageCallback = function()
{
    var widget = gui.currentPageWidget();
    widget.TargetDirectoryLineEdit.textChanged.connect(this, Controller.prototype.targetChanged);
    Controller.prototype.targetChanged(widget.TargetDirectoryLineEdit.text);
}

Controller.prototype.targetChanged = function (text) {
    installer.setValue("RemoveTargetDir", true);
    if (text != "" && installer.fileExists(text + "/components.xml")) {
        if(QMessageBox.question("OverwriteTargetDirectory", "Overwrite target directory", "Do you want to overwrite previous installation?", QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes) {
            installer.setValue("RemoveTargetDir", false);
    }
}

It's simple. You should do the following:

  1. If you don't have a installer folder take a look into Qt/Tools/QtInstallerFramework/3.0/examples/. For this example I made a copy of online folder, which I'm gonna call my_installer_example.

  2. Update the repository for upload from packages_update.

From my_installer_example run:

./../bin/repogen --update -p packages_update/ repoForUpload

The flag --updade allows to take an existing repository (repoForUpload) as input and only changes the components that are specified as additional parameters.

  1. Upload repoForUpload in the server, specified by the tag in config/config.xml.

  2. Run the maintenancetool located into the folder installation.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top