1#!/usr/bin/env python 2 3 4############################################################################# 5## 6## Copyright (C) 2010 Riverbank Computing Limited. 7## Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). 8## All rights reserved. 9## 10## This file is part of the examples of PyQt. 11## 12## $QT_BEGIN_LICENSE:BSD$ 13## You may use this file under the terms of the BSD license as follows: 14## 15## "Redistribution and use in source and binary forms, with or without 16## modification, are permitted provided that the following conditions are 17## met: 18## * Redistributions of source code must retain the above copyright 19## notice, this list of conditions and the following disclaimer. 20## * Redistributions in binary form must reproduce the above copyright 21## notice, this list of conditions and the following disclaimer in 22## the documentation and/or other materials provided with the 23## distribution. 24## * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor 25## the names of its contributors may be used to endorse or promote 26## products derived from this software without specific prior written 27## permission. 28## 29## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 30## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 31## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 32## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 33## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 34## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 35## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 36## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 38## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 39## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 40## $QT_END_LICENSE$ 41## 42############################################################################# 43 44 45from PyQt5.QtCore import QDir, QFile, QFileInfo, QIODevice, QUrl 46from PyQt5.QtWidgets import (QApplication, QDialog, QDialogButtonBox, 47 QHBoxLayout, QLabel, QLineEdit, QMessageBox, QProgressDialog, 48 QPushButton, QVBoxLayout) 49from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest 50 51 52class HttpWindow(QDialog): 53 def __init__(self, parent=None): 54 super(HttpWindow, self).__init__(parent) 55 56 self.url = QUrl() 57 self.qnam = QNetworkAccessManager() 58 self.reply = None 59 self.outFile = None 60 self.httpGetId = 0 61 self.httpRequestAborted = False 62 63 self.urlLineEdit = QLineEdit('https://www.qt.io') 64 65 urlLabel = QLabel("&URL:") 66 urlLabel.setBuddy(self.urlLineEdit) 67 self.statusLabel = QLabel( 68 "Please enter the URL of a file you want to download.") 69 self.statusLabel.setWordWrap(True) 70 71 self.downloadButton = QPushButton("Download") 72 self.downloadButton.setDefault(True) 73 self.quitButton = QPushButton("Quit") 74 self.quitButton.setAutoDefault(False) 75 76 buttonBox = QDialogButtonBox() 77 buttonBox.addButton(self.downloadButton, QDialogButtonBox.ActionRole) 78 buttonBox.addButton(self.quitButton, QDialogButtonBox.RejectRole) 79 80 self.progressDialog = QProgressDialog(self) 81 82 self.urlLineEdit.textChanged.connect(self.enableDownloadButton) 83 self.qnam.authenticationRequired.connect( 84 self.slotAuthenticationRequired) 85 self.qnam.sslErrors.connect(self.sslErrors) 86 self.progressDialog.canceled.connect(self.cancelDownload) 87 self.downloadButton.clicked.connect(self.downloadFile) 88 self.quitButton.clicked.connect(self.close) 89 90 topLayout = QHBoxLayout() 91 topLayout.addWidget(urlLabel) 92 topLayout.addWidget(self.urlLineEdit) 93 94 mainLayout = QVBoxLayout() 95 mainLayout.addLayout(topLayout) 96 mainLayout.addWidget(self.statusLabel) 97 mainLayout.addWidget(buttonBox) 98 self.setLayout(mainLayout) 99 100 self.setWindowTitle("HTTP") 101 self.urlLineEdit.setFocus() 102 103 def startRequest(self, url): 104 self.reply = self.qnam.get(QNetworkRequest(url)) 105 self.reply.finished.connect(self.httpFinished) 106 self.reply.readyRead.connect(self.httpReadyRead) 107 self.reply.downloadProgress.connect(self.updateDataReadProgress) 108 109 def downloadFile(self): 110 self.url = QUrl(self.urlLineEdit.text()) 111 fileInfo = QFileInfo(self.url.path()) 112 fileName = fileInfo.fileName() 113 114 if not fileName: 115 fileName = 'index.html' 116 117 if QFile.exists(fileName): 118 ret = QMessageBox.question(self, "HTTP", 119 "There already exists a file called %s in the current " 120 "directory. Overwrite?" % fileName, 121 QMessageBox.Yes | QMessageBox.No, QMessageBox.No) 122 123 if ret == QMessageBox.No: 124 return 125 126 QFile.remove(fileName) 127 128 self.outFile = QFile(fileName) 129 if not self.outFile.open(QIODevice.WriteOnly): 130 QMessageBox.information(self, "HTTP", 131 "Unable to save the file %s: %s." % (fileName, self.outFile.errorString())) 132 self.outFile = None 133 return 134 135 self.progressDialog.setWindowTitle("HTTP") 136 self.progressDialog.setLabelText("Downloading %s." % fileName) 137 self.downloadButton.setEnabled(False) 138 139 self.httpRequestAborted = False 140 self.startRequest(self.url) 141 142 def cancelDownload(self): 143 self.statusLabel.setText("Download canceled.") 144 self.httpRequestAborted = True 145 if self.reply is not None: 146 self.reply.abort() 147 self.downloadButton.setEnabled(True) 148 149 def httpFinished(self): 150 if self.httpRequestAborted: 151 if self.outFile is not None: 152 self.outFile.close() 153 self.outFile.remove() 154 self.outFile = None 155 156 self.reply.deleteLater() 157 self.reply = None 158 self.progressDialog.hide() 159 return 160 161 self.progressDialog.hide() 162 self.outFile.flush() 163 self.outFile.close() 164 165 redirectionTarget = self.reply.attribute(QNetworkRequest.RedirectionTargetAttribute) 166 167 if self.reply.error(): 168 self.outFile.remove() 169 QMessageBox.information(self, "HTTP", 170 "Download failed: %s." % self.reply.errorString()) 171 self.downloadButton.setEnabled(True) 172 elif redirectionTarget is not None: 173 newUrl = self.url.resolved(redirectionTarget) 174 175 ret = QMessageBox.question(self, "HTTP", 176 "Redirect to %s?" % newUrl.toString(), 177 QMessageBox.Yes | QMessageBox.No) 178 179 if ret == QMessageBox.Yes: 180 self.url = newUrl 181 self.reply.deleteLater() 182 self.reply = None 183 self.outFile.open(QIODevice.WriteOnly) 184 self.outFile.resize(0) 185 self.startRequest(self.url) 186 return 187 else: 188 fileName = QFileInfo(QUrl(self.urlLineEdit.text()).path()).fileName() 189 self.statusLabel.setText("Downloaded %s to %s." % (fileName, QDir.currentPath())) 190 191 self.downloadButton.setEnabled(True) 192 193 self.reply.deleteLater() 194 self.reply = None 195 self.outFile = None 196 197 def httpReadyRead(self): 198 if self.outFile is not None: 199 self.outFile.write(self.reply.readAll()) 200 201 def updateDataReadProgress(self, bytesRead, totalBytes): 202 if self.httpRequestAborted: 203 return 204 205 self.progressDialog.setMaximum(totalBytes) 206 self.progressDialog.setValue(bytesRead) 207 208 def enableDownloadButton(self): 209 self.downloadButton.setEnabled(self.urlLineEdit.text() != '') 210 211 def slotAuthenticationRequired(self, authenticator): 212 import os 213 from PyQt5 import uic 214 215 ui = os.path.join(os.path.dirname(__file__), 'authenticationdialog.ui') 216 dlg = uic.loadUi(ui) 217 dlg.adjustSize() 218 dlg.siteDescription.setText("%s at %s" % (authenticator.realm(), self.url.host())) 219 220 dlg.userEdit.setText(self.url.userName()) 221 dlg.passwordEdit.setText(self.url.password()) 222 223 if dlg.exec_() == QDialog.Accepted: 224 authenticator.setUser(dlg.userEdit.text()) 225 authenticator.setPassword(dlg.passwordEdit.text()) 226 227 def sslErrors(self, reply, errors): 228 errorString = ", ".join([str(error.errorString()) for error in errors]) 229 230 ret = QMessageBox.warning(self, "HTTP Example", 231 "One or more SSL errors has occurred: %s" % errorString, 232 QMessageBox.Ignore | QMessageBox.Abort) 233 234 if ret == QMessageBox.Ignore: 235 self.reply.ignoreSslErrors() 236 237 238if __name__ == '__main__': 239 240 import sys 241 242 app = QApplication(sys.argv) 243 httpWin = HttpWindow() 244 httpWin.show() 245 sys.exit(httpWin.exec_()) 246