python3 pyside6图形库学习笔记及实践(一)

前言

本系列文章为b站PySide6教程以及官方文档的学习笔记

原视频传送门:【已完结】PySide6百炼成真,带你系统性入门Qt

官方文档链接:Qt for Python

基础框架

我们来实现一个最简单的窗口,并借由其代码来初步认识pyside6的结构

from PySide6.QtWidgets import QApplication, QMainWindow

class MyWindow(QMainWindow):
    def __init__(self):
        super().__init__()

if __name__ =="__main__":
    app =QApplication()
    window = MyWindow()
    window.show()
    app.exec()

首先是导入的QApplicationQMainWindow类,这些类是用于创建 GUI 应用程序的基本类。

然后我们从QMainWindow类继承我们自己的窗口类Mywindow,这个类将用于创建应用程序的主窗口,此时类中只调用了父类的构造函数。

主程序中则创建了QApplicationMyWindow类的实例,QApplication 是一个必需的类,它管理应用程序的控制流和主要设置。

window.show()用于显示MyWindow 实例,这将使窗口可见并允许用户与它进行交互

app.exec()用于启动应用程序的事件循环。事件循环是一个无限循环,它等待用户输入和系统事件,并相应地更新应用程序的状态。

这段代码的运行效果如下

请添加图片描述

基础控件

一般来说一个应用程序的运行逻辑无非是用户输入->用户交互->输出

那么这就涉及到三种最基本的控件:按钮、标签和输入框

想要给窗体添加控件,需要在窗体类的构造函数中添加控件实例

QPushButton

该控件需要从PySide6.QtWidgets导入

from PySide6.QtWidgets import QPushButton
btn = QPushButton("Click me", self)

但是光一个控件肯定不行,我们还需要设置它的一些属性,来满足高级需求

事实上,当我们想了解一个控件有哪些属性,以及这些属性分别有什么功能时,可以在Qt Designer上进行测试

当我们配置好vscode中的扩展插件PYQT Integration后,只需在文件上右键就能快速打开Qt Designer

请添加图片描述

我们只需拖动一个部件到窗体上,即可在右侧窗口查看并调试它的一些属性

请添加图片描述

这里列出几个PushButton常用的属性

属性作用
geometry(几何)坐标位置、尺寸大小
text按钮上显示的文字
toolTip鼠标放在按钮上时显示的提示文字

想要为控件实例设置属性,需要调用set+属性名的方法

如下示例

from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton

class MyWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        btn = QPushButton("Click me", self)
        btn.setGeometry(100, 100, 200, 50) #设置(x,y)坐标为(100,100),而宽高分别为200和50
        btn.setText("new text") #重新设置的文字会覆盖初始化时的文字
        btn.setToolTip("tips")

QLabel

该控件需要从PySide6.QtWidgets导入

from PySide6.QtWidgets import QLable
lb = QLable("Hello", self)

下面是一些常用的标签特有属性

属性作用
text标签上显示的文字
textFormat如PlainText、MarkdownText和RichText形式
alignment文本对齐方式
pixmap显示图片

QLineEdit

该控件需要从PySide6.QtWidgets导入

from PySide6.QtWidgets import QLineEdit
input = QLineEdit("框中预留文字", self)

下面是一些常用的输入框特有属性

属性作用
maxLength最大输入长度
readOnly是否设置为只读模式
placeholderText框中无任何输入时显示的文字
pixmap显示图片

初识QtDesigner

制作一个简单页面

登录框

首先我们需要考虑页面中会出现哪些种控件

一般来说登录页面会需要输入账号密码,所以会需要输入框

而提示以及提交则需要标签和按钮控件

将这些元素拖拽入窗口中,并进行初步的属性设置,我们就能得到一个简易的窗口模板

请添加图片描述

此时按Ctrl+R或者点击窗体>预览,我们就能预览当前窗口的效果

请添加图片描述

