2017-01-26 10 views
1

많은 소스의 레코드가 포함 된 큰 파일의 데이터를 플롯하는 Python 응용 프로그램에서 작업하고 있습니다. 사용자에게 제공하려는 옵션 중 하나는 원하는 경우 이러한 소스의 하위 집합에 대해서만 플롯 할 수있는 옵션입니다. 먼저 파일을 읽은 다음 고유 한 항목 수를 확인한 다음 각 소스에 고유 한 이름을 가진 QCheckBox()를 만듭니다. 각 소스는 고유 한 이름을가집니다. 이 특별한 경우 데이터 파일은 키가 고유 한 소스 인 거대한 사전으로 파싱됩니다. 각 확인란에 대해 stateChange() 이벤트에 연결 한 다음 상자가 선택되어 있지 않으면 해당 소스에 대해 플로팅을 사용하지 않도록하려고합니다. 이 경우 확인란이 선택되거나 선택 취소되면 소스 목록에서 소스를 추가/제거합니다. 내가 겪고있는 문제는 모든 체크 박스가 결국 내 목록의 최종 소스에 연결된다는 것입니다.동적으로 생성 된 QCheckBoxes에 대한 stateChange 이벤트에 연결

처음에는 생성 된 창이 올바르게 보이며 각 단추의 이름이 적절합니다. 버튼을 누를 때마다 btnstate()는 해당 버튼과 관련된 텍스트를 간단히 인쇄합니다. 예제의 라디오 버튼과 같이 각 버튼을 명시 적으로 정의 할 수 있으면이 메서드가 작동합니다. 두 단추 중 하나를 클릭하면 인쇄 된 단추의 정확한 이름을 얻을 수 있지만 확인란을 선택 취소하거나 다시 선택하면 btnstate가 "test4"를 인쇄합니다.

내가 뭘 잘못하고 있니? 나는이 일어나고있는 이유는 파이썬이 결합 루프 내부 참조를 바인드 해제하는 방법과 관련이있다 생각

import sys 
from PyQt4.QtGui import * 

def btnstate(b): 
    print b.text() 

def main(): 
    app = QApplication([]) 
    widget = QWidget() 
    layout = QVBoxLayout() 
    widget.setLayout(layout) 
    radio_layout = QHBoxLayout() 
    checkbox_layout = QHBoxLayout() 

    #setup radio buttons for config pop-up window 
    r1 = QRadioButton("Page Count") 
    r2 = QRadioButton("Date") 
    r1.toggled.connect(lambda:btnstate(r1)) 
    r2.toggled.connect(lambda:btnstate(r2)) 
    radio_layout.addWidget(r1) 
    radio_layout.addWidget(r2) 

    cbs = [] 
    for idx, serial in enumerate(["test1", "test2", "test3", "test4"]): 
     temp = QCheckBox(serial) 
     temp.setText(serial) 
     temp.setChecked(True) 
     checkbox_layout.addWidget(temp) 
     temp.stateChanged.connect(lambda:btnstate(temp)) 
     cbs.append(temp) 

    layout.addLayout(radio_layout) 
    layout.addLayout(checkbox_layout) 
    widget.show() 
    sys.exit(app.exec_()) 


if __name__ == '__main__': 
    main() 
+0

텍스트가 너무 많아 설명을 단순화하십시오. – eyllanesc

답변

0

: 여기

코드 (소스 더미 값으로 변경)입니다. 각 반복마다 temp이 재정의되기 때문에 슬롯이 업데이트되어 모든 버튼에 대해 동일한 람다를 효과적으로 호출합니다. 파이썬 참조의 세부 사항에 대한 제 이해가 심오하지 않기 때문에 일종의 손으로 물결 모양입니다. 하지만 Qt에 대한 파이썬 바인딩은 파이썬의 참조 및 가비지 수집과 관련하여 많은 문제가 있다는 것을 알고 있습니다. 위젯을 삭제할 때, Qt 객체 계층 구조가 파이썬에서 완전히 작동하지 않기 때문입니다.

어쨌든, 더 실제적으로 꽤 쉬운 수정이 있습니다. functools.partial 메서드를 사용하여 부분 함수를 람다가 아닌 슬롯으로 정의 할 수 있습니다. 버튼을 첫 번째 객체로 바인딩하고 버튼 상태 (신호 인수로 방출 됨)는 언 바운드 상태로 둡니다. 그래서 같이 :

루프에서 다음
import functools 

def btnstate(button, state): 
    print button.text() 

: 각 상자가 체크/체크되지 않은 경우이 실행

for idx, serial in enumerate(['test1', 'test2', 'test3', 'test4']): 
    temp = QCheckBox(serial) 
    checkbox_layout.addWidget(temp) 
    temp.stateChanged.connect(functools.partial(btnstate, serial)) 

, 지금 올바른 라벨을 인쇄 얻을.

편집 :

은 Qt의 객체 계층 구조와 이상한 방식으로 상호 작용하는 파이썬의 참조 횟수의 또 다른 예를 들어 this post를 참조하십시오.

+0

각'lambda '는 다르지만 모두 동일한'temp' 변수를 보게됩니다.이 변수는 루프에서 마지막으로 작성된 확인란에 바인딩되어 있습니다. 'partial'과 같은 해결책은 현재 변수를 기본 인자로 캐시하는 것입니다 :'lambda state, temp = temp : btnstate (temp)'. 문제는 실제로 가비지 콜렉션이 아닌 스코프에 관한 것입니다. 파이썬과 관련된 것입니다. – ekhumoro

+0

@ekhumoro 아, 맞습니다. 범위 지정은 GC가 아닙니다. 이것을 지적 해 주셔서 고맙습니다. 이런 종류의 세부 사항을 생각한 지 오래되었습니다. – bnaecker