今天尝试了下 quamash 框架,该框架是一个 PyQt
的异步事件循环封装库,使用 Python3+ 的 asyncio
这个异步库。在看了该项目的内容后发现只有一个简单的进度条例子,故尝试用其来下载网络图片并显示。
# 安装依赖
- pip install quamash
- pip install aiohttp
- Python3.5+ 和 PyQt5
这里使用 aiohttp
是因为它基于 asyncio
封装的网络操作库,常见的 get
、 post
等方法,不过它只支持 Python3.5 及以上的版本,主要是它使用了 async def 这样的语法。
# 说明
- 在创建
QApplication
后随即设置替换事件循环loop
app = QApplication(sys.argv)
loop = QEventLoop(app)
asyncio.set_event_loop(loop)
w = Window()
- 通过
asyncio.ensure_future(func(), loop=loop)
来执行某个异步函数
# 流程
Window | → | initSession(初始化 session) |
↓ | ||
下载按钮 | → | doDownload(执行_doDownload 方法) |
↓ | ||
session.get(下载 json 数据进行解析) | ||
↓ | ||
添加到界面 | ← | _doDownloadImage(对单张图片进行下载) |
# 源码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Created on 2018年10月24日
@author: Irony
@site: https://github.com/892768447
@email: 892768447@qq.com
@file: AsyncioUiClient
@description:
"""
import asyncio
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPixmap, QMovie
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton,\
QApplication, QListWidget, QListWidgetItem, QLabel, QMessageBox
import aiohttp
from quamash import QEventLoop
__Author__ = """By: Irony
QQ: 892768447
Email: 892768447@qq.com"""
__Copyright__ = "Copyright (c) 2018 Irony"
__Version__ = "Version 1.0"
Url = 'https://www.doutula.com/api/search?keyword=%E6%9C%80%E6%96%B0%E8%A1%A8%E6%83%85&mime=0&page={}'
Headers = {
':authority': 'www.doutula.com',
':method': 'GET',
':scheme': 'https',
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'accept-language': 'zh-CN,zh;q=0.9',
'cache-control': 'max-age=0',
'dnt': '1',
'upgrade-insecure-requests': '1',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.6756.400 QQBrowser/10.2.2498.400'
}
class Window(QWidget):
def __init__(self, *args, **kwargs):
super(Window, self).__init__(*args, **kwargs)
layout = QVBoxLayout(self)
self.listWidget = QListWidget(self)
self.listWidget.setSpacing(2) # item直接的间隔
# 隐藏横向滚动条
self.listWidget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
# 让list 从左到右排列
self.listWidget.setFlow(self.listWidget.LeftToRight)
# 自动换行
self.listWidget.setWrapping(True)
self.listWidget.setResizeMode(self.listWidget.Adjust)
self.buttonMsg = QPushButton('弹出提示框', self, clicked=self.showMessage)
self.buttonDown = QPushButton('下载图片', self, clicked=self.doDownload)
layout.addWidget(self.listWidget)
layout.addWidget(self.buttonMsg)
layout.addWidget(self.buttonDown)
self.currentPage = 0
self.initSession() # 其实没必要,session主要用在需要登录的网站。缓存cookie用
def initSession(self):
async def _initSession():
# 初始化session
self.session = aiohttp.ClientSession(loop=loop)
print(self.session)
asyncio.ensure_future(_initSession(), loop=loop)
async def _doDownloadImage(self, url):
# 下载图片并添加到界面
async with self.session.get(url) as resp:
data = await resp.read()
if not data:
print('下载失败: ', url)
return
path = os.path.join('tmp', os.path.basename(url))
with open(path, 'wb') as fp:
fp.write(data)
item = QListWidgetItem(url, self.listWidget)
image = QPixmap(path)
item.setSizeHint(image.size())
label = QLabel(self.listWidget)
label.setPixmap(image)
if path.endswith('.gif'): # 可能是动态图
label.setMovie(QMovie(path))
self.listWidget.setItemWidget(item, label)
self.listWidget.scrollToBottom()
async def _doDownload(self):
# 下载工作
if self.currentPage == -1:
QMessageBox.information(self, '提示', '已经没有更多了')
return
self.currentPage += 1
url = Url.format(self.currentPage)
print('get url: ', url)
async with self.session.get(url, headers=Headers) as resp:
data = await resp.json()
if not data:
return
data = data.get('data', None)
if not data:
self.currentPage = -1
print('已经是最后一页了')
return
# 解析json并生成item添加到界面中
for entity in data.get('list', []):
url = entity.get('image_url', None)
if not url:
continue
await self._doDownloadImage(url) # 下载图片
def doDownload(self):
# 响应按钮点击调用
asyncio.ensure_future(self._doDownload(), loop=loop)
def showMessage(self):
# 显示对话框
app.aboutQt()
def closeEvent(self, event):
if not self.session.closed:
asyncio.ensure_future(self.session.close(), loop=loop)
super(Window, self).closeEvent(event)
if __name__ == '__main__':
import sys
import cgitb
import os
os.makedirs('tmp', exist_ok=True)
sys.excepthook = cgitb.enable(1, None, 5, 'text')
app = QApplication(sys.argv)
loop = QEventLoop(app)
asyncio.set_event_loop(loop)
w = Window()
w.show()
with loop:
loop.run_forever()
# 效果图
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 PyQt!