def loadModel(self): imd = None try: loader = ModelLoader(self.ui.mDataLineEdit.text()) models = loader.detect_models() model_names = map(lambda m: m.name, models) self._log_output("Looking up models: " + ', '.join(model_names)) ili = loader.gen_lookup_ili() qDebug(ili) wpsreq = self._create_wps_request(ili) url = self.ui.mIlisMetaUrlLineEdit.text() req = QNetworkRequest(QUrl(url)) req.setHeader(QNetworkRequest.ContentTypeHeader, 'application/xml') data = QByteArray() data.append(wpsreq) reply = QgsNetworkAccessManager.instance().blockingPost(req, data) if reply.error() == QNetworkReply.NoError: result = reply.content() imd = self._parse_wps_response(result) except Exception as e: qDebug("Exception during IlisModel download") if imd is None: self._show_log_window() QgsApplication.messageLog().logMessage( "Couldn't download Ilismeta model", "Interlis", Qgis.MessageLevel(1)) self.ui.mModelLineEdit.setText("") else: fh, imdfn = tempfile.mkstemp(suffix='.imd') os.close(fh) with codecs.open(imdfn, "w", encoding='utf-8') as file: file.write(imd) self.ui.mModelLineEdit.setText(imdfn)
def send_request(self, request_type: str = "search"): """Sends a request to the Isogeo's API using QNetworkRequestManager. That's the handle_reply method which get the API's response. See below. :param str request_type: type of request to send. Options: - 'token' - 'search' - 'details' - 'shares' """ logger.info( "-------------- Sending a '{}' request --------------".format( request_type)) # creating the QNetworkRequest appropriate to the request_type request = self.create_request(request_type) # post request for 'token' request if request_type == "token": data = QByteArray() data.append(urlencode({"grant_type": "client_credentials"})) self.request = self.qnam.post(request, data) # get request for other else: self.request = self.qnam.get(request) # to catch SSL errors # (see https://github.com/isogeo/isogeo-plugin-qgis/issues/266) self.request.sslErrors.connect(self.ssl_error_catcher) # since https://github.com/isogeo/isogeo-plugin-qgis/issues/288, the slot is # connected to the request's signal and no more to QgsNetworkAccessManager's one # because otherwise, the plugin doesn't work on QGIS 3.6.x self.request.finished.connect(partial(self.handle_reply, self.request)) return
def api_authentification(self): logger.debug("\n------------------ Authentication ------------------") # creating credentials header crd_header_value = QByteArray() crd_header_value.append("Basic ") crd_header_value.append( base64.b64encode("{}:{}".format(self.app_id, self.app_secrets).encode()) ) crd_header_name = QByteArray() crd_header_name.append("Authorization") # creating Content-Type header ct_header_value = QByteArray() ct_header_value.append("application/json") # creating request token_rqst = QNetworkRequest(QUrl(self.token_url)) # setting headers token_rqst.setRawHeader(crd_header_name, crd_header_value) token_rqst.setHeader(token_rqst.ContentTypeHeader, ct_header_value) # creating data data = QByteArray() data.append(urlencode({"grant_type": "client_credentials"})) # requesting and handle reply logger.debug("Asking for token") token_reply = self.naMngr.post(token_rqst, data) token_reply.finished.connect(partial(self.api_handle_token, reply=token_reply))
def ask_for_token(self, c_id, c_secret): """Ask a token from Isogeo API authentification page. This send a POST request to Isogeo API with the user id and secret in its header. The API should return an access token """ # check if API access are already set if self.api_id != 0 and self.api_secret != 0: logger.info("User_authentication function is trying " "to get a token from the id/secret") self.request_status_clear = 1 pass else: logger.error("No id/secret.") return 0 # API token request headervalue = "Basic " + base64.b64encode(c_id + ":" + c_secret) data = urlencode({"grant_type": "client_credentials"}) databyte = QByteArray() databyte.append(data) url = QUrl("https://id.api.isogeo.com/oauth/token") request = QNetworkRequest(url) request.setRawHeader("Authorization", headervalue) if self.request_status_clear: self.request_status_clear = 0 token_reply = self.manager.post(request, databyte) token_reply.finished.connect( partial(self.handle_token, answer=token_reply)) QgsMessageLog.logMessage("Authentication succeeded", "Isogeo") else: pass
def api_get_request(self): logger.debug("\n------------------ Sending request ------------------") # creating credentials header logger.debug("Creating credentials header") crd_header_value = QByteArray() crd_header_value.append(self.token) crd_header_name = QByteArray() crd_header_name.append("Authorization") # creating request rqst = QNetworkRequest(QUrl(self.request_url)) logger.debug("Creating request : {}".format(rqst.url())) # setting credentials header rqst.setRawHeader(crd_header_name, crd_header_value) logger.debug("Setting credentials header : {}".format( rqst.rawHeader(crd_header_name))) # sending request rqst_reply = self.naMngr.get(rqst) logger.debug("Sending request") rqst_reply.finished.connect( partial(self.api_handle_request, reply=rqst_reply))
class CloudqubeJsonReply(CloudqubeReply): def __init__(self, reply, stream, callback, finished, error): super().__init__(reply, error) #reply.readyRead.connect(self.read) reply.finished.connect(self.finished) reply.setReadBufferSize(1024 * 1024) self._callback = callback self._finished = finished self._data = QByteArray() # Storing request data stream so that it doesn't get # destroyed before sending it self._stream = stream def read(self): bytes_available = self._reply.bytesAvailable() data = self._reply.read(bytes_available) self._data.append(data) def finished(self): err = self._reply.error() if (err == QNetworkReply.NoError): self._data.append(self._reply.readAll()) data_str = self._data.data().decode('utf-8') if len(data_str) > 0: self._callback(json.loads(data_str)) else: self._callback() self._finished(self) else: QgsMessageLog.logMessage('Error {0}: {1}!'.format( err, self._reply.errorString()))
def save_cookies(self): with QMutexLocker(self.mutex): cookies = self.allCookies() data = QByteArray() for cookie in cookies: if not cookie.isSessionCookie(): data.append(cookie.toRawForm()) data.append("\n") QSettings().setValue("irmt/cookies", data)
def create_request(self, request_type: str): """Creates a QNetworkRequest() with appropriate headers and URL according to the 'request_type' parameter. :param str request_type: type of request to create. Options: - 'token' - 'search' - 'details' - 'shares' :returns: the QNetworkRequest to send to the Isogeo's API :rtype: QNetworkRequest """ # creating headers (same steps wathever request_type value) header_value = QByteArray() header_name = QByteArray() header_name.append("Authorization") # for token request if request_type == "token": # filling request header with credentials header_value.append("Basic ") header_value.append( base64.b64encode("{}:{}".format(self.app_id, self.app_secret).encode())) # creating the QNetworkRequest from oAuth2 authentication URL request = QNetworkRequest(QUrl(self.api_url_token)) # creating and setting the 'Content-type header' ct_header_value = QByteArray() ct_header_value.append("application/json") request.setHeader(request.ContentTypeHeader, ct_header_value) # for other request_type, setting appropriate url else: if request_type == "shares": url = QUrl("{}/shares".format(self.api_url_base)) elif request_type == "search" or request_type == "details": url = QUrl(self.currentUrl) else: logger.debug( "Unkown request type asked : {}".format(request_type)) raise ValueError return 0 # filling request header with token header_value.append(self.token) request = QNetworkRequest(url) # creating QNetworkRequest from appropriate url request.setRawHeader(header_name, header_value) return request
def api_get_request(self): # creating credentials header crd_header_value = QByteArray() crd_header_value.append(self.token) crd_header_name = QByteArray() crd_header_name.append("Authorization") # creating request rqst = QNetworkRequest(QUrl(self.request_url)) # setting credentials header rqst.setRawHeader(crd_header_name, crd_header_value) # sending request and handle API reply rqst_reply = self.naMngr.get(rqst) rqst_reply.finished.connect( partial(self.api_handle_request, reply=rqst_reply))
def run(): def sslVerifyNone(req): conf = req.sslConfiguration() conf.setPeerVerifyMode( QSslSocket.VerifyNone ) req.setSslConfiguration( conf ) req = QNetworkRequest( url ) sslVerifyNone( req ) # Need for Windows, error 'Handshake failed' if json_request is None: reply = self.nam.get( req ) else: req.setHeader( QNetworkRequest.ContentTypeHeader, "application/json" ) data = QByteArray() data.append( json.dumps( json_request ) ) reply = self.nam.post( req, data ) if reply is None: response = { 'isOk': False, 'message': "Network error", 'errorCode': -1 } self.finished.emit( response ) return self.abortReply.connect( reply.abort ) if not self.responseAllFinished: self._connectReply( reply )
def test_network_mixin_post(self): """Test network mixin post""" request_url = 'http://jsonplaceholder.typicode.com/posts' post_data = QByteArray() post_data.append("title=%s&" % self.api_results['title']) post_data.append("body=%s&" % self.api_results['body']) post_data.append("userId=%d&" % self.api_results['userId']) manager = NetworkMixin(request_url=request_url) manager.connect_post(post_data) while not manager.reply.isFinished(): QCoreApplication.processEvents() self.assertIsNotNone(manager.get_json_results()) self.assertEqual(manager.get_json_results()['userId'], self.api_results['userId']) self.assertEqual(manager.get_json_results()['body'], self.api_results['body'])
def xmlDownloaded(self): """ populate the plugins object with the fetched data """ reply = self.sender() reposName = reply.property('reposName') if reply.error() != QNetworkReply.NoError: # fetching failed self.mRepositories[reposName]["state"] = 3 self.mRepositories[reposName]["error"] = reply.errorString() if reply.error() == QNetworkReply.OperationCanceledError: self.mRepositories[reposName]["error"] += "\n\n" + QCoreApplication.translate("QgsPluginInstaller", "If you haven't canceled the download manually, it was most likely caused by a timeout. In this case consider increasing the connection timeout value in QGIS options window.") elif reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) == 301: redirectionUrl = reply.attribute(QNetworkRequest.RedirectionTargetAttribute) if redirectionUrl.isRelative(): redirectionUrl = reply.url().resolved(redirectionUrl) redirectionCounter = reply.property('redirectionCounter') + 1 if redirectionCounter > 4: self.mRepositories[reposName]["state"] = 3 self.mRepositories[reposName]["error"] = QCoreApplication.translate("QgsPluginInstaller", "Too many redirections") else: # Fire a new request and exit immediately in order to quietly destroy the old one self.requestFetching(reposName, redirectionUrl, redirectionCounter) reply.deleteLater() return else: reposXML = QDomDocument() content = reply.readAll() # Fix lonely ampersands in metadata a = QByteArray() a.append("& ") b = QByteArray() b.append("& ") content = content.replace(a, b) reposXML.setContent(content) pluginNodes = reposXML.elementsByTagName("pyqgis_plugin") if pluginNodes.size(): for i in range(pluginNodes.size()): fileName = pluginNodes.item(i).firstChildElement("file_name").text().strip() if not fileName: fileName = QFileInfo(pluginNodes.item(i).firstChildElement("download_url").text().strip().split("?")[0]).fileName() name = fileName.partition(".")[0] experimental = False if pluginNodes.item(i).firstChildElement("experimental").text().strip().upper() in ["TRUE", "YES"]: experimental = True deprecated = False if pluginNodes.item(i).firstChildElement("deprecated").text().strip().upper() in ["TRUE", "YES"]: deprecated = True trusted = False if pluginNodes.item(i).firstChildElement("trusted").text().strip().upper() in ["TRUE", "YES"]: trusted = True icon = pluginNodes.item(i).firstChildElement("icon").text().strip() if icon and not icon.startswith("http"): icon = "http://{}/{}".format(QUrl(self.mRepositories[reposName]["url"]).host(), icon) if pluginNodes.item(i).toElement().hasAttribute("plugin_id"): plugin_id = pluginNodes.item(i).toElement().attribute("plugin_id") else: plugin_id = None plugin = { "id": name, "plugin_id": plugin_id, "name": pluginNodes.item(i).toElement().attribute("name"), "version_available": pluginNodes.item(i).toElement().attribute("version"), "description": pluginNodes.item(i).firstChildElement("description").text().strip(), "about": pluginNodes.item(i).firstChildElement("about").text().strip(), "author_name": pluginNodes.item(i).firstChildElement("author_name").text().strip(), "homepage": pluginNodes.item(i).firstChildElement("homepage").text().strip(), "download_url": pluginNodes.item(i).firstChildElement("download_url").text().strip(), "category": pluginNodes.item(i).firstChildElement("category").text().strip(), "tags": pluginNodes.item(i).firstChildElement("tags").text().strip(), "changelog": pluginNodes.item(i).firstChildElement("changelog").text().strip(), "author_email": pluginNodes.item(i).firstChildElement("author_email").text().strip(), "tracker": pluginNodes.item(i).firstChildElement("tracker").text().strip(), "code_repository": pluginNodes.item(i).firstChildElement("repository").text().strip(), "downloads": pluginNodes.item(i).firstChildElement("downloads").text().strip(), "average_vote": pluginNodes.item(i).firstChildElement("average_vote").text().strip(), "rating_votes": pluginNodes.item(i).firstChildElement("rating_votes").text().strip(), "icon": icon, "experimental": experimental, "deprecated": deprecated, "trusted": trusted, "filename": fileName, "installed": False, "available": True, "status": "not installed", "error": "", "error_details": "", "version_installed": "", "zip_repository": reposName, "library": "", "readonly": False } qgisMinimumVersion = pluginNodes.item(i).firstChildElement("qgis_minimum_version").text().strip() if not qgisMinimumVersion: qgisMinimumVersion = "2" qgisMaximumVersion = pluginNodes.item(i).firstChildElement("qgis_maximum_version").text().strip() if not qgisMaximumVersion: qgisMaximumVersion = qgisMinimumVersion[0] + ".99" # if compatible, add the plugin to the list if not pluginNodes.item(i).firstChildElement("disabled").text().strip().upper() in ["TRUE", "YES"]: if isCompatible(pyQgisVersion(), qgisMinimumVersion, qgisMaximumVersion): # add the plugin to the cache plugins.addFromRepository(plugin) self.mRepositories[reposName]["state"] = 2 else: # no plugin metadata found self.mRepositories[reposName]["state"] = 3 if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) == 200: self.mRepositories[reposName]["error"] = QCoreApplication.translate("QgsPluginInstaller", "Server response is 200 OK, but doesn't contain plugin metatada. This is most likely caused by a proxy or a wrong repository URL. You can configure proxy settings in QGIS options.") else: self.mRepositories[reposName]["error"] = QCoreApplication.translate("QgsPluginInstaller", "Status code:") + " {} {}".format( reply.attribute(QNetworkRequest.HttpStatusCodeAttribute), reply.attribute(QNetworkRequest.HttpReasonPhraseAttribute) ) self.repositoryFetched.emit(reposName) # is the checking done? if not self.fetchingInProgress(): self.checkingDone.emit() reply.deleteLater()
def xmlDownloaded(self): """ populate the plugins object with the fetched data """ reply = self.sender() reposName = reply.property('reposName') if reply.error() != QNetworkReply.NoError: # fetching failed self.mRepositories[reposName]["state"] = 3 self.mRepositories[reposName]["error"] = reply.errorString() if reply.error() == QNetworkReply.OperationCanceledError: self.mRepositories[reposName][ "error"] += "\n\n" + QCoreApplication.translate( "QgsPluginInstaller", "If you haven't canceled the download manually, it was most likely caused by a timeout. In this case consider increasing the connection timeout value in QGIS options window." ) elif reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) == 301: redirectionUrl = reply.attribute( QNetworkRequest.RedirectionTargetAttribute) if redirectionUrl.isRelative(): redirectionUrl = reply.url().resolved(redirectionUrl) redirectionCounter = reply.property('redirectionCounter') + 1 if redirectionCounter > 4: self.mRepositories[reposName]["state"] = 3 self.mRepositories[reposName][ "error"] = QCoreApplication.translate( "QgsPluginInstaller", "Too many redirections") else: # Fire a new request and exit immediately in order to quietly destroy the old one self.requestFetching(reposName, redirectionUrl, redirectionCounter) reply.deleteLater() return else: reposXML = QDomDocument() content = reply.readAll() # Fix lonely ampersands in metadata a = QByteArray() a.append("& ") b = QByteArray() b.append("& ") content = content.replace(a, b) reposXML.setContent(content) pluginNodes = reposXML.elementsByTagName("pyqgis_plugin") if pluginNodes.size(): for i in range(pluginNodes.size()): fileName = pluginNodes.item(i).firstChildElement( "file_name").text().strip() if not fileName: fileName = QFileInfo( pluginNodes.item(i).firstChildElement( "download_url").text().strip().split("?") [0]).fileName() name = fileName.partition(".")[0] experimental = False if pluginNodes.item(i).firstChildElement( "experimental").text().strip().upper() in [ "TRUE", "YES" ]: experimental = True deprecated = False if pluginNodes.item(i).firstChildElement( "deprecated").text().strip().upper() in [ "TRUE", "YES" ]: deprecated = True trusted = False if pluginNodes.item(i).firstChildElement( "trusted").text().strip().upper() in [ "TRUE", "YES" ]: trusted = True icon = pluginNodes.item(i).firstChildElement( "icon").text().strip() if icon and not icon.startswith("http"): icon = "http://{}/{}".format( QUrl(self.mRepositories[reposName]["url"]).host(), icon) if pluginNodes.item(i).toElement().hasAttribute( "plugin_id"): plugin_id = pluginNodes.item(i).toElement().attribute( "plugin_id") else: plugin_id = None plugin = { "id": name, "plugin_id": plugin_id, "name": pluginNodes.item(i).toElement().attribute("name"), "version_available": pluginNodes.item(i).toElement().attribute("version"), "description": pluginNodes.item(i).firstChildElement( "description").text().strip(), "about": pluginNodes.item(i).firstChildElement( "about").text().strip(), "author_name": pluginNodes.item(i).firstChildElement( "author_name").text().strip(), "homepage": pluginNodes.item(i).firstChildElement( "homepage").text().strip(), "download_url": pluginNodes.item(i).firstChildElement( "download_url").text().strip(), "category": pluginNodes.item(i).firstChildElement( "category").text().strip(), "tags": pluginNodes.item(i).firstChildElement( "tags").text().strip(), "changelog": pluginNodes.item(i).firstChildElement( "changelog").text().strip(), "author_email": pluginNodes.item(i).firstChildElement( "author_email").text().strip(), "tracker": pluginNodes.item(i).firstChildElement( "tracker").text().strip(), "code_repository": pluginNodes.item(i).firstChildElement( "repository").text().strip(), "downloads": pluginNodes.item(i).firstChildElement( "downloads").text().strip(), "average_vote": pluginNodes.item(i).firstChildElement( "average_vote").text().strip(), "rating_votes": pluginNodes.item(i).firstChildElement( "rating_votes").text().strip(), "icon": icon, "experimental": experimental, "deprecated": deprecated, "trusted": trusted, "filename": fileName, "installed": False, "available": True, "status": "not installed", "error": "", "error_details": "", "version_installed": "", "zip_repository": reposName, "library": "", "readonly": False } qgisMinimumVersion = pluginNodes.item(i).firstChildElement( "qgis_minimum_version").text().strip() if not qgisMinimumVersion: qgisMinimumVersion = "2" qgisMaximumVersion = pluginNodes.item(i).firstChildElement( "qgis_maximum_version").text().strip() if not qgisMaximumVersion: qgisMaximumVersion = qgisMinimumVersion[0] + ".99" # if compatible, add the plugin to the list if not pluginNodes.item(i).firstChildElement( "disabled").text().strip().upper() in [ "TRUE", "YES" ]: if isCompatible(pyQgisVersion(), qgisMinimumVersion, qgisMaximumVersion): # add the plugin to the cache plugins.addFromRepository(plugin) self.mRepositories[reposName]["state"] = 2 else: # no plugin metadata found self.mRepositories[reposName]["state"] = 3 if reply.attribute( QNetworkRequest.HttpStatusCodeAttribute) == 200: self.mRepositories[reposName][ "error"] = QCoreApplication.translate( "QgsPluginInstaller", "Server response is 200 OK, but doesn't contain plugin metatada. This is most likely caused by a proxy or a wrong repository URL. You can configure proxy settings in QGIS options." ) else: self.mRepositories[reposName][ "error"] = QCoreApplication.translate( "QgsPluginInstaller", "Status code:") + " {} {}".format( reply.attribute( QNetworkRequest.HttpStatusCodeAttribute), reply.attribute( QNetworkRequest.HttpReasonPhraseAttribute)) self.repositoryFetched.emit(reposName) # is the checking done? if not self.fetchingInProgress(): self.checkingDone.emit() reply.deleteLater()
class FileDownloader(): """The blueprint for downloading file from url.""" def __init__(self, url, output_path, progress_dialog=None): """Constructor of the class. .. versionchanged:: 3.3 removed manager parameter. :param url: URL of file. :type url: str :param output_path: Output path. :type output_path: str :param progress_dialog: Progress dialog widget. :type progress_dialog: QWidget """ # noinspection PyArgumentList self.manager = QgsNetworkAccessManager.instance() self.url = QUrl(url) self.output_path = output_path self.progress_dialog = progress_dialog if self.progress_dialog: self.prefix_text = self.progress_dialog.labelText() self.output_file = None self.reply = None self.downloaded_file_buffer = None self.finished_flag = False def download(self): """Downloading the file. :returns: True if success, otherwise returns a tuple with format like this (QNetworkReply.NetworkError, error_message) :raises: IOError - when cannot create output_path """ # Prepare output path self.output_file = QFile(self.output_path) if not self.output_file.open(QFile.WriteOnly): raise IOError(self.output_file.errorString()) # Prepare downloaded buffer self.downloaded_file_buffer = QByteArray() # Request the url request = QNetworkRequest(self.url) self.reply = self.manager.get(request) self.reply.readyRead.connect(self.get_buffer) self.reply.finished.connect(self.write_data) self.manager.requestTimedOut.connect(self.request_timeout) if self.progress_dialog: # progress bar def progress_event(received, total): """Update progress. :param received: Data received so far. :type received: int :param total: Total expected data. :type total: int """ # noinspection PyArgumentList QgsApplication.processEvents() self.progress_dialog.adjustSize() human_received = humanize_file_size(received) human_total = humanize_file_size(total) label_text = tr("%s : %s of %s" % ( self.prefix_text, human_received, human_total)) self.progress_dialog.setLabelText(label_text) self.progress_dialog.setMaximum(total) self.progress_dialog.setValue(received) # cancel def cancel_action(): """Cancel download.""" self.reply.abort() self.reply.deleteLater() self.reply.downloadProgress.connect(progress_event) self.progress_dialog.canceled.connect(cancel_action) # Wait until finished # On Windows 32bit AND QGIS 2.2, self.reply.isFinished() always # returns False even after finished slot is called. So, that's why we # are adding self.finished_flag (see #864) while not self.reply.isFinished() and not self.finished_flag: # noinspection PyArgumentList QgsApplication.processEvents() result = self.reply.error() try: http_code = int(self.reply.attribute( QNetworkRequest.HttpStatusCodeAttribute)) except TypeError: # If the user cancels the request, the HTTP response will be None. http_code = None self.reply.abort() self.reply.deleteLater() if result == QNetworkReply.NoError: return True, None elif result == QNetworkReply.UnknownNetworkError: return False, tr( 'The network is unreachable. Please check your internet ' 'connection.') elif http_code == 408: msg = tr( 'Sorry, the server aborted your request. ' 'Please try a smaller area.') LOGGER.debug(msg) return False, msg elif http_code == 509: msg = tr( 'Sorry, the server is currently busy with another request. ' 'Please try again in a few minutes.') LOGGER.debug(msg) return False, msg elif result == QNetworkReply.ProtocolUnknownError or \ result == QNetworkReply.HostNotFoundError: # See http://doc.qt.io/qt-5/qurl-obsolete.html#encodedHost encoded_host = self.url.toAce(self.url.host()) LOGGER.exception('Host not found : %s' % encoded_host) return False, tr( 'Sorry, the server is unreachable. Please try again later.') elif result == QNetworkReply.ContentNotFoundError: LOGGER.exception('Path not found : %s' % self.url.path()) return False, tr('Sorry, the layer was not found on the server.') else: return result, self.reply.errorString() def get_buffer(self): """Get buffer from self.reply and store it to our buffer container.""" buffer_size = self.reply.size() data = self.reply.read(buffer_size) self.downloaded_file_buffer.append(data) def write_data(self): """Write data to a file.""" self.output_file.write(self.downloaded_file_buffer) self.output_file.close() self.finished_flag = True def request_timeout(self): """The request timed out.""" if self.progress_dialog: self.progress_dialog.hide()
class FileDownloader(): """The blueprint for downloading file from url.""" def __init__(self, url, output_path, progress_dialog=None): """Constructor of the class. .. versionchanged:: 3.3 removed manager parameter. :param url: URL of file. :type url: str :param output_path: Output path. :type output_path: str :param progress_dialog: Progress dialog widget. :type progress_dialog: QWidget """ # noinspection PyArgumentList self.manager = QgsNetworkAccessManager.instance() self.url = QUrl(url) self.output_path = output_path self.progress_dialog = progress_dialog if self.progress_dialog: self.prefix_text = self.progress_dialog.labelText() self.output_file = None self.reply = None self.downloaded_file_buffer = None self.finished_flag = False def download(self): """Downloading the file. :returns: True if success, otherwise returns a tuple with format like this (QNetworkReply.NetworkError, error_message) :raises: IOError - when cannot create output_path """ # Prepare output path self.output_file = QFile(self.output_path) if not self.output_file.open(QFile.WriteOnly): raise IOError(self.output_file.errorString()) # Prepare downloaded buffer self.downloaded_file_buffer = QByteArray() # Request the url request = QNetworkRequest(self.url) self.reply = self.manager.get(request) self.reply.readyRead.connect(self.get_buffer) self.reply.finished.connect(self.write_data) self.manager.requestTimedOut.connect(self.request_timeout) if self.progress_dialog: # progress bar def progress_event(received, total): """Update progress. :param received: Data received so far. :type received: int :param total: Total expected data. :type total: int """ # noinspection PyArgumentList QgsApplication.processEvents() self.progress_dialog.adjustSize() human_received = humanize_file_size(received) human_total = humanize_file_size(total) label_text = tr( "%s : %s of %s" % (self.prefix_text, human_received, human_total)) self.progress_dialog.setLabelText(label_text) self.progress_dialog.setMaximum(total) self.progress_dialog.setValue(received) # cancel def cancel_action(): """Cancel download.""" self.reply.abort() self.reply.deleteLater() self.reply.downloadProgress.connect(progress_event) self.progress_dialog.canceled.connect(cancel_action) # Wait until finished # On Windows 32bit AND QGIS 2.2, self.reply.isFinished() always # returns False even after finished slot is called. So, that's why we # are adding self.finished_flag (see #864) while not self.reply.isFinished() and not self.finished_flag: # noinspection PyArgumentList QgsApplication.processEvents() result = self.reply.error() try: http_code = int( self.reply.attribute(QNetworkRequest.HttpStatusCodeAttribute)) except TypeError: # If the user cancels the request, the HTTP response will be None. http_code = None self.reply.abort() self.reply.deleteLater() if result == QNetworkReply.NoError: return True, None elif result == QNetworkReply.UnknownNetworkError: return False, tr( 'The network is unreachable. Please check your internet ' 'connection.') elif http_code == 408: msg = tr('Sorry, the server aborted your request. ' 'Please try a smaller area.') LOGGER.debug(msg) return False, msg elif http_code == 509: msg = tr( 'Sorry, the server is currently busy with another request. ' 'Please try again in a few minutes.') LOGGER.debug(msg) return False, msg elif result == QNetworkReply.ProtocolUnknownError or \ result == QNetworkReply.HostNotFoundError: # See http://doc.qt.io/qt-5/qurl-obsolete.html#encodedHost encoded_host = self.url.toAce(self.url.host()) LOGGER.exception('Host not found : %s' % encoded_host) return False, tr( 'Sorry, the server is unreachable. Please try again later.') elif result == QNetworkReply.ContentNotFoundError: LOGGER.exception('Path not found : %s' % self.url.path()) return False, tr('Sorry, the layer was not found on the server.') else: return result, self.reply.errorString() def get_buffer(self): """Get buffer from self.reply and store it to our buffer container.""" buffer_size = self.reply.size() data = self.reply.read(buffer_size) self.downloaded_file_buffer.append(data) def write_data(self): """Write data to a file.""" self.output_file.write(self.downloaded_file_buffer) self.output_file.close() self.finished_flag = True def request_timeout(self): """The request timed out.""" if self.progress_dialog: self.progress_dialog.hide()