PyQt自动连接信号

3

我希望使用自动连接功能。我正在使用以下示例:

http://www.eurion.net/python-snippets/snippet/Connecting%20signals%20and%20slots.html

它可以工作,但我想创建自己的信号和插槽,而不是使用内置信号的示例。 例如,这里有一个自定义信号和自定义插槽的示例,但不起作用:
import sys
from PyQt4 import QtGui, QtCore

class SignalsAndSlots(QtGui.QWidget):

    testSignal = QtCore.pyqtSignal(str,name='testSignal')  



    def __init__(self):
        QtGui.QMainWindow.__init__(self)
        self.setObjectName('testObject')
        self.label = QtGui.QLabel(self)

        QtCore.QMetaObject.connectSlotsByName(self)
        self.emitSignal()


    def emitSignal(self):
        self.testSignal.emit('message')  

    @QtCore.pyqtSlot(str,name='on_testObject_testSignal')     
    def autoSlot(self,msg):
        self.label.setText(msg)

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    gui = SignalsAndSlots()
    gui.show()
    app.exec_()

非常感谢


尝试从不同于self的对象中发出信号,并将该对象的引用放入self中。我只能猜测,但我认为connectSlotsByName()不会考虑将self作为要连接的对象之一。 - Ber
2个回答

4
Ber是正确的。这是PyQt文档中的说明: QMetaObject.connectSlotsByName递归搜索给定对象的所有子对象 [...]
下面是一个具有自定义信号的简单示例:
import sys
from PyQt4 import QtGui, QtCore

class CustomButton(QtGui.QPushButton):
    custom_clicked = QtCore.pyqtSignal(str, name='customClicked')
    def mousePressEvent(self, event):
        self.custom_clicked.emit("Clicked!")

class SignalsAndSlots(QtGui.QWidget):

    def __init__(self):
        QtGui.QMainWindow.__init__(self)
        layout = QtGui.QHBoxLayout(self)
        self.custom_button = CustomButton("Press Me", self)
        self.custom_button.setObjectName('customButton')
        self.label = QtGui.QLabel("Nothing...", parent=self)
        layout.addWidget(self.custom_button)
        layout.addWidget(self.label)
        QtCore.QMetaObject.connectSlotsByName(self)

    @QtCore.pyqtSlot(str, name='on_customButton_customClicked')     
    def autoSlot(self, msg):
        self.label.setText(msg)

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    gui = SignalsAndSlots()
    gui.show()
    app.exec_()

但是我认为你应该考虑不使用对象名称。新式信号连接更加整洁优雅。这里有一个相同的应用程序:

import sys
from PyQt4 import QtGui, QtCore

class CustomButton(QtGui.QPushButton):
    custom_clicked = QtCore.pyqtSignal(str)
    def mousePressEvent(self, event):
        self.custom_clicked.emit("Clicked!")

class SignalsAndSlots(QtGui.QWidget):

    def __init__(self):
        QtGui.QMainWindow.__init__(self)
        layout = QtGui.QHBoxLayout(self)
        self.custom_button = CustomButton("Press Me", self)
        self.custom_button.setObjectName('customButton')
        self.label = QtGui.QLabel("Nothing...", parent=self)
        layout.addWidget(self.custom_button)
        layout.addWidget(self.label)
        self.custom_button.custom_clicked.connect(self.on_clicked)

    def on_clicked(self, msg):
        self.label.setText(msg)

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    gui = SignalsAndSlots()
    gui.show()
    app.exec_()

谢谢,现在我能够创建一个带有自动连接功能的qtimer,它正在发出信号。这个功能对我很有用,因为我正在使用Qt设计师插件功能。 - Poseidon

0

我对QtCore.QMetaObject.connectSlotsByName()的文档进行了一些调查。

为此,我制作了一个MCVE来收集不起作用的事物与正常工作的事物:

#!/usr/bin/python3

import sys
from PyQt5.QtCore import QT_VERSION_STR
from PyQt5.QtCore import QMetaObject, pyqtSlot
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QPushButton, QWidget

