在桌面应用中,往往需要从网络获取一些数据,比如下某个图像或者文本文件,查询城市天气预报,加载网络地图等等。在Qt中,提供一些网络处理类,可以很方便地实现上面列出的需求。核心类QNetworkAccessManager类处理应用程序发送网络请求和接收回复,类QNetworkrequest类保存QNetworkAccessManager发送的请求。QNetworkreply类管理使用QNetworkAccessManager发送的请求的数据和标头。
QNetworkAccessManager简介
使用QNetworkAccessManager类创建网络访问对象,该对象保存其发送的请求的通用配置和设置。它包含代理和缓存配置及与此类问题相关的信号外,还包含可用于监视网络操作进度的回复信号。一个QNetworkAccessManager实例对于整个Qt应用程序应该足够了。由于QNetworkAccessManager基于QObject,因此只能在其所属的线程中使用。
创建QNetworkAccessManager对象后,应用程序可以使用它通过网络发送请求。QNetworkAccessManager提供了一组标准函数,这些函数接受一个请求和可选数据,并且每个函数都返回一个QNetworkReply对象。返回的对象用于获取对相应请求所做响应所返回的任何数据。
一个简单的网络下载可以通过以下方式完成:
nam =QNetworkAccessManager(self) nam.) nam.get(QNetworkRequest(QUrl(';)
QNetworkAccessManager具有异步API。replyFinished调用上面的插槽时,它采用的参数是QNetworkReply对象,其中包含下载的数据以及元数据(标头等)。
注意:请求完成后,用户有责任在适当的时候删除QNetworkReply对象。不要在连接finished()的插函数内直接删除它。但可以使用deleteLater()函数来删除QNetworkReply对象。
注意: QNetworkAccessManager将接收到的请求排队。且并行执行的请求数取决于协议。当前,对于台式机平台上的HTTP协议,针对一个主机/端口组合并行执行6个请求。
假设QNetworkAccessManage管理器对象已经存在,那么一个更复杂的示例可以是:
request = QNetworkRequest() reque(QUrl(';)) reque('User-Agent','MyOwnBrowser 1.0') reply = nam.get(request) re) re) re)
QNetworkAccessManager 常用函数:
- get(self, request): 发送request以获取内容,返回一个打开的用于读取的新QNetworkReply对象,只要有新数据到达,该对象就会发出readyRead()信号。
- put(self, request, data): 将data内容上载到目标request,并返回一个新的QNetworkReply对象。调用此函数时,必须打开数据进行读取并保持有效,直到发出用于此回复的finish()信号为止。由协议决定是否有任何可用于从返回的对象读取的内容。对于HTTP,服务器可以发送一个小的HTML页面,指示上传成功(成功)。其他协议可能会在其答复中包含内容。注意:对于HTTP,此请求将发送一个PUT请求,大多数服务器都不允许。表单上传一般使用POST机制(包括通过HTML表单上传文件的机制)。
- post(self, request, data): 将HTTP POST请求发送到由request指定的目标,并返回一个已打开以供读取的新QNetworkReply对象,其中将包含服务器发送的回复。数据设备的内容将被上载到服务器。数据必须处于打开状态以供读取,并且必须保持有效,直到发出此回复的finish()信号为止。注意:在HTTP和HTTPS以外的协议上发送POST请求是未定义的,可能会失败。
- head(self, request): 投递一个request,以获得网络头信息,返回一个包含该头信息的QNetworkReply对象。
QNetworkAccessManager常用信号:
- authenticationRequired(self, reply, authenticator): 每当最终服务器在传递所请求的内容之前请求身份验证时,都会发出此信号。连接到该信号的插槽应填充身份验证器对象中内容的凭据(可以通过检查答复对象来确定)。
- encrypted(self, reply): SSL/TLS会话成功完成初始握手后,将发出此信号。此时,尚未传输任何用户数据。该信号可用于对证书链进行其他检查,例如在网站的证书已更改时通知用户。该回复参数指定网络回复负责。如果答复不符合预期的标准,则应通过连接到此信号的插槽调用QNe()来终止答复。可以使用QNe()方法检查正在使用的SSL配置。
- finished(self, reply): 每当待处理的网络回复结束时,都会发出此信号。该回复参数将包含一个指向刚刚结束的答复。该信号与QNe()信号一起发射。
- sslErrors(self, reply): 如果SSL/TLS会话在设置期间遇到错误(包括证书验证错误),则发出此信号。
QNetworkRequest简介
QNetworkRequest类是网络访问API的一部分,它包含通过网络发送请求所必需的信息以及一个URL和一些可用于修改请求的辅助信息。
常用函数有:
- setUrl(sef, url): 将网络请求引用的URL设置为url。
- url(self): 返回网络请求所引用的URL。
- setHeader(self, header): 设置标题信息为header, 覆盖任何以前设置的标头。此操作还设置等效的原始HTTP标头。
- header(self, header): 如果此请求中存在已知的网络头header,则返回该值。如果不存在,则返回无效值。
- setPriority(self, priority): 将请求的优先级设置为priority。
- priority(self): 返回请求的优先级。
- setRawHeader(self, headername, value): 将标头的值设置为headerValue。
- rawHeader(self, headername): 返回标头headerName的原始形式。如果不存在这样的标头,则返回一个空的QByteArray。
- setAttribute(self, code, value): 设置与代码code相关联的属性的值为value。如果已经设置了该属性,则先前的值将被丢弃。
- attribute(self, code, defaultValue): 返回与代码code关联的属性。如果尚未设置属性,则返回defaultValue。
QNetworkReply简介
- QNetworkReply继承自QIODevice,它包含与QNetworkAccessManager发布的请求相关的数据和元数据。与QNetworkRequest一样,它包含URL和标头(已解析和原始形式),有关回复状态的一些信息以及回复本身的内容。QNetworkReply是一个顺序访问QIODevice,这意味着从对象读取数据后,该数据将不再由设备保留。因此,应用程序有责任在需要时保留此数据。每当从网络接收并处理更多数据时,就会发出readyRead()信号。
QNetworkReply常用函数:
- abort(self): 立即中止操作并关闭所有打开的网络连接。仍在进行中的上传也会终止。并发射finished()信号。
- attribute(self, code): 返回代码为code的属性值.
- error(self): 返回在处理请求期间发现的错误。如果未发现错误,则返回NoError。
- errorString(self): 返回可读的最后发生的设备错误的描述。
- readAll(self): 从设备读取所有剩余数据,并将其作为字节数组返回。
- readLine(self, maxlen): 设备读取一行ASCII字符,最大为maxSize -1个字节,将字符存储在data中,并返回读取的字节数。如果无法读取一行但没有发生错误,则此函数返回0。如果发生错误,则此函数返回可以读取的长度,如果未读取则返回-1。
- read(self, maxlen): 从设备最多读取maxSize个字节到data中,并返回读取的字节数。如果发生错误(例如,尝试从以WriteOnly模式打开的设备中读取数据),则此函数返回-1。
- write(self, data): 将数据的最大maxSize字节从数据写入设备。返回实际写入的字节数;如果发生错误,则返回-1。
QNetworkReply常用信号:
- downloadProgress(self, bytesReceived, bytesTotal):发出此信号以指示此网络下载进度(如果有)。如果没有与此请求相关的下载,则此信号将以0作为bytesReceived和bytesTotal的值发出一次。
- encrypted(self):SSL / TLS会话成功完成初始握手后,将发出此信号。
- errorOccurred(self, code):当答复检测到处理错误时,将发出此信号。
- finished(self):答复完成处理后,将发出此信号。发出此信号后,将不再对答复的数据或元数据进行任何更新。
- metaDataChanged(self):只要回复中的元数据发生更改,就会发出此信号。
- preSharedKeyAuthenticationRequired(self, authenticator):如果SSL / TLS握手协商PSK密码套件,则会发出此信号,因此需要PSK身份验证。
- redirectAllowed(self):当处理redirected()信号的客户端代码验证了新URL时,它将发出此信号以允许重定向继续进行。
- redirected(self, url):如果在请求中设置了QNe,并且服务器以3xx状态(特别是301、302、303、305、307或308状态代码)响应, 如果位置标头中包含有效网址,则发出此信号。
- sslErrors(self, errors):如果SSL/TLS会话在设置期间遇到错误(包括证书验证错误),则发出此信号。该errors参数包含错误信息列表。
- uploadProgress(self, bytesSent, bytesTotal):发出此信号以指示此网络请求的上载部分(如果有)的进度。如果没有与此请求相关的上载,则不会发出此信号。
测试
试代码以pyqt5-examples样例代码为基础, 演示了如何使用QNetworkAccessManager,QNetworkRequest,QNetworkReply 从网站中下载文件并保存在本地。 完整代码如下:
import sys from PyQ import Qt, QDir, QFile, QFileInfo, QIODevice, QUrl from PyQ import (QApplication, QDialog, QDialogButtonBox, QHBoxLayout, QVBoxLayout, QLabel, QLineEdit, QProgressDialog, QMessageBox, QPushButton) from PyQ import QNetworkAccessManager, QNetworkRequest,QNetworkReply class DemoDownloadFile(QDialog): def __init__(self, parent=None): super(DemoDownloadFile, self).__init__(parent) # 设置窗口标题 ('实战 Qt for Python: 文件下载') # 设置窗口大小 (400, 120) = QUrl() = QNetworkAccessManager() = None = None = 0 = False () def initUi(self): #编辑下载地址 LineEdit = QLineEdit(';) urlLabel = QLabel('网址(&U):') urlLabel.setBuddy(LineEdit) #状态信息 = QLabel('在这里输入要下载的文件的网址。') .setWordWrap(True) #下载和退出按钮 = QPushButton('下载') .setDefault(True) = QPushButton('退出') .setAutoDefault(False) buttonBox = QDialogButtonBox() bu(, QDialogBu) bu(, QDialogBu) #进度显示对话框 = None #连接slot函数 LineEdit.textChanged.connect) .authenticationRequired.connect) .sslErrors.connect) .clicked.connect) .clicked.connect) topLayout = QHBoxLayout() (urlLabel) (LineEdit) mainLayout = QVBoxLayout() mainLayout.setSpacing(16) mainLayout.addLayout(topLayout) mainLayout.addWidget() mainLayout.addWidget(buttonBox) mainLayout.addStretch(1) (mainLayout) LineEdit.setFocus() #发出下载请求 def startRequest(self, url): = .get(QNetworkRequest(url)) .) .readyRead.connect) .downloadProgress.connect) #下载文件 def downloadFile(self): = QUrl(LineEdit.text()) fileInfo = QFileInfo(.path()) filename = () #如果没有文件名,就假定一个缺省文件 if not filename: filename = 'index.html' #如果存储目录有相同文件名称,则询问是否覆盖 if QFile.exists(filename): ret = QMe(self, '下载文件', '在当前目录下已经存着文件 %s。覆盖它吗?' % filename, QMe | QMe, QMe) if ret == QMe: return #删除原来的文件 QFile.remove(filename) = QFile(filename) if not .open): QMe(self, '下载文件', '不能保存文件 %s: %s.' % (filename, .errorString())) = None return = QProgressDialog(self) .canceled.connect) .setWindowTitle('下载文件') .setLabelText('正在下载 %s.' % filename) .setEnabled(False) = False () #终止下载 def cancelDownload(self): .setText('下载被取消了.') = True if is not None: .abort() .setEnabled(True) #下载完成后 def httpFinished(self): #释放进度对话框 if is not None: .hide() .deleteLater() = None #如果被终止下载了,做相应的善后处理 if : if is not None: .close() .remove() = None .deleteLater() = None return .flush() .close() redirectionTarget = .attribute) if .error(): .remove() QMe(self, '文件下载', '下载失败: %s.' % .errorString()) .setEnabled(True) elif redirectionTarget is not None: newUrl = .resolved(redirectionTarget) ret = QMe(self, '下载文件', '重定向到文件 %s?' % newUrl.toString(), QMe | QMe) if ret == QMe: = newUrl .deleteLater() = None .open) .resize(0) () return else: filename = QFileInfo(QUrl(LineEdit.text()).path()).fileName() .setText('文件 %s 下载到 %s.' % (filename, QDir.currentPath())) .setEnabled(True) .deleteLater() = None = None #保存下载的数据 def httpReadyRead(self): if is not None: .write(.readAll()) #更新进度对话框的进度显示信息 def updateDataReadProgress(self, bytesRead, totalBytes): if : return if is not None: .setMaximum(totalBytes) .setValue(bytesRead) #下载地址发生了改变,如果地址为空,则禁用下载按钮,否则启用 def enableDownloadButton(self): .setEnabled(LineEdit.text() != '') #处理许可下载信息 def slotAuthenticationRequired(self, authenticator): import os from PyQt5 import uic ui = os.(__file__), 'au;) dlg = uic.loadUi(ui) dlg.adjustSize() dlg.('%s at %s' % (), .host())) dlg.u(.userName()) dlg.(.password()) if dlg.exec() == QDialog.Accepted: au()) au()) #SSL 错误处理 def sslErrors(self, reply, errors): errorString = ", ".join([str()) for error in errors]) ret = QMe(self, 'HTTP 文件下载示例', '发生了SSL错误: %s' % errorString, QMe | QMe) if ret == QMe: .ignoreSslErrors() if __name__ == '__main__': app = QApplication) window = DemoDownloadFile() window.show() ())
运行结果如下图:
使用http请求进行文件下载
本文知识点
- QNetworkAccessManager类处理应用程序发送网络请求和接收回复。
- QNetworkRequest类包含通过网络发送请求所必需的信息。
- QNetworkReply包含与QNetworkAccessManager发布的请求相关的数据和元数据。
- 如何实现一个简单的网络文件下载功能。
- QProgressDialog的使用。
前一篇: 实战PyQt5: 129-SQL数据库操作