Commit 5868dbc1 authored by Don Gagne's avatar Don Gagne

Added progress indicator, download speed

Also updated to latest QGCUASFileManager implementation.
parent 1fa53faa
......@@ -30,107 +30,190 @@
QGCUASFileView::QGCUASFileView(QWidget *parent, QGCUASFileManager *manager) :
QWidget(parent),
_manager(manager)
_manager(manager),
_listInProgress(false),
_downloadInProgress(false)
{
_ui.setupUi(this);
bool success = connect(_ui.listFilesButton, SIGNAL(clicked()), this, SLOT(_refreshTree()));
// Progress bar is only visible while a download is in progress
_ui.progressBar->setVisible(false);
bool success;
Q_UNUSED(success); // Silence retail unused variable error
// Connect UI signals
success = connect(_ui.listFilesButton, SIGNAL(clicked()), this, SLOT(_refreshTree()));
Q_ASSERT(success);
success = connect(_ui.downloadButton, SIGNAL(clicked()), this, SLOT(_downloadFiles()));
success = connect(_ui.downloadButton, SIGNAL(clicked()), this, SLOT(_downloadFile()));
Q_ASSERT(success);
success = connect(_ui.treeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), this, SLOT(_currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)));
Q_ASSERT(success);
success = connect(&_listCompleteTimer, SIGNAL(timeout()), this, SLOT(_listCompleteTimeout()));
Q_ASSERT(success);
Q_UNUSED(success); // Silence retail unused variable error
}
void QGCUASFileView::_downloadFiles(void)
/// @brief Downloads the file currently selected in the tree view
void QGCUASFileView::_downloadFile(void)
{
QString dir = QFileDialog::getExistingDirectory(this, tr("Download Directory"),
Q_ASSERT(!_downloadInProgress);
_ui.statusText->clear();
QString downloadToHere = QFileDialog::getExistingDirectory(this, tr("Download Directory"),
QDir::homePath(),
QFileDialog::ShowDirsOnly
| QFileDialog::DontResolveSymlinks);
// And now download to this location
QString path;
QTreeWidgetItem* item = _ui.treeWidget->currentItem();
if (item && item->type() == _typeFile) {
_downloadFilename.clear();
do {
QString name = item->text(0).split("\t")[0]; // Strip off file sizes
// If this is the file name and not a directory keep track of the download file name
if (_downloadFilename.isEmpty()) {
_downloadFilename = name;
}
path.prepend("/" + name);
item = item->parent();
} while (item);
qDebug() << "Download: " << path;
bool success = connect(_manager, SIGNAL(statusMessage(QString)), this, SLOT(_downloadStatusMessage(QString)));
Q_ASSERT(success);
success = connect(_manager, SIGNAL(errorMessage(QString)), this, SLOT(_downloadStatusMessage(QString)));
Q_ASSERT(success);
Q_UNUSED(success);
_manager->downloadPath(path, QDir(dir));
_ui.downloadButton->setEnabled(false);
_downloadInProgress = true;
_connectDownloadSignals();
_manager->downloadPath(path, QDir(downloadToHere));
}
}
/// @brief Called when length of file being downloaded is known.
void QGCUASFileView::_downloadLength(unsigned int length)
{
Q_ASSERT(_downloadInProgress);
// Setup the progress bar
QProgressBar* bar = _ui.progressBar;
bar->setMinimum(0);
bar->setMaximum(length);
bar->setValue(0);
bar->setVisible(true);
_ui.downloadButton->setEnabled(true);
_downloadStartTime.start();
_ui.statusText->setText(tr("Downloading: %1").arg(_downloadFilename));
}
/// @brief Called to update the progress of the download.
/// @param bytesReceived Current count of bytes received for current download
void QGCUASFileView::_downloadProgress(unsigned int bytesReceived)
{
static uint lastSecsReported = 0;
Q_ASSERT(_downloadInProgress);
_ui.progressBar->setValue(bytesReceived);
// Calculate and display download rate. Only update once per second.
uint kbReceived = bytesReceived / 1024;
uint secs = _downloadStartTime.elapsed() / 1000;
if (secs != 0) {
uint kbPerSec = kbReceived / secs;
if (kbPerSec != 0 && secs != lastSecsReported) {
lastSecsReported = secs;
_ui.statusText->setText(tr("Downloading: %1 %2 KB/sec").arg(_downloadFilename).arg(kbPerSec));
}
}
}
/// @brief Called when the download associated with the QGCUASFileManager::downloadPath command completes.
void QGCUASFileView::_downloadComplete(void)
{
Q_ASSERT(_downloadInProgress);
_ui.downloadButton->setEnabled(true);
_ui.progressBar->setVisible(false);
_downloadInProgress = false;
_disconnectDownloadSignals();
_ui.statusText->setText(tr("Download complete: %1").arg(_downloadFilename));
}
/// @brief Called when an error occurs during a download.
/// @param msg Error message
void QGCUASFileView::_downloadErrorMessage(const QString& msg)
{
if (_downloadInProgress) {
_ui.downloadButton->setEnabled(true);
_ui.progressBar->setVisible(false);
_downloadInProgress = false;
_disconnectDownloadSignals();
_ui.statusText->setText(tr("Error: ") + msg);
}
}
/// @brief Refreshes the directory list tree.
void QGCUASFileView::_refreshTree(void)
{
Q_ASSERT(!_listInProgress);
_ui.treeWidget->clear();
_ui.statusText->clear();
_walkIndexStack.clear();
_walkItemStack.clear();
_walkIndexStack.append(0);
_walkItemStack.append(_ui.treeWidget->invisibleRootItem());
bool success = connect(_manager, SIGNAL(statusMessage(QString)), this, SLOT(_treeStatusMessage(QString)));
Q_ASSERT(success);
success = connect(_manager, SIGNAL(errorMessage(QString)), this, SLOT(_treeErrorMessage(QString)));
Q_ASSERT(success);
success = connect(_manager, SIGNAL(listComplete(void)), this, SLOT(_listComplete(void)));
Q_ASSERT(success);
Q_UNUSED(success);
// Don't queue up more than once
_ui.listFilesButton->setEnabled(false);
_listInProgress = true;
_connectListSignals();
_requestDirectoryList("/");
}
void QGCUASFileView::_treeStatusMessage(const QString& msg)
/// @brief Adds the specified directory entry to the tree view.
void QGCUASFileView::_listEntryReceived(const QString& entry)
{
Q_ASSERT(_listInProgress);
int type;
if (msg.startsWith("F")) {
if (entry.startsWith("F")) {
type = _typeFile;
} else if (msg.startsWith("D")) {
} else if (entry.startsWith("D")) {
type = _typeDir;
if (msg == "D." || msg == "D..") {
if (entry == "D." || entry == "D..") {
return;
}
} else {
Q_ASSERT(false);
return; // Silence maybe-unitialized on type below
return; // Silence maybe-unitialized on type
}
QTreeWidgetItem* item;
item = new QTreeWidgetItem(_walkItemStack.last(), type);
Q_CHECK_PTR(item);
item->setText(0, msg.right(msg.size() - 1));
item->setText(0, entry.right(entry.size() - 1));
}
void QGCUASFileView::_treeErrorMessage(const QString& msg)
/// @brief Called when an error occurs during a directory listing.
/// @param msg Error message
void QGCUASFileView::_listErrorMessage(const QString& msg)
{
QTreeWidgetItem* item;
item = new QTreeWidgetItem(_walkItemStack.last(), _typeError);
Q_CHECK_PTR(item);
item->setText(0, tr("Error: ") + msg);
// Fake listComplete signal after an error
_listComplete();
if (_listInProgress) {
_ui.listFilesButton->setEnabled(true);
_listInProgress = false;
_disconnectListSignals();
_ui.statusText->setText(tr("Error: ") + msg);
}
}
void QGCUASFileView::_listComplete(void)
{
_clearListCompleteTimeout();
Q_ASSERT(_listInProgress);
// Walk the current items, traversing down into directories
......@@ -171,54 +254,71 @@ Again:
if (_walkIndexStack.count() != 0) {
goto Again;
} else {
disconnect(_manager, SIGNAL(statusMessage(QString)), this, SLOT(_treeStatusMessage(QString)));
disconnect(_manager, SIGNAL(errorMessage(QString)), this, SLOT(_treeErrorMessage(QString)));
disconnect(_manager, SIGNAL(listComplete(void)), this, SLOT(_listComplete(void)));
_ui.listFilesButton->setEnabled(true);
_listInProgress = false;
_disconnectListSignals();
}
}
}
void QGCUASFileView::_downloadStatusMessage(const QString& msg)
{
disconnect(_manager, SIGNAL(statusMessage(QString)), this, SLOT(_downloadStatusMessage(QString)));
disconnect(_manager, SIGNAL(errorMessage(QString)), this, SLOT(_downloadStatusMessage(QString)));
QMessageBox msgBox;
msgBox.setWindowModality(Qt::ApplicationModal);
msgBox.setText(msg);
msgBox.exec();
}
void QGCUASFileView::_currentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous)
{
Q_UNUSED(previous);
// FIXME: Should not enable when downloading
_ui.downloadButton->setEnabled(current ? (current->type() == _typeFile) : false);
}
void QGCUASFileView::_setupListCompleteTimeout(void)
void QGCUASFileView::_requestDirectoryList(const QString& dir)
{
Q_ASSERT(!_listCompleteTimer.isActive());
_listCompleteTimer.setSingleShot(true);
_listCompleteTimer.start(_listCompleteTimerTimeoutMsecs);
qDebug() << "List:" << dir;
_manager->listDirectory(dir);
}
void QGCUASFileView::_clearListCompleteTimeout(void)
/// @brief Connects to the signals associated with the QGCUASFileManager::downloadPath method. We only leave these signals connected
/// while a download because there may be multiple UAS, which in turn means multiple QGCUASFileView instances. We only want the signals
/// connected to the active FileView which is doing the current download.
void QGCUASFileView::_connectDownloadSignals(void)
{
Q_ASSERT(_listCompleteTimer.isActive());
bool success;
Q_UNUSED(success); // Silence retail unused variable error
_listCompleteTimer.stop();
success = connect(_manager, SIGNAL(downloadFileLength(unsigned int)), this, SLOT(_downloadLength(unsigned int)));
Q_ASSERT(success);
success = connect(_manager, SIGNAL(downloadFileProgress(unsigned int)), this, SLOT(_downloadProgress(unsigned int)));
Q_ASSERT(success);
success = connect(_manager, SIGNAL(downloadFileComplete(void)), this, SLOT(_downloadComplete(void)));
Q_ASSERT(success);
success = connect(_manager, SIGNAL(errorMessage(const QString&)), this, SLOT(_downloadErrorMessage(const QString&)));
Q_ASSERT(success);
}
void QGCUASFileView::_listCompleteTimeout(void)
void QGCUASFileView::_disconnectDownloadSignals(void)
{
_treeErrorMessage(tr("Timeout waiting for listComplete signal"));
disconnect(_manager, SIGNAL(downloadFileLength(unsigned int)), this, SLOT(_downloadLength(unsigned int)));
disconnect(_manager, SIGNAL(downloadFileProgress(unsigned int)), this, SLOT(_downloadProgress(unsigned int)));
disconnect(_manager, SIGNAL(downloadFileComplete(void)), this, SLOT(_downloadComplete(void)));
disconnect(_manager, SIGNAL(errorMessage(const QString&)), this, SLOT(_downloadErrorMessage(const QString&)));
}
void QGCUASFileView::_requestDirectoryList(const QString& dir)
/// @brief Connects to the signals associated with the QGCUASFileManager::listDirectory method. We only leave these signals connected
/// while a download because there may be multiple UAS, which in turn means multiple QGCUASFileView instances. We only want the signals
/// connected to the active FileView which is doing the current download.
void QGCUASFileView::_connectListSignals(void)
{
qDebug() << "List:" << dir;
_setupListCompleteTimeout();
_manager->listDirectory(dir);
bool success;
Q_UNUSED(success); // Silence retail unused variable error
success = connect(_manager, SIGNAL(listEntry(const QString&)), this, SLOT(_listEntryReceived(const QString&)));
Q_ASSERT(success);
success = connect(_manager, SIGNAL(listComplete(void)), this, SLOT(_listComplete(void)));
Q_ASSERT(success);
success = connect(_manager, SIGNAL(errorMessage(const QString&)), this, SLOT(_listErrorMessage(const QString&)));
Q_ASSERT(success);
}
void QGCUASFileView::_disconnectListSignals(void)
{
disconnect(_manager, SIGNAL(listEntry(const QString&)), this, SLOT(_listEntryReceived(const QString&)));
disconnect(_manager, SIGNAL(listComplete(void)), this, SLOT(_listComplete(void)));
disconnect(_manager, SIGNAL(errorMessage(const QString&)), this, SLOT(_listErrorMessage(const QString&)));
}
......@@ -42,29 +42,38 @@ protected:
private slots:
void _refreshTree(void);
void _downloadFiles(void);
void _treeStatusMessage(const QString& msg);
void _treeErrorMessage(const QString& msg);
void _listEntryReceived(const QString& entry);
void _listErrorMessage(const QString& msg);
void _listComplete(void);
void _downloadStatusMessage(const QString& msg);
void _downloadFile(void);
void _downloadLength(unsigned int length);
void _downloadProgress(unsigned int length);
void _downloadErrorMessage(const QString& msg);
void _downloadComplete(void);
void _currentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous);
void _listCompleteTimeout(void);
private:
void _setupListCompleteTimeout(void);
void _clearListCompleteTimeout(void);
void _connectDownloadSignals(void);
void _disconnectDownloadSignals(void);
void _connectListSignals(void);
void _disconnectListSignals(void);
void _requestDirectoryList(const QString& dir);
static const int _typeFile = QTreeWidgetItem::UserType + 1;
static const int _typeDir = QTreeWidgetItem::UserType + 2;
static const int _typeError = QTreeWidgetItem::UserType + 3;
QTimer _listCompleteTimer; ///> Used to signal a timeout waiting for a listComplete signal
static const int _listCompleteTimerTimeoutMsecs = 5000; ///> Timeout in msecs for listComplete timer
QList<int> _walkIndexStack;
QList<QTreeWidgetItem*> _walkItemStack;
Ui::QGCUASFileView _ui;
QString _downloadFilename; ///< File currently being downloaded, not including path
QTime _downloadStartTime; ///< Time at which download started
bool _listInProgress; ///< Indicates that a listDirectory command is in progress
bool _downloadInProgress; ///< Indicates that a downloadPath command is in progress
};
#endif // QGCUASFILEVIEW_H
......@@ -14,13 +14,23 @@
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="1">
<item row="3" column="1">
<widget class="QPushButton" name="listFilesButton">
<property name="text">
<string>List Files</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="3">
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>24</number>
</property>
<property name="textVisible">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="0" colspan="3">
<widget class="QTreeWidget" name="treeWidget">
<property name="headerHidden">
......@@ -33,7 +43,7 @@
</column>
</widget>
</item>
<item row="1" column="2">
<item row="3" column="2">
<widget class="QPushButton" name="downloadButton">
<property name="enabled">
<bool>false</bool>
......@@ -43,6 +53,19 @@
</property>
</widget>
</item>
<item row="2" column="0" colspan="3">
<widget class="QLabel" name="statusText">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment