信号与槽
信号和槽关联
信号(signal
)和槽(slot
)是Qt
的核心机制,通过建立信号和槽的连接可以实现对象之间的通信,当信号发射(emit
)时,连接的槽函数将会自动执行。
在Qt
中,每一个QObject
对象和PyQt
中所有继承自QWidget
的控件都支持信号与槽机制。
信号和槽通过QObject.signal.connect()
连接。
所以从QObject
类或其子类(如QWidget
)派生的类都能够包含信号和槽,当对象改变其状态时,信号就由该对象发射出去,槽用于接收信号,多个信号可以与单个槽进行连接,单个信号也可以与多个槽进行连接,一个信号可以连接另外一个信号,信号与槽的连接方式可以是同步的也可以是异步的,信号和槽构建了一种强大的控件机制。
在Qt
中,通过Qt
信号槽机制对鼠标或键盘在界面上的操作进行响应处理,如鼠标单击按钮的处理。
定义信号
PyQt
的内置信号是自动定义的,使用PyQt5.QtCore.pyqtSignal()
函数可以为QObject创建一个信号,使用pyqtSingnal()函数可以把信号定义为类的属性。
为QObject
对象创建信号
使用pyqtSingnal()
函数创建一个或多个重载的未绑定的信号作为类的属性,信号只能在QObject
的子类中定义
class foo(QObject):
valueChanged = pyqtSingnal([dict], [list])
信号必须在类创建时定义,不能在后续动态的添加。types
参数表示定义信号时参数的类型,name
参数表示信号的名字,该项缺少时使用类的属性名字。
使用pyqtSingnal()
函数创建信号,信号可以传递多个参数,并指定信号传递参数的类型。
为控件创建信号
自定义控件WinForm
创建一个btnClickedSignal
信号:
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class WinForm(QMainWindow):
btnClickedSignal = pyqtSingnal()
操作信号
使用
connect()
函数将信号绑定到槽函数上。使用
disconnect()
函数解除信号与槽的绑定。使用
emit()
函数可以发射信号。
信号与槽的应用
内置信号与槽的使用
内置信号的使用,是指在发射信号时,使用窗口控件的函数,而不是使用自定义函数。
通过QObject.signal.connect()
将一个的QObject
的信号连接到另外一个QObject
的槽函数。
内置信号和内置槽函数:
btn = QPushButton("关闭按钮")
bth.clicked.connect(self.close()) # 单击按钮触发内置信号clicked和内置槽函数self.close
将按钮对象内置的clicked
信号连接到槽函数close()
内置信号与自定义槽函数
以按钮关闭为例:
# -*- coding: utf-8 -*-
from PyQt5.QtWidgets import *
import sys
class Winform(QWidget):
def __init__(self,parent=None):
super().__init__(parent)
self.setWindowTitle('内置的信号和自定义槽函数示例')
self.resize(330, 50 )
btn = QPushButton('关闭', self)
btn.clicked.connect(self.btn_close) # 内置信号连接自定义槽函数
def btn_close(self): # 自定义槽函数
self.close()
if __name__ == '__main__':
app = QApplication(sys.argv)
win = Winform()
win.show()
sys.exit(app.exec_())
单击按钮时触发按钮内置信号clicked
,绑定自定义的槽函数btn_close()
自定义信号和内置槽函数
以关闭按钮为例:
# -*- coding: utf-8 -*-
from PyQt5.QtWidgets import *
from PyQt5.QtCore import pyqtSignal
import sys
class Winform(QWidget):
button_clicked_signal = pyqtSignal() # 自定义信号,不带参数
def __init__(self,parent=None):
super().__init__(parent)
self.setWindowTitle('自定义信号和内置槽函数示例')
self.resize(330, 50 )
btn = QPushButton('关闭', self)
btn.clicked.connect(self.btn_clicked) # 连接信号和槽
self.button_clicked_signal.connect(self.close) # 接收信号,连接到槽
def btn_clicked(self):
self.button_clicked_signal.emit() # 发送自定义信号,无参数
if __name__ == '__main__':
app = QApplication(sys.argv)
win = Winform()
win.show()
sys.exit(app.exec_())
单击按钮触发自定义信号button_clicked_signal
,连接内置槽函数self.close
自定义信号与槽函数的使用
在发射信号时,不使用窗口控件的函数,而是使用自定义函数(使用pyqtSingnal
类实例发射信号)
自定义信号与槽函数可以灵活地实现一些业务逻辑。
按钮关闭案例:
# -*- coding: utf-8 -*-
from PyQt5.QtWidgets import *
from PyQt5.QtCore import pyqtSignal
import sys
class Winform(QWidget):
button_clicked_signal = pyqtSignal() # 自定义信号,不带参数
def __init__(self,parent=None):
super().__init__(parent)
self.setWindowTitle('自定义信号和槽函数示例')
self.resize(330, 50 )
btn = QPushButton('关闭', self)
btn.clicked.connect(self.btn_clicked) # 连接信号和槽
self.button_clicked_signal.connect(self.btn_close) # 接收信号,连接到自定义槽函数
def btn_clicked(self):
self.button_clicked_signal.emit() # 发送自定义信号,无参数
def btn_close(self): # 自定义槽函数
self.close()
if __name__ == '__main__':
app = QApplication(sys.argv)
win = Winform()
win.show()
sys.exit(app.exec_())
单击按钮时触发自定义信号button_clicked_signal
,绑定自定义的槽函数btn_close()
其他自定义信号和自定义槽函数案例:
# -*- coding: utf-8 -*-
from PyQt5.QtCore import QObject, pyqtSignal
# 信号对象
class QTypeSignal(QObject):
sendmsg = pyqtSignal(object) # 定义一个信号
def __init__(self):
super(QTypeSignal, self).__init__()
def run(self):
# 发射信号的实现
self.sendmsg.emit('Hello Pyqt5')
# 槽对象
class QTypeSlot(QObject):
def __init__(self):
super(QTypeSlot, self).__init__()
# 槽对象里的槽函数
def get(self, msg): # 槽函数接收数据
print("QSlot get msg => " + msg)
if __name__ == '__main__':
send = QTypeSignal()
slot = QTypeSlot()
#1
print('--- 把信号绑定到槽函数 ---')
send.sendmsg.connect(slot.get) # 将信号与槽函数get()绑定起来,槽函数能接收到所发射的信号'Hello Pyqt5'
send.run()
#2
print('--- 把信号断开槽函数 ---')
send.sendmsg.disconnect(slot.get) # 断开信号与槽函数get连接,槽函数是接收不到发射信号的
send.run()
高级自定义信号与槽
我们通过自己喜欢的方式定义信号与槽函数,并传递参数。
自定义信号一般流程如下:(1)定义信号 (2)定义槽函数 (3)连接信号与槽函数 (4)发射信号
定义信号
通过类成员变量定义信号对象:
class MyWidget(QWidget):
Signal_NoParameters = pyqtSignal() # 无参数的信号
Signal_OneParameter = pyqtSignal(int) # 带一个参数(整数)的信号
Signal_OneParameter = pyqtSignal(list) # 带一个列表类型参数的信号
Signal_OneParameter = pyqtSignal(dict) # 带一个字典类型参数的信号
Signal_OneParameter_Overload = pyqtSignal([int],[str]) # 带一个参数(整数或者字符串)的重载版本的信号
Signal_TwoParameters = pyqtSignal(int, str) # 带两个参数(整数,字符串)的信号
# 带两个参数([整数,整数]或者[整数,字符串])的重载版本的信号
Signal_TwoParameters_Overload = pyqtSignal([int,int],[int,str])
自定义信号需要在__init__()
函数之前定义
自定义信号可以传递str
、int
、list
、object
、float
、tuple
、dict
等多类型的参数
定义槽函数
定义一个槽函数,它有多个不同的输入参数:
class MyWidget(QWidget):
def setValue_NoParameters(self):
```无参数的槽函数```
pass
def setValue_OneParameter(self,nIndex):
```带一个参数(整数)的槽函数```
pass
def setValue_OneParameter_String(self,szIndex):
```带一个参数(字符串)的槽函数```
pass
def setValue_TwoParameters(self,x,y):
```带两个参数(整数,整数)的槽函数```
pass
def setValue_TwoParameters_String(self,x,szY):
```带两个参数(整数,字符串)的槽函数```
pass
连接信号与槽函数
通过connect
方法连接信号与槽函数或者可调用对象:
app = QApplication(sys.argv)
widget = MyWidget()
widget.Signal_NoParameters.connect(self.setValue_NoParameters) # 连接无参数的信号
widget.Signal_OneParameter.connect(self.setValue_OneParameter) # 连接一个带整数参数的信号
# 连接带一个整数参数,经过重载的信号
widget.Signal_OneParameter_Overload[int].connect(self.setValue_OneParameter)
# 连接带一个字符串参数,经过重载的信号
widget.Signal_OneParameter_Overload[str].connect(self.setValue_OneParameter_String)
# 连接带一个信号,它有两个整数参数
widget.Signal_TwoParameters.connect(self.setValue_TwoParameters)
# 连接带两个参数(整数,整数)的重载版本的信号
widget.Signal_TwoParameter_Overload[int, int].connect(self.setValue_TwoParameters)
# 连接带两个参数(整数,字符串)的重载版本的信号
widget.Signal_TwoParameters_Overload[int, str].connect(self.setValue_TwoParameters_String)
widget.show()
发射信号
通过emit
方法发射信号:
class MyWidget(QWidget):
def mousePressEvent(self, event):
self.Signal_NoParameters.emit() # 发射无参数的信号
self.Signal_OneParameter.emit(1) # 发射带一个参数(整数)的信号
self.Signal_OneParameter_Overload.emit(1) # 发射带一个参数(整数)的重载版本的信号
self.Signal_OneParameter_Overload.emit("abc") # 发射带一个参数(字符串)的重载版本的信号
self.Signal_TwoParameters.emit(1,"abc") # 发射带两个参数(整数,字符串)的信号
self.Signal_TwoParameters_Overload.emit(1,2)# 发射带两个参数(整数,整数)的重载版本的信号
self.Signal_TwoParameters_Overload.emit(1,"abc") # 发射带两个参数(整数,字符串)的重载版本的信号
使用自定义参数
信号发出的参数个数一定要大于槽函数接收的参数个数,否则会报错
对于信号clicked
来说,是没有参数的,但是对于槽函数,我们希望可以接收参数,我们可以通过自定义参数的传递来解决,通常使用lambda
表达式
button1.clicked.connect(lambda: self.onButtonClick(1))
# 槽函数设计,希望可以引入参数,使用lambda表达式传递按钮数字给槽函数
def onButtonClick(self, n):
QMessageBox.information(self, "信息提示框", 'Button {0} clicked'.format{n})
信号与槽的断开
当我们想临时或永久断开某个信号与槽函数的连接,可以使用disconnect()
方法
self.signal.disconnect(self.sinCall)
Qt Designer
来设计信号与槽
实现功能:当单击关闭按钮后关闭窗口
在Qt Designer
窗口拖拽push Button
控件到窗体中,在text
属性中改为“关闭窗口”,将objectName
属性值改为“closeWinBtn”
单击“Edit”
--> “编辑信号/槽” --> 进入编辑模式,在发射者(关闭窗口按钮上)按住鼠标不放,拖到接收者(Form
窗体上),就建立了连接,着会弹出“配置连接”的对话框,如下图所示:
在“配置连接”的对话框中勾选显示从QWidget
继承的信号和槽,左侧的closeWinBtn
按钮的信号栏里选择clicked()
信号,右侧的Form
槽函数中选择close()
,这就意味着,对“关闭窗口”按钮点击会发射clicked()
信号,这个信号会被Form
窗体的槽函数close()
捕捉到,并触发该窗体close
行为及关闭该窗体。
连接信号和槽成功后发现在编辑信号/槽模式下,其信号和槽的关系连线是红色的
将button_close.ui
文件通过PyUIC
工具生成button_close.py
文件,通过button_close.py
文件可以发现是通过以下代码进行连接的:
self.cloesWinBtn.clicked.connect(Form.close)
使用QObject.signal.connect()
连接的槽函数不要加括号,否则会报错。
可以在信号/槽编辑器中获取默认可用的信号与槽列表,还可以进行编辑调整。
多线程中信号与槽的使用
多线程使用方法是通过QThread
函数
在开发程序时会执行一些耗时的操作,会导致界面卡顿,我们可以使用多线程来解决该问题,使用主线程更新界面,使用子线程实时处理数据,最终将结果显示到界面上。
以下是一个多线程更新跟新数据的案例:
# -*- coding: utf-8 -*-
from PyQt5.QtCore import QThread , pyqtSignal, QDateTime
from PyQt5.QtWidgets import QApplication, QDialog, QLineEdit
import time
import sys
class BackendThread(QThread):
# 通过类成员对象定义信号对象
update_date = pyqtSignal(str)
# 处理要做的业务逻辑
def run(self):
while True:
data = QDateTime.currentDateTime()
currTime = data.toString("yyyy-MM-dd hh:mm:ss")
self.update_date.emit( str(currTime) )
time.sleep(1)
class Window(QDialog):
def __init__(self):
QDialog.__init__(self)
self.setWindowTitle('pyqt5界面实时更新例子')
self.resize(400, 100)
self.input = QLineEdit(self)
self.input.resize(400, 100)
self.initUI()
def initUI(self):
# 创建线程,来模拟后台的耗时操作,使用BackendThread线程类在后台处理数据,每秒发射一个自定义信号update_date
self.backend = BackendThread()
# 连接信号,把线程类的信号update_date连接到槽函数handleDisplay
self.backend.update_date.connect(self.handleDisplay)
# 开始线程
self.backend.start()
# 将当前时间输出到文本框
def handleDisplay(self, data):
self.input.setText(data)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec_())
事件处理机制
相对于信号与槽用于解决窗口控件的某些特定行为,事件处理机制可以对窗口控件做更深层次的研究,如自定义窗口等。
当采用信号与槽机制处理不了时,可以考虑事件处理机制。信号与槽是对事件处理机制的高级封装
常见的事件类型:
事件类型 | 描述 |
---|---|
键盘事件 | 按钮按下和松开 |
鼠标事件 | 鼠标指针移动、鼠标按键按下和松开 |
拖放事件 | 用鼠标进行拖放 |
滚轮事件 | 鼠标滚轮滚动 |
绘屏事件 | 重绘屏幕的某些部分 |
定时事件 | 定时器到时 |
焦点事件 | 键盘焦点移动 |
进入和离开事件 | 鼠标指针移入Widget内,或者移出 |
移动事件 | Widget的位置改变 |
大小改变事件 | Widget的大小改变 |
显示和隐藏事件 | Widget显示和隐藏 |
窗口事件 | 窗口是否为当前窗口 |
使用事件处理的方法
常用的方法有:重新实现事件函数(mousePressEvent()
、keyPressEvent()
、paintEvent()
)、重新实现QObject.event()
对于绘图事件,event
函数会交给paintEvent
函数处理;对于鼠标事件,event
函数会交给mouseMoveEvent
函数处理;对于键盘按下事件,event
函数会交给keyPressEvent
函数处理
案例--重新实现事件函数和重新实现event
函数
import sys
from PyQt5.QtCore import (QEvent, QTimer, Qt)
from PyQt5.QtWidgets import (QApplication, QMenu, QWidget)
from PyQt5.QtGui import QPainter
class Widget(QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.justDoubleClicked = False
self.key = ""
# 建立text和message两个变量,使用paintEvent函数把它们输出到窗口中
self.text = ""
self.message = ""
self.resize(400, 300)
self.move(100, 100)
self.setWindowTitle("Events")
# 避免窗口大小重绘事件的影响,可以把参数0改变成3000(3秒)
QTimer.singleShot(0, self.giveHelp)
def giveHelp(self):
self.text = "请点击这里触发追踪鼠标功能"
self.update() # 重绘事件,也就是触发paintEvent函数。update函数的作用是更新窗口
# 重新实现关闭事件
def closeEvent(self, event):
print("Closed")
# 重新实现上下文菜单事件,对于上下文事件,主要影响message变量的结果
def contextMenuEvent(self, event):
menu = QMenu(self)
oneAction = menu.addAction("&One")
twoAction = menu.addAction("&Two")
oneAction.triggered.connect(self.one)
twoAction.triggered.connect(self.two)
if not self.message:
menu.addSeparator()
threeAction = menu.addAction("Thre&e")
threeAction.triggered.connect(self.three)
menu.exec_(event.globalPos())
# 上下文菜单槽函数
def one(self):
self.message = "Menu option One"
self.update()
def two(self):
self.message = "Menu option Two"
self.update()
def three(self):
self.message = "Menu option Three"
self.update()
# 重新实现绘制事件,是代码的核心事件,主要跟踪text和message这两个变量的信息
def paintEvent(self, event):
text = self.text
i = text.find("\n\n")
if i >= 0:
text = text[0:i]
if self.key: # 若触发了键盘按钮,则在文本信息中记录这个按钮信息。
text += "\n\n你按下了: {0}".format(self.key)
painter = QPainter(self)
painter.setRenderHint(QPainter.TextAntialiasing)
painter.drawText(self.rect(), Qt.AlignCenter, text) # 绘制信息文本的内容
if self.message: # 若消息文本存在则在底部居中绘制消息,5秒钟后清空消息文本并重绘。
painter.drawText(self.rect(), Qt.AlignBottom | Qt.AlignHCenter,
self.message)
QTimer.singleShot(5000, self.clearMessage)
QTimer.singleShot(5000, self.update)
# 清空消息文本的槽函数
def clearMessage(self):
self.message = ""
# 重新实现调整窗口大小事件
def resizeEvent(self, event):
self.text = "调整窗口大小为: QSize({0}, {1})".format(
event.size().width(), event.size().height())
self.update()
# 重新实现鼠标释放事件
def mouseReleaseEvent(self, event):
# 若鼠标释放为双击释放,则不跟踪鼠标移动
# 若鼠标释放为单击释放,则需要改变跟踪功能的状态,如果开启跟踪功能的话就跟踪,不开启跟踪功能就不跟踪
if self.justDoubleClicked:
self.justDoubleClicked = False
else:
self.setMouseTracking(not self.hasMouseTracking()) # 单击鼠标
if self.hasMouseTracking():
self.text = "开启鼠标跟踪功能.\n" + \
"请移动一下鼠标!\n" + \
"单击鼠标可以关闭这个功能"
else:
self.text = "关闭鼠标跟踪功能.\n" + \
"单击鼠标可以开启这个功能"
self.update()
# 重新实现鼠标移动事件
def mouseMoveEvent(self, event):
if not self.justDoubleClicked:
globalPos = self.mapToGlobal(event.pos()) # 窗口坐标转换为屏幕坐标
self.text = """鼠标位置:
窗口坐标为:QPoint({0}, {1})
屏幕坐标为:QPoint({2}, {3}) """.format(event.pos().x(), event.pos().y(), globalPos.x(), globalPos.y())
self.update()
# 重新实现鼠标双击事件
def mouseDoubleClickEvent(self, event):
self.justDoubleClicked = True
self.text = "你双击了鼠标"
self.update()
# 重新实现键盘按下事件
def keyPressEvent(self, event):
self.key = ""
if event.key() == Qt.Key_Home:
self.key = "Home"
elif event.key() == Qt.Key_End:
self.key = "End"
elif event.key() == Qt.Key_PageUp:
if event.modifiers() & Qt.ControlModifier:
self.key = "Ctrl+PageUp"
else:
self.key = "PageUp"
elif event.key() == Qt.Key_PageDown:
if event.modifiers() & Qt.ControlModifier:
self.key = "Ctrl+PageDown"
else:
self.key = "PageDown"
elif Qt.Key_A <= event.key() <= Qt.Key_Z:
if event.modifiers() & Qt.ShiftModifier:
self.key = "Shift+"
self.key += event.text()
if self.key:
self.key = self.key
self.update()
else:
QWidget.keyPressEvent(self, event)
# 重新实现其他事件,适用于PyQt没有提供该事件的处理函数的情况,Tab键由于涉及焦点切换,不会传递给keyPressEvent,因此,需要在这里重新定义。
def event(self, event):
if (event.type() == QEvent.KeyPress and
event.key() == Qt.Key_Tab):
self.key = "在event()中捕获Tab键"
self.update()
return True
return QWidget.event(self, event)
if __name__ == "__main__":
app = QApplication(sys.argv)
form = Widget()
form.show()
app.exec_()
窗口数据传递
一个窗口间的数据是如何在控件中传递的,多个窗口间不同的窗口是如何传递数据的
对于多个窗口传递数据,有两种方法:
- 主窗口获取子窗口中控件的属性
- 通过信号与槽机制,一般通过子窗口发射信号的形式传递数据,主窗口的槽函数获取这些数据
单一窗口的数据传递
对于单一窗口的数据传递,一般是一个控件的变化影响另一个控件的变化,可以通过信号和槽进行传递
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import QWidget,QLCDNumber,QSlider,QVBoxLayout,QApplication
from PyQt5.QtCore import Qt
class WinForm(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
# 先创建滑块和 LCD 部件
lcd = QLCDNumber(self)
slider = QSlider(Qt.Horizontal, self)
# 通过QVboxLayout来设置布局
vBox = QVBoxLayout()
vBox.addWidget(lcd)
vBox.addWidget(slider)
self.setLayout(vBox)
# valueChanged()是Qslider的一个信号函数,只要slider的值发生改变,它就会发射一个信号,然后通过connect连接信号的接收部件,也就是lcd。
slider.valueChanged.connect(lcd.display)
self.setGeometry(300,300,350,150)
self.setWindowTitle("信号与槽:连接滑块LCD")
if __name__ == '__main__':
app = QApplication(sys.argv)
form = WinForm()
form.show()
sys.exit(app.exec_())
程序运行结果:改变滑块的值,相应变化的结果会显示在LCD
中
多窗口数据传递
调用属性方法
将多个参数写到一个窗口中,会使主窗口很臃肿,所以一般添加一个按钮调用对话框,在对话框中进行参数的选择,关闭对话框时将参数值返回给主窗口。
自定义一个对话框作为一个子窗口,为后续主窗口来调用这个子窗口的属性
# -*- coding: utf-8 -*-
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class DateDialog(QDialog):
def __init__(self, parent=None):
super(DateDialog, self).__init__(parent)
self.setWindowTitle('DateDialog')
# 在布局中添加部件
layout = QVBoxLayout(self)
self.datetime = QDateTimeEdit(self)
self.datetime.setCalendarPopup(True)
self.datetime.setDateTime(QDateTime.currentDateTime())
layout.addWidget(self.datetime)
# 使用两个button(ok和cancel)分别连接accept()和reject()槽函数
buttons = QDialogButtonBox(
QDialogButtonBox.Ok | QDialogButtonBox.Cancel,
Qt.Horizontal, self)
buttons.accepted.connect(self.accept)
buttons.rejected.connect(self.reject)
layout.addWidget(buttons)
# 从对话框中获取当前日期和时间
def dateTime(self):
return self.datetime.dateTime()
# 静态方法创建对话框并返回 (date, time, accepted)
@staticmethod
def getDateTime(parent=None): # 在类中定义一个静态函数getDateTime(),来返回三个时间值
dialog = DateDialog(parent) # 静态函数中实例化DateDialog类
result = dialog.exec_() # 调用 dialog.exec_()来显示执行对话框,通过返回值来判断用户单击的是OK还是Cancel
date = dialog.dateTime()
return (date.date(), date.time(), result == QDialog.Accepted)
创建一个调用对话框的主窗口
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from DateDialog import DateDialog # 调用DateDialog.py文件的DateDialog类
class WinForm(QWidget):
def __init__(self, parent=None):
super(WinForm, self).__init__(parent)
self.resize(400, 90)
self.setWindowTitle('对话框关闭时返回值给主窗口例子')
# 创建相关控件,同时连接槽函数
self.lineEdit = QLineEdit(self)
self.button1 = QPushButton('弹出对话框1')
self.button1.clicked.connect(self.onButton1Click)
self.button2 = QPushButton('弹出对话框2')
self.button2.clicked.connect(self.onButton2Click)
# 进行布局
gridLayout = QGridLayout()
gridLayout.addWidget(self.lineEdit)
gridLayout.addWidget(self.button1)
gridLayout.addWidget(self.button2)
self.setLayout(gridLayout)
def onButton1Click(self): # 直接在主窗口程序中实例化该对话框,再调用该对话框的函数来获取返回值
dialog = DateDialog(self) # DateDialog.py文件的DateDialog类
result = dialog.exec_()
date = dialog.dateTime()
self.lineEdit.setText(date.date().toString())
print('\n日期对话框的返回值')
print('date=%s' % str(date.date()))
print('time=%s' % str(date.time()))
print('result=%s' % result)
dialog.destroy()
def onButton2Click(self): # 在主窗口程序中调用子窗口的静态函数,利用静态函数的特点,在子窗口的静态函数中创建实例化对象
date, time, result = DateDialog.getDateTime()
self.lineEdit.setText(date.toString())
print('\n日期对话框的返回值')
print('date=%s' % str(date))
print('time=%s' % str(time))
print('result=%s' % result)
if result == QDialog.Accepted:
print('点击确认按钮')
else:
print('点击取消按钮')
if __name__ == "__main__":
app = QApplication(sys.argv)
form = WinForm()
form.show()
sys.exit(app.exec_())
程序运行结果:在子窗口中选择相关时间后,点击OK,会显示在主窗口的lineEdit中
信号与槽方法
通过信号与槽进行多窗口的数据传递,一般是通过子窗口发射信号,主窗口通过槽函数捕获信号,然后获取信号里面的数据。
创建一个子窗口对话框文件
# -*- coding: utf-8 -*-
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class DateDialog(QDialog):
Signal_OneParameter = pyqtSignal(str)
def __init__(self, parent=None):
super(DateDialog, self).__init__(parent)
self.setWindowTitle('子窗口:用来发射信号')
# 在布局中添加部件
layout = QVBoxLayout(self)
self.label = QLabel(self)
self.label.setText('前者发射内置信号\n后者发射自定义信号') # 用于给用户提示
self.datetime_inner = QDateTimeEdit(self)
self.datetime_inner.setCalendarPopup(True)
self.datetime_inner.setDateTime(QDateTime.currentDateTime())
self.datetime_emit = QDateTimeEdit(self)
self.datetime_emit.setCalendarPopup(True)
self.datetime_emit.setDateTime(QDateTime.currentDateTime())
# 子窗口中的控件布局管理
layout.addWidget(self.label)
layout.addWidget(self.datetime_inner)
layout.addWidget(self.datetime_emit)
# 使用两个button(ok和cancel)分别连接accept()和reject()槽函数
buttons = QDialogButtonBox(
QDialogButtonBox.Ok | QDialogButtonBox.Cancel,
Qt.Horizontal, self)
buttons.accepted.connect(self.accept)
buttons.rejected.connect(self.reject)
layout.addWidget(buttons)
# 控件datetime_emit的时间发生变化时,会触发子窗口的槽函数emit_signal
self.datetime_emit.dateTimeChanged.connect(self.emit_signal)
def emit_signal(self): # 槽函数emit_signal会发发射自定义信号Signal_OneParameter,传递date_str参数给主函数的槽函数
date_str = self.datetime_emit.dateTime().toString()
self.Signal_OneParameter.emit(date_str)
主窗口代码:获取子窗口的信号,并将其绑定在自己的槽函数上,就实现了子窗口的控件与主窗口的控件的信号与槽的绑定
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from DateDialog2 import DateDialog
class WinForm(QWidget):
def __init__(self, parent=None):
super(WinForm, self).__init__(parent)
self.resize(400, 90)
self.setWindowTitle('信号与槽传递参数的示例')
# 创建相关的控件
self.open_btn = QPushButton('获取时间')
self.lineEdit_inner = QLineEdit(self)
self.lineEdit_emit = QLineEdit(self)
self.open_btn.clicked.connect(self.openDialog) # 连接槽函数
self.lineEdit_inner.setText('接收子窗口内置信号的时间')
self.lineEdit_emit.setText('接收子窗口自定义信号的时间')
# 进行布局
grid = QGridLayout()
grid.addWidget(self.lineEdit_inner)
grid.addWidget(self.lineEdit_emit)
grid.addWidget(self.open_btn)
self.setLayout(grid)
def openDialog(self):
dialog = DateDialog(self) # 调用子窗口的DateDialog类
# 连接子窗口的内置信号与主窗口的槽函数
dialog.datetime_inner.dateTimeChanged.connect(self.deal_inner_slot)
# 连接子窗口的自定义信号与主窗口的槽函数
dialog.Signal_OneParameter.connect(self.deal_emit_slot)
dialog.show()
def deal_inner_slot(self, date):
self.lineEdit_inner.setText(date.toString())
def deal_emit_slot(self, dateStr):
self.lineEdit_emit.setText(dateStr)
if __name__ == "__main__":
app = QApplication(sys.argv)
form = WinForm()
form.show()
sys.exit(app.exec_())