请添加图片描述

我们会发现窗口的标题还是默认的,我们可以直接去设置窗体本身的属性

请添加图片描述

当设计完毕后,我们可以将设计文件保存为.ui后缀的文件

请添加图片描述

计算器

同样我们也能拖拉出一个计算器的UI,这些过程能让我们逐渐熟悉QtDesigner的操作

请添加图片描述

编译UI文件

在QtDesigner中设计好了界面UI后,只能对其进行预览

如果想在程序中运行并显示我们设计的UI,则需要进一步将其编译为python源码,即py文件

我们可以之间执行如下指令(在安装pyside6的python环境下)

pyside6-uic xxx.ui -o xxx.py

或者在vscode中,我们可以利用插件PYQT Integration的功能

右键ui文件,选择PYQT:Compile Form即可

请添加图片描述

使用编译得到的py文件

上一步中,我们通过编译login.ui文件得到了UI_login.py的py源码

窗口文件在一个叫Ui_Form的类中

请添加图片描述

两种方法在其他的程序中调用这个生成的窗口UI

  1. 在需要调用的地方创建一个该对象的实例

    from Ui_login import Ui_Form
    class MyWindow(QWidget):
        def __init__(self):
            super().__init__()
            self.login = Ui_Form()
            self.login.setupUi(self)
    

    setupUi为我们生成的UI的类中的函数,参数需要将我们当前的窗体传进去,这里我们直接传self

    需要注意的是我们的MyWindow继承的窗口类型需要与UI文件的一致

  2. 第二种方法则是利用python多继承的特性,即我们的窗口可以继承多个类

    将Ui_Form也作为我们窗口的父类

    from Ui_login import Ui_Form
    class MyWindow(QWidget,Ui_Form):
        def __init__(self):
            super().__init__()
            self.setupUi(self)
    

信号与槽

概念

PYQT界面的交互需要依靠信号与槽,类似于其他图形界面编程中的事件响应

事件响应机制的图形界面会不断地update,来检测页面中是否有什么元素发生了变化

而信号与槽的机制中,只有界面元素发出信号给相应的槽,页面才会进行修改

信号 (Signals)

  • 定义:信号是PySide6(和Qt)中的一个关键概念,是从对象发送的消息,表明发生了某种事件或状态变化。
  • 特点:信号不包含处理逻辑,它们只负责通知事件的发生。

槽 (Slots)

  • 定义:槽是用来接收信号的方法。当与信号相连的特定事件发生时,相应的槽函数会被调用。
  • 特点:槽可以是任何可调用的Python函数或方法。

信号与槽 vs 事件触发响应

事件触发响应

  • 机制:基于事件循环,当用户进行操作(如点击、键入)时,事件被生成并放入事件队列,然后由应用程序逐个处理。
  • 应用:通常用于处理用户输入、窗口变化等。
  • 优点
    • 直观性:事件处理通常更直观,易于理解。
    • 控制性:可以在事件处理中有更多控制,如事件过滤。
  • 缺点
    • 紧耦合:事件处理函数通常与特定控件或场景紧密相关。
    • 处理复杂性:对于复杂的交互,事件处理可能变得复杂和冗长。

信号与槽

  • 机制:基于信号的发送和槽的接收,更侧重于对象间的通信。
  • 应用:适合于不同组件间的通信,例如,一个组件的行为触发另一个组件的反应。
  • 优点
    • 解耦:发信者和接收者不需要知道彼此的存在。
    • 灵活性:可以连接多个槽到一个信号,或将一个槽连接到多个信号。

示例

from PySide6.QtWidgets import QApplication, QPushButton

app = QApplication([])

# 创建一个按钮
button = QPushButton("Click me")

# 定义槽函数
def on_button_clicked():
    print("Button clicked!")

# 将按钮的clicked信号连接到槽函数
button.clicked.connect(on_button_clicked)

button.show()
app.exec()

