How to connect a signal slot (qt-designer) with a python function
我创建了一个应用程序,该应用程序的主窗口包括MDI区域和MDI区域的子窗口。这两个窗口都是通过QT Designer创建的,并保存为ui文件。我的python脚本加载了主窗口,并提供了打开子窗口的功能。到目前为止,一切正常!
现在我有子窗口中的按钮,它应该触发影响主窗口中元素的功能(例如,在MDI区域之外的" PlainTextEdit"元素中显示文本)。
在Qt-Designer中,我可以定义信号和自定义插槽。
pushButton-> clicked()-> MainWindow-> printText()
我的问题是:我必须写些什么在python代码中以捕获" printText()"插槽上的信号,才能在以下代码中执行功能?
我正在使用Python 3.7和Pyside2。
如果我运行脚本,则终端中将显示以下信息:
QObject::connect: No such slot QMainWindow::printText()
QObject::connect: (sender name: 'pushButton')
QObject::connect: (receiver name: 'MainWindow')
通过...的默认方式
...不起作用,因为pushButton在另一个类中定义为主窗口。 (子窗口类)
而且我也不能在子窗口类中添加此代码,因为使用被调用的函数(self.function),我无法访问主窗口中的元素。
主窗口类中用于捕获信号的插槽的声明(到目前为止我发现)也不起作用:
1 2 3 4 5 | @Slot() def printText(self): # name of the slot # function which should be executed if the button is clicked self.ui.textOutput.setPlainText("This is a test !") |
[编辑]
如果已添加所有三个文件的代码。
该示例包含2个子窗口。主ui文件中包含的第一个ist(始终通过执行激活)。第二个子窗口是独立的,可以通过主菜单按钮显示。
py文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | import sys from PySide2.QtUiTools import QUiLoader from PySide2.QtWidgets import QApplication, QMainWindow, QWidget, QMdiSubWindow, QMdiArea from PySide2.QtCore import QFile, Slot, Signal # Variable which contains the subwindow ID # Required to determine if a subwindow is already open state_limitedSubWindow = None # Main class for loading the UI class MyUI(QMainWindow): def __init__(self, ui_file, parent = None): super(MyUI, self).__init__(parent) # (1) Open UI file ui_file = QFile(ui_file) ui_file.open(QFile.ReadOnly) # (2) Loading UI file ... uiLoader = QUiLoader() # ... and creating an instance of the content self.ui = uiLoader.load(ui_file) # (3) Close file ui_file.close() # (4) Optional: Customize loaded UI # E.g. Set a window title self.ui.setWindowTitle("Test") # (5) Show the loaded and optionally customized UI self.ui.show() # A limited subwindow (only on instance can be active) self.ui.actionOpenSubWindow.triggered.connect(self.func_limitedSubWindow) @Slot() def printText(): print("Debug: Inside the __init__.") @Slot() def printText(self): print("Debug: Inside the MainWindow class") self.printing() # Limited subwindow via action @Slot() def func_limitedSubWindow(self): # loading global var which contains the subwindow ID global state_limitedSubWindow if state_limitedSubWindow == None: limitedSubWindow = LimitedSubWindow("test_sub.ui") self.ui.mdiArea.addSubWindow(limitedSubWindow) limitedSubWindow.show() # Save ID of the new created subwindow in the global variable state_limitedSubWindow = limitedSubWindow.winId() # Console output subwindow ID print(state_limitedSubWindow) else: print("Window already exists !") @Slot() def printing(self): self.ui.textOutput.setPlainText("Test") @Slot() def printText(): print("Debug: Outside of the class file") # Class for the limited second window (only 1 instance can be active) # This class can of course be in a separate py file # The base widget of the UI file must be QWidget !!! class LimitedSubWindow(QWidget): def __init__(self, ui_limitedSubWindow_file, parent = None): super(LimitedSubWindow, self).__init__(parent) # (1) Open UI file ui_limitedSubWindow_file = QFile(ui_limitedSubWindow_file) ui_limitedSubWindow_file.open(QFile.ReadOnly) # (2) Loading UI file ... ui_limitedSubWindow_Loader = QUiLoader() # ... and creating an instance of the content self.ui_limitedSubWindow = ui_limitedSubWindow_Loader.load(ui_limitedSubWindow_file, self) # (3) Close file ui_limitedSubWindow_file.close() self.setMinimumSize(400, 200) self.setWindowTitle("Limited subwindow") self.ui_limitedSubWindow.pushButton.clicked.connect(self.test) # Close event resets the variable which contains the ID def closeEvent(self, event): global state_limitedSubWindow # Reset the global variable state_limitedSubWindow = None event.accept() if __name__ =="__main__": app = QApplication(sys.argv) # Creating an instance of the loading class frame = MyUI("test.ui") sys.exit(app.exec_()) |
主要ui文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>MainWindow</class> <widget class="QMainWindow" name="MainWindow"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>800</width> <height>600</height> </rect> </property> <property name="windowTitle"> <string>MainWindow</string> </property> <widget class="QWidget" name="centralwidget"> <widget class="QWidget" name="horizontalLayoutWidget"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>791</width> <height>551</height> </rect> </property> <layout class="QHBoxLayout" name="horizontalLayout"> <item> <widget class="QPlainTextEdit" name="textInput"/> </item> <item> <widget class="QMdiArea" name="mdiArea"> <property name="enabled"> <bool>true</bool> </property> <property name="maximumSize"> <size> <width>517</width> <height>16777215</height> </size> </property> <widget class="QWidget" name="subwindow"> <property name="minimumSize"> <size> <width>400</width> <height>400</height> </size> </property> <property name="windowTitle"> <string>Subwindow</string> </property> <widget class="QPushButton" name="pushButton"> <property name="geometry"> <rect> <x>160</x> <y>200</y> <width>90</width> <height>28</height> </rect> </property> <property name="text"> <string>PushButton</string> </property> </widget> </widget> </widget> </item> <item> <widget class="QPlainTextEdit" name="textOutput"/> </item> </layout> </widget> </widget> <widget class="QMenuBar" name="menubar"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>800</width> <height>25</height> </rect> </property> <widget class="QMenu" name="menuWorkbench"> <property name="title"> <string>Workbench</string> </property> </widget> </widget> <widget class="QStatusBar" name="statusbar"/> <property name="text"> <string>Caesar Cipher</string> </property> </action> <property name="text"> <string>Test text</string> </property> </action> </widget> <resources/> <connections> <connection> <sender>pushButton</sender> <signal>clicked()</signal> <receiver>MainWindow</receiver> <slot>printText()</slot> <hints> <hint type="sourcelabel"> <x>386</x> <y>263</y> </hint> <hint type="destinationlabel"> <x>399</x> <y>299</y> </hint> </hints> </connection> </connections> <slots> <slot>printText()</slot> </slots> </ui> |
子ui文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>Form</class> <widget class="QWidget" name="Form"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>315</width> <height>242</height> </rect> </property> <property name="windowTitle"> <string>Form</string> </property> <widget class="QPushButton" name="pushButton"> <property name="geometry"> <rect> <x>90</x> <y>80</y> <width>90</width> <height>28</height> </rect> </property> <property name="text"> <string>PushButton</string> </property> </widget> </widget> <resources/> <connections/> </ui> |
printText插槽属于MyUI类,但是需要.ui的插槽必须属于self.ui,但不幸的是,在PySide2中,使用QUiLoader不能从.ui创建类。
因此解决方案是使用
该文件夹包含以下文件:
1 2 3 | a"?a"€a"€ main.py a"?a"€a"€ test_sub.ui a""a"€a"€ test.ui |
然后,您必须打开位于项目文件夹中的终端或CMD并执行:
1 2 | pyside2-uic test_sub.ui -o test_sub_ui.py -x pyside2-uic test.ui -o test_ui.py -x |
因此您必须获得以下结构:
1 2 3 4 5 6 | . a"?a"€a"€ main.py a"?a"€a"€ test_sub.ui a"?a"€a"€ test_sub_ui.py a"?a"€a"€ test.ui a""a"€a"€ test_ui.py |
此后,您必须修改main.py(有关更多信息,请阅读前面的答案):
main.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | import sys from PySide2 import QtCore, QtWidgets from test_ui import Ui_MainWindow from test_sub_ui import Ui_Form # Variable which contains the subwindow ID # Required to determine if a subwindow is already open state_limitedSubWindow = None class MyUI(QtWidgets.QMainWindow, Ui_MainWindow): def __init__(self, parent=None): super(MyUI, self).__init__(parent) self.setupUi(self) self.setWindowTitle("Test") self.mdiArea.addSubWindow(self.subwindow) self.actionOpenSubWindow.triggered.connect(self.func_limitedSubWindow) @QtCore.Slot() def printText(self): print("Debug: Inside the MainWindow class") self.printing() @QtCore.Slot() def func_limitedSubWindow(self): # loading global var which contains the subwindow ID global state_limitedSubWindow if state_limitedSubWindow == None: limitedSubWindow = LimitedSubWindow() self.mdiArea.addSubWindow(limitedSubWindow) limitedSubWindow.show() # Save ID of the new created subwindow in the global variable state_limitedSubWindow = limitedSubWindow.winId() # Console output subwindow ID print(state_limitedSubWindow) else: print("Window already exists !") pass @QtCore.Slot() def printing(self): self.textOutput.setPlainText("Test") # Class for the limited second window (only 1 instance can be active) # This class can of course be in a separate py file # The base widget of the UI file must be QWidget !!! class LimitedSubWindow(QtWidgets.QWidget, Ui_Form): def __init__(self, parent = None): super(LimitedSubWindow, self).__init__(parent) self.setupUi(self) self.setMinimumSize(400, 200) self.setWindowTitle("Limited subwindow") # Close event resets the variable which contains the ID def closeEvent(self, event): global state_limitedSubWindow # Reset the global variable state_limitedSubWindow = None event.accept() if __name__ =="__main__": app = QtWidgets.QApplication(sys.argv) # Creating an instance of the loading class frame = MyUI() frame.show() sys.exit(app.exec_()) |
因此,结论是QUiLoader有很多限制,因此最好使用uic。
如果您不想使用uic,那么在上一个答案中,我已经说明了如何将uic模块从PyQt5转换为PySide2
完整的解决方案可以在这里找到