function call through signal changes default keyed arguments to 'False'. Why?

≯℡__Kan透↙ 提交于 2021-02-16 20:21:06

问题


When calling a function via a signal connection as in

mybutton.clicked.connect(myfunction)

the function is called with all its arguments set to False. Even though default arguments have been set. Is this the expected behavior?

The code below shows a simple example. For my particular case having all arguments set to False is not a big issue as I can simply catch it with an if statement. However I feel that knowledge on why it is implemented this way may be helpful for further projects.

This simple program shows the behavior I would expect.

def function(foo=None):
    print(foo)

function()

None

With this minimal Qt example however, the functions argument is no longer 'None', but 'False'.

import sys
from PyQt5.QtWidgets import QMainWindow, QGridLayout, QWidget, QPushButton, QApplication

class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        centralWidget = QWidget(self)
        self.setCentralWidget(centralWidget)
        gridLayout = QGridLayout(self)
        centralWidget.setLayout(gridLayout)
        btn = QPushButton("button",self)
        gridLayout.addWidget(btn)
        btn.clicked.connect(self.function)

    def function(self, foo=None):
        print(foo)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    mainWin = MainWindow()
    mainWin.show()
    sys.exit( app.exec_() )

False

The question, i guess, is: Do I have to live with no having the default values available in my functions (i.e. do I need to reset them once I notice that they were set to False) or is there any smarter way to handle it?


回答1:


clicked is an overloaded signal, that is, they are 2 signals with the same name and with different signatures.

clicked = QtCore.pyqtSignal([], [bool])
# clicked = QtCore.pyqtSignal()
# clicked = QtCore.pyqtSignal(bool)

For more detail read this answer.

When the connection is made PyQt determines which of the signals will use, in this case the arguments of your function are similar to the second form so the signal will send the boolean False, and in your case it will replace the default value.

If you do not want that to happen then you must force PyQt to use the first form using the @QtCore.pyqtSlot() decorator so that it does not send you any parameter and therefore your function uses the default parameter.

import sys
from PyQt5 import QtCore, QtWidgets


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        centralWidget = QtWidgets.QWidget()
        self.setCentralWidget(centralWidget)
        gridLayout = QtWidgets.QGridLayout(centralWidget)
        btn = QtWidgets.QPushButton("button")
        gridLayout.addWidget(btn)
        btn.clicked.connect(self.function)

    @QtCore.pyqtSlot()
    def function(self, foo=None):
        print(foo)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    mainWin = MainWindow()
    mainWin.show()
    sys.exit(app.exec_())

Output:

None



回答2:


Another possible solution is to force keyword-only parameters with * syntax:

class MainWindow(QtWidgets.QMainWindow):
    ...
    
    def function(self, *, foo=None):
        print(foo)


来源:https://stackoverflow.com/questions/56422246/function-call-through-signal-changes-default-keyed-arguments-to-false-why

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!