在这个例子中,当按钮被点击时,clicked信号被发出,然后on_button_clicked槽函数被调用。

我们通过.connect将信号与槽连接起来

实践

完善登录框

为了了解我们之前设计的UI中各控件的对象名,我们可以回到QtDesigner中查看

请添加图片描述

例如第一个输入框,它被自动命名为lineEdit,那么我们在代码中就能通过self.lineEdit调用它

from PySide6.QtWidgets import QApplication, QWidget, QLineEdit
from Ui_login import Ui_Form
class MyWindow(QWidget,Ui_Form):
    def __init__(self):
        super().__init__()

        self.setupUi(self)
        self.pushButton.clicked.connect(self.loginFuc)
    def loginFuc(self):
        username = self.lineEdit.text()
        password = self.lineEdit_2.text()
        if username =="admin" and password =="123456":
            print("登录成功")
        else:
            print("登录失败")
if __name__ =="__main__":
    app =QApplication()
    window = MyWindow()
    window.show()
    app.exec()

在代码中,我们让之前的UI界面有了响应,即用户名和密码输对时会在控制台输出"登录成功",否则输出"登陆失败“

完善计算器

我们来对之前的计算器界面重新布局一下

布局的好处是缩放界面时,控件的位置与大小也能自动的做出相应调整

请添加图片描述

对每一行按钮水平布局后,我们再对整体进行垂直布局

请添加图片描述

此时的控件还没有与窗口的位置形成相对关系,无法对页面缩放做出响应

请添加图片描述

我们将页面整体改为垂直布局,并适当调整最上方输入框的高度

请添加图片描述

此时我们就能得到一个较为整齐的计算器界面

为了后续在代码中更清晰地编写信号与槽的逻辑,我们需要对页面控件重新命名

请添加图片描述

例如.按钮,我们将其命名为pushButton_dot

那么计算器的逻辑中我们没有必要绑定太多槽

一个思路是先利用其他按钮的信号生成算式的字符串,每次调用槽的时候刷新输入框的显示

def addNumber(self,number):
    self.lineEdit.clear()
    self.expression+=number
    self.lineEdit.setText(str(self.expression))

在代码中,我们新建一个bind()函数来记录绑定关系,然后在初始化函数中一并调用bind即可

这样能保证初始化函数的简洁性

def bind(self):
    self.pushButton_0.clicked.connect(lambda:self.addNumber('0'))
    self.pushButton_1.clicked.connect(lambda:self.addNumber('1'))  
    self.pushButton_2.clicked.connect(lambda:self.addNumber('2'))
    self.pushButton_3.clicked.connect(lambda:self.addNumber('3'))
    self.pushButton_4.clicked.connect(lambda:self.addNumber('4'))
    self.pushButton_5.clicked.connect(lambda:self.addNumber('5'))      
    self.pushButton_6.clicked.connect(lambda:self.addNumber('6'))
    self.pushButton_7.clicked.connect(lambda:self.addNumber('7'))
    self.pushButton_8.clicked.connect(lambda:self.addNumber('8'))
    self.pushButton_9.clicked.connect(lambda:self.addNumber('9'))
    self.pushButton_add.clicked.connect(lambda:self.addNumber('+'))
    self.pushButton_sub.clicked.connect(lambda:self.addNumber('-'))
    self.pushButton_mul.clicked.connect(lambda:self.addNumber('*'))
    self.pushButton_div.clicked.connect(lambda:self.addNumber('/'))
    self.pushButton_dot.clicked.connect(lambda:self.addNumber('.'))
    self.pushButton_enter.clicked.connect(self.count)

{% note info %}
在 Python 编程语言中,lambda 关键字用于创建匿名函数,这种函数称为 lambda 函数。Lambda 函数可以接受任何数量的参数,但只能有一个表达式。它们通常用于需要函数对象的地方,但又不想在代码中定义完整的函数。Lambda 函数的基本语法如下:

