我有以下测试代码:
from os import path
from PySide6.QtCore import QObject, QMetaObject
from PySide6.QtUiTools import QUiLoader
from PySide6.QtWidgets import QApplication
class MyWin(QObject):
def __init__(self, parent=None):
super().__init__(parent)
self.ui = QUiLoader().load(path.join(path.dirname(__file__), "MainWindow.ui"))
self.ui.pushButton.clicked.connect(self.on_pushButton_clicked)
def show(self):
self.ui.show()
def on_pushButton_clicked(self):
print("button pushed!")
app = QApplication([])
win = MyWin()
win.show()
app.exec()
及其关联的MainWindow.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">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item>
<widget class="QTableView" name="tableView"/>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>19</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>
...按预期工作。
问题是:如何更换线路:
self.ui.pushButton.clicked.connect(self.on_pushButton_clicked)
使用 QMetaObject.connectSlotsByName(???)
进行等效操作?
这里的问题是 PySide6 QUiLoader
无法将小部件添加为 self
的子级(因为 PyQt6 uic.loadUi(filename, self)
可以do),因此我被迫将 UI 放在一个单独的变量(self.ui
)中,而插槽在“父”MyWin
中定义。
如何规避限制?
我问的原因是我的真实程序有无数的信号/槽,并且手动connect()
它们是一个真正的PITA(并且容易出错)
更新:
根据建议,我修改了 MyWin
以继承 QWidget
,但启用 self.ui.setParent(self)
足以阻止 UI 显示。
from os import path
from PySide6.QtCore import QMetaObject
from PySide6.QtUiTools import QUiLoader
from PySide6.QtWidgets import QApplication, QWidget
class MyWin(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.ui = QUiLoader().load(path.join(path.dirname(__file__), "MainWindow.ui"))
self.ui.pushButton.clicked.connect(self.on_pushButton_clicked)
self.ui.setParent(self)
# QMetaObject.connectSlotsByName(self)
def myshow(self):
self.ui.show()
def on_pushButton_clicked(self):
print("button pushed!")
app = QApplication([])
win = MyWin()
win.myshow()
app.exec()
我还看到一些奇怪的错误:
mcon@ikea:~/projects/pyside6-test$ venv/bin/python t.py
qt.pysideplugin: Environment variable PYSIDE_DESIGNER_PLUGINS is not set, bailing out.
qt.pysideplugin: No instance of QPyDesignerCustomWidgetCollection was found.
Qt WebEngine seems to be initialized from a plugin. Please set Qt::AA_ShareOpenGLContexts using QCoreApplication::setAttribute and QSGRendererInterface::OpenGLRhi using QQuickWindow::setGraphicsApi before constructing QGuiApplication.
^C^C^C^C
Terminated
我需要从另一个终端终止进程,正常的 Ctrl-C 会被忽略。
更新2: 我根据@ekhumoro的建议进一步更新了代码:
from os import path
from PySide6.QtCore import QMetaObject
from PySide6.QtUiTools import QUiLoader
from PySide6.QtWidgets import QApplication, QWidget, QMainWindow
class UiLoader(QUiLoader):
_baseinstance = None
def createWidget(self, classname, parent=None, name=''):
if parent is None and self._baseinstance is not None:
widget = self._baseinstance
else:
widget = super(UiLoader, self).createWidget(classname, parent, name)
if self._baseinstance is not None:
setattr(self._baseinstance, name, widget)
return widget
def loadUi(self, uifile, baseinstance=None):
self._baseinstance = baseinstance
widget = self.load(uifile)
QMetaObject.connectSlotsByName(widget)
return widget
class MyWin(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
UiLoader().loadUi(path.join(path.dirname(__file__), "MainWindow.ui"), self)
# self.pushButton.clicked.connect(self.on_pushButton_clicked)
QMetaObject.connectSlotsByName(self)
def on_pushButton_clicked(self):
print("button pushed!")
app = QApplication([])
win = MyWin()
win.show()
app.exec()
这也不起作用:它显示 GUI,但按钮单击未连接(除非我明确取消注释该行)。
我做错了什么?
最佳答案
回答标题中所述的问题:
可以通过将容器小部件设置为 ui-widget 的父级来修复原始示例。但是,还需要一些额外的步骤。首先,ui-widget 的标志必须包含 Qt.Window ,否则它将成为不可见窗口的子窗口。其次,必须重新实现 ui-widget 的 close 事件,以便应用程序正确关闭。最后,自动连接的插槽必须用 QtCore.Slot
装饰。
这是一个完整的示例:
from os import path
from PySide6.QtCore import Qt, QEvent, Slot, QMetaObject
from PySide6.QtUiTools import QUiLoader
from PySide6.QtWidgets import QApplication, QWidget
class MyWin(QWidget):
def __init__(self):
super().__init__()
self.ui = QUiLoader().load(
path.join(path.dirname(__file__), "MainWindow.ui"))
self.ui.setParent(self, self.ui.windowFlags() | Qt.WindowType.Window)
self.ui.installEventFilter(self)
QMetaObject.connectSlotsByName(self)
def eventFilter(self, source, event):
if event.type() == QEvent.Type.Close and source is self.ui:
QApplication.instance().quit()
return super().eventFilter(source, event)
def myshow(self):
self.ui.show()
@Slot()
def on_pushButton_clicked(self):
print("button pushed!")
app = QApplication(['Test'])
win = MyWin()
win.myshow()
app.exec()
PS:另见我完全修改后的alternative solution 使用 loadUi
风格的方法,现在可以在 PySide2 和 PySide6 上正常工作。
关于qt-designer - PySide6在使用 `QMetaObject.connectSlotsByName(MainWindow)`时如何使用 `QUiLoader().load(...)`,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74254074/