Better installation feedback for package manager

Now the confirmation dialog stays open and will show a log with the
installation progress and results.

The "marked" icon has been changed to look a bit less painted.
This commit is contained in:
Matthias Koefferlein 2017-06-05 12:54:09 +02:00
parent 46fccb99bc
commit 1b0dcb5bf9
5 changed files with 448 additions and 149 deletions

View File

@ -13,7 +13,7 @@
<property name="windowTitle">
<string>Ready for Installation</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QLabel" name="label">
<property name="text">
@ -25,66 +25,304 @@
</widget>
</item>
<item>
<widget class="QTreeWidget" name="list">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="allColumnsShowFocus">
<bool>true</bool>
</property>
<column>
<property name="text">
<string>Package</string>
</property>
</column>
<column>
<property name="text">
<string>Action</string>
</property>
</column>
<column>
<property name="text">
<string>Version</string>
</property>
</column>
<column>
<property name="text">
<string>Download link</string>
</property>
</column>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Press &quot;Ok&quot; to install or update these packages or &quot;Cancel&quot; to abort.</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>6</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
<widget class="QWidget" name="layoutWidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTreeWidget" name="list">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="allColumnsShowFocus">
<bool>true</bool>
</property>
<column>
<property name="text">
<string>Package</string>
</property>
</column>
<column>
<property name="text">
<string>Action</string>
</property>
</column>
<column>
<property name="text">
<string>Version</string>
</property>
</column>
<column>
<property name="text">
<string>Download link</string>
</property>
</column>
</widget>
</item>
<item>
<widget class="QFrame" name="confirm_panel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Press &quot;Ok&quot; to install or update these packages or &quot;Cancel&quot; to abort.</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>6</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>300</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="ok_button">
<property name="text">
<string>Ok</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cancel_button">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QFrame" name="log_panel">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QListView" name="log_view">
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="isWrapping" stdset="0">
<bool>false</bool>
</property>
<property name="resizeMode">
<enum>QListView::Adjust</enum>
</property>
<property name="uniformItemSizes">
<bool>true</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="attn_frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="layResources.qrc">:/warn_16.png</pixmap>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>There are errors or warnings</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_3">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="close_button">
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
@ -128,38 +366,5 @@
<resources>
<include location="layResources.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>SaltManagerInstallConfirmationDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>273</x>
<y>431</y>
</hint>
<hint type="destinationlabel">
<x>276</x>
<y>448</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>SaltManagerInstallConfirmationDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>351</x>
<y>426</y>
</hint>
<hint type="destinationlabel">
<x>363</x>
<y>445</y>
</hint>
</hints>
</connection>
</connections>
<connections/>
</ui>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 1001 B

View File