class MainWindow(QMainWindow):
  def __init__(self):
    QMainWindow.__init__(self)
    # build GUI
    qMain = QWidget()
    qVBox = QVBoxLayout()
    qBtnPreview1 = QPushButton("Preview 1")
    qBtnPreview1.setObjectName("preview1")
    qVBox.addWidget(qBtnPreview1)
    qBtnPreview2 = QPushButton("Preview 2")
    qBtnPreview2.setObjectName("preview2")
    qVBox.addWidget(qBtnPreview2)
    qBtnPreview3 = QPushButton("Preview 3")
    qBtnPreview3.setObjectName("preview3")
    qVBox.addWidget(qBtnPreview3)
    qBtnPreview4 = QPushButton("Preview 4")
    qBtnPreview4.setObjectName("preview4")
    qVBox.addWidget(qBtnPreview4)
    qBtnPreview5 = QPushButton("Preview 5")
    qBtnPreview5.setObjectName("preview5")
    qVBox.addWidget(qBtnPreview5)
    qBtnPreview6 = QPushButton("Preview 6")
    qBtnPreview6.setObjectName("preview6")
    qVBox.addWidget(qBtnPreview6)
    qMain.setLayout(qVBox)
    self.setCentralWidget(qMain)
    # install signal handlers
    qBtnPreview1.clicked.connect(lambda: print("preview1 clicked."))
    qBtnPreview2.clicked.connect(lambda: print("preview2 clicked."))
    qBtnPreview3.clicked.connect(lambda: print("preview3 clicked."))
    qBtnPreview4.clicked.connect(lambda: print("preview4 clicked."))
    qBtnPreview5.clicked.connect(lambda: print("preview5 clicked."))
    qBtnPreview6.clicked.connect(lambda: print("preview6 clicked."))
    QMetaObject.connectSlotsByName(self)

  @pyqtSlot()
  def preview1(self):
    print("MainWindow.preview1() called.")
  
  @pyqtSlot()
  def preview2_clicked(self):
    print("MainWindow.preview2_clicked() called.")
  
  @pyqtSlot()
  def on_preview3(self):
    print("MainWindow.on_preview3() called.")

  @pyqtSlot()
  def on_preview4_clicked(self):
    print("MainWindow.on_preview4_clicked() called.")
  
  @pyqtSlot(name='on_preview5_clicked')
  def preview5_clicked(self):
    print("MainWindow.preview5_clicked() called.")

  def on_preview6_clicked(self):
    print("MainWindow.on_preview6_clicked() called.")
    
if __name__ == '__main__':
  print("Qt Version: {}".format(QT_VERSION_STR))
  app = QApplication(sys.argv)
  # build GUI
  qWinMain = MainWindow()
  qWinMain.show()
  # runtime loop
  sys.exit(app.exec_())

输出:

$ ./testQMetaObjectConnectSlotsByName.py 
Qt Version: 5.9.3

snapshot of testQMetaObjectConnectSlotsByName.py

点击六个按钮各一次后,我得到了:

preview1 clicked.
preview2 clicked.
preview3 clicked.
preview4 clicked.
MainWindow.on_preview4_clicked() called.
preview5 clicked.
MainWindow.preview5_clicked() called.
preview6 clicked.
MainWindow.on_preview6_clicked() called.
MainWindow.on_preview6_clicked() called.

观察结果:

  1. MainWindow.preview1()MainWindow.preview2_clicked()MainWindow.on_preview3() 未连接。它们不遵循 QtCore.QMetaObject.connectSlotsByName() 所需的约定。

  2. MainWindow.on_preview4_clicked() 按名称连接 - 正确遵循所需的名称约定。

  3. MainWindow.preview5_clicked() 也已连接 - 通过使用 @pyqtSlot(name='on_preview5_clicked') 给出所需的名称。

  4. MainWindow.on_preview6_clicked() 也已连接 - 即使没有明确标记为槽。 (但是,由于某种原因它会被调用两次,这可能是不希望的。)

  5. 显式连接在任何情况下都有效,并且对我来说似乎更加健壮。

进一步阅读:


实际上,这是我对另一个问题的回答(SO: 如何在每个Qlabel PyQt5中显示两个图像),直到我意识到QtCore.QMetaObject.connectSlotsByName()不太可能在那个问题中发挥作用。 因此,我将它移到了这里,即使问题有点过时,但可能更合适。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接