GUI编程--PySide2--简单使用全流程

文章目录

  • 官网
  • 第一个PySide2应用
  • 配置pyside2的界面设计师
  • 配置pyside2-uic
  • 案例练习
  • 布局总结
  • 发布exe程序
  • 关联组
  • QTabWidget使用
  • Qss
  • 子线程
  • PySide2 可视化

官网

官方文档

# 安装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应用

点击按钮随机切换人名。
GUI编程--PySide2--简单使用全流程_第1张图片

# __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
GUI编程--PySide2--简单使用全流程_第2张图片
GUI编程--PySide2--简单使用全流程_第3张图片
 
使用:
Tools>external tools> PySide2Designer 创建一个窗口如下图:
Ctrl + s 保存为xx.ui文件
Ctrl + r 预览
Ctrl + n 新建
Ctrl + o 打开
 
选中多个子控件或者父控件,进行布局:
Ctrl + 1 水平布局;
Ctrl + 2 垂直布局
Ctrl + 5 Grid布局
Ctrl + 0 解除布局

GUI编程--PySide2--简单使用全流程_第4张图片

 
保存后,产生的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)

GUI编程--PySide2--简单使用全流程_第5张图片

 
pycharm:
Ctrl + f 搜索
Ctrl + r 替换
断点 + debugger 可以查看相应变量
 
另外ui文件也可以转为python文件,需要使用uic,在python3的安装目录Scripts下有如下工具:
GUI编程--PySide2--简单使用全流程_第6张图片
将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目录,有如下的文件:
GUI编程--PySide2--简单使用全流程_第7张图片
将pyside2-uic.exe 配置到pycharm中即可,方式同designer.exe
Program: python目录/Scripts/pyside2-uic.exe
Arguments: $FileName$ -o $FileNameWithoutExtension$.py
Working directory: $FileDir$
 
 

案例练习

使用Qt designer 实现如下界面
GUI编程--PySide2--简单使用全流程_第8张图片
使用qt designer 设计布局

  1. 首先拖入子控件,并摆放整齐

  2. 设计布局,选中相关的控件,右键->布局->选择布局
    GUI编程--PySide2--简单使用全流程_第9张图片
     
    最后的整体垂直布局,没有红色框线,只需在Form上右键-布局即可。
    布局内的控件是一个整体,单个的控件不再支持手动拖拽。
    button缩放水平fixed、垂直fixed,水平布局可居中。
    单个label 水平布局也居中。
    请求头+按钮-按钮控件的sizePolicy 水平伸缩分别为4 1 1;两个按钮之间还可以放spacer,并调整宽度。
    最后添加spacer,调整控件间距,不要与控件一起选中,然后布局。
    伸缩:可选中单个子控件(设置sizePolicy),也可选中整个红线框(layoutStretch-3,2,3,…)综合设置各个子控件的伸缩比例。
    GUI编程--PySide2--简单使用全流程_第10张图片
     
    碰到的问题, 就是整体没有实现布局,所以窗口缩放时,内容控件不变,在父控件右键-布局即可
    GUI编程--PySide2--简单使用全流程_第11张图片

  3. 设计好布局,保存文件

  4. 使用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)
  1. 微调界面
    "清除"按钮 缩小 居中
    GUI编程--PySide2--简单使用全流程_第12张图片
    在按钮旁边再拖入一个水平布局,将清除按钮 加入水平布局
    GUI编程--PySide2--简单使用全流程_第13张图片
    GUI编程--PySide2--简单使用全流程_第14张图片
    GUI编程--PySide2--简单使用全流程_第15张图片
    spacer间距
    GUI编程--PySide2--简单使用全流程_第16张图片

布局总结

  1. 先把所有的子控件,按照位置摆放好
  2. 从最内层,使用Layout布局,选中(所有相关)子控件>右键>布局
  3. 从内依次到外,设置Layout
  4. 最后调整Layout相关边距、伸缩比例等。
     
     
    案例练习
    GUI编程--PySide2--简单使用全流程_第17张图片
     
    1.先把子控件放到窗口
    2.从内到外,选中控件,右键-布局
    3.两个QGroupBox是两个独立的容器,先分别各自实现布局。
    4.两个容器所在的父控件,直接右键-布局
    5.布局完成,即可随着窗口的变化而缩放,然后微调
    GUI编程--PySide2--简单使用全流程_第18张图片

 

发布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使用

  1. 使用Qt designer直接拖入
  2. 在tab页上右键,添加页、删除页
  3. 点击一个tab页,可以编辑其属性,可以Ctrl + 1 水平布局等
    GUI编程--PySide2--简单使用全流程_第19张图片
     

Qss

Qt Style Sheet 设置GUI 样式

Qt Style Sheet 官网
GUI编程--PySide2--简单使用全流程_第20张图片
伪类选择器

# 按钮为禁用状态时的样式
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)

子线程完成,发送信号,更新界面,而不是直接更新界面。
在子线程中,更新界面会发生意外错误,如主界面崩溃等

  1. 自定义类,继承(QObject)
  2. 实例化对象,并通过信号绑定槽函数
  3. 子线程中,使用emit触发信号
  4. 主线程更新界面
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 可视化

官网案例

你可能感兴趣的:(python,GUI,python,pyside2,gui)