PySide 发射信号导致 Python 崩溃
我正在学习一本叫做《用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 个回答
这是因为PySide和PyQt4之间存在一些差异,具体的内容可以在这里找到(严格来说,这其实是PySide的一个bug——使用不支持的信号语法应该会报错,而不是让程序崩溃)。
你正在使用的书是专门为PyQt4写的,所以在使用的时候,你需要了解PySide和PyQt4之间的不同。比如可以参考这里。
需要注意的是,你的脚本在PyQt4版本下运行得很好,无论信号的括号部分是否存在——关键是它们要一致。不过,这只适用于用户自定义的信号,对于预定义的Qt信号和槽,你必须始终包含签名中的括号部分。
还有一点你需要知道的是,你使用的信号/槽语法已经被一种更符合Python风格的新语法所取代。因此,建议你在某个时候阅读一下这里的指导,如果你的书没有涉及到这些内容的话。
把 SIGNAL("atzero")
替换成 SIGNAL("atzero(int)")
,在 checkzero
和 Form.__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_()