(PyQt5信号与槽(五)窗口数据传递)
五、窗口数据传递
1、单一窗口数据传递
- 在开发程序时,如果这个程序只有一个窗口,则应该关心这个窗口里面的各个控件之间是如何传递数据的;如果这个程序有多个窗口,那么还应该关心不同的窗口之间是如何传递数据的。
- 对于具有单一窗口的程序来说,一个控件的变化会影响另一个控件的变化,这种变化利用信号与槽机制非常容易解决。
- 案例——使用滑块控制LCD变化
# -*- coding:utf-8 -*- """ # @Time:2022/12/13 0013 0:00 # @Author:晚秋拾叶 # @File:qt07_winSignalSlot06.py # @PyCharm之Python """ import sys from PyQt5.QtWidgets import * from PyQt5.QtCore import * class WinForm(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): lcd = QLCDNumber(self) slider = QSlider(Qt.Horizontal, self) vBox = QVBoxLayout() vBox.addWidget(lcd) vBox.addWidget(slider) self.setLayout(vBox) # 此处是内置信号与内置槽的基本应用了 slider.valueChanged.connect(lcd.display) self.setGeometry(300, 300, 550, 300) self.setWindowTitle("信号与槽:连接滑块和LCD") if __name__ == "__main__": app = QApplication(sys.argv) form = WinForm() form.show() app.exec_()
- 案例——使用滑块控制LCD变化
- 效果如图
- 代码分析
-
这个程序首先创建滑块和LCD控件,而且这两个控件都是系统自带的,很容易实现。
lcd = QLCDNumber(self) slider = QSlider(Qt.Horizontal, self)
-
然后,通过垂直布局器完成布局,而且还要放到QWidget总控件中。
vBox = QVBoxLayout() vBox.addWidget(lcd) vBox.addWidget(slider) self.setLayout(vBox)
-
最后,接连到QSlidre控件的valueChanged()信号函数和LCD面板控件的display()槽函数。这样就是一个典型的内置信号与内置槽的接连了。
-
除了valueChange()之外,QSlider还有sliderPressured()、sliderMoved()、sliderReleased()等信号函数。
-
2、多窗口数据传递:调用属性
- 在PyQt编程过程中,经常会遇到输入或选择多个参数的问题。把多个参数写到一个窗口中,主窗口会显得很臃肿,所以一般是添加一个按钮,调用对话框,在对话框中进行参数的选择,关闭对话框时将参数值返回给主窗口。
- PyQt提供了一些标准的对话框类,用于输入数据、修改数据、更改应用的设置等,常见的有QFileDialog、QInputDialog、QColorDialog、QFontDialog等。在不同的窗口之间传参有两种常用的方式:一种是在自定义对话框之间通过属性传参;另一种是在窗口之间使用信号与槽机制传参。
- 在下面例子中,将自定义对话框作为一个子窗口,后面会新建一个主窗口来调用这个子窗口的属性。
- 示例文件1——自定义的对话框:
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): dialog = DateDialog(parent) result = dialog.exec_() date = dialog.dateTime() return (date.date(), date.time(), result == QDialog.Accepted)
- 示例文件2——调用上面对话框的主文件:
# -*- coding:utf-8 -*- """ # @Time:2022/12/13 0013 0:23 # @Author:晚秋拾叶 # @File:qt07_callDialogMainWin.py # @PyCharm之Python """ import sys from PyQt5.QtWidgets import * from qt07_DateDialog import 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) 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和Cancel)分别连接两个槽函数(accept()和reject())。
- 在类中定义一个静态函数getDateTime(),该静态函数返回3个时间值。原理是利用静态函数的特性,在静态函数中实例代DateDialog类,并调用dialog.exec_()函数来显式执行对话框。通过dialog.exec_()的返回值来判断用户单击的是OK还是Cancel,然后再做出下一步判断。
- 本例中,主窗口调用对话框用了两种方法:“弹出对话框1”和“弹出对话框2”。分别说明如下:
- (1)第一种方法:在主窗口程序中调用子窗口的静态函数,实际上这种方法与第一种方法是一样的,只不过它是利用静态函数的特点,在子窗口的静态函数中创建实例化对象。
- (2)第二种方法:直接在主窗口程序中实例化该对话框,然后调用该对话框的函数来获取返回值,根据对话框的返回值单击确认按钮还是取消按钮来进行下一步操作。
3、多窗口数据传递:信号与槽
- 对于多窗口的数据传递,一般是通过子窗口发射信号的,主窗口通过槽函数捕获这个信号,然后获取信号里面的数据。子窗口发射的信号有两种,其中一种是发射PyQt内置的一些信号;另一种是发射自定义的信号。
- 发射自定义信号的好处是,它的参数类型可以自定义。比如发射一个自定义信号,它的参数类型可以为int、str、dict、list 等;如果发射内置信号,则只能是特定的几个参数。
- 示例文件1——自定义的对话框2:
# -*- coding:utf-8 -*- """ # @Time:2022/12/13 0013 1:03 # @Author:晚秋拾叶 # @File:qt07_DateDialog2.py # @PyCharm之Python """ from PyQt5.QtCore 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) self.datetime_emit.dateTimeChanged.connect(self.emit_signal) def emit_signal(self): date_str = self.datetime_emit.dateTime().toString() self.Signal_OneParameter.emit(date_str)
- 示例文件2——调用上面对话框的主文件2:
# -*- coding:utf-8 -*- """ # @Time:2022/12/13 0013 1:04 # @Author:晚秋拾叶 # @File:qt07_callDialogMainWin2.py # @PyCharm之Python """ import sys from PyQt5.QtWidgets import * from qt07_DateDialog2 import DateDialog class WinForm(QWidget): def __init__(self, parent=None): super(WinForm, self).__init__(parent) self.resize(550, 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) '''连接子窗口的内置信号与主窗口的槽函数''' 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_())
- 运行结果
- 代码分析
- 上面代码的逻辑其实很简单,对于子窗口,关键是如何实现符合特定条件的自定义信号的发射问题。下面的代码表示当控件datatime_emit时间发生变化时,就会触发子窗口的槽函数emit_signal,接着这个槽函数发射自定义信号Signal_OneParameter,这个信号是为了传递data_str参数给主函数的槽函数。
- 对于主窗口,关键是获取子窗口的信号,并把它绑定在自己的槽函数上,这样就实现了子窗口的控件与主窗口的控件的信号与槽的绑定。下面的代码是这个文件的关键,展示了如何把子窗口的内置信号与自定义信号绑定到主窗口的槽函数 。