要对我们项目的功能进行封装,做一个GUI界面
开始学图形界面,从最热门的pyqt5学起~~
学习大神的视频做得学习笔记
Python Qt 图形界面编程 - 华为大叔7天带你入门 - PySide2 PyQt5 PyQt PySide
下载QT的详细教程:下载QT的详细教程
下载QT相关内容的链接集合:下载QT相关内容的链接集合:
//即下载QT creator
Qt库里面有非常强大的图形界面开发库,但是Qt库是C++语言开发的,PySide2、PyQt5可以让我们通过Python语言使用Qt。
PySide2、PyQt5 这两者有什么区别呢?
PySide2 是Qt的 亲儿子
PyQt5 是Qt还没有亲儿子之前的收的义子 (Riverbank Computing这个公司开发的)。
那为什么 PyQt5 这个义子 反而比 PySide2 这个亲儿子更出名呢?
原因很简单:PySide2 这亲儿子最近(2018年7月)才出生。
但是亲儿子毕竟是亲儿子,Qt准备大力培养,PySide2 或许更有前途。
已经在使用 PyQt5 的朋友不要皱眉, 两个库的使用 对程序员来说,差别很小:它们的调用接口几乎一模一样。
如果你的程序是PyQt5开发的,通常只要略作修改,比如把导入的名字从 PyQt5 换成 PySide2 就行了。反之亦然。
命令行中直接执行
pip install pyqt5-tools
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QPlainTextEdit
app = QApplication([])
window = QMainWindow()
window.resize(500, 400)
window.move(300, 310)
window.setWindowTitle('薪资统计')
textEdit = QPlainTextEdit(window)
textEdit.setPlaceholderText("请输入薪资表")
textEdit.move(10,25)
textEdit.resize(300,350)
button = QPushButton('统计', window)
button.move(380,80)
window.show()
app.exec_()
QApplication
app = QApplication([])
QApplication 提供了整个图形界面程序的底层管理功能,比如:
初始化、程序入口参数的处理,用户事件(对界面的点击、输入、拖拽)分发给各个对应的控件,等等…
既然QApplication要做如此重要的初始化操作,所以,我们必须在任何界面控件对象创建前,先创建它。
QMainWindow(主窗口)、QPlainTextEdit(文本框)、QPushButton(按钮)
window = QMainWindow()
window.resize(500, 400)//决定它的大小宽度500 高度400
window.move(300, 310)// 控制主窗口出现在显示器屏幕的什么位置 300决定宽 310决定高 距离屏幕右上角的距离
window.setWindowTitle('薪资统计')//窗口的标题
textEdit = QPlainTextEdit(window)//创建在window内
textEdit.setPlaceholderText("请输入薪资表")//提示文本
textEdit.move(10,25)//相对于父窗口的位置 除了标题框的位置
textEdit.resize(300,350)//设置文本框的大小
button = QPushButton('统计', window)//按钮标签以及父窗口
button.move(380,80)/相对于父窗口的位置 除了标题框的位置
QMainWindow、QPlainTextEdit、QPushButton 是3个控件类,分别对应界面的主窗口、文本框、按钮
他们都是控件基类对象QWidget的子类。
要在界面上 创建一个控件 ,就需要在程序代码中 创建 这个 控件对应类 的一个 实例对象。
window.show()//执行这个方法 窗口才能出来 表示要展示在界面上
app.exec_()//防止展示window.show()一闪而过就没了 等待用户的输入 这个程序是一个死循环
在 Qt 系统中, 当界面上一个控件被操作时,比如 被点击、被输入文本、被鼠标拖拽等, 就会发出 信号 ,英文叫 signal。就是表明一个事件(比如被点击、被输入文本)发生了。
我们可以预先在代码中指定 处理这个 signal 的函数,这个处理 signal 的函数 叫做 slot 。
定义一个函数
def handleCalc():
//print('统计按钮被点击了')
info = textEdit.toPlainText()
指定如果发生了button 按钮被点击的事情,需要让 handleCalc 来处理,像这样
加载按钮处
button = QPushButton('统计', window)
button.move(380,80)
button.clicked.connect(handleCalc)
//让handleCalc来处理button被点击的操作
//把button被点击(clicked)的信号(signal),连接(connect)到了handleCalc这样的一个slot上
完整的代码定义
def handleCalc():
info = textEdit.toPlainText()
# 薪资20000 以上 和 以下 的人员名单
salary_above_20k = ''
salary_below_20k = ''
for line in info.splitlines():
if not line.strip():
continue
parts = line.split(' ')
# 去掉列表中的空字符串内容
parts = [p for p in parts if p]
name,salary,age = parts
if int(salary) >= 20000:
salary_above_20k += name + '\n'
else:
salary_below_20k += name + '\n'
QMessageBox.about(window,
'统计结果',
f'''薪资20000 以上的有:\n{salary_above_20k}
\n薪资20000 以下的有:\n{salary_below_20k}'''
)
from PySide2.QtWidgets import QApplication, QMainWindow, QPushButton, QPlainTextEdit,QMessageBox
class Stats():
def __init__(self):
self.window = QMainWindow()
self.window.resize(500, 400)
self.window.move(300, 300)
self.window.setWindowTitle('薪资统计')
self.textEdit = QPlainTextEdit(self.window)
self.textEdit.setPlaceholderText("请输入薪资表")
self.textEdit.move(10, 25)
self.textEdit.resize(300, 350)
self.button = QPushButton('统计', self.window)
self.button.move(380, 80)
self.button.clicked.connect(self.handleCalc)
def handleCalc(self):
info = self.textEdit.toPlainText()
# 薪资20000 以上 和 以下 的人员名单
salary_above_20k = ''
salary_below_20k = ''
for line in info.splitlines():
if not line.strip():
continue
parts = line.split(' ')
# 去掉列表中的空字符串内容
parts = [p for p in parts if p]
name,salary,age = parts
if int(salary) >= 20000:
salary_above_20k += name + '\n'
else:
salary_below_20k += name + '\n'
QMessageBox.about(self.window,
'统计结果',
f'''薪资20000 以上的有:\n{salary_above_20k}
\n薪资20000 以下的有:\n{salary_below_20k}'''
)
app = QApplication([])
stats = Stats()
stats.window.show()
app.exec_()
测试数据
薛蟠 4560 25
薛蝌 4460 25
薛宝钗 35776 23
薛宝琴 14346 18
王夫人 43360 45
王熙凤 24460 25
王子腾 55660 45
王仁 15034 65
尤二姐 5324 24
贾芹 5663 25
贾兰 13443 35
贾芸 4522 25
尤三姐 5905 22
贾珍 54603 35
路径
在下载了PySide2 就会有qt designer
C:\Users\phl\AppData\Local\Programs\Python\Python38\Lib\site-packages\PySide2
前提:现在我有一个GUI界面设计.ui的文件与py代码在同一目录下
from PySide2.QtWidgets import QApplication, QMessageBox
from PySide2.QtUiTools import QUiLoader
from PySide2.QtCore import QFile
def __init__(self):
# 从文件中加载UI定义
qfile_stats = QFile("GUI界面设计.ui")
qfile_stats.open(QFile.ReadOnly)
qfile_stats.close()
# 从 UI 定义中动态 创建一个相应的窗口对象
# 注意:里面的控件对象也成为窗口对象的属性了
# 比如 self.ui.button , self.ui.textEdit
self.ui = QUiLoader().load(qfile_stats)
from PyQt5 import uic
class Stats:
def __init__(self):
# 从文件中加载UI定义
self.ui = uic.loadUi("GUI界面设计.ui")
完整代码
# from PySide2.QtWidgets import QApplication, QMessageBox
# from PySide2.QtUiTools import QUiLoader
# from PySide2.QtCore import QFile
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QPlainTextEdit, QMessageBox
from PyQt5 import uic
class Stats:
def __init__(self):
# 从文件中加载UI定义
# qfile_stats = QFile("GUI界面设计.ui")
# qfile_stats.open(QFile.ReadOnly)
# qfile_stats.close()
# 从 UI 定义中动态 创建一个相应的窗口对象
# 注意:里面的控件对象也成为窗口对象的属性了
# 比如 self.ui.button , self.ui.textEdit
# self.ui = QUiLoader().load(qfile_stats)
self.ui = uic.loadUi("GUI界面设计.ui")
self.ui.button.clicked.connect(self.handleCalc)//注意修改的地方 原先没有ui button一定要是ui中对象名
def handleCalc(self):
info = self.ui.textEdit.toPlainText()
//textEdit也要是ui中的对象名
salary_above_20k = ''
salary_below_20k = ''
for line in info.splitlines():
if not line.strip():
continue
parts = line.split(' ')
parts = [p for p in parts if p]
name,salary,age = parts
if int(salary) >= 20000:
salary_above_20k += name + '\n'
else:
salary_below_20k += name + '\n'
QMessageBox.about(self.ui,//同样原先显示时是self.window
'统计结果',
f'''薪资20000 以上的有:\n{salary_above_20k}
\n薪资20000 以下的有:\n{salary_below_20k}'''
)
app = QApplication([])
stats = Stats()
stats.ui.show()//原先是stats现在是stats.ui
app.exec_()
pyuic5 main.ui > ui_main.py
pyside2-uic main.ui > ui_main.py
然后在你的代码文件中这样使用定义界面的类
import sys
from PyQt5 import QtWidgets, uic
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from ui_main import Ui_Form
//ui_main是我转化过来的py文件 Ui_Form转化的类型 我转化的是Ui_Form 可以是Ui_MainWindow
class MainWindow(QtWidgets.QMainWindow, Ui_Form):
def __init__(self, *args, obj=None, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.setupUi(self)
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
使得框体随着窗体变化而变化
情况1:按钮控件过大
效果图 //将文本框与按钮垂直布局
//但是又不希望按钮控件那么大 所以需要调整空间位置和大小
调整“消息头”的水平伸展数据为2 使得“消息头”为其他两个按钮的两倍大小
//如果调整成其他大小的时候 米有那么明显 因为“+””-“两个按钮也有自己的最小的大小
//当放大窗体时“消息头”就会随之变大 而“+””-“不会变化
情况3:调整控件上下间距
要调整控件上下间距,可以给控件添加layout,然后通过设定layout的上下的padding 和 margin 来调整间距。
希望调整”消息体“下的文本框与左边的文本框一样大
创建一个水平布局的layout 将”消息体“放入其中
调整上图的数据 就可以调整”消息体“的大小了
可以通过添加 horizontal spacer 进行控制,也可以通过layout的左右margin
使用控件:
调整大小:
对界面控件进行布局,按照如下步骤操作
先不使用任何Layout,把所有控件 按位置 摆放在界面上
然后先从 最内层开始 进行控件的 Layout 设定
逐步拓展到外层 进行控件的 Layout设定
最后调整 layout中控件的大小比例, 优先使用 Layout的 layoutStrentch 属性来控制
首先下载pyinstaller
pip install pyinstaller
例如我现在要发行static.py
# from PySide2.QtWidgets import QApplication, QMessageBox
# from PySide2.QtUiTools import QUiLoader
# from PySide2.QtCore import QFile
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QPlainTextEdit, QMessageBox
from PyQt5 import uic
class Stats:
def __init__(self):
# 从文件中加载UI定义
# qfile_stats = QFile("GUI界面设计.ui")
# qfile_stats.open(QFile.ReadOnly)
# qfile_stats.close()
# 从 UI 定义中动态 创建一个相应的窗口对象
# 注意:里面的控件对象也成为窗口对象的属性了
# 比如 self.ui.button , self.ui.textEdit
# self.ui = QUiLoader().load(qfile_stats)
self.ui = uic.loadUi("GUI界面设计.ui")
self.ui.button.clicked.connect(self.handleCalc)
def handleCalc(self):
info = self.ui.textEdit.toPlainText()
salary_above_20k = ''
salary_below_20k = ''
for line in info.splitlines():
if not line.strip():
continue
parts = line.split(' ')
parts = [p for p in parts if p]
name,salary,age = parts
if int(salary) >= 20000:
salary_above_20k += name + '\n'
else:
salary_below_20k += name + '\n'
QMessageBox.about(self.ui,
'统计结果',
f'''薪资20000 以上的有:\n{salary_above_20k}
\n薪资20000 以下的有:\n{salary_below_20k}'''
)
app = QApplication([])
stats = Stats()
stats.ui.show()
app.exec_()
cd到当前目录后 输入命令语句
pyinstaller static.py --noconsole --hidden-import PySide2.QtXml
这时候项目根目录C:\Users\phl\PycharmProjects\pythonProject1
就多出了两个目录
打开dist\static` 运行static.exe会出错
这时候把程序所需要的ui文件拷贝到打包目录中即可了
因为PyInstaller只能分析出需要哪些代码文件。 而你的程序动态打开的资源文件,比如 图片、excel、ui这些,它是不会帮你打包的。
缺少了GUI界面设计.ui这个文件的加载 将GUI界面设计.ui文件粘贴到dist\static中即可
现在打开运行static.exe就不会出错了
一个可运行的文件就生成了
将C:\Users\phl\PycharmProjects\pythonProject1\dist中的static
文件夹进行打包 就可发送给别人使用了
如何知道是缺少哪一个文件使得程序不能运行呢?
–noconsole 指定不要命令行窗口,否则我们的程序运行的时候,还会多一个黑窗口。 但是我建议大家可以先去掉这个参数,等确定运行成功后,再加上参数重新制作exe。因为这个黑窗口可以显示出程序的报错,这样我们容易找到问题的线索。
–hidden-import PySide2.QtXml 参数是因为这个 QtXml库是动态导入,PyInstaller没法分析出来,需要我们告诉它。
关于动态导入库
最后,别忘了,把程序所需要的ui文件拷贝到打包目录中。
因为PyInstaller只能分析出需要哪些代码文件。 而你的程序动态打开的资源文件,比如 图片、excel、ui这些,它是不会帮你打包的。 图片、excel、ui需要我们自行导入。
主窗口图标
我们程序运行的窗口,需要显示自己的图标,这样才更像一个正式的产品。
通过如下代码,我们可以把一个png图片文件作为 程序窗口图标。
from PyQt5.QtGui import QIcon
app = QApplication([])
# 加载 icon
app.setWindowIcon(QIcon('logo.png'))
注意:这些图标png文件,在使用PyInstaller创建可执行程序时,也要拷贝到程序所在目录。否则可执行程序运行后不会显示图标。
//jpg也可以
可以在PyInstaller创建可执行程序时,通过参数 --icon=“logo.ico” 指定。
比如
pyinstaller httpclient.py --noconsole --hidden-import PySide2.QtXml --icon="logo.ico"
注意参数一定是存在的ico文件,不能是png等图片文件。
如果你只有png文件,可以通过在线的png转ico文件网站,生成ico,比如下面两个网站
网站一
网站二