[education/rkward] rkward: Start showing some progress information inline, to avoid flood of dialogs
Thomas Friedrichsmeier
null at kde.org
Sun May 1 21:37:49 BST 2022
Git commit 648b705ce847870bc4f5913fc4f1d0ac7c4e9169 by Thomas Friedrichsmeier.
Committed on 29/04/2022 at 21:15.
Pushed by tfry into branch 'master'.
Start showing some progress information inline, to avoid flood of dialogs
M +10 -9 rkward/dialogs/rkloadlibsdialog.cpp
M +2 -1 rkward/dialogs/rkloadlibsdialog.h
M +100 -0 rkward/misc/rkprogresscontrol.cpp
M +26 -0 rkward/misc/rkprogresscontrol.h
M +1 -0 rkward/rbackend/rcommand.cpp
M +7 -4 rkward/rbackend/rcommand.h
https://invent.kde.org/education/rkward/commit/648b705ce847870bc4f5913fc4f1d0ac7c4e9169
diff --git a/rkward/dialogs/rkloadlibsdialog.cpp b/rkward/dialogs/rkloadlibsdialog.cpp
index c178fcdc..b862105f 100644
--- a/rkward/dialogs/rkloadlibsdialog.cpp
+++ b/rkward/dialogs/rkloadlibsdialog.cpp
@@ -25,6 +25,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
#include <KLocalizedString>
#include <kmessagebox.h>
#include <kuser.h>
+#include <KMessageWidget>
#include "../rbackend/rkrinterface.h"
#include "../rbackend/rksessionvars.h"
@@ -655,8 +656,8 @@ InstallPackagesWidget::InstallPackagesWidget (RKLoadLibsDialog *dialog) : QWidge
hbox->addLayout (vbox);
hbox->setStretchFactor (vbox, 2);
- packages_status = new RKRPackageInstallationStatus (this);
packages_view = new QTreeView (this);
+ packages_status = new RKRPackageInstallationStatus(this, packages_view);
packages_view->setSortingEnabled (true);
model = new RKRPackageInstallationStatusSortFilterModel (this);
model->setSourceModel (packages_status);
@@ -669,7 +670,6 @@ InstallPackagesWidget::InstallPackagesWidget (RKLoadLibsDialog *dialog) : QWidge
connect (packages_view, &QTreeView::clicked, this, &InstallPackagesWidget::rowClicked);
packages_view->setRootIsDecorated (false);
packages_view->setIndentation (0);
- packages_view->setEnabled (false);
packages_view->setMinimumHeight (packages_view->sizeHintForRow (0) * 15); // force a decent height
packages_view->setMinimumWidth (packages_view->fontMetrics ().width ("This is to force a sensible min width for the packages view (empty on construction)"));
vbox->addWidget (packages_view);
@@ -727,7 +727,6 @@ void InstallPackagesWidget::initialize () {
RK_TRACE (DIALOGS);
packages_status->initialize (parent->chain);
- packages_view->setEnabled (true);
// Force a good width for the icon column, particularly for MacOS X.
packages_view->header ()->resizeSection (0, packages_view->sizeHintForIndex (model->index (0, 0, model->index (RKRPackageInstallationStatus::NewPackages, 0, QModelIndex ()))).width () + packages_view->indentation ());
for (int i = 1; i <= RKRPackageInstallationStatus::PackageName; ++i) {
@@ -878,7 +877,7 @@ void PackageInstallParamsWidget::liblocsChanged (const QStringList &newlist) {
/////////// RKRPackageInstallationStatus /////////////////
-RKRPackageInstallationStatus::RKRPackageInstallationStatus (QObject* parent) : QAbstractItemModel (parent) {
+RKRPackageInstallationStatus::RKRPackageInstallationStatus (QObject* parent, QWidget* display_area) : QAbstractItemModel (parent), display_area(display_area) {
RK_TRACE (DIALOGS);
_initialized = false;
}
@@ -932,11 +931,13 @@ void RKRPackageInstallationStatus::initialize (RCommandChain *chain) {
_initialized = true; // will be re-set to false, should the command fail / be cancelled
RCommand *command = new RCommand (".rk.get.package.installation.state ()", RCommand::App | RCommand::GetStructuredData);
- connect (command->notifier (), &RCommandNotifier::commandFinished, this, &RKRPackageInstallationStatus::statusCommandFinished);
- RKProgressControl *control = new RKProgressControl (this, i18n ("<p>Please stand by while searching for installed and available packages.</p><p><strong>Note:</strong> This requires a working internet connection, and may take some time, esp. if one or more repositories are temporarily unavailable.</p>"), i18n ("Searching for packages"), RKProgressControl::CancellableProgress | RKProgressControl::AutoCancelCommands);
- control->addRCommand (command, true);
- RInterface::issueCommand (command, chain);
- control->doModal (true);
+ connect(command->notifier(), &RCommandNotifier::commandFinished, this, &RKRPackageInstallationStatus::statusCommandFinished);
+ RKInlineProgressControl *control = new RKInlineProgressControl(display_area, true);
+ control->messageWidget()->setText(i18n("<p>Please stand by while searching for installed and available packages.</p><p><strong>Note:</strong> This requires a working internet connection, and may take some time, esp. if one or more repositories are temporarily unavailable.</p>"));
+ control->addRCommand(command);
+ //control->setAutoCloseWhenCommandsDone(true);
+ RInterface::issueCommand(command, chain);
+ control->show(100);
}
void RKRPackageInstallationStatus::statusCommandFinished (RCommand *command) {
diff --git a/rkward/dialogs/rkloadlibsdialog.h b/rkward/dialogs/rkloadlibsdialog.h
index ee60ec3c..cdf29c5f 100644
--- a/rkward/dialogs/rkloadlibsdialog.h
+++ b/rkward/dialogs/rkloadlibsdialog.h
@@ -137,7 +137,7 @@ private:
class RKRPackageInstallationStatus : public QAbstractItemModel {
Q_OBJECT
public:
- explicit RKRPackageInstallationStatus (QObject* parent);
+ explicit RKRPackageInstallationStatus (QObject* parent, QWidget* diplay_area);
~RKRPackageInstallationStatus ();
void initialize (RCommandChain *chain);
@@ -204,6 +204,7 @@ private:
bool _initialized;
QStringList current_repos;
+ QWidget *display_area;
};
class RKRPackageInstallationStatusSortFilterModel : public QSortFilterProxyModel {
diff --git a/rkward/misc/rkprogresscontrol.cpp b/rkward/misc/rkprogresscontrol.cpp
index f3eba6cd..839ab119 100644
--- a/rkward/misc/rkprogresscontrol.cpp
+++ b/rkward/misc/rkprogresscontrol.cpp
@@ -381,3 +381,103 @@ void RKProgressControlDialog::closeEvent (QCloseEvent *e) {
}
}
+#include <KMessageWidget>
+#include <KStandardAction>
+
+RKInlineProgressControl::RKInlineProgressControl(QWidget *display_area, bool allow_cancel) : QObject(display_area),
+ autoclose(false),
+ allow_cancel(allow_cancel),
+ display_area(display_area),
+ prevent_close_message(nullptr),
+ close_action(nullptr) {
+ RK_TRACE(MISC);
+
+ display_area->window()->installEventFilter(this);
+
+ wrapper = new QWidget(display_area);
+ auto layout = new QVBoxLayout(wrapper);
+ message_widget = new KMessageWidget();
+ message_widget->setWordWrap(true);
+ message_widget->setCloseButtonVisible(false); // we want a button, instead, for consistency with cancel
+ if (allow_cancel) {
+ setCloseAction(i18n("Cancel"));
+ }
+ output_display = new QTextEdit();
+ layout->addWidget(message_widget);
+ layout->addWidget(output_display);
+ wrapper->resize(display_area->size());
+}
+
+RKInlineProgressControl::~RKInlineProgressControl() {
+ RK_TRACE(MISC);
+
+ display_area->window()->removeEventFilter(this);
+ delete wrapper;
+ for (int i = 0; i < unfinished_commands.size(); ++i) {
+ RInterface::instance()->cancelCommand(unfinished_commands[i]);
+ }
+}
+
+void RKInlineProgressControl::setCloseAction(const QString &label) {
+ RK_TRACE(MISC);
+
+ if (!close_action) {
+ close_action = KStandardAction::create(KStandardAction::Close, this, &QObject::deleteLater, this);
+ message_widget->addAction(close_action);
+ }
+ close_action->setText(label);
+}
+
+void RKInlineProgressControl::addRCommand(RCommand *command) {
+ RK_TRACE(MISC);
+ unfinished_commands.append(command);
+ connect(command->notifier(), &RCommandNotifier::commandFinished, this, [this](RCommand *c) {
+ unfinished_commands.removeAll(c);
+ if (c->failed()) {
+ autoclose = false;
+ message_widget->setMessageType(KMessageWidget::Error);
+ }
+ if (unfinished_commands.isEmpty()) {
+ if (autoclose) {
+ deleteLater();
+ } else {
+ setCloseAction(i18n("Close"));
+ }
+ }
+ });
+ connect(command->notifier(), &RCommandNotifier:: commandOutput, this, [this](RCommand *, const ROutput *o) {
+ if (o->type == ROutput::Output) {
+ output_display->setTextColor(RKStyle::viewScheme()->foreground(KColorScheme::NormalText).color());
+ } else {
+ output_display->setTextColor(RKStyle::viewScheme()->foreground(KColorScheme::NegativeText).color());
+ }
+ output_display->insertPlainText(o->output);
+ });
+}
+
+void RKInlineProgressControl::show(int delay_ms) {
+ RK_TRACE(MISC);
+ if (delay_ms > 0) {
+ QTimer::singleShot(delay_ms, wrapper, &QWidget::show);
+ } else {
+ wrapper->show();
+ }
+}
+
+bool RKInlineProgressControl::eventFilter(QObject *, QEvent *e) {
+ RK_TRACE(MISC);
+ if (e->type() == QEvent::Resize) {
+ QTimer::singleShot(0, this, [this]() {
+ wrapper->resize(display_area->size());
+ });
+ return false;
+ }
+ if (!allow_cancel && e->type() == QEvent::Close) {
+ // TODO
+ prevent_close_message = new KMessageWidget(i18n("An operation is still running, please wait."));
+ return true;
+ }
+ return false;
+}
+
+
diff --git a/rkward/misc/rkprogresscontrol.h b/rkward/misc/rkprogresscontrol.h
index 82f6a5a2..9ee1d7d1 100644
--- a/rkward/misc/rkprogresscontrol.h
+++ b/rkward/misc/rkprogresscontrol.h
@@ -99,4 +99,30 @@ protected:
void rCommandDone (RCommand *command) override;
};
+class KMessageWidget;
+class QAction;
+
+class RKInlineProgressControl : public QObject {
+public:
+/** @param allow_cancel Whether the operation may be cancelled. If true, a cancel button will be added. If false, will also prevent the parent dialog from being closed. Default: true */
+ RKInlineProgressControl(QWidget *display_area, bool allow_cancel);
+ ~RKInlineProgressControl();
+ void addRCommand(RCommand *command);
+ void setAutoCloseWhenCommandsDone(bool _autoclose) { autoclose = _autoclose; };
+ void show(int delay_ms=0);
+ KMessageWidget *messageWidget() { return message_widget; };
+private:
+ void setCloseAction(const QString &label);
+ bool eventFilter(QObject *, QEvent *e) override;
+ bool autoclose;
+ bool allow_cancel;
+ QWidget* wrapper;
+ QWidget* display_area;
+ KMessageWidget *message_widget;
+ KMessageWidget *prevent_close_message;
+ QTextEdit *output_display;
+ QList<RCommand*> unfinished_commands;
+ QAction* close_action;
+};
+
#endif
diff --git a/rkward/rbackend/rcommand.cpp b/rkward/rbackend/rcommand.cpp
index 2c205958..36961fa8 100644
--- a/rkward/rbackend/rcommand.cpp
+++ b/rkward/rbackend/rcommand.cpp
@@ -122,6 +122,7 @@ void RCommand::newOutput (ROutput *output) {
if (receivers[i] == 0) continue;
receivers[i]->newOutput (this, output);
}
+ if (_notifier) _notifier->emitOutput(this, output);
}
void RCommand::commandLineIn () {
diff --git a/rkward/rbackend/rcommand.h b/rkward/rbackend/rcommand.h
index bbb56f01..fa6d29e9 100644
--- a/rkward/rbackend/rcommand.h
+++ b/rkward/rbackend/rcommand.h
@@ -67,12 +67,15 @@ typedef QList<ROutput*> ROutputList;
class RCommandNotifier : public QObject {
Q_OBJECT
signals:
- void commandFinished (RCommand *command);
+/** given command has finished (not necessarily successfully) */
+ void commandFinished(RCommand *command);
+ void commandOutput(RCommand *command, const ROutput* output);
private:
friend class RCommand;
- RCommandNotifier ();
- ~RCommandNotifier ();
- void emitFinished (RCommand *command) { emit commandFinished (command); };
+ RCommandNotifier();
+ ~RCommandNotifier();
+ void emitFinished(RCommand *command) { emit commandFinished(command); };
+ void emitOutput(RCommand *command, const ROutput* output) { emit commandOutput(command, output); };
};
/** For introductory information on using RCommand, see \ref UsingTheInterfaceToR
More information about the rkward-tracker
mailing list