pyqt5界面开发一些笔记
给ui类添加信号插槽和其他功能
一般ui类通过 qtdesigner 来设计,然后用 pyuic 来讲ui文件转成py代码,此时如果在py代码中添加对应的插槽函数功能等,会存在一个问题,因为ui文件经常改,每次pyuic转换都会覆盖之前的,之前写的插槽函数等就白写了。
正确的做法是,另外写一个相同但有不同后缀名字的py文件,然后在这个新的py文件中使用类继承的方式类创建新类,在新类中填写插槽函数等功能。
如下所示:
# 1、首先用qtdesinger设计ui,得到 demo_ui.ui
# 2、新建类文件,demo_win.py
from PyQt5.QtWidgets import QMainWindow
from PyQt5.uic import loadUiType
UI_CLASS, _ = loadUiType('demo_ui.ui')
class MainApp(QMainWindow, UI_CLASS):
def __init__(self): # 定义构造方法
QMainWindow.__init__(self)
# setupUi生成ui后,就可以添加按钮事件等的功能了
self.setupUi(self)
self.btn.clicked.connect(self.btn_func) # 此处给按钮添加插槽函数
def btn_func(self):
do_something
子窗口暂停主窗口
创建Qdialog窗口,等待Qdialog完成用户输入或确认,然后继续执行主窗口的函数,否则就会是类似非阻塞线程的工作模式了。如下所示:
class TemplateInputDailog:
弹出输入框或确认对话框,让用户输入,阻塞主进程
def __init__(self):
self.dialog = QDialog() # 注意是Qdialog,而不是QInputDialog
self.dialog.setWindowModality(Qt.ApplicationModal) # 阻塞主进程等待用户输入完成
self.dialog.setWindowTitle(title) # 设置标题
self.layout = QFormLayout(self.dialog) # 使用layout排版,并附着在dialog窗体中
# 添加输入框
self.label = QLabel(label_str)
self.input = QLineEdit()
self.layout.addRow(self.label, self.input)
# 确认或取消按钮
self.check_btn = QPushButton('确认')
self.close_btn = QPushButton('取消')
self.check_btn.clicked.connect(self.check_btn_func)
self.close_btn.clicked.connect(self.close_btn_func)
self.layout.addRow(self.check_btn, self.close_btn)
self.dialog.exec_() # 注意此处用exec_方法
子界面给主界面发送信号(跨界面通信)
这应该是很常见的需求了,当然也不是那么好理解的。一般来说跨进程通信可以使用队列什么的,但是在界面编程中,如果使用队列,就需要消费者对队列进行监听,此处容易造成主程序卡死?
在Qt中,正确的方法是使用信号与槽,这也是Qt的核心精华所在,使用信号与槽功能可以使得事件编程变得非常非常简单。但是信号与槽要理解起来也有点困难,特别是在Qt编程中,主要是具体写法可能会造成不工作但也不知道为什么。
关于信号与槽,简单来说就是 回调函数 。信号就是A发生了一个操作,比如点击按钮,槽函数就是回调函数,比如按钮的回调函数是关闭窗口、打开对话框等。
在Qt中有很多内置信号,比如点击按钮、点击列表元素、拖动进度条、内容改变监测等,也有很多槽函数,比如关闭窗口。
同界面信号与槽函数1
这个比较简单,直接在界面(控件)类中定义按钮的信号,在类中直接定义回调函数即可,比如下面的例子:
from PyQt5.uic import loadUiType
Ui_MainWindow, _ = loadUiType('Ui_MainWindow.ui')
class MyWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MyWindow, self).__init__(parent)
self.setupUi(self)
# 定义按钮连接的槽函数
self.button_name.clicked.connect(self.button_callback_func)
def button_callback_func(self):
# 定义槽函数的内容
pass
跨界面信号与槽
跨界面信号与槽比较难,但是掌握关键点也很简单的,那就是:
(1)信号函数需要定义成一个全局变量(或子界面可以调用的变量)
(2)且不能是主界面的self变量
(3)子界面是主界面的self属性,这样子界面才能调用主界面的信号属性
说的比较难懂,看下面的例子:
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QMainWindow, QWidget
from PyQt5.uic import loadUiType
# 导入用户ui文件
Ui_MainWindow, _ = loadUiType('Ui_MainWindow.ui')
class subWindow(QWidget):
# 在子界面 subWindow 中定义信号,注意不能在 __init__ 中定义成 self.signal
# 如果是pyside2库,则是 signal = Signal(str)
signal = pyqtSignal(str)
def __init__(self, parent=None):
super(subWindow, self).__init__(parent)
# 定义子界面的按钮事件
self.button_name.clicked.connect(self.button_callback_func)
def button_callback_func(self):
"""点击按钮,发射信号"""
self.signal.emit("子界面发送信号")
class mainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(mainWindow, self).__init__(parent)
self.setupUi(self)
# 此处需要在主界面的 __init__ 函数中实例化子界面,将子界面当做主界面的属性来管理
self.subwin = subWindow()
# 实例化子界面后,就可以指定子界面的信号对应的槽函数了
self.subwin.signal.connect(self.signal_callback_func)
def signal_callback_func(self, param):
# 定义槽函数,注意此函数带有一个参数 param,即信号发送过来的数据
do_something(param)
pyside6中的信号与槽用法
pyside6中的信号与槽的语法会严谨一些,不过用起来也是差不多的。
from PySide6.QtCore import QStringListModel, Slot, Signal
from PySide6.QtWidgets import QMainWindow, QWidget
from PySide6.uic import loadUiType
# 导入用户ui文件
Ui_MainWindow, _ = loadUiType('Ui_MainWindow.ui')
class subWindow(QWidget):
# 在子界面 subWindow 中定义信号,注意不能在 __init__ 中定义成 self.signal
signal_name = Signal(str)
def __init__(self, parent=None):
super(subWindow, self).__init__(parent)
# 定义子界面的按钮事件
self.button_name.clicked.connect(self.button_callback_func)
def button_callback_func(self):
"""点击按钮,发射信号"""
self.signal_name.emit("子界面发送信号")
class mainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(mainWindow, self).__init__(parent)
self.setupUi(self)
# 此处需要在主界面的 __init__ 函数中实例化子界面,将子界面当做主界面的属性来管理
self.subwin = subWindow()
# 实例化子界面后,就可以指定子界面的信号对应的槽函数了
self.subwin.signal.connect(self.signal_callback_func)