@ -25,8 +25,7 @@
#include "tlFileUtils.h"
#include "tlWebDAV.h"
#include "ui_SaltManagerInstallConfirmationDialog.h"
#include <memory>
#include <QTreeWidgetItem>
#include <QMessageBox>
@ -35,32 +34,81 @@ namespace lay
// ----------------------------------------------------------------------------------
class ConfirmationDialog
: public QDialog, private Ui::SaltManagerInstallConfirmationDialog
ConfirmationDialog::ConfirmationDialog (QWidget *parent)
: QDialog (parent), m_confirmed (false), m_cancelled (false), m_closed (false), m_file (50000, true)
{
public:
ConfirmationDialog (QWidget *parent)
: QDialog (parent)
{
Ui::SaltManagerInstallConfirmationDialog::setupUi (this);
Ui::SaltManagerInstallConfirmationDialog::setupUi (this);
connect (ok_button, SIGNAL (clicked ()), this, SLOT (confirm_pressed ()));
connect (cancel_button, SIGNAL (clicked ()), this, SLOT (cancel_pressed ()));
connect (close_button, SIGNAL (clicked ()), this, SLOT (close_pressed ()));
log_panel->hide ();
attn_frame->hide ();
log_view->setModel (&m_file);
connect (&m_file, SIGNAL (layoutChanged ()), log_view, SLOT (scrollToBottom ()));
connect (&m_file, SIGNAL (attention_changed (bool)), attn_frame, SLOT (setVisible (bool)));
}
void
ConfirmationDialog::add_info (const std::string &name, bool update, const std::string &version, const std::string &url)
{
QTreeWidgetItem *item = new QTreeWidgetItem (list);
m_items_by_name.insert (std::make_pair (name, item));
item->setFlags (item->flags () & ~Qt::ItemIsSelectable);
item->setText (0, tl::to_qstring (name));
item->setText (1, update ? tr ("UPDATE") : tr ("INSTALL"));
item->setText (2, tl::to_qstring (version));
item->setText (3, tl::to_qstring (url));
for (int column = 0; column < list->colorCount (); ++column) {
item->setData (column, Qt::ForegroundRole, update ? Qt::blue : Qt::black);
}
}
void add_info (const std::string &name, bool update, const std::string &version, const std::string &url)
{
QTreeWidgetItem *item = new QTreeWidgetItem (list);
void
ConfirmationDialog::separator ()
{
m_file.separator ();
}
item->setFlags (item->flags () & ~Qt::ItemIsSelectable);
void
ConfirmationDialog::mark_error (const std::string &name)
{
set_icon_for_name (name, QIcon (QString::fromUtf8 (":/error_16.png")));
}
item->setText (0, tl::to_qstring (name));
item->setText (1, update ? tr ("UPDATE") : tr ("INSTALL"));
item->setText (2, tl::to_qstring (version));
item->setText (3, tl::to_qstring (url));
void
ConfirmationDialog::mark_success (const std::string &name)
{
set_icon_for_name (name, QIcon (QString::fromUtf8 (":/marked_16.png")));
}
for (int column = 0; column < list->colorCount (); ++column) {
item->setData (column, Qt::ForegroundRole, update ? Qt::blue : Qt::black);
}
void
ConfirmationDialog::set_icon_for_name (const std::string &name, const QIcon &icon)
{
std::map<std::string, QTreeWidgetItem *>::const_iterator i = m_items_by_name.find (name);
if (i != m_items_by_name.end ()) {
i->second->setData (0, Qt::DecorationRole, icon);
}
};
}
void
ConfirmationDialog::start ()
{
confirm_panel->hide ();
log_panel->show ();
close_button->setEnabled (false);
}
void
ConfirmationDialog::finish ()
{
close_button->setEnabled (true);
}
// ----------------------------------------------------------------------------------
@ -182,23 +230,17 @@ SaltDownloadManager::fetch_missing (const lay::Salt &salt_mine, tl::AbsoluteProg
}
}
bool
SaltDownloadManager::show_confirmation_dialog (QWidget *parent, const lay::Salt &salt)
lay::ConfirmationDialog *
SaltDownloadManager::make_confirmation_dialog (QWidget *parent, const lay::Salt &salt)
{
// Stop with a warning if there is nothing to do
if (m_registry.empty()) {
QMessageBox::warning (parent, tr ("Nothing to do"), tr ("No packages need update or are marked for installation"));
return false;
}
lay::ConfirmationDialog dialog (parent);
lay::ConfirmationDialog *dialog = new lay::ConfirmationDialog (parent);
// First the packages to update
for (std::map<std::string, Descriptor>::const_iterator p = m_registry.begin (); p != m_registry.end (); ++p) {
const lay::SaltGrain *g = salt.grain_by_name (p->first);
if (g) {
// \342\206\222 is UTF-8 "right arrow"
dialog.add_info (p->first, true, g->version () + " \342\206\222 " + p->second.version, p->second.url);
dialog->add_info (p->first, true, g->version () + " \342\206\222 " + p->second.version, p->second.url);
}
}
@ -206,19 +248,37 @@ SaltDownloadManager::show_confirmation_dialog (QWidget *parent, const lay::Salt
for (std::map<std::string, Descriptor>::const_iterator p = m_registry.begin (); p != m_registry.end (); ++p) {
const lay::SaltGrain *g = salt.grain_by_name (p->first);
if (!g) {
dialog.add_info (p->first, false, p->second.version, p->second.url);
dialog->add_info (p->first, false, p->second.version, p->second.url);
}
}
return dialog.exec ();
return dialog;
}
bool
SaltDownloadManager::execute (lay::Salt &salt)
SaltDownloadManager::execute (QWidget *parent, lay::Salt &salt)
{
bool result = true;
// Stop with a warning if there is nothing to do
if (m_registry.empty()) {
QMessageBox::warning (parent, tr ("Nothing to do"), tr ("No packages need update or are marked for installation"));
return true;
}
tl::RelativeProgress progress (tl::to_string (QObject::tr ("Downloading packages")), m_registry.size (), 1);
std::auto_ptr<lay::ConfirmationDialog> dialog (make_confirmation_dialog (parent, salt));
dialog->setModal (true);
dialog->show ();
while (! dialog->is_confirmed ()) {
QCoreApplication::processEvents (QEventLoop::AllEvents | QEventLoop::WaitForMoreEvents, 100);
if (dialog->is_cancelled () || ! dialog->isVisible ()) {
return false;
}
}
dialog->start ();
bool result = true;
for (std::map<std::string, Descriptor>::const_iterator p = m_registry.begin (); p != m_registry.end (); ++p) {
@ -230,13 +290,22 @@ SaltDownloadManager::execute (lay::Salt &salt)
}
if (! salt.create_grain (p->second.grain, target)) {
dialog->mark_error (p->first);
result = false;
} else {
dialog->mark_success (p->first);
}
++progress;
dialog->separator ();
}
dialog->finish ();
while (! dialog->is_closed () && dialog->isVisible ()) {
QCoreApplication::processEvents (QEventLoop::AllEvents | QEventLoop::WaitForMoreEvents, 100);
}
return result;
}

