我创建了一个应用程序,其主窗口包括MDI区域和MDI区域的子窗口。这两个窗口都是通过QT Designer创建的,并保存为ui文件。我的python脚本加载了主窗口,并提供了打开子窗口的功能。到目前为止有效!
现在我有子窗口中的按钮,它应该触发影响主窗口中元素的功能(例如,在MDI区域之外的“ PlainTextEdit”元素中显示文本)。 在Qt-Designer中,我可以定义信号和自定义插槽。
pushButton-> clicked()-> MainWindow-> printText()
我的问题是:我必须在python代码中写什么才能捕获“ printText()”插槽上的信号,然后在以下代码中执行功能?
我正在使用Python 3.7和Pyside2。
如果我运行脚本,则终端中将显示以下信息:
QObject :: connect:没有这样的插槽QMainWindow :: printText()
QObject :: connect :(发件人名称:'pushButton')
QObject :: connect :(接收方名称:“ MainWindow”)
通过...的默认方式
self.pushButton.clicked.connect(self.function)
...不起作用,因为pushButton在另一个类中定义为主窗口。 (子窗口类)
而且我也无法在子窗口类中添加此代码,因为使用调用的函数(self.function),我无法访问主窗口中的元素。
主窗口类中用于捕获信号的插槽的声明(到目前为止我发现)也不起作用:
@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文件:
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文件:
<?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>
<addaction name="actionOpenSubWindow"/>
</widget>
<addaction name="menuWorkbench"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<action name="actionOpenSubWindow">
<property name="text">
<string>Caesar Cipher</string>
</property>
</action>
<action name="actionTestText">
<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文件:
<?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>
答案 0 :(得分:0)
printText插槽属于MyUI类,但是需要.ui的插槽必须属于self.ui,但是不幸的是,在PySide2中,使用QUiLoader不能从.ui创建类。
因此解决方案是使用pyside2-uic
将.ui转换为.py,因为它将生成一个类。
该文件夹包含以下文件:
├── main.py
├── test_sub.ui
└── test.ui
然后,您必须打开位于项目文件夹中的终端或CMD并执行:
pyside2-uic test_sub.ui -o test_sub_ui.py -x
pyside2-uic test.ui -o test_ui.py -x
因此,您必须获得以下结构:
.
├── main.py
├── test_sub.ui
├── test_sub_ui.py
├── test.ui
└── test_ui.py
之后,您必须修改main.py(有关更多信息,请阅读此previous answer):
main.py
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,那么在previous answer中,我已经说明了如何将uic模块从PyQt5转换为PySide2
可以找到完整的解决方案here