Skip to content

Commit ead8990

Browse files
sverhoevenJorisGoosen
authored andcommitted
Give user feedback about download and installation progress
1 parent 675878c commit ead8990

File tree

3 files changed

+136
-6
lines changed

3 files changed

+136
-6
lines changed

Desktop/components/JASP/Widgets/ModulesMenu.qml

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,17 +372,24 @@ FocusScope
372372
moduleStore.downloadInProgress = true
373373
moduleStore.downloadTotal = request.totalBytes
374374
moduleStore.downloadProgress = Qt.binding(function() { return request.receivedBytes; })
375+
moduleStore.currentDownloadRequest = request
375376
request.accept()
376377
}
377378
else
378379
request.cancel()
379380
}
380381

381382
onDownloadFinished: function(request) {
383+
moduleStore.downloadInProgress = false
384+
moduleStore.currentDownloadRequest = null
385+
if (request.state !== WebEngineDownloadRequest.DownloadCompleted) {
386+
console.log("Download interrupted:", request.interruptReasonString)
387+
return
388+
}
382389
console.log("Download finished:", request.downloadFileName)
383390
let path = request.downloadDirectory + '/' + request.downloadFileName
391+
moduleLibrary.startInstalling()
384392
dynamicModules.installJASPModule(path)
385-
moduleStore.downloadInProgress = false
386393
}
387394
}
388395

@@ -401,6 +408,7 @@ FocusScope
401408
property bool installInProgress: false;
402409
property int downloadProgress;
403410
property int downloadTotal;
411+
property var currentDownloadRequest: null;
404412

405413
webChannel.registeredObjects: [ moduleStoreWebChannel ]
406414

@@ -441,6 +449,89 @@ FocusScope
441449

442450
focus: true
443451

