PySide 发射信号导致 Python 崩溃

1 投票
2 回答
4064 浏览
提问于 2025-04-17 09:53

我正在学习一本叫做《用Python和Qt快速编写图形界面程序》的书,遇到了一个关于信号和槽的项目问题。我下载了作者的代码来和自己的代码对比,发现看起来都一样,但当我从一个派生的旋转框类发出信号时,Python就崩溃了。以下是我所有的代码:

import sys
from PySide.QtCore import *
from PySide.QtGui import *

class ZeroSpinBox(QSpinBox):
    zeros = 0

    def __init__(self, parent=None):
        super(ZeroSpinBox, self).__init__(parent)
        self.connect(self, SIGNAL("valueChanged(int)"), self.checkzero)

    def checkzero(self):
        if self.value() == 0:
            self.zeros += 1
            self.emit(SIGNAL("atzero"), self.zeros)

class Form(QDialog):
    def __init__(self, parent= None):
        super(Form, self).__init__(parent)

        dial = QDial()
        dial.setNotchesVisible(True)
        spinbox = ZeroSpinBox()
        spinbox.setRange(0,200)
        dial.setRange(0,200)

        layout = QHBoxLayout()
        layout.addWidget(dial)
        layout.addWidget(spinbox)
        self.setLayout(layout)

        self.connect(dial, SIGNAL("valueChanged(int)"), spinbox, SLOT("setValue(int)"))
        self.connect(spinbox, SIGNAL("valueChanged(int)"), dial, SLOT("setValue(int)"))
        self.connect(spinbox, SIGNAL("atzero"), self.announce)

        self.setWindowTitle("Signals and Slots Part 2")

    def announce(self, zeros):
        print "ZeroSpinBox has been at zero %d times" % zeros


if __name__ == "__main__":
    app = QApplication(sys.argv)
    form = Form()
    form.show()
    app.exec_()

我的问题发生在旋转框的值降到零的时候,这时会调用ZeroSpinBox类中的checkzero(self)方法。self.zeros += 1这一行是没问题的,但在发出信号的那一行,Windows报告说Python.exe崩溃了。我收到的错误信息是“python.exe已停止工作”,控制台显示“进程以退出代码-1073741819结束”。

你知道为什么会这样吗?我使用的是Python 2.7.2和PyQT4以及PySide。

2 个回答

2

这是因为PySide和PyQt4之间存在一些差异,具体的内容可以在这里找到(严格来说,这其实是PySide的一个bug——使用不支持的信号语法应该会报错,而不是让程序崩溃)。

你正在使用的书是专门为PyQt4写的,所以在使用的时候,你需要了解PySide和PyQt4之间的不同。比如可以参考这里

需要注意的是,你的脚本在PyQt4版本下运行得很好,无论信号的括号部分是否存在——关键是它们要一致。不过,这只适用于用户自定义的信号,对于预定义的Qt信号和槽,你必须始终包含签名中的括号部分。

还有一点你需要知道的是,你使用的信号/槽语法已经被一种更符合Python风格的新语法所取代。因此,建议你在某个时候阅读一下这里的指导,如果你的书没有涉及到这些内容的话。

4

SIGNAL("atzero") 替换成 SIGNAL("atzero(int)"),在 checkzeroForm.__init__ 里都要这样做,因为你现在的写法没有传递任何参数。

补充说明:你在“新风格”里的代码,

import sys
from PySide.QtCore import *
from PySide.QtGui import *

class ZeroSpinBox(QSpinBox):
    zeros = 0

    def __init__(self, parent=None):
        super(ZeroSpinBox, self).__init__(parent)
        self.valueChanged.connect(self.checkzero)

    atzero = Signal(int)

    def checkzero(self):
        if self.value() == 0:
            self.zeros += 1
            self.atzero.emit(self.zeros)

class Form(QDialog):
    def __init__(self, parent= None):
        super(Form, self).__init__(parent)

        dial = QDial()
        dial.setNotchesVisible(True)
        spinbox = ZeroSpinBox()
        spinbox.setRange(0,200)
        dial.setRange(0,200)

        layout = QHBoxLayout()
        layout.addWidget(dial)
        layout.addWidget(spinbox)
        self.setLayout(layout)

        dial.valueChanged.connect(spinbox.setValue)
        spinbox.valueChanged.connect(dial.setValue)
        spinbox.atzero.connect(self.announce)

        self.setWindowTitle("Signals and Slots Part 2")

    @Slot(int)
    def announce(self, zeros):
        print "ZeroSpinBox has been at zero %d times" % zeros


if __name__ == "__main__":
    app = QApplication(sys.argv)
    form = Form()
    form.show()
    app.exec_()

撰写回答