for ループでウィジェットを動的に作成する際、シグナルとの接続でラムダ式を使う場合には注意が必要です。
例えば、以下のコードはラムダ式を使っていますが、
for i in range(5):
button = QPushButton(f'Button{i}')
button.clicked.connect(lambda: print(f'Button{i} clicked'))
この場合、どのボタンを押しても Button4 clicked
と出力されます。
ラムダ式は遅延評価のためです。
これについては以下の記事を参考にしてください。
じゃあデフォルト引数を与えて即時評価してやろう以下のように変更すると、
for i in range(5):
button = QPushButton(f'Button{i}')
button.clicked.connect(lambda x=i: print(f'Button{x} clicked'))
今度は ButtonFalse clicked
と出力されてしまいました。QPushButton
の clicked
シグナルは、ボタンの状態を引数で渡してくるそうなので、デフォルト引数が bool 値で上書きされたためです。
かと言って、引数の数を増やすとエラーになります。
なので、この場合ラムダ式は使えず、functools.partial
を使うしかありません。
for i in range(5):
button = QPushButton(f'Button{i}')
button.clicked.connect(partial(self.__clicked, i))
以下、動作確認用のコードです。
import sys
from functools import partial
from PySide6.QtWidgets import *
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
vbox_layout = QVBoxLayout()
for i in range(5):
button = QPushButton(f'Button{i}')
button.clicked.connect(partial(self.__clicked, i))
vbox_layout.addWidget(button)
widget = QWidget()
widget.setLayout(vbox_layout)
self.setCentralWidget(widget)
def __clicked(self, i):
print(f'Button{i} clicked')
if __name__ == '__main__':
app = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
sys.exit(app.exec())