2011-01-02 4 views
7

저는 PyQt4로 계산기를 만들고 버튼에서 '클릭 한 (clicked)'신호를 연결하려고하는데 예상대로 작동하지 않습니다. 내가 나중에 그들을 연결하려고 루프 내에서 숫자에 대한 내 버튼을 만들고있어.루프에서 PyQt4의 슬롯과 신호를 연결하십시오.

def __init__(self):  
    for i in range(0,10): 
     self._numberButtons += [QPushButton(str(i), self)] 
     self.connect(self._numberButtons[i], SIGNAL('clicked()'), lambda : self._number(i)) 

def _number(self, x): 
    print(x) 

버튼을 클릭하면 모두 '9'가 인쇄됩니다. 그 이유는 무엇이며 어떻게 해결할 수 있습니까?

답변

12

이것은 스코프, 이름 조회 및 클로저가 파이썬에서 정의되는 방법입니다.

파이썬은 할당과 함수의 매개 변수 목록을 통해 네임 스페이스에 새로운 바인딩을 도입합니다. 따라서 i은 실제로 lambda의 네임 스페이스에 정의되어 있지 않지만 __init__()의 네임 스페이스에 정의되어 있습니다. 결과적으로 람다에서 i의 이름 검색은 __init__()의 네임 스페이스로 끝납니다. i은 결국 9에 바인딩됩니다. 이를 "폐쇄 (closure)"라고합니다.

기본값으로 키워드 인수로 i을 전달하면 이러한 사실은 알 수 없지만 잘 정의 된 의미를 해결할 수 있습니다. 말했듯이, 매개 변수 목록에있는 이름은 그래서 lambda 내부 i.__init__()i에서 독립되고, 로컬 네임 스페이스에 새로운 바인딩을 소개 :

self._numberButtons[i].clicked.connect(lambda i=i: self._number(i)) 

더 읽을 덜 마법 대안 functools.partial입니다 :

self._numberButtons[i].clicked.connect(partial(self._number, i)) 

새로운 스타일의 신호 및 슬롯 구문을 편의상 간단하게 사용하고 있습니다. 예전 스타일의 구문은 똑같이 작동합니다.

+0

'functools.partial'을 사용하면 매우 좋습니다. +1 – delnan

+0

감사합니다. 나는 functools.partial 솔루션으로 갈 것이다. – lukad

2

클로저가 생성됩니다. 클로저는 실제로 변수의 값이 아니라 변수의 값을 캡처합니다. __init__의 끝에, irange(0, 10)의 마지막 요소, 즉 9입니다. 이 범위에서 작성한 모든 람다는이 i을 참조하며 호출 될 때만 호출 할 때 i의 값을받습니다 (단, 별도의 변수를 참조하는 람다를 작성하는 __init__의 별도 호출!).

  1. 기본 매개 변수를 사용 :이 문제를 방지하려면 두 개의 인기있는 방법이

    있습니다 lambda i=i: self._number(i)은. 이 작업은 기본 매개 변수가 함수 정의 시간에 값을 바인딩하기 때문입니다.

  2. 도우미 함수 helper = lambda i: (lambda: self._number(i))을 정의하고 루프에 helper(i)을 사용하십시오. 이것은 "outer"ii이 바인딩 될 때 평가되고 - ​​앞에서 언급했듯이 다음 번 호출에서 생성 된 다음 클로저가 helper이 다른 변수를 참조하기 때문에 작동합니다.
0

Qt 방법을 사용하고 대신 QSignalMapper을 사용하십시오.

+1

하지 마십시오. 'QSignalMapper'는 람다 (lambdas) 나 부분 함수가없는 C++ 98의 잔재이며 그러한 해결 방법이 필요한 악의 부분입니다.그러나 파이썬에서는'QSignalMapper'는'functools.partial()'이나 lambda와 비교해 볼 때 단순히 불필요하게 부풀어 오른 것입니다. 'partial()'또는'lambda'를 가진 솔루션은'QSignalMapper'보다 더 짧고 우아합니다. – lunaryorn

+0

오 잘 그 선택에 대해, 나는 Qt/C++ 호환성을위한'QSignalMapper'를 선택할 것입니다. – ismail

+1

물론 선택의 문제이지만, 나는 당신의 선택을 이해하지 못하고 따라갈 수 없습니다. 파이썬의 특별한 기능을 유지하거나 C++ 호환성에 도달하기 위해 포기할 경우, C++을 사용할 수도 있습니다.) – lunaryorn

관련 문제