I'm trying to pass extra data through a Qt signal using a lambda, for example when connecting
triggeredorclickedsignals to actions created in a loop. But I getTypeError: <lambda>() missing 1 required positional argument: 'checked'. I've tried every combination I can think of and nothing works. How do I fix this?
This is one of the most common stumbling blocks when working with signals and slots in PySide6 and PyQt6. You're connecting a signal like clicked or triggered to a lambda, and you want to pass some extra data along — like a filename or a value from a loop. It looks like it should work, but instead you get a confusing TypeError.
Let's walk through why this happens, and then look at two clean ways to solve it.
The Problem
Suppose you're building a list of buttons in a loop, and you want each button click to call a method with a different value:
for value in ["A", "B", "C"]:
button = QPushButton(value)
button.clicked.connect(lambda val=value: self.handle_click(val))
Or maybe you're building a "Recent Files" menu:
for file in recent_files:
action = QAction(file, self)
menu.addAction(action)
action.triggered.connect(lambda filename=file: self.open_file(filename))
In both cases, you might see:
TypeError: <lambda>() missing 1 required positional argument: 'checked'
Or, perhaps even more confusingly, your method receives False instead of the filename or value you expected.
Why This Happens
Both the clicked and triggered signals can send a checked boolean parameter. Whether they actually send it (and whether your slot must accept it) depends on which version and binding of Qt you're using. The behavior has varied between PyQt5, PyQt6, PySide2, and PySide6.
When the signal does send the checked value, it gets passed as the first positional argument to your lambda. If your lambda isn't expecting it, you get the TypeError. If your lambda is using a keyword argument like filename=file, the checked boolean overwrites it — which is why you might see your app trying to open a file called False.
Here's what's happening step by step:
- The signal
triggered(checked)emits with one argument:False(orTruefor checkable actions). - Your lambda
lambda filename=file: ...has one keyword parameter. - Python assigns the signal's positional argument (
False) tofilename, overwriting the default you set.
Accept checked in Your Lambda
The most straightforward fix is to explicitly accept the checked argument in your lambda, and use a separate keyword argument for your extra data:
for file in recent_files:
action = QAction(file, self)
menu.addAction(action)
action.triggered.connect(lambda checked, filename=file: self.open_file(filename))
Here, checked catches the boolean that the signal sends, and filename keeps the value you assigned from the loop. The checked value is simply ignored inside the lambda body.
The same pattern works for clicked:
for value in ["A", "B", "C"]:
button = QPushButton(value)
button.clicked.connect(lambda checked, val=value: self.handle_click(val))
If you want to handle both cases where the signal may or may not send the checked parameter, you can use *args:
button.clicked.connect(lambda *args, val=value: self.handle_click(val))
The *args absorbs any positional arguments the signal sends (zero or one), and val stays safely as a keyword argument.
Use functools.partial Instead
An alternative that avoids lambda altogether is functools.partial. This creates a new callable with some arguments pre-filled:
from functools import partial
for file in recent_files:
action = QAction(file, self)
menu.addAction(action)
action.triggered.connect(partial(self.open_file, file))
With partial, Qt's signal system recognizes that self.open_file accepts one argument and that it's already been provided. The extra checked argument from the signal is handled gracefully — Qt will pass it as an additional argument, and since open_file only accepts one (beyond self), it's simply discarded.
This approach tends to be cleaner when you're working in loops, because you don't need to worry about variable scoping or the checked parameter at all.
The Loop Variable Trap
While we're here, it's worth mentioning a related issue that often appears alongside this one. If you write a lambda in a loop without capturing the loop variable as a default argument, every lambda ends up using the last value of the variable:
# ❌ Bug: all lambdas will use the last value of `file`
for file in recent_files:
action = QAction(file, self)
action.triggered.connect(lambda checked: self.open_file(file))
This happens because the lambda captures the variable file, not its value at the time the lambda was created. By the time any action is triggered, the loop has finished and file holds its final value.
The fix is to capture the current value using a default keyword argument:
# ✅ Correct: each lambda captures its own copy of `file`
for file in recent_files:
action = QAction(file, self)
action.triggered.connect(lambda checked, filename=file: self.open_file(filename))
Or, use functools.partial, which naturally captures the value at the time it's called:
# ✅ Also correct
action.triggered.connect(partial(self.open_file, file))
Complete Working Example
Here's a full example that demonstrates both approaches — lambda and functools.partial — in a working application with a "Recent Files" menu:
import sys
from functools import partial
from PyQt6.QtGui import QAction
from PyQt6.QtWidgets import (
QApplication,
QMainWindow,
QMessageBox,
)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Recent Files Example")
# Simulated list of recent files.
recent_files = [
"report_2024.pdf",
"notes.txt",
"photo.png",
]
menubar = self.menuBar()
file_menu = menubar.addMenu("&File")
# --- Approach 1: Using lambda ---
lambda_menu = file_menu.addMenu("Open Recent (lambda)")
for file in recent_files:
action = QAction(file, self)
lambda_menu.addAction(action)
action.triggered.connect(
lambda checked, filename=file: self.open_file(filename)
)
# --- Approach 2: Using functools.partial ---
partial_menu = file_menu.addMenu("Open Recent (partial)")
for file in recent_files:
action = QAction(file, self)
partial_menu.addAction(action)
action.triggered.connect(partial(self.open_file, file))
def open_file(self, filename):
QMessageBox.information(
self,
"Opening File",
f"You selected: {filename}",
)
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
Run this and try clicking the different menu items. Both submenus work correctly — each action opens a message box showing the correct filename.
Which Approach Should You Use?
Both work well. The lambda approach gives you more flexibility if you need to do something more complex inside the slot connection. The functools.partial approach is more concise and sidesteps the checked issue entirely, making it a good default choice when you're simply forwarding a value to a method.
Whichever you choose, the pattern is the same: make sure your callable accounts for the checked argument that Qt's signals may send, and capture loop variables by value rather than by reference.
Create GUI Applications with Python & Qt6 by Martin Fitzpatrick
(PySide6 Edition) The hands-on guide to making apps with Python — Over 15,000 copies sold!