将信号连接到插槽会立即发出信号

时间:2014-02-25 04:17:42

标签: python pyqt python-3.3 pyqt5

我正在用python 3.3.3和pyqt5编写程序。我连接了很多信号和插槽没有问题。这个问题导致了问题。我的代码如下:

   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)

此代码使用供应商和模型填充组合框。供应商组合框在启动时填充。模型组合框填充了每个供应商的不同数据。每次用户选择不同的供应商时,必须使用不同的列表更新模型组合框。

会发生什么:

当调用方法populateVendorAndModelComboBoxes时,程序的第一部分将供应商列表放入供应商组合框中。然后将在currentTextChanged信号和_vendorChanged插槽之间建立连接。然后应该首先调用_vendorChanged函数来设置Model组合框。从那时起,只要用户选择新的供应商,就应该调用_vendorChanged函数。

发生了什么:

当currentTextChanged信号和_vendorChanged插槽之间建立连接时,会立即调用_vendorChanged函数。它不应该立即调用_vendorChanged函数。任何我的其他信号/插槽连接都不会发生这种情况。 _vendorChanged函数执行时没有输出错误,然后执行点回退到vendorComboBox.currentTextChanged.connect ....语句,我立即得到一个错误TypeError:参数1有意外类型'NoneType'。

如果我注释掉了建立连接的语句,程序就会正常运行。供应商组合框由供应商填充,模型组合框填充了列表中第一个供应商的模型。这表明_vendorChanges代码工作正常。

我有两个问题。为什么connect语句导致_vendorChanged函数立即执行?错误消息的原因是什么?

3 个答案:

答案 0 :(得分:0)

错误是由于尝试连接到函数调用的结果(在本例中为None)而不是函数对象本身引起的。当然,这也解释了为什么函数立即执行。

您应该将函数调用包装在lambda中,如下所示:

    vendorComboBox.currentTextChanged.connect(
        lambda: _vendorChanged(vendorComboBox, dictVendors, modelComboBox))

答案 1 :(得分:0)

在ekhumoro的回答基础上,您还可以让信号将currentText传递给lambda函数。这意味着您只需将文本传递给函数,而不必在以后获取currentText。

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函数:

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)

希望有所帮助!

答案 2 :(得分:0)

优秀的答案,但只是想添加这个解释:

vendorComboBox.currentTextChanged.connect(_vendorChanged(vendorComboBox, dictVendors, modelComboBox))

与做

相同
obj = _vendorChanged(vendorComboBox, dictVendors, modelComboBox)
vendorComboBox.currentTextChanged.connect(obj)

现在您应该看到obj不是函数,而是使用3个参数调用_vendorChanged函数的结果。这意味着你得到信号立即触发的印象,从而调用你的函数,但事实上它只是你的函数按照指示执行。第二个问题是_vendorChanged没有返回任何东西,所以obj实际上是None。由于您尝试将信号连接到None,因此会出现此错误。解决方案在其他答案中给出。