文章目录
官网
# 安装pyside2
pip install pyside2 # 比较新的pyside6 + python3.7+
# 注意导入时
import PySide2
from PySide2.QtCore import QTimer, QDateTime, Qt
from PySide2.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QVBoxLayout
from PySide2.QtGui import QIcon, QPixmap, QPicture, QFont, QPalette, QKeyEvent, QMouseEvent
使用方式
同pyqt5 类似
第一个PySide2应用
点击按钮随机切换人名。
# __author__ = "laufing"
# class_based_qt
# laufing_qt
import sys
import random
from PySide2.QtWidgets import QApplication, QWidget, QDesktopWidget, QLabel, QPushButton, QBoxLayout, QVBoxLayout, QHBoxLayout, QFormLayout, QGridLayout, QStackedLayout
from PySide2.QtCore import QTimer, QDateTime, QDate, QTime, Qt
from PySide2.QtGui import QIcon, QPixmap, QPicture, QPalette, QColor, QFont, QKeyEvent, QMouseEvent
# 定义自己的窗口控件
class MyWidget(QWidget):
def __init__(self):
super().__init__()
# 实例化一个窗口,并居中显示
desk = QDesktopWidget().geometry()
width, height = desk.width(), desk.height()
self.resize(500, 400)
self.move(width//2 - self.width()//2, height//2 - self.height()//2)
self.setWindowTitle("PySide2 应用")
self.setWindowIcon(QIcon("./imgs/dog.jpg"))
# 人物
self.choiceList = ["jack", 'lucy', 'rose']
# 垂直布局
vb = QVBoxLayout()
vb.setContentsMargins(10, 10, 10, 10)
# label标签
self.label = QLabel("欢迎 %s"%self.choiceList[0])
self.label.setAlignment(Qt.AlignmentFlag.AlignCenter)
# 按钮
self.button = QPushButton("点击我")
self.button.setStyleSheet("backgroundd-color: lightblue;")
# 信号与槽
self.button.clicked.connect(self.magic)
vb.addWidget(self.label)
vb.addWidget(self.button)
# 父控件设置布局
self.setLayout(vb)
def magic(self):
print("点击了按钮...")
self.label.setText(self.label.text().split()[0] + " " + random.choice(self.choiceList))
if __name__ == '__main__':
app = QApplication(sys.argv)
# 实例化我的窗口
window = MyWidget()
window.show()
exit_code = app.exec_()
sys.exit(exit_code)
配置pyside2的界面设计师
用于布局、设计界面
,实现布局与代码分离!!
在安装的
python环境中Scripts目录下有designer.exe
,将其绝对路径配置在pycharm中:
file>setttings>tools>external tools
使用:
Tools>external tools> PySide2Designer 创建一个窗口如下图:
Ctrl + s 保存为xx.ui文件
Ctrl + r 预览
Ctrl + n 新建
Ctrl + o 打开
选中多个子控件或者父控件,进行布局:
Ctrl + 1 水平布局;
Ctrl + 2 垂直布局
Ctrl + 5 Grid布局
Ctrl + 0 解除布局
保存后,
产生的ui文件 是xml文件内容
,使用时,第一种方式就是
python加载ui文件
;第二种方式是
转换为py文件
。
# 导入类
from PySide2.QtUiTools import QUiLoader # 模块下只有一个类
from PySide2.QtCore import QFile, QObject
from PySide2.QtWidgets import QApplication, QWidget, QPushButton, QLabel
from PySide2.QtWidgets import QMainWindow, QWidget
# 定义自己的类
class MyWindow():
def __init__(self):
# 封装文件对象
qfile = QFile("jack.ui")
# 打开文件 成功则返回True
qfile.open(QFile.ReadOnly) # 只读方式打开
# 读取数据(字节串)
# l = qfile.readLine()
# print("读取的一行数据:", l)
# 关闭文件
qfile.close()
# 加载所有的文件内容,返回父窗口对象
# 子控件 成为父控件的属性
self.win = QUiLoader().load(qfile)
print("加载所有的文件:", self.win, type(self.win), self.win.inherits("QWidget"))
def show(self):
# 信号与槽函数 这里的pushButton是对象的名字
self.win.pushButton.clicked.connect(lambda :print("点击了按钮......"))
self.win.show()
if __name__ == '__main__':
# 程序必须具有基本结构
import sys
app = QApplication(sys.argv)
win = MyWindow()
win.show()
exit_code = app.exec_()
print("退出的状态码:", exit_code)
sys.exit(exit_code)
pycharm:
Ctrl + f 搜索
Ctrl + r 替换
断点 + debugger 可以查看相应变量
另外ui文件也可以转为python文件
,需要使用uic,在python3的安装目录Scripts下有如下工具:
将pyside2-uic.exe的绝对路径配置在pycharm中,方法同designer的配置。
xxxx\pyside2-uic.exe
$FileName$ -o $FileNameWithoutExtension$.py
$FileDir$
然后在ui文件上,右键>External tools > pyside2uic 即可转为py文件。
配置pyside2-uic
将Qt ui文件转为python文件
在python的安装目录下的Scripts目录,有如下的文件:
将pyside2-uic.exe 配置到pycharm中即可,方式同designer.exe
Program
: python目录/Scripts/pyside2-uic.exe
Arguments
: $FileName$ -o $FileNameWithoutExtension$.py
Working directory
: $FileDir$
案例练习
使用Qt designer 实现如下界面
使用qt designer 设计布局
:
-
首先拖入子控件,并摆放整齐
-
设计布局,选中相关的控件,右键->布局->选择布局
最后的整体垂直布局
,没有红色框线,只需在Form上右键-布局即可。
布局内的控件是一个整体,单个的控件不再支持手动拖拽。
button缩放水平fixed、垂直fixed,水平布局可居中。
单个label 水平布局也居中。
请求头
,
+按钮
,
-按钮
控件的sizePolicy 水平伸缩分别为4 1 1;两个按钮之间还可以放spacer,并调整宽度。
最后添加spacer,调整控件间距,不要与控件一起选中,然后布局。
伸缩:可选中单个子控件(设置sizePolicy),也可选中整个红线框(layoutStretch-3,2,3,…)综合设置各个子控件的伸缩比例。
碰到的问题, 就是整体没有实现布局,所以窗口缩放时,内容控件不变
,在父控件右键-布局即可
-
设计好布局,保存文件
-
使用python 加载ui文件
#
from PySide2.QtUiTools import QUiLoader # 模块下只有一个类
from PySide2.QtCore import QFile, QObject
from PySide2.QtWidgets import QApplication, QWidget, QPushButton, QLabel
from PySide2.QtWidgets import QMainWindow, QWidget
class MyWindow():
def __init__(self):
# 封装文件对象
qfile = QFile("jack.ui")
# 打开文件
qfile.open(QFile.ReadOnly) # 只读方式打开
# 读取数据(字节串)
# l = qfile.readLine()
# print("读取的一行数据:", l)
# 关闭文件
qfile.close()
# 加载所有的文件内容,返回父窗口对象
# 子控件 成为父控件的属性
self.win = QUiLoader().load(qfile)
print("加载所有的文件:", self.win, type(self.win), self.win.inherits("QWidget"))
def show(self):
self.win.clearBtn.clicked.connect(lambda :print("点击了清空......"))
self.win.show()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
win = MyWindow()
win.show()
exit_code = app.exec_()
print("退出的状态码:", exit_code)
sys.exit(exit_code)
-
微调界面
“清除”按钮 缩小 居中
在按钮旁边再拖入一个水平布局,将清除按钮 加入水平布局
spacer间距
布局总结
-
先把所有的
子控件
,按照位置摆放好 - 从最内层,使用Layout布局,选中(所有相关)子控件>右键>布局
- 从内依次到外,设置Layout
-
最后调整Layout相关边距、伸缩比例等。
案例练习
1.先把子控件放到窗口
2.从内到外,选中控件,右键-布局
3.两个QGroupBox是两个独立的容器,先分别各自实现布局。
4.两个容器所在的父控件,直接右键-布局
5.布局完成,即可随着窗口的变化而缩放,然后微调
发布exe程序
将python程序打包为.exe程序,这里使用
pyinstaller
打包
安装pyinstaller
:
$ pip install pyinstaller
$ pyinstaller --version
$ pyinstaller --help
打包单个python脚本
:
# -F 打包成一个可执行文件
# -w windows 平台
# --noconsole 程序运行时没有控制台
# --hidden-import 动态导入 (打包前导入)
# --hidden-import csv --hidden-import pathlib
# --icon=程序打包后的图标,需要是.ico文件
pyinstaller -F -w main.py --noconsole --hidden-import PySide2.QtXml --icon='xxxx.ico'
# 注意 此时的静态文件并不会打包到.exe,如sqlite.db or ui文件等
# 需要手动复制到dist下的相应目录
打包整个项目
:
- 将整个项目根目录配置到PYTHONPATH;
- 简单打包项目的入口文件即可;
- 不在python搜索路径中的包,还可以通过-p参数指定;
# 简单打包
pyinstaller -F -w --noconsole main.py -p 其他的包(python路径搜索不到)
打包静态文件
- –add-data “data.db;.” 打包数据文件到入口文件的同级目录
- 打包后,在应用程序内调用静态资源,需要补充如下代码
if getattr(sys, 'frozen', None):
basedir = sys._MEIPASS
else:
basedir = os.path.dirname(__file__)
# 调用
db_path = os.path.join(basedir, 'data.db')
关联组
相关联的单选、多选放入一个容器组,如QGroupBox、QWidget等
QTabWidget使用
- 使用Qt designer直接拖入
- 在tab页上右键,添加页、删除页
-
点击一个tab页,可以编辑其属性,可以Ctrl + 1 水平布局等
Qss
Qt Style Sheet 设置GUI 样式
Qt Style Sheet 官网
伪类选择器
# 按钮为禁用状态时的样式
QPushButton:disabled{
color: cyan;
}
# 鼠标悬浮 且为勾选状态时 的样式
QCheckBox:hover:checked {
color: red;
}
常用属性
# 背景色
background-color:red;
background-image: url(./imgs/dog.jpg)
# 边框
border: 1px solid red;
border: none;
width: 200px;
height: 100px;
font-family: 黑体;
font-weight: bold;
font-style: italic;
font-size: 16px;
margin: 0;
padding: 10px;
子线程
可以使用python内置模块threading,也可以使用Qt封装的QThread;
如果子线程处理的任务与Qt相关,则使用QThread
简单使用
# 耗费时间的任务
def task(t):
print("耗费时间的任务")
time.sleep(t)
print("任务结束...")
# 槽函数,内部创建子线程
def myFunc():
print("开启子线程")
thread = Thread(target=task, args=(10,), name="task1")
# 开启子线程,不用等待子线程执行结束
thread.start()
# 按钮绑定槽函数
btn.clicked.connect(myFunc)
子线程完成,发送信号,更新界面
,而不是直接更新界面。
在子线程中,更新界面会发生意外错误,如主界面崩溃等
- 自定义类,继承(QObject)
- 实例化对象,并通过信号绑定槽函数
- 子线程中,使用emit触发信号
- 主线程更新界面
from PySide2.QtCore import QObject, Signal
# 定义自己的类
class MySignal(QObject):
# 类属性是一个信号对象, 并标识传递的数据类型
s1 = Signal(str)
# 具体使用
class Ui_Form(object):
def __init__(self):
super(Ui_Form, self).__init__()
# 实例化信号对象,并绑定槽函数
self.sig = MySignal()
self.sig.s1.connect(self.updateGui)
def updateGui(self, *args, **kwargs):
# 主线程更新
self.pushButton.setText(args[0])
def setupUi(self, Form):
if not Form.objectName():
Form.setObjectName(u"Form")
Form.setEnabled(True)
Form.resize(1153, 614)
self.pushButton = QPushButton(Form)
self.pushButton.setObjectName(u"pushButton")
self.pushButton.setGeometry(QRect(510, 120, 93, 28))
self.pushButton.clicked.connect(self.myFunc)
self.radioButton = QRadioButton(Form)
self.radioButton.setObjectName(u"radioButton")
self.radioButton.setGeometry(QRect(510, 200, 115, 19))
self.retranslateUi(Form)
QMetaObject.connectSlotsByName(Form)
# setupUi
def retranslateUi(self, Form):
Form.setWindowTitle(QCoreApplication.translate("Form", u"Form", None))
self.pushButton.setText(QCoreApplication.translate("Form", u"\u70b9\u51fb", None))
self.radioButton.setText(QCoreApplication.translate("Form", u"RadioButton", None))
# retranslateUi
# 槽函数,内部创建子线程
def myFunc(self):
# 耗费时间的任务
def task(t):
print("耗费时间的任务")
for i in range(10):
print("任务执行中...{}".format(i))
# 发送信号给主线程,更新界面
self.sig.s1.emit("{}".format(i))
time.sleep(1)
print("开启线子程")
thread = Thread(target=task, args=(10,), name="task1")
thread.start()
PySide2 可视化