Python图形界面开发
程序的用户交互界面我们称之为UI
目前python语言GUI有以下选择
- Tkinter:官方标准库,稳定,程序小,控件较少
- wxPython:基于wxWidgets的python库,控件丰富,文档少,用户少
- Pyside2、PyQt5:基于Qt的python库,控件丰富,跨平台体验好,文档完善用户多,但程序较大。(是真的555
# 豆瓣源
pip install pyside2 -i https://pypi.douban.com/simple/
利用QtDesigner进行布局规python划
pip安装好pyside2包后到package文件夹下有Designer.exe,打开进行布局,如下是我的布局。这里要注意,一定要把所有控件布局在一起,这样控件才会随着软件的缩放而整体缩放。
具体使用方法这里参考官方文档:QApplication Class | Qt Widgets 5.15.5
中文版简化教程:Python Qt 简介 | 白月黑羽 (byhy.net)
对源程序进行重构
源程序目标网址类型较少,在这里更换为Wallhaven。
pyside2自定义类例程
from PySide2.QtWidgets import QApplication, QMessageBox
from PySide2.QtUiTools import QUiLoader
class Wallpaper:
def __init__(self):
self.wp.button
self.wp.textEdit
# 注意,刚刚设计的.ui保存至程序当前目录下
self.wp = QUiLoader().load('main.ui')
# xxxx是.ui中的button参数名称
self.wp.xxxx.clicked.connect(self.handleCalc)
def handleCalc(self):
# button点击后运行函数
app = QApplication([])
stats = Stats()
stats.wp.show()
app.exec_()
重构爬虫函数进行类封装
class Wallpaper:
def __init__(self):
# sort即是选择类型
self.sort = 0
self.signal_1 = 2
self.signal_2 = 0
self.sorts_name = ['动漫', '女生动漫', '明日方舟', '城市', '简约'
, '科幻', '繁星', '英雄联盟', '太空', '航海王', '天空', '风景']
self.ui = QUiLoader().load('wallpaper.ui')
self.ui.button.clicked.connect(self.handleCalc)
# 添加类型
self.ui.cb.addItems(self.sorts_name)
# 将类型选择框与信号槽连接 self.ui.cb.currentIndexChanged.connect(self.selectionchange)
# 打印抓取进度
self.ui.ms.text_print.connect(self.gui)
def selectionchange(self, i):
# 获取当前类型选中序号i,0为起始
self.sort = i
def handleCalc(self):
# 对每个网址的最大页数分别赋值
page = [2763, 2678, 58, 315, 539, 418, 208, 151, 336, 38, 764, 1311]
# 进行勾选框判断
if self.ui.check.isChecked():
# 停止或开始信号标记
self.signal_1 += 1
# 满足开始信号
if self.signal_1 % 2 == 1:
# 更改button名称
self.ui.button.setText('停止')
# 列表元素均为网址,在此插入,略
sorts = [anime, anime_girl, ark_nights, cityscape, minimalism, science, stars, legends, space, one_peace, sky, landscape]
base_url = sorts[self.sort]
# 对文件夹名称、路径分类化
fold_name = self.sorts_name[self.sort]
fold_path = self.sorts_name[self.sort] + '\\'
# <insert>
if self.signal_1 % 2 == 0:
self.ui.button.setText('开始')
else:
# 勾选框未勾选提示
msgBox1 = QMessageBox()
msgBox1.setWindowTitle("( ゜-゜)つロ")
msgBox1.setText('记得勾选"我已阅读《使用手册》"哦~')
msgBox1.setStandardButtons(QMessageBox.Ok)
msgBox1.setDefaultButton(QMessageBox.Ok)
ret = msgBox1.exec_()
def gui(self, text):
# 打印抓取进度
self.ui.infoBox.append(str(text))
# 让infoBox刷新至最新一条
self.ui.infoBox.ensureCursorVisible()
app = QApplication(sys.argv)
# 可以更换自己的logo,保存在当前目录下
app.setWindowIcon(QIcon('logo.png'))
wallpaper_catch = Wallpaper()
wallpaper_catch.wp.show()
# 当最后一个线程结束时,程序关闭
sys.exit(app.exec_())
requests向服务器请求需要时间,爬虫打印进程又直接对主程序进行渲染,其中最末尾的app.exec_()就是让主线程,不断循环处理用户操作的事件。要解决这个问题,就要让爬虫在子线程里运行,注意不能直接对主窗口进行渲染操作
python既支持多进程,也支持多线程。运行在操作系统中的每一个进程都可以拥有多个线程,每个进程具有自己的内存。即便如此,网页抓取不能仅仅依靠提高进程的数量,而应该从代码优化的角度考虑问题。
from threading import Thread # 引入threading模块
同时,我们要让子线程在打印时发出信号给主线程(推荐所有图片渲染的操作都在主线程中进行,避免出现程序无响应的问题)
因此我们创建Catch_Signal类,继承自QObject类:
class Catch_Signals(QObject):
procedure = Signal(str)
在Wallpaper类中定义实例属性:
self.wp.ms = Catch_Signals()
self.wp.ms.procedure.connect(self.gui)
再在Wallpaper类中定义GUI绘制函数:
def gui(self, text):
self.wp.infoBox.append(str(text))
self.wp.infoBox.ensureCursorVisible()
将爬虫程序放入子线程:
# 插入<insert>处
thread = Thread(target=self.threadSend,
args=(base_url, fold_name, fold_path))
thread.start()
# 定义子线程函数
def threadSend(self, base_url, fold_name, fold_path):
try:
# 爬虫函数略
except requests.URLRequired as e:
# 略
补充import:
from PySide2.QtWidgets import QApplication, QMessageBox
from PySide2.QtUiTools import QUiLoader
from PySide2.QtGui import QIcon
from PySide2.QtCore import Signal, QObject
对源程序继续完善
Sleep延迟
我们在for循环里加入sleep函数
引入库:
import time
from random import randint
for循环下载img最后一行加入:
time.sleep(sleep_time / 100)
当然,子进程try:第一行加入:
sleep_time = randint(1, 201)
保证在每次下载间隔1~2s
sys.exit(app.exec_())
sys.exit(app.exec_())这行代码的意思是,当最后一个线程结束时,退出命令行(即总程序)。但是一旦子线程开始便无法通过这行语句停下来,尝试用signal进行判断限制,无果;尝试直接将其放入子线程,无果;尝试寻找退出主程序弹窗函数,无果…
注意!这里是个坑,当ui界面是动态加载时,重写closeEvent函数是无效的!
也就是说,当需要重写closeEvent函数时,必须要静态加载ui文件,在同目录cmd命令行下运行以下命令
pyside2-uic xxxxx.ui -> xxxxx.py
然后在主函数中调用该py文件即相当于调用ui文件
更改logo
from PySide2.QtGui import QIcon
# 根目录下保存名为'logo.png'的logo
app.setWindowIcon(QIcon('logo.png'))
当然这时Qt窗口的logo,APP的logo会在打包时添加
附属文件插入
打包完成后别着急运行,好些个附属文件还没有转移,例如logo.png文件是没有被打包进去的,因此需要我们手动粘贴到相应目录下。
这时运行可执行文件即可看到我们的程序。