PyQt5

本文介绍了PyQt5的安装,展示了基础框架的创建。通过实例解释了多线程在GUI编程中的重要性,包括主线程和子线程的交互,以及如何避免界面卡顿。还提到了QtDesigner工具的使用,以及如何进行UI和逻辑的分离。此外,文章讨论了加载资源、计时器的使用、程序打包以及页面跳转的方法,最后涉及了类的继承和setupUi的调用。
摘要由CSDN通过智能技术生成

PyQt5

安装

安装PyQt5,PyQt5-tools

pip install PyQt5 -i https://pypi.douban.com/simple
pip install PyQt5-tools -i https://pypi.douban.com/simple

框架

import sys

from PyQt5.QtWidgets import QApplication,QWidget

if __name__ == '__main__':
	# 1.创建程序 
    app = QApplication(sys.argv) # 打印sys.argv输出['D:/Workspace/yolov5-6.0/testqt.py']
    # 2.创建界面对象
    w = QWidget()
    # 3.设置对象属性
    w.setWindowTitle("第一个qt")
    # 4.展示界面
    w.show()
    # 5.程序循环等待,监听有无事件发生
    app.exec_()

如果删除掉w.show()后运行程序,会有一个程序在运行但是没有任何界面。

Qt Designer

到下面这个文件夹中将designer.exe发送到桌面,以后.ui文件都可以用它打开
在这里插入图片描述

入门教程

教程

多线程

推荐阅读:
九、PyQt5多线程编程

1. 介绍

背景:用户在点击按钮后,希望界面做出较快反应,最讨厌出现未响应状态。出现未响应说明当前线程在执行一个耗时操作,该操作完不成,界面也不能更新。
问题再现:现在使用sleep()模拟耗时操作,如果将其放在主线程中,我们点击按钮1,那么会出现:
在这里插入图片描述
此时按钮2点击不了,也不能关闭界面,整个界面无法操作。

原因分析:点击按钮1触发的槽函数是:

def click_1(self):
    for i in range(10):
        print("是UI线程中执行....%d" % (i + 1))
        time.sleep(1)

运行python程序时,os为其分配一个线程,click_1就运行在这个主线程中,函数没有运行完,其他都操作不了。

对比:先点击按钮2,再点击按钮1,会出现:
在这里插入图片描述
已经定义了自己的线程类:

class MyThread(QThread):
    def __init__(self):
        super().__init__()

    def run(self):
        for i in range(10):
            print("是MyThread线程中执行....%d" % (i + 1))
            time.sleep(1)

按钮2对应槽函数为:

def click_2(self):
    self.my_thread = MyThread()  # 创建线程
    self.my_thread.start()  # 开始线程

继承于专门的线程类,系统可以进行线程调度,不会出现一个线程一直占用cpu的情况,所以此时再点击按钮1后,两个任务交替执行。

解决方法:在实际开发中,负责UI展示,刷新的主线程要与耗时子线程分开,主线程负责刷新界面,展示界面。子线程负责耗时操作,如:网络交互、磁盘IO等。主子线程各司其职,保证系统正常运行,提升整体用户体验。

2. 主线程(直接操控ui)和子线程之间传参

参考:
6. PyQt5 中的多线程的使用(上)
7. PyQt5 中的多线程的使用(下)

数据的相互传递分为ui界面到任务线程和任务线程到ui界面。

下面是二者案例:

from form import Ui_Form
from PyQt5.QtWidgets import *
from PyQt5.QtCore import QThread, pyqtSignal
import sys
import time

class MyWin(QWidget,Ui_Form):
    """docstring for Mywine"""
    def __init__(self):
        super(MyWin, self).__init__()
        self.setupUi(self)
        self.mythread = MyThread() # 实例化自己建立的任务线程类
        self.mythread.signal.connect(self.callback) #设置任务线程发射信号触发的函数

    def test(self): # 这里test就是槽函数, 当点击按钮时执行 test 函数中的内容, 注意有一个参数为 self
        self.mythread.data = 5 # 这句就是给线程的实例化一个属性给其赋值,在线程里面就可以调用了
        self.mythread.start() # 启动任务线程

    def callback(self,i): # 这里的 i 就是任务线程传回的数据
        self.pushButton.setText(i)

class MyThread(QThread): # 建立一个任务线程类
    signal = pyqtSignal(str) #设置触发信号传递的参数数据类型,这里是字符串
    def __init__(self):
        super(MyThread, self).__init__()

    def run(self): # 在启动线程后任务从这个函数里面开始执行
        print(self.data) # 调用传递过来的数据

if __name__ == '__main__':
    app = QApplication(sys.argv)
    mywin = MyWin() # 实例化一个窗口小部件
    mywin.setWindowTitle('Hello world!') # 设置窗口标题
    mywin.show() #显示窗口
    sys.exit(app.exec())

3.子线程与子线程之间传参

什么是主线程:

pyqt的主界面使用的是主线程,可以看做是一个死循环。一旦主线程中产生了较为耗时的操作,将导致主线程出现假死的现象,体现在GUI界面就是无响应和无法进行任何操作。

在进行GUI程序设计时一般遵循GUI界面和代码界面分开设计的原则,主线程只负责管理基本GUI的动作,而耗时的操作则通过子线程进行计算。

所有信号与槽函数的连接都必须在主线程中完成。
具体方法是:在主线程中创建子线程实例,将子线程作为主线程的成员,如此可以实现子线程与子线程,子线程与主线程之间的信号传递。

案例:
PyQt5 GUI 接收UDP数据并动态绘图(多线程间信号传递)
PyQt5学习笔记:子线程与子线程之间数据传输,利用主线程实现(包括主线程传给子线程参数实现)

界面和逻辑分离

分离,最主要的就是使用界面文件的方式:PyQt加载.ui文件的四种方法