452+
// Progress bar overlay for download and installation
453+
Rectangle
454+
{
455+
id: progressOverlay
456+
anchors.fill: parent
457+
color: jaspTheme.grayDarker
458+
visible: moduleStore.downloadInProgress || moduleLibrary.isInstalling
459+
z: 10
460+
461+
Column
462+
{
463+
anchors.centerIn: parent
464+
spacing: 10 * preferencesModel.uiScale
465+
width: 300 * preferencesModel.uiScale
466+
467+
Text
468+
{
469+
id: progressText
470+
text: moduleStore.downloadInProgress ? qsTr("Downloading module...") : qsTr("Installing module...")
471+
color: "white"
472+
font.pixelSize: 16 * preferencesModel.uiScale
473+
anchors.horizontalCenter: parent.horizontalCenter
474+
}
475+
476+
// TODO show progress of installation
477+
Rectangle
478+
{
479+
id: progressBarBackground
480+
width: parent.width
481+
height: 30 * preferencesModel.uiScale
482+
color: jaspTheme.grayDarker
483+
border.color: jaspTheme.uiBorder
484+
border.width: 1
485+
radius: 3
486+
visible: moduleStore.downloadInProgress
487+
488+
Rectangle
489+
{
490+
id: progressBarFill
491+
width: moduleStore.downloadTotal > 0 ? (parent.width * moduleStore.downloadProgress / moduleStore.downloadTotal) : 0
492+
height: parent.height
493+
color: jaspTheme.blue
494+
radius: parent.radius
495+
496+
Behavior on width
497+
{
498+
enabled: preferencesModel.animationsOn
499+
PropertyAnimation { duration: 100 }
500+
}
501+
}
502+
503+
Text
504+
{
505+
anchors.centerIn: parent
506+
text: moduleStore.downloadTotal > 0 ? Math.round((moduleStore.downloadProgress / moduleStore.downloadTotal) * 100) + "%" : "0%"
507+
color: "white"
508+
font.pixelSize: 12 * preferencesModel.uiScale
509+
}
510+
}
511+
512+
RoundedButton
513+
{
514+
id: cancelButton
515+
text: qsTr("Cancel")
516+
width: 120 * preferencesModel.uiScale
517+
height: 30 * preferencesModel.uiScale
518+
anchors.horizontalCenter: parent.horizontalCenter
519+
// TODO also allow to cancel installation
520+
visible: moduleStore.downloadInProgress
521+
522+
onClicked:
523+
{
524+
if (moduleStore.currentDownloadRequest !== null) {
525+
moduleStore.currentDownloadRequest.cancel()
526+
}
527+
moduleStore.downloadInProgress = false
528+
moduleStore.currentDownloadRequest = null
529+
}
530+
toolTip: qsTr("Cancel download")
531+
}
532+
}
533+
}
534+
444535
Item
445536
{
446537
id: dropShadow

Desktop/modules/modulelibrary.cpp

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "installedmodules.h"
88
#include "dynamicmodules.h"
99
#include "dynamicmodule.h"
10+
#include "engine/enginesync.h"
1011
#include "utilities/dynamicruntimeinfo.h"
1112

1213
ModuleLibrary * ModuleLibrary::_singleton = nullptr;
@@ -18,12 +19,31 @@ ModuleLibrary::ModuleLibrary(QObject *parent)
1819

1920
if (auto *dynMods = Modules::DynamicModules::dynMods())
2021
{
21-
connect(dynMods, &Modules::DynamicModules::dynamicModuleAdded, this, [this](Modules::DynamicModule *) { emitEnvironmentInfoChanged(); });
22+
connect(dynMods, &Modules::DynamicModules::dynamicModuleAdded, this, [this](Modules::DynamicModule *) {
23+
emitEnvironmentInfoChanged();
24+
finishInstalling();
25+
});
2226
connect(dynMods, &Modules::DynamicModules::dynamicModuleChanged, this, [this](Modules::DynamicModule *) { emitEnvironmentInfoChanged(); });
23-
connect(dynMods, &Modules::DynamicModules::dynamicModuleReplaced, this, [this](Modules::DynamicModule *, Modules::DynamicModule *) { emitEnvironmentInfoChanged(); });
24-
// TODO there is a timing issue here, the dynamicModuleUninstalled signal is emitted before the module is fully uninstalled,
25-
// causing UI to update too early and show the module as still installed.
26-
connect(dynMods, &Modules::DynamicModules::dynamicModuleUninstalled,this, [this](const QString &) { emitEnvironmentInfoChanged(); });
27+
connect(dynMods, &Modules::DynamicModules::dynamicModuleReplaced, this, [this](Modules::DynamicModule *, Modules::DynamicModule *) { emitEnvironmentInfoChanged(); });
28+
}
29+
if (auto *engineSync = EngineSync::singleton())
30+
{
31+
connect(engineSync, &EngineSync::moduleInstallationSucceeded, this, [this]() {
32+
emitEnvironmentInfoChanged();
33+
finishInstalling();
34+
});
35+
connect(engineSync, &EngineSync::moduleInstallationFailed, this, [this](const QString &, const QString &) {
36+
emitEnvironmentInfoChanged();
37+
finishInstalling();
38+
});
39+
connect(engineSync, &EngineSync::moduleUninstallationSucceeded, this, [this]() {
40+
emitEnvironmentInfoChanged();
41+
finishInstalling();
42+
});
43+
connect(engineSync, &EngineSync::moduleUninstallationFailed, this, [this](const QString &, const QString &) {
44+
emitEnvironmentInfoChanged();
45+
finishInstalling();
46+
});
2747
}
2848

2949
if (auto *prefs = PreferencesModel::prefs())
@@ -82,3 +102,15 @@ void ModuleLibrary::emitEnvironmentInfoChanged()
82102
{
83103
emit environmentInfoChanged(getEnvironmentInfo());
84104
}
105+
106+
void ModuleLibrary::startInstalling()
107+
{
108+
_isInstalling = true;
109+
emit isInstallingChanged();
110+
}
111+
112+
void ModuleLibrary::finishInstalling()
113+
{
114+
_isInstalling = false;
115+
emit isInstallingChanged();
116+
}

Desktop/modules/modulelibrary.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class DynamicModule;
3131
class ModuleLibrary : public QObject
3232
{
3333
Q_OBJECT
34+
Q_PROPERTY(bool isInstalling READ isInstalling NOTIFY isInstallingChanged)
3435

3536
public:
3637
explicit ModuleLibrary(QObject *parent = 0);
@@ -39,16 +40,22 @@ class ModuleLibrary : public QObject
3940

4041
Q_INVOKABLE QVariantMap getEnvironmentInfo() const;
4142
Q_INVOKABLE void uninstallJASPModule(const QString &moduleName);
43+
Q_INVOKABLE void startInstalling();
44+
Q_INVOKABLE void finishInstalling();
45+
46+
bool isInstalling() const { return _isInstalling; }
4247

4348
signals:
4449
void environmentInfoChanged(const QVariantMap &environmentInfo);
50+
void isInstallingChanged();
4551

4652
private:
4753
QVariantMap installedModulesInfo() const;
4854
void emitEnvironmentInfoChanged();
4955

5056
private:
5157
static ModuleLibrary *_singleton;
58+
bool _isInstalling = false;
5259
};
5360

5461
#endif // MODULELIBRARY_H

0 commit comments

Comments
 (0)