lambda arguments: expression

{% endnote %}

在绑定信号时如果我们直接写成:

self.pushButton_0.clicked.connect(self.addNumber('0'))

这样当python解释器读到这一行代码时会立即执行 self.addNumber('0') ,这并不是我们想要的。我们希望的是,每次按钮被点击时,才调用 self.addNumber('0')。我们需要传递一个函数对象给connect

为了解决这个问题,我们使用 lambda 创建了一个匿名函数,这个匿名函数没有参数,并在被调用时执行 self.addNumber('0')。这样,每次按钮被点击时,实际上是调用这个匿名函数,然后这个匿名函数再去调用 self.addNumber('0')

{% note primary %}

使用 lambda 这种方式使得我们可以在不创建额外的命名函数的情况下,传递带有参数的方法作为信号的槽函数。这

{% endnote %}

当最后当点击计算按钮时,借助python中的eval()函数,我们能将生成的字符串变为算式

def count(self):
    self.result = eval(self.expression)
    self.lineEdit.setText(str(self.result))
    self.expression = str(self.result)

当然我们也可以为计算器加上清空和回退的功能

self.pushButton_clear.clicked.connect(self.clear)
self.pushButton_back.clicked.connect(self.back)

......

def clear(self):
    self.lineEdit.clear()
    self.expression = ''
        
def back(self):
    self.lineEdit.clear()
    self.expression = self.expression[:-1]
    self.lineEdit.setText(str(self.expression))

最终的效果如下

请添加图片描述

可以使用 Python 3 和 PySide6 库来编写支持异步操作的 GUI 应用程序。其中,asyncio 是 Python 的异步编程库,而 PySide6 则是 Qt 库的 Python 绑定。 在使用 PySide6 开发 GUI 应用程序时,可以使用 asyncio 的 event loop 来支持异步操作。一般来说,可以使用 PySide6 的 QThread 类来创建新线程,然后在新线程中执行异步任务。另外,也可以使用 asyncio 库的协程来执行异步任务。 下面是一个使用 asyncio 和 PySide6 编写的简单 GUI 应用程序的示例: ```python import sys import asyncio from PySide6.QtCore import QThread, Signal from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel class WorkerThread(QThread): finished = Signal() def run(self): asyncio.run(self.do_work()) async def do_work(self): for i in range(10): await asyncio.sleep(1) print(f"Working... ({i+1}/10)") self.finished.emit() class MainWindow(QMainWindow): def __init__(self): super().__init__() self.label = QLabel("Click the button to start working.") self.label.setAlignment(Qt.AlignCenter) self.setCentralWidget(self.label) self.button = QPushButton("Start working") self.button.clicked.connect(self.start_working) self.setCentralWidget(self.button) self.worker_thread = WorkerThread() self.worker_thread.finished.connect(self.finish_working) def start_working(self): self.button.setEnabled(False) self.worker_thread.start() def finish_working(self): self.label.setText("Work is done!") self.button.setEnabled(True) if __name__ == '__main__': app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_()) ``` 在这个示例中,我们创建了一个继承自 QThread 的 WorkerThread 类,用于执行异步任务。WorkerThread 类中使用了 asyncio 库的 event loop 来执行异步任务。在执行异步任务时,我们使用了 asyncio.sleep() 方法模拟了一些耗时操作。 在 MainWindow 类中,我们创建了一个 QPushButton 对象来触发异步任务的执行。当用户点击按钮时,我们会禁用按钮,并启动 WorkerThread 线程来执行异步任务。当异步任务执行完成时,我们会恢复按钮的状态,并更新 QLabel 对象的文本来显示异步任务的执行结果。 这只是一个简单的示例,实际上,在编写更复杂的 GUI 应用程序时,还需要考虑更多的细节。例如,如何处理异步任务的异常、如何在 GUI 线程中更新界面等等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

handsomelky

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值