总结一下,我们pyqt5其实主要讨论下面这两种方法

一、方法一(传统)

借鉴

  1. 使用QtDesigner进行界面设计,保存为xxx.ui文件
  2. 在Pycharm中使用PyUIC生成xxx.py文件
  3. 编写代码调用生成的xxx.py

方法一的两种初始化方法

二、方法二(现代)

  1. 使用QtDesigner进行界面设计,保存为xxx.ui文件
  2. 在.py文件中直接调用.ui文件运行

三、对比

【PyQt】pyqt加载调用ui界面文件的两种方法
方法1:使用pyuic编译ui文件
优点:

  1. 允许继承,和普通类一样
  2. 运行程序时没有额外的负载

缺点:

  1. 每次修改ui文件时都得手动将 .ui 编译为 .py

方法2:在代码中使用loadUi直接加载ui文件

优点:

  1. 修改ui时py代码无需修改
  2. 编译额外时间

缺点:

  1. 不允许继承
  2. PyCharm中无法使用代码检查和自动补全
  3. 使用 uic.loadUi() 简单粗暴,不用编译 .ui文件,直接就可以加载调用,相当于是把 .ui 文件当做了一个资源文件。所以在发布软件的时候,原始的.ui文件就必须和.exe一起发布,就像img等资源文件一样,所以要考虑而能否直接将 .ui 文件 发布给用户(用户可能会破坏该文件),需要仔细考虑。

界面使用designer设计,逻辑通过读取.ui获取界面属性。这样频繁改动style不会影响逻辑代码。
早期的方式是:利用QtDesigner来设计界面,再通过批处理脚本pyuic5.bat将ui文件转换成python源文件。不过由于要响应事件操作,往往会将相应的槽函数写在ui的py文件里(前面的示例就是这样),这样,界面和逻辑的开发代码就混合在一起了,每一次的ui的更新都会伴随着转换后py文件的修改,很不合理。

PyCharm设置Qt-Designer、PyUIC、PyRcc外部工具

PyCharm安装PyQt5及其工具(Qt Designer、PyUIC、PyRcc)详细教程
理解了界面和逻辑分离,并进行了一系列对比是不是发现还是方法一(传统)比较好,如果使用该方法,就需要下面三板斧。
在这里插入图片描述
[PyCharm安装PyQt5及其工具(Qt Designer、PyUIC、PyRcc)详细教程]

加载图片的方法

PyQt5 资源加载总结

计时器

	...
	self.timer = QTimer(self)
	self.count = 0
	self.timer.timeout.connect(self.showNum)
	self.startCount()
	
def startCount(self):
	self.timer.start(1000)
	
def showNum(self):
	self.count = self.count + 1
	print(self.count)

timer走完1秒后会发出timeout信号,触发槽函数showNum

打包

如果使用loadui方式,需要按照代码相对路径放置资源文件

页面跳转

pyqt 主界面控制切换窗口方法
页面跳转controller法

python有关super.__init__()

Python中调用父类方法的三种方式
Python多继承supper调用父类MRO顺序
python中super().init()

继承父类和setupui

Python & PyQt学习随笔:PyQt主程序的基本框架
为什么要调用setupui

根据UI类派生一个子类
在主程序中,需要根据UI对应类以及QtWidgets派生一个新类,在该新类中实现所有槽函数的代码。
关于派生的新类请注意:

  1. 一定要有两个基类,一个是UI界面窗口的窗口类,一个是UI类本身;
  2. 一定要实现新类的构造方法,并在构造方法中调用父类的构造方法;
  3. 新类的构造方法中要调用self.setupUi(self) setupUi为PyUIC生成的UI类图形界面初始化的重要函数。

案例:

class w_ReadExecl(QtWidgets.QWidget,Ui_Ui_tableView):#派生一个新类
    def __init__(self): #新类构造函数,必须有
        super(w_ReadExecl, self).__init__() #调用父类构造函数,必须有
        self.setupUi(self)  #进行图形界面初始化,必须有
              self.tableView.setEditTriggers(QtWidgets.QAbstractItemView.DoubleClicked | QtWidgets.QAbstractItemView.SelectedClicked)

    def showExcel(self): #按钮的槽函数
        filename = self.e_InputFileName.text()
        sheetname = self.inputSheetName.text()

        hashead = self.inputHasHead.isChecked()
        print(f"即将显示{filename}.[{sheetname }]"
		........

装载ui.py的两种方式

from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import QApplication, QWidget
from ui_calc import Ui_Calc
# 方式一
class MyCalc(QWidget):
	def __init__(self, parent=None):
		super().__init__(parent)
		self.ui = Ui_Calc()
		self.ui.setupUi(self)
	
	@pyqtSlot(int)
	def on_inputSpinBox1_valueChanged(self, value):
		self.ui.outputWidget.setText(str(value + self.ui.inputSpinBox2.value()))
	
	@pyqtSlot(int)
	def on_inputSpinBox2_valueChanged(self, value):
		self.ui.outputWidget.setText(str(value + self.ui.inputSpinBox1.value()))

# 方式二
class MyCalc2(QWidget, Ui_Calc):
	def __init__(self, parent=None):
		super().__init__(parent)
		self.setupUi(self)
	
	@pyqtSlot(int)
	def on_inputSpinBox1_valueChanged(self, value):
		self.outputWidget.setText(str(value + self.inputSpinBox2.value()))

	@pyqtSlot(int)
	def on_inputSpinBox2_valueChanged(self, value):
		self.outputWidget.setText(str(value + self.inputSpinBox1.value()))

if __name__ == '__main__':
	import sys
	app = QApplication(sys.argv)
	win = MyCalc()
	# win = MyCalc2()
	win.show()
	sys.exit(app.exec_())
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值