1

我正在用 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 函数来设置模型组合框。从那时起,每当用户选择新的供应商时,都应该调用 _vendorChanged 函数。

怎么了:

当 currentTextChanged 信号和 _vendorChanged 插槽之间建立连接时,会立即调用 _vendorChanged 函数。它不应立即调用 _vendorChanged 函数。我的任何其他信号/插槽连接都不会发生这种情况。_vendorChanged 函数执行没有错误,然后执行点返回到 vendorComboBox.currentTextChanged.connect.... 语句,我立即收到错误 TypeError: argument 1 has unexpected type 'NoneType'。

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

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

4

3 回答 3

0

很好的答案,但只想添加这个解释:

正在做

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

和做的一样

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

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

于 2014-02-25T20:09:40.830 回答
0

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

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

    vendorComboBox.currentTextChanged.connect(
        lambda: _vendorChanged(vendorComboBox, dictVendors, modelComboBox))
于 2014-02-25T05:37:34.710 回答
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)

希望有帮助!

于 2014-02-25T06:53:38.947 回答