View File

@ -25,8 +25,11 @@
#include "layCommon.h"
#include "laySaltGrain.h"
#include "layLogViewerDialog.h"
#include "tlProgress.h"
#include "ui_SaltManagerInstallConfirmationDialog.h"
#include <QObject>
#include <string>
#include <map>
@ -36,6 +39,40 @@ namespace lay
class Salt;
class ConfirmationDialog
: public QDialog, private Ui::SaltManagerInstallConfirmationDialog
{
Q_OBJECT
public:
ConfirmationDialog (QWidget *parent);
void add_info (const std::string &name, bool update, const std::string &version, const std::string &url);
bool is_confirmed () const { return m_confirmed; }
bool is_cancelled () const { return m_cancelled; }
bool is_closed () const { return m_closed; }
void start ();
void separator ();
void finish ();
void mark_error (const std::string &name);
void mark_success (const std::string &name);
private slots:
void confirm_pressed () { m_confirmed = true; }
void cancel_pressed () { m_cancelled = true; }
void close_pressed () { m_closed = true; }
private:
bool m_confirmed, m_cancelled, m_closed;
lay::LogFile m_file;
std::map<std::string, QTreeWidgetItem *> m_items_by_name;
void set_icon_for_name (const std::string &name, const QIcon &icon);
};
/**
* @brief The download manager
*
@ -73,25 +110,16 @@ public:
*/
void compute_dependencies (const lay::Salt &salt, const Salt &salt_mine);
/**
* @brief Presents a dialog showing the packages scheduled for download
*
* This method requires all dependencies to be computed. It will return false
* if the dialog is not confirmed.
*
* "salt" needs to be the currently installed packages so the dialog can
* indicate which packages will be updated.
*/
bool show_confirmation_dialog (QWidget *parent, const lay::Salt &salt);
/**
* @brief Actually execute the downloads
*
* This method will return false if anything goes wrong.
* Failed packages will be removed entirely after they have been listed in
* an error dialog.
* This method will show a confirmation dialog and start installation
* if this dialog is confirmed. It will return false if
* the dialog was cancelled and an exception if something goes
* wrong.
* It will return true if the packages were installed successfully.
*/
bool execute (lay::Salt &salt);
bool execute (QWidget *parent, lay::Salt &salt);
private:
struct Descriptor
@ -110,6 +138,7 @@ private:
bool needs_iteration ();
void fetch_missing (const lay::Salt &salt_mine, tl::AbsoluteProgress &progress);
lay::ConfirmationDialog *make_confirmation_dialog (QWidget *parent, const lay::Salt &salt);
};
}

View File

@ -529,16 +529,12 @@ BEGIN_PROTECTED
}
manager.compute_dependencies (*mp_salt, m_salt_mine);
if (manager.show_confirmation_dialog (this, *mp_salt)) {
if (manager.execute (this, *mp_salt)) {
if (update) {
unmark_all_update ();
} else {
unmark_all_new ();
}
if (! manager.execute (*mp_salt)) {
throw tl::Exception (tl::to_string (tr ("Failed to install some of the selected packages. Please see log for details.")));
}
}
END_PROTECTED