问题
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