일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- Apple
- Home Assistant
- ConnectedHomeIP
- 티스토리챌린지
- 미국주식
- 코스피
- 엔비디아
- RS-485
- 공모주
- Bestin
- 해외주식
- 오블완
- Python
- homebridge
- 국내주식
- matter
- raspberry pi
- 현대통신
- MQTT
- 월패드
- esp32
- 파이썬
- 매터
- 힐스테이트 광교산
- 홈네트워크
- 퀄컴
- 나스닥
- 배당
- Espressif
- 애플
- 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 |