【问题标题】:How to connect a signal slot (qt-designer) with a python function如何用python函数连接信号槽(qt-designer)
【发布时间】:2019-04-03 22:04:25
【问题描述】:

我创建了一个应用程序,其主窗口包括一个 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: (发送者名称: 'pushButton')

QObject::connect: (receiver name: 'MainWindow')

  1. 默认方式通过... self.pushButton.clicked.connect(self.function) ...不起作用,因为 pushButton 在另一个类中定义为主窗口。 (子窗口类) 而且我也无法在子窗口类中添加此代码,因为使用调用的函数(self.function)我无法访问主窗口中的元素。

  2. 在主窗口类中声明插槽(我目前发现)来捕获信号,也不起作用:

@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>

【问题讨论】:

  • 您是否尝试将其与定义在 QMainWindow 之外的函数连接起来?你在哪里调用连接?在__init__ 中?如果是这样,你打电话给super 吗?
  • QMainWindow 类之外的“printText()”定义也不起作用。我还在 QMainWindow 类本身和该类的__init__ 中尝试了定义。 super 被调用。
  • @LuminousLizard 如果你需要帮助,你应该提供一个 MCVE,也许错误在于你没有显示,所以分享 .py 和 .ui
  • @eyllanesc 这有点难以展示一个例子。它将包含一个 py 文件和一个漏洞 ui 文件 (xml) 的一部分。
  • @LuminousLizard 如果您只分享片段,我可能会要求您提供 MCVE,因为错误可能在您代码的隐藏部分中,所以让我们节省时间并提供我所要求的内容。

标签: python qt-designer pyside2


【解决方案1】:

printText slot 属于 MyUI 类,但需要 .ui 的 slot 必须属于 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):

ma​​in.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找到

【讨论】:

  • 但我对此还有一个问题:此解决方案适用于在 Qt 设计器的主窗口中直接添加/创建的子窗口(test.ui 包含两个窗口)并由信号槽链接-编辑。如何使用独立子窗口中的按钮(通过 py 代码加载的“test_sub.ui”)执行相同的“printText()”函数? self.pushButton.clicked.connect(MyUI.printText) 不能从主窗口类调用“printText()”函数。
猜你喜欢
  • 1970-01-01
  • 2013-06-13
  • 1970-01-01
  • 2012-10-15
  • 1970-01-01
  • 1970-01-01
  • 2012-02-03
  • 2015-03-14
  • 2016-11-10
相关资源
最近更新 更多