第一个坑,类Form。里面涉及到需要修改的,还是connet方法,原来的是这样:
=========================================
self.connect(dial, SIGNAL("valueChanged(int)"),
spinbox.setValue)
self.connect(spinbox, SIGNAL("valueChanged(int)"),
dial.setValue)
=========================================
在PyQt5,必须改成:
=========================================
dial.valueChanged.connect(spinbox.setValue)
spinbox.valueChanged.connect(dial.setValue)
=========================================
也就是说,建立各类的连接必须由该类的connect方法去实现。这样语法更为简洁。
第二个坑,类Form2。在“connet”这一部份,作者使用了QT槽,据说用SLOT()语法可能会更高效一些(中文版书P96):
=========================================
self.connect(dial, SIGNAL("valueChanged(int)"),
spinbox, SLOT("setValue(int)"))
self.connect(spinbox, SIGNAL("valueChanged(int)"),
dial, SLOT("setValue(int)"))
=========================================
查了一下官网,这种方法已经不支持了。 SIGNAL和SLOT已经作为装饰器使用了。总之,Form2还是得改成上面Form的样子:
=========================================
dial.valueChanged.connect(spinbox.setValue)
spinbox.valueChanged.connect(dial.setValue)
=========================================
第三个坑,类ZeroSpinBox和Form3。这两个类是相关联的。也是最大的一个坑。先上原来的代码:
=======================================================================
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 Form3(QDialog):
def __init__(self, parent=None):
super(Form3, self).__init__(parent)
dial = QDial()
dial.setNotchesVisible(True)
zerospinbox = ZeroSpinBox()
layout = QHBoxLayout()
layout.addWidget(dial)
layout.addWidget(zerospinbox)
self.setLayout(layout)
self.connect(dial, SIGNAL("valueChanged(int)"),
zerospinbox, SLOT("setValue(int)"))
self.connect(zerospinbox, SIGNAL("valueChanged(int)"),
dial, SLOT("setValue(int)"))
self.connect(zerospinbox, SIGNAL("atzero"), self.announce)
self.setWindowTitle("Signals and Slots")
def announce(self, zeros):
print "ZeroSpinBox has been at zero %d times" % zeros
=======================================================================
在类ZeroSpinBox里面,有一个很让人费解的东西,就是SIGNAL("atzero"),简直就是凭空出来的,原书上说“它会发射自定义的atzero”信号,但在PYQT5中,这种自定义的信号必须先用 pyqtSignal初始化:
atzero = pyqtSignal(int)
然后才能发射出去。
self.atzero.emit(self.zeros),对比一下原来的方法: self.emit(SIGNAL("atzero"), self.zeros),和connect的使用方法很相似。
另外,在类Form3中的这几个语句:
self.connect(dial, SIGNAL("valueChanged(int)"),
zerospinbox, SLOT("setValue(int)"))
self.connect(zerospinbox, SIGNAL("valueChanged(int)"),
dial, SLOT("setValue(int)"))
self.connect(zerospinbox, SIGNAL("atzero"), self.announce)
要改成:
dial.valueChanged.connect(zerospinbox.setValue)
zerospinbox.valueChanged.connect(dial.setValue)
zerospinbox.atzero.connect(self.announce)
这下简洁多了。zerospinbox.atzero.connect(self.announce)这一句,实际上是将atzero与announce方法相连接。只要拔号盘归零一次,就将归零的次数显示出来。announce为作一个“槽”,是要加上装饰器的,所以才有了下面的改动:
@pyqtSlot(int)
def announce(self, zeros):
print("ZeroSpinBox has been at zero %d times" % zeros)
第四个,有了前面的坑,这都不算坑了,类From4:
self.connect(lineedit, SIGNAL("textChanged(QString)"),
self.consoleEcho)
改成
lineedit.textChanged.connect(self.consoleEcho)
将consoleEcho方法,加上装饰器,作为槽使用:
@pyqtSlot(str)
def consoleEcho(self, text):
print(text)
原来的unicode(text),要将unicode去掉,因为phtyon3.x默认是unicode
第五个坑,类TaxRate和外部函数rateChanged
================================================
class TaxRate(QObject):
def __init__(self):
super(TaxRate, self).__init__()
self.__rate = 17.5
def rate(self):
return self.__rate
def setRate(self, rate):
if rate != self.__rate:
self.__rate = rate
self.emit(SIGNAL("rateChanged"), self.__rate)
def rateChanged(value):
print "TaxRate changed to %.2f%%" % value
================================================
这里迷惑人的是,SIGNAL("rateChanged")里的rateChanged,不是那个外部函数, rateChanged(value),而是和前面的atzero一样,是一个信号。所以,事先也要是必须要初始化的:
rateChanged = pyqtSignal(float)
然后,setRate方法里的emit方法,要改成:self.rateChanged.emit(self.__rate)。用rateChanged这个信号将self.__rate发射出去,发射到 rateChanged(value)这个函数里。因此,最后的代码:
================================================
vat = TaxRate()
vat.connect(vat, SIGNAL("rateChanged"), rateChanged)
vat.setRate(17.5) # No change will occur (new rate is the same)
vat.setRate(8.5) # A change will occur (new rate is different)
================================================
vat对象的connet方法,要改成:vat.rateChanged.connect(rateChanged),vat后面跟的rateChanged是信号,connect后面跟的rateChanged,是那个外部函数 rateChanged(value)。
第六个坑:
书中的源代码文件是signals.pyw,必须另存为signals.py,这个程序在命令提示符中显示出输出的内容。为什么会这样?因为pyw文件是静默模式,所有向原有的 stdout 和 stderr 的输出都无效,所有从原有的 stdin 的读取都只会得到 EOF
最后,上个图,理解一下ZeroSpinBox和 Form3通过信号和槽的关联方式:
需要特别注意的是,在运行程序的时候,在命令行下边,要用完整的 python signals.py 参数 运行,单独运行signals.py,参数无法接收。目前我也没找到好的解决办法。
=========================================
self.connect(dial, SIGNAL("valueChanged(int)"),
spinbox.setValue)
self.connect(spinbox, SIGNAL("valueChanged(int)"),
dial.setValue)
=========================================
在PyQt5,必须改成:
=========================================
dial.valueChanged.connect(spinbox.setValue)
spinbox.valueChanged.connect(dial.setValue)
=========================================
也就是说,建立各类的连接必须由该类的connect方法去实现。这样语法更为简洁。
第二个坑,类Form2。在“connet”这一部份,作者使用了QT槽,据说用SLOT()语法可能会更高效一些(中文版书P96):
=========================================
self.connect(dial, SIGNAL("valueChanged(int)"),
spinbox, SLOT("setValue(int)"))
self.connect(spinbox, SIGNAL("valueChanged(int)"),
dial, SLOT("setValue(int)"))
=========================================
查了一下官网,这种方法已经不支持了。 SIGNAL和SLOT已经作为装饰器使用了。总之,Form2还是得改成上面Form的样子:
=========================================
dial.valueChanged.connect(spinbox.setValue)
spinbox.valueChanged.connect(dial.setValue)
=========================================
第三个坑,类ZeroSpinBox和Form3。这两个类是相关联的。也是最大的一个坑。先上原来的代码:
=======================================================================
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 Form3(QDialog):
def __init__(self, parent=None):
super(Form3, self).__init__(parent)
dial = QDial()
dial.setNotchesVisible(True)
zerospinbox = ZeroSpinBox()
layout = QHBoxLayout()
layout.addWidget(dial)
layout.addWidget(zerospinbox)
self.setLayout(layout)
self.connect(dial, SIGNAL("valueChanged(int)"),
zerospinbox, SLOT("setValue(int)"))
self.connect(zerospinbox, SIGNAL("valueChanged(int)"),
dial, SLOT("setValue(int)"))
self.connect(zerospinbox, SIGNAL("atzero"), self.announce)
self.setWindowTitle("Signals and Slots")
def announce(self, zeros):
print "ZeroSpinBox has been at zero %d times" % zeros
=======================================================================
在类ZeroSpinBox里面,有一个很让人费解的东西,就是SIGNAL("atzero"),简直就是凭空出来的,原书上说“它会发射自定义的atzero”信号,但在PYQT5中,这种自定义的信号必须先用 pyqtSignal初始化:
atzero = pyqtSignal(int)
然后才能发射出去。
self.atzero.emit(self.zeros),对比一下原来的方法: self.emit(SIGNAL("atzero"), self.zeros),和connect的使用方法很相似。
另外,在类Form3中的这几个语句:
self.connect(dial, SIGNAL("valueChanged(int)"),
zerospinbox, SLOT("setValue(int)"))
self.connect(zerospinbox, SIGNAL("valueChanged(int)"),
dial, SLOT("setValue(int)"))
self.connect(zerospinbox, SIGNAL("atzero"), self.announce)
要改成:
dial.valueChanged.connect(zerospinbox.setValue)
zerospinbox.valueChanged.connect(dial.setValue)
zerospinbox.atzero.connect(self.announce)
这下简洁多了。zerospinbox.atzero.connect(self.announce)这一句,实际上是将atzero与announce方法相连接。只要拔号盘归零一次,就将归零的次数显示出来。announce为作一个“槽”,是要加上装饰器的,所以才有了下面的改动:
@pyqtSlot(int)
def announce(self, zeros):
print("ZeroSpinBox has been at zero %d times" % zeros)
第四个,有了前面的坑,这都不算坑了,类From4:
self.connect(lineedit, SIGNAL("textChanged(QString)"),
self.consoleEcho)
改成
lineedit.textChanged.connect(self.consoleEcho)
将consoleEcho方法,加上装饰器,作为槽使用:
@pyqtSlot(str)
def consoleEcho(self, text):
print(text)
原来的unicode(text),要将unicode去掉,因为phtyon3.x默认是unicode
第五个坑,类TaxRate和外部函数rateChanged
================================================
class TaxRate(QObject):
def __init__(self):
super(TaxRate, self).__init__()
self.__rate = 17.5
def rate(self):
return self.__rate
def setRate(self, rate):
if rate != self.__rate:
self.__rate = rate
self.emit(SIGNAL("rateChanged"), self.__rate)
def rateChanged(value):
print "TaxRate changed to %.2f%%" % value
================================================
这里迷惑人的是,SIGNAL("rateChanged")里的rateChanged,不是那个外部函数, rateChanged(value),而是和前面的atzero一样,是一个信号。所以,事先也要是必须要初始化的:
rateChanged = pyqtSignal(float)
然后,setRate方法里的emit方法,要改成:self.rateChanged.emit(self.__rate)。用rateChanged这个信号将self.__rate发射出去,发射到 rateChanged(value)这个函数里。因此,最后的代码:
================================================
vat = TaxRate()
vat.connect(vat, SIGNAL("rateChanged"), rateChanged)
vat.setRate(17.5) # No change will occur (new rate is the same)
vat.setRate(8.5) # A change will occur (new rate is different)
================================================
vat对象的connet方法,要改成:vat.rateChanged.connect(rateChanged),vat后面跟的rateChanged是信号,connect后面跟的rateChanged,是那个外部函数 rateChanged(value)。
第六个坑:
书中的源代码文件是signals.pyw,必须另存为signals.py,这个程序在命令提示符中显示出输出的内容。为什么会这样?因为pyw文件是静默模式,所有向原有的 stdout 和 stderr 的输出都无效,所有从原有的 stdin 的读取都只会得到 EOF
最后,上个图,理解一下ZeroSpinBox和 Form3通过信号和槽的关联方式:
需要特别注意的是,在运行程序的时候,在命令行下边,要用完整的 python signals.py 参数 运行,单独运行signals.py,参数无法接收。目前我也没找到好的解决办法。