일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
- 공모주
- 매터
- 힐스테이트 광교산
- RS-485
- 월패드
- matter
- 나스닥
- MQTT
- 파이썬
- 홈네트워크
- 배당
- Bestin
- 오블완
- 해외주식
- homebridge
- 국내주식
- 현대통신
- 라즈베리파이
- esp32
- 애플
- ConnectedHomeIP
- 티스토리챌린지
- raspberry pi
- 미국주식
- Espressif
- Apple
- Home Assistant
- Python
- 취미생활
- 마이크로소프트
- Today
- Total
YOGYUI
PyQt5 - Connect pyqtSignal in For loop (lambda problem) 본문

PyQt5의 pyqtSignal 혹은 pyqtBoundSignal을 함수와 연결(connect)시, 여러 객체를 for문 안에서 lambda를 사용하여 연결할 경우 정상적으로 동작하지 않는 문제가 있다
다음 예시를 통해 문제를 확인해보자
if __name__ == '__main__':
import sys
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
app = QCoreApplication.instance()
if app is None:
app = QApplication(sys.argv)
btns = [
QPushButton('BUTTON1'),
QPushButton('BUTTON2'),
QPushButton('BUTTON3')
]
widget = QWidget()
edit = QLineEdit()
vlayout = QVBoxLayout(widget)
for btn in btns:
vlayout.addWidget(btn)
btn.clicked.connect(lambda: edit.setText(btn.text() + ' is Clicked'))
vlayout.addWidget(edit)
widget.show()
app.exec_()



버튼 세개 모두 클릭했을 때 세번째 버튼의 텍스트인 'BUTTON3'가 호출되는 것을 볼 수 있다
이는 PyQt의 문제가 아니라, for문안에서 lambda를 사용할 경우 lambda가 호출되는 시점의 전달인자 (btn.text())가 연결함수(edit.setText)와 연동되기 때문에 최후에 바인딩된 'BUTTON3'가 모든 호출 시 사용되기 때문이다
이를 해결하려면 일반적으로는 각 버튼별로 따로 signal binding을 해줘야 한다
btns[0].clicked.connect(lambda: edit.setText(btns[0].text() + ' is Clicked'))
btns[1].clicked.connect(lambda: edit.setText(btns[1].text() + ' is Clicked'))
btns[2].clicked.connect(lambda: edit.setText(btns[2].text() + ' is Clicked'))
하지만 연결해야할 객체가 많아지면 코드가 지저분해지는 문제가 있다
이를 위해 python built-in module 중 functools의 partial을 활용하면 for문을 사용할 수 있다
(https://docs.python.org/3/library/functools.html)

중요한 대목은 'freezes some portion of a function's arguments'!!
다음과 같이 코드를 수정해보자
(lambda 대신 functools.partial(func, args)를 활용하도록 바꾼 것이 전부)
if __name__ == '__main__':
import sys
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from functools import partial
app = QCoreApplication.instance()
if app is None:
app = QApplication(sys.argv)
btns = [
QPushButton('BUTTON1'),
QPushButton('BUTTON2'),
QPushButton('BUTTON3')
]
widget = QWidget()
edit = QLineEdit()
vlayout = QVBoxLayout(widget)
for btn in btns:
vlayout.addWidget(btn)
btn.clicked.connect(partial(edit.setText, btn.text() + ' is Clicked'))
vlayout.addWidget(edit)
widget.show()
app.exec_()



UI 코드를 짤 때 유사한 동작을 해야하는 컨트롤들은 lambda를 사용해서 공통함수로 처리하는 경우가 많은데, for문을 사용하고자 할 경우 partial을 사용해서 아주 간단하게 구현할 수 있다
끝~!
[참고]
https://stackoverflow.com/questions/19837486/lambda-in-a-loop
'Software > Python' 카테고리의 다른 글
Python - list 요소 뒤집기 (reverse list elements) (0) | 2021.08.20 |
---|---|
Python - 윈도OS 환경 변수 변경하기 (Modify Environment Variables) (1) | 2021.08.17 |
PyQt5 - QTextEdit Tab Width 설정하기 (0) | 2021.07.15 |
Python - Decorator in Class (0) | 2021.07.05 |
PyQt5 - QDoubleSpinbox 더블클릭 시 텍스트 전체선택 (0) | 2021.06.21 |