将信号连接到插槽立即导致信号被发射
问题描述
我正在使用 python 3.3.3 和 pyqt5 编写程序.我已经连接了许多信号和插槽,没有问题.这导致了一个问题.我的代码如下:
I am writing a program with python 3.3.3 and pyqt5. I have connected many signals and slots with no problem. This one is causing a problem. My code follows:
def populateVendorAndModelComboBoxes(self, vendorComboBox, modelComboBox):
dictVendors = {}
#for rclass in sorted(list(directory.DRV_TO_RADIO.values())):
for rclass in list(directory.DRV_TO_RADIO.values()):
if not issubclass(rclass, chirp_common.CloneModeRadio) and
not issubclass(rclass, chirp_common.LiveRadio):
continue
if not rclass.VENDOR in dictVendors:
dictVendors[rclass.VENDOR] = []
dictVendors[rclass.VENDOR].append(rclass)
vendorComboBox.addItems(sorted(list(dictVendors)))
def _vendorChanged(vendorCBox, vendorsDict, modelCBox):
modelsList = vendorsDict[vendorCBox.currentText()]
added_models = []
modelCBox.clear()
for rclass in modelsList:
if rclass.MODEL not in added_models:
added_models.append(rclass.MODEL)
print("adding to modelCB")
modelCBox.addItems(sorted(added_models))
print("Done adding to modelCB")
vendorComboBox.currentTextChanged.connect(_vendorChanged(vendorComboBox, dictVendors, modelComboBox))
_vendorChanged(vendorComboBox, dictVendors, modelComboBox)
此代码使用供应商和模型填充组合框.供应商组合框在启动时填充.模型组合框填充了每个供应商的不同数据.每次用户选择不同的供应商时,必须使用不同的列表更新模型组合框.
This code populates comboboxes with vendors and models. The vendor combobox is populated at startup. The model combobox is filled with different data for each vendor. Every time the user picks a different vendor, the model combobox must be updated with a different list.
应该发生什么:
当调用方法 populateVendorAndModelComboBoxes 时,程序的第一部分将供应商列表放入供应商组合框中.然后将在 currentTextChanged 信号和 _vendorChanged 插槽之间建立连接.然后应该首先调用 _vendorChanged 函数来设置模型组合框.从那时起,每当用户选择新的供应商时,都应该调用 _vendorChanged 函数.
When the method populateVendorAndModelComboBoxes is called, the first part of the program puts the vendor list in the vendor combobox. Then a connection will be made between the currentTextChanged signal and the _vendorChanged slot. Then the _vendorChanged function should be initially called to setup the Model combobox. From then on, the _vendorChanged function should be called whenever the user picks a new vendor.
发生了什么:
当 currentTextChanged 信号和 _vendorChanged 槽建立连接时,立即调用 _vendorChanged 函数.它不应立即调用 _vendorChanged 函数.我的任何其他信号/插槽连接都不会发生这种情况._vendorChanged 函数执行没有错误,然后执行点返回到 vendorComboBox.currentTextChanged.connect.... 语句,我立即收到错误 TypeError: argument 1 has unexpected type 'NoneType'.
When the connection is made between the currentTextChanged signal and the _vendorChanged slot, the _vendorChanged function is immediately called. It should not immediately call the _vendorChanged function. This does not happen with any of my other signal / slot connections. The _vendorChanged function is executed with out error, then the execution point drops back to the vendorComboBox.currentTextChanged.connect.... statement and I immediately get an error TypeError: argument 1 has unexpected type 'NoneType'.
如果我注释掉建立连接的语句,程序就可以正常工作.供应商组合框填充了供应商,模型组合框填充了列表中第一个供应商的模型.这表明 _vendorChanges 代码工作正常.
If I comment out the statement that makes the connection, the program works without error. The vendor combobox is filled with vendors and the model combobox is filled with models of the first vendor in the list. That indicates that the _vendorChanges code is working properly.
我有两个问题.为什么 connect 语句会导致 _vendorChanged 函数立即执行?错误信息的原因是什么?
I have two questions. Why does the connect statement cause the _vendorChanged function to be immediately executed? What is the cause of the error message?
解决方案
基于 ekhumoro 的回答,您还可以让信号将 currentText 传递给 lambda 函数.这意味着您只需将文本传递给函数,而不必稍后再获取 currentText.
Building on ekhumoro's answer, you could also let the signal pass the currentText to the lambda function. This means you would just pass the text into the function and not have to get the currentText later.
def _vendorChanged(vendorText, vendorsDict, modelCBox):
modelsList = vendorsDict[vendorText]
added_models = []
modelCBox.clear()
for rclass in modelsList:
if rclass.MODEL not in added_models:
added_models.append(rclass.MODEL)
print("adding to modelCB")
modelCBox.addItems(sorted(added_models))
print("Done adding to modelCB")
vendorComboBox.currentTextChanged[str].connect(
lambda vendorText: _vendorChanged(vendorText, dictVendors, modelComboBox))
另外,如果您不需要在每次发出信号时根据 lambda 函数的当前范围更新对 dictVendors 和 modelComboBox 的引用,您可以将它们排除在参数列表之外,让 _vendorChanged 函数简单从它的父范围继承它们(这与 lambda 的父范围相同......所以我不确定有什么区别......).这样做的吸引力在于您不再需要 lamda 为信号提供可调用的...您可以直接给它 _vendorChanged 函数:
Also, if you don't need the references to dictVendors and modelComboBox to update based on the current scope of the lambda function every time the signal is emitted, you could leave them out of the parameter list and let the _vendorChanged function simply inherit them from it's parent scope (which is the same as the lambda's parent scope... so I'm not sure there's be any difference...). The appeal of this is that you no longer need the lamda to provide the signal with a callable... you can give it the _vendorChanged function directly:
def _vendorChanged(vendorText):
modelsList = dictVendors[vendorText]
added_models = []
modelComboBox.clear()
for rclass in modelsList:
if rclass.MODEL not in added_models:
added_models.append(rclass.MODEL)
print("adding to modelCB")
modelComboBox.addItems(sorted(added_models))
print("Done adding to modelCB")
vendorComboBox.currentTextChanged[str].connect(_vendorChanged)
希望有帮助!
相关文章