From d482a92858f181c76229dc977ee9c159ba36515f Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Thu, 17 Sep 2015 15:56:30 +0200 Subject: Polish the HTTP example. - Remove unneeded member variables. - Use new connection syntax. - Streamline code. - Add a QCheckBox for launching the file after download and make the default file name and download directory configureable. - Make status messages more verbose. - Set Password echo mode on authentication dialog. - Extract the progress dialog to a separate class that is directly connected to the QNetworkReply, which is created on demand. Set set minimum and duration on it. This fixes a crash that currently occurs when clicking "Abort" on the SSL error dialog and "Cancel" on the progress dialog that is then re-shown due to its internal force timer/minimum duration handling. - Resize according to screen size. Task-number: QTBUG-48332 Change-Id: Ia2611e63fe96d6f49e4cdd06049a206ddb2c2864 Reviewed-by: David Faure --- examples/network/http/authenticationdialog.ui | 6 +- examples/network/http/httpwindow.cpp | 274 +++++++++++++++----------- examples/network/http/httpwindow.h | 31 +-- examples/network/http/main.cpp | 5 +- 4 files changed, 189 insertions(+), 127 deletions(-) (limited to 'examples') diff --git a/examples/network/http/authenticationdialog.ui b/examples/network/http/authenticationdialog.ui index 82d908cffb..5baf1bd97e 100644 --- a/examples/network/http/authenticationdialog.ui +++ b/examples/network/http/authenticationdialog.ui @@ -41,7 +41,11 @@ - + + + QLineEdit::Password + + diff --git a/examples/network/http/httpwindow.cpp b/examples/network/http/httpwindow.cpp index 39623fd6dd..301431fd5e 100644 --- a/examples/network/http/httpwindow.cpp +++ b/examples/network/http/httpwindow.cpp @@ -40,85 +40,134 @@ #include #include +#include #include "httpwindow.h" #include "ui_authenticationdialog.h" -HttpWindow::HttpWindow(QWidget *parent) - : QDialog(parent) -{ #ifndef QT_NO_SSL - urlLineEdit = new QLineEdit("https://qt-project.org/"); +static const char defaultUrl[] = "https://qt-project.org/"; #else - urlLineEdit = new QLineEdit("http://qt-project.org/"); +static const char defaultUrl[] = "http://qt-project.org/"; #endif +static const char defaultFileName[] = "index.html"; - urlLabel = new QLabel(tr("&URL:")); - urlLabel->setBuddy(urlLineEdit); - statusLabel = new QLabel(tr("Please enter the URL of a file you want to " - "download.")); - statusLabel->setWordWrap(true); - - downloadButton = new QPushButton(tr("Download")); - downloadButton->setDefault(true); - quitButton = new QPushButton(tr("Quit")); - quitButton->setAutoDefault(false); - - buttonBox = new QDialogButtonBox; - buttonBox->addButton(downloadButton, QDialogButtonBox::ActionRole); - buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole); +ProgressDialog::ProgressDialog(const QUrl &url, QWidget *parent) + : QProgressDialog(parent) +{ + setWindowTitle(tr("Download Progress")); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + setLabelText(tr("Downloading %1.").arg(url.toDisplayString())); + setMinimum(0); + setValue(0); + setMinimumDuration(0); +} - progressDialog = new QProgressDialog(this); +void ProgressDialog::networkReplyProgress(qint64 bytesRead, qint64 totalBytes) +{ + setMaximum(totalBytes); + setValue(bytesRead); +} - connect(urlLineEdit, SIGNAL(textChanged(QString)), - this, SLOT(enableDownloadButton())); +HttpWindow::HttpWindow(QWidget *parent) + : QDialog(parent) + , statusLabel(new QLabel(tr("Please enter the URL of a file you want to download.\n\n"), this)) + , urlLineEdit(new QLineEdit(defaultUrl)) + , downloadButton(new QPushButton(tr("Download"))) + , launchCheckBox(new QCheckBox("Launch file")) + , defaultFileLineEdit(new QLineEdit(defaultFileName)) + , downloadDirectoryLineEdit(new QLineEdit) + , reply(Q_NULLPTR) + , file(Q_NULLPTR) + , httpRequestAborted(false) +{ + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + setWindowTitle(tr("HTTP")); - connect(&qnam, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), - this, SLOT(slotAuthenticationRequired(QNetworkReply*,QAuthenticator*))); + connect(&qnam, &QNetworkAccessManager::authenticationRequired, + this, &HttpWindow::slotAuthenticationRequired); #ifndef QT_NO_SSL - connect(&qnam, SIGNAL(sslErrors(QNetworkReply*,QList)), - this, SLOT(sslErrors(QNetworkReply*,QList))); + connect(&qnam, &QNetworkAccessManager::sslErrors, + this, &HttpWindow::sslErrors); #endif - connect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelDownload())); - connect(downloadButton, SIGNAL(clicked()), this, SLOT(downloadFile())); - connect(quitButton, SIGNAL(clicked()), this, SLOT(close())); - QHBoxLayout *topLayout = new QHBoxLayout; - topLayout->addWidget(urlLabel); - topLayout->addWidget(urlLineEdit); + QFormLayout *formLayout = new QFormLayout; + urlLineEdit->setClearButtonEnabled(true); + connect(urlLineEdit, &QLineEdit::textChanged, + this, &HttpWindow::enableDownloadButton); + formLayout->addRow(tr("&URL:"), urlLineEdit); + QString downloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); + if (downloadDirectory.isEmpty() || !QFileInfo(downloadDirectory).isDir()) + downloadDirectory = QDir::currentPath(); + downloadDirectoryLineEdit->setText(QDir::toNativeSeparators(downloadDirectory)); + formLayout->addRow(tr("&Download directory:"), downloadDirectoryLineEdit); + formLayout->addRow(tr("Default &file:"), defaultFileLineEdit); + launchCheckBox->setChecked(true); + formLayout->addRow(launchCheckBox); + + QVBoxLayout *mainLayout = new QVBoxLayout(this); + mainLayout->addLayout(formLayout); + + mainLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding)); - QVBoxLayout *mainLayout = new QVBoxLayout; - mainLayout->addLayout(topLayout); + statusLabel->setWordWrap(true); mainLayout->addWidget(statusLabel); + + downloadButton->setDefault(true); + connect(downloadButton, &QAbstractButton::clicked, this, &HttpWindow::downloadFile); + QPushButton *quitButton = new QPushButton(tr("Quit")); + quitButton->setAutoDefault(false); + connect(quitButton, &QAbstractButton::clicked, this, &QWidget::close); + QDialogButtonBox *buttonBox = new QDialogButtonBox; + buttonBox->addButton(downloadButton, QDialogButtonBox::ActionRole); + buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole); mainLayout->addWidget(buttonBox); - setLayout(mainLayout); - setWindowTitle(tr("HTTP")); urlLineEdit->setFocus(); } -void HttpWindow::startRequest(QUrl url) +void HttpWindow::startRequest(const QUrl &requestedUrl) { + url = requestedUrl; + httpRequestAborted = false; + reply = qnam.get(QNetworkRequest(url)); - connect(reply, SIGNAL(finished()), - this, SLOT(httpFinished())); - connect(reply, SIGNAL(readyRead()), - this, SLOT(httpReadyRead())); - connect(reply, SIGNAL(downloadProgress(qint64,qint64)), - this, SLOT(updateDataReadProgress(qint64,qint64))); + connect(reply, &QNetworkReply::finished, this, &HttpWindow::httpFinished); + connect(reply, &QIODevice::readyRead, this, &HttpWindow::httpReadyRead); + + ProgressDialog *progressDialog = new ProgressDialog(url, this); + progressDialog->setAttribute(Qt::WA_DeleteOnClose); + connect(progressDialog, &QProgressDialog::canceled, this, &HttpWindow::cancelDownload); + connect(reply, &QNetworkReply::downloadProgress, progressDialog, &ProgressDialog::networkReplyProgress); + connect(reply, &QNetworkReply::finished, progressDialog, &ProgressDialog::hide); + progressDialog->show(); + + statusLabel->setText(tr("Downloading %1...").arg(url.toString())); } void HttpWindow::downloadFile() { - url = urlLineEdit->text(); + const QString urlSpec = urlLineEdit->text().trimmed(); + if (urlSpec.isEmpty()) + return; - QFileInfo fileInfo(url.path()); - QString fileName = fileInfo.fileName(); - if (fileName.isEmpty()) - fileName = "index.html"; + const QUrl newUrl = QUrl::fromUserInput(urlSpec); + if (!newUrl.isValid()) { + QMessageBox::information(this, tr("Error"), + tr("Invalid URL: %1: %2").arg(urlSpec, newUrl.errorString())); + return; + } + QString fileName = newUrl.fileName(); + if (fileName.isEmpty()) + fileName = defaultFileLineEdit->text().trimmed(); + if (fileName.isEmpty()) + fileName = defaultFileName; + QString downloadDirectory = QDir::cleanPath(downloadDirectoryLineEdit->text().trimmed()); + if (!downloadDirectory.isEmpty() && QFileInfo(downloadDirectory).isDir()) + fileName.prepend(downloadDirectory + '/'); if (QFile::exists(fileName)) { - if (QMessageBox::question(this, tr("HTTP"), + if (QMessageBox::question(this, tr("Overwrite Existing File"), tr("There already exists a file called %1 in " "the current directory. Overwrite?").arg(fileName), QMessageBox::Yes|QMessageBox::No, QMessageBox::No) @@ -127,23 +176,27 @@ void HttpWindow::downloadFile() QFile::remove(fileName); } - file = new QFile(fileName); - if (!file->open(QIODevice::WriteOnly)) { - QMessageBox::information(this, tr("HTTP"), - tr("Unable to save the file %1: %2.") - .arg(fileName).arg(file->errorString())); - delete file; - file = 0; + file = openFileForWrite(fileName); + if (!file) return; - } - progressDialog->setWindowTitle(tr("HTTP")); - progressDialog->setLabelText(tr("Downloading %1.").arg(fileName)); downloadButton->setEnabled(false); // schedule the request - httpRequestAborted = false; - startRequest(url); + startRequest(newUrl); +} + +QFile *HttpWindow::openFileForWrite(const QString &fileName) +{ + QScopedPointer file(new QFile(fileName)); + if (!file->open(QIODevice::WriteOnly)) { + QMessageBox::information(this, tr("Error"), + tr("Unable to save the file %1: %2.") + .arg(QDir::toNativeSeparators(fileName), + file->errorString())); + return Q_NULLPTR; + } + return file.take(); } void HttpWindow::cancelDownload() @@ -156,52 +209,56 @@ void HttpWindow::cancelDownload() void HttpWindow::httpFinished() { + QFileInfo fi; + if (file) { + fi.setFile(file->fileName()); + file->close(); + delete file; + file = Q_NULLPTR; + } + if (httpRequestAborted) { - if (file) { - file->close(); - file->remove(); - delete file; - file = 0; - } reply->deleteLater(); - progressDialog->hide(); + reply = Q_NULLPTR; return; } - progressDialog->hide(); - file->flush(); - file->close(); - - - QVariant redirectionTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); if (reply->error()) { - file->remove(); - QMessageBox::information(this, tr("HTTP"), - tr("Download failed: %1.") - .arg(reply->errorString())); + QFile::remove(fi.absoluteFilePath()); + statusLabel->setText(tr("Download failed:\n%1.").arg(reply->errorString())); downloadButton->setEnabled(true); - } else if (!redirectionTarget.isNull()) { - QUrl newUrl = url.resolved(redirectionTarget.toUrl()); - if (QMessageBox::question(this, tr("HTTP"), - tr("Redirect to %1 ?").arg(newUrl.toString()), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - url = newUrl; - reply->deleteLater(); - file->open(QIODevice::WriteOnly); - file->resize(0); - startRequest(url); + reply->deleteLater(); + reply = Q_NULLPTR; + return; + } + + const QVariant redirectionTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + + reply->deleteLater(); + reply = Q_NULLPTR; + + if (!redirectionTarget.isNull()) { + const QUrl redirectedUrl = url.resolved(redirectionTarget.toUrl()); + if (QMessageBox::question(this, tr("Redirect"), + tr("Redirect to %1 ?").arg(redirectedUrl.toString()), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) { + downloadButton->setEnabled(true); return; } - } else { - QString fileName = QFileInfo(QUrl(urlLineEdit->text()).path()).fileName(); - statusLabel->setText(tr("Downloaded %1 to %2.").arg(fileName).arg(QDir::currentPath())); - downloadButton->setEnabled(true); + file = openFileForWrite(fi.absoluteFilePath()); + if (!file) { + downloadButton->setEnabled(true); + return; + } + startRequest(redirectedUrl); + return; } - reply->deleteLater(); - reply = 0; - delete file; - file = 0; + statusLabel->setText(tr("Downloaded %1 bytes to %2\nin\n%3") + .arg(fi.size()).arg(fi.fileName(), QDir::toNativeSeparators(fi.absolutePath()))); + if (launchCheckBox->isChecked()) + QDesktopServices::openUrl(QUrl::fromLocalFile(fi.absoluteFilePath())); + downloadButton->setEnabled(true); } void HttpWindow::httpReadyRead() @@ -214,15 +271,6 @@ void HttpWindow::httpReadyRead() file->write(reply->readAll()); } -void HttpWindow::updateDataReadProgress(qint64 bytesRead, qint64 totalBytes) -{ - if (httpRequestAborted) - return; - - progressDialog->setMaximum(totalBytes); - progressDialog->setValue(bytesRead); -} - void HttpWindow::enableDownloadButton() { downloadButton->setEnabled(!urlLineEdit->text().isEmpty()); @@ -230,18 +278,18 @@ void HttpWindow::enableDownloadButton() void HttpWindow::slotAuthenticationRequired(QNetworkReply*,QAuthenticator *authenticator) { - QDialog dlg; + QDialog authenticationDialog; Ui::Dialog ui; - ui.setupUi(&dlg); - dlg.adjustSize(); - ui.siteDescription->setText(tr("%1 at %2").arg(authenticator->realm()).arg(url.host())); + ui.setupUi(&authenticationDialog); + authenticationDialog.adjustSize(); + ui.siteDescription->setText(tr("%1 at %2").arg(authenticator->realm(), url.host())); // Did the URL have information? Fill the UI // This is only relevant if the URL-supplied credentials were wrong ui.userEdit->setText(url.userName()); ui.passwordEdit->setText(url.password()); - if (dlg.exec() == QDialog::Accepted) { + if (authenticationDialog.exec() == QDialog::Accepted) { authenticator->setUser(ui.userEdit->text()); authenticator->setPassword(ui.passwordEdit->text()); } @@ -253,12 +301,12 @@ void HttpWindow::sslErrors(QNetworkReply*,const QList &errors) QString errorString; foreach (const QSslError &error, errors) { if (!errorString.isEmpty()) - errorString += ", "; + errorString += '\n'; errorString += error.errorString(); } - if (QMessageBox::warning(this, tr("HTTP"), - tr("One or more SSL errors has occurred: %1").arg(errorString), + if (QMessageBox::warning(this, tr("SSL Errors"), + tr("One or more SSL errors has occurred:\n%1").arg(errorString), QMessageBox::Ignore | QMessageBox::Abort) == QMessageBox::Ignore) { reply->ignoreSslErrors(); } diff --git a/examples/network/http/httpwindow.h b/examples/network/http/httpwindow.h index f1dc2e1f80..cc632e5bab 100644 --- a/examples/network/http/httpwindow.h +++ b/examples/network/http/httpwindow.h @@ -41,39 +41,46 @@ #ifndef HTTPWINDOW_H #define HTTPWINDOW_H -#include +#include #include #include QT_BEGIN_NAMESPACE -class QDialogButtonBox; class QFile; class QLabel; class QLineEdit; -class QProgressDialog; class QPushButton; class QSslError; class QAuthenticator; class QNetworkReply; - +class QCheckBox; QT_END_NAMESPACE +class ProgressDialog : public QProgressDialog { + Q_OBJECT + +public: + explicit ProgressDialog(const QUrl &url, QWidget *parent = Q_NULLPTR); + +public slots: + void networkReplyProgress(qint64 bytesRead, qint64 totalBytes); +}; + class HttpWindow : public QDialog { Q_OBJECT public: - HttpWindow(QWidget *parent = 0); + explicit HttpWindow(QWidget *parent = Q_NULLPTR); - void startRequest(QUrl url); + void startRequest(const QUrl &requestedUrl); private slots: void downloadFile(); void cancelDownload(); void httpFinished(); void httpReadyRead(); - void updateDataReadProgress(qint64 bytesRead, qint64 totalBytes); void enableDownloadButton(); void slotAuthenticationRequired(QNetworkReply*,QAuthenticator *); #ifndef QT_NO_SSL @@ -81,19 +88,19 @@ private slots: #endif private: + QFile *openFileForWrite(const QString &fileName); + QLabel *statusLabel; - QLabel *urlLabel; QLineEdit *urlLineEdit; - QProgressDialog *progressDialog; QPushButton *downloadButton; - QPushButton *quitButton; - QDialogButtonBox *buttonBox; + QCheckBox *launchCheckBox; + QLineEdit *defaultFileLineEdit; + QLineEdit *downloadDirectoryLineEdit; QUrl url; QNetworkAccessManager qnam; QNetworkReply *reply; QFile *file; - int httpGetId; bool httpRequestAborted; }; diff --git a/examples/network/http/main.cpp b/examples/network/http/main.cpp index 5857af265c..b4960008ec 100644 --- a/examples/network/http/main.cpp +++ b/examples/network/http/main.cpp @@ -39,6 +39,7 @@ ****************************************************************************/ #include +#include #include #include "httpwindow.h" @@ -47,8 +48,10 @@ int main(int argc, char *argv[]) { QApplication app(argc, argv); - HttpWindow httpWin; + const QRect availableSize = QApplication::desktop()->availableGeometry(&httpWin); + httpWin.resize(availableSize.width() / 5, availableSize.height() / 5); + httpWin.move((availableSize.width() - httpWin.width()) / 2, (availableSize.height() - httpWin.height()) / 2); httpWin.show(); return app.exec(); -- cgit v1.2.3