2014-07-22 1 views
0

버튼 클릭시 루프를 종료해야하는 PySide (Qt) GUI를 사용하고 있습니다. PySide에 대한 버튼 클릭은 신호를 내보내고 신호는 연결된 기능을 호출합니다. 그러나 신호가 함수를 호출하면 오류 처리를위한 자체 시스템을 사용합니다.파이썬에서 "with"문 (컨텍스트 관리자)을 수동으로 종료하는 방법이 있습니까

신호 오류 처리에 대한 수정을 정말로하고 싶지 않습니다.

오류가 발생하지 않고 "함께"문을 나눌 수있는 방법이 있습니까?

import sys 
import time 
from PySide import QtGui, QtCore 

class MyClassError(Exception): pass 

class MyClass(QtGui.QProgressDialog): 

    def __init__(self, *args, **kwargs): 
     super().__init__(*args, **kwargs) 

     # Properties 
     self.setWindowFlags(QtCore.Qt.Dialog | QtCore.Qt.WindowTitleHint) 
     self.setWindowModality(QtCore.Qt.WindowModal) 
     self.setMinimumDuration(2000) # if progress is finished in 2 sec it wont show 
     self.setRange(0, 99) 
    # end Constructor 

    def breakOut(self): 
     print("break out") 
     self.__exit__(MyClassError, "User cancel!", "") # does not break out of "with" 
     raise MyClassError("User cancel!") # PySide just prints Exception 
    # end breakOut 

    def __enter__(self): 
     self.canceled.connect(self.breakOut) 
     return self 
    # end enter (with) 

    def __exit__(self, etype, value, traceback): 
#   self.canceled.disconnect(self.breakOut) 
     if etype is None or etype == MyClassError: 
      return True 
     raise 
    # end exit 
# end class MyClass 

if __name__ == "__main__": 
    app = QtGui.QApplication(sys.argv) 

    window = QtGui.QMainWindow() 
    window.show() 

    myinst = MyClass() 
    window.setCentralWidget(myinst) 

    val = 0 
    with myinst: 
     for i in range(100): 
      myinst.setLabelText(str(i)) 
      myinst.setValue(i) 
      val = i+1 
      time.sleep(0.1) 
    # end with 
    print(val) 

    sys.exit(app.exec_()) 

신호에서의 PySide/Qt 오류 처리에서 보았던 수정 사항이 좋지 않습니다. 이 클래스를 사용하는 다른 사용자가 새 QApplication을 사용해야 함을 의미하는 QApplication을 대체합니다.

PySide/Qt 신호 오류 처리와 관련된 쉬운 방법이 있으며 "with"문을 벗어날 수있는 대안이 있습니다.

답변

2

첫째, @ dano의 대답에 대한 귀하의 의견을 with 문 내에서 장기 실행 작업을하고 싶다는 의미로 해석했습니다.

몇 가지 근본적인 오해가 있다고 생각합니다.

  1. 귀하의 with 문 (그리고 내 코드) app.exec_() 호출하기 전에이다. app.exec_()이 호출 될 때까지 Qt 이벤트 루프가 실행되지 않습니다. 따라서 귀하의 모든 with 진술은 시작된 이벤트 루프에 의해 처리 될 이벤트를 큐잉하는 것입니다. 따라서 with 문 내의 코드를 취소하는 qt 신호는 루프가 완료된 후 이후에 인 이벤트 루프가 시작될 때까지 신호가 처리되지 않으므로 작동하지 않습니다 (적어도 위에 제공된 테스트 케이스에서는 .

    이 문제를 해결하는 방법은 QT 이벤트 루프에 의해 실행되는 콜백에 with 문을 배치하는 것입니다 (예 : QTimer 또는 유사에 의해 대기하는 기능)

  2. 은 심지어 당신이 해결 가정 위의 경우, 당신은 결코 버튼을 클릭하여 장기 실행 작업을 방해 할 수 있습니다 (또는 Qt 이벤트 루프와 같은 스레드에 있기 때문에, Qt 이벤트 루프는 장기 실행 작업이 끝날 때까지 새로운 명령 (예 : 취소 버튼 누르기)을 처리 할 수 ​​없으므로 모든 종류의 신호). 장기 실행 작업이

    인 경우이를 해결하려면 장기 실행 작업을 스레드 (파이썬 스레드 또는 QThread) 또는 다른 프로세스에 넣어야합니다.그러나 이러한 접근법은 자체적으로 어려움을 겪습니다. 주로 스레드에서 GUI를 직접 업데이트하지 말아야한다는 사실을 중심으로합니다. 이 문제를 해결할 수있는 몇 가지 옵션이 있습니다. herehere을 참조하십시오. 스레드에서 GUI를 안전하게 업데이트하는 질문이 있습니다. 도움이 필요하면 qtutils이라고하는 라이브러리에이 내용 중 일부를 래핑했습니다.

나는 그러나 내가 현재의 접근 방식은 작동하지 않습니다 분명히했습니다 희망이 직접 with 문을 중단하는 방법에 대한 질문을 해결하지 않습니다 알고 있습니다. 스레드 또는 프로세스로 전환하면 스레드가 조건을 읽고 자체를 종료 할 때까지 기다리지 않고 스레드/프로세스를 즉시 죽일 수 있어야합니다. (물론 하드 킬링을하면 의도하지 않은 부작용이 발생할 수 있습니다. , 그러나 그것이 당신이하고 싶은 것처럼 보이기 때문에, 나는 당신이 이것을 생각하고 그것이 항상 좋을 것이라고 결정했다고 가정 할 것입니다.)

+0

오해를 드려 죄송합니다. 그러나 샘플 코드는 진행되는 모든 것을 반영하지 않습니다. QProgressDialog의 setValue 메소드는 GUI 이벤트를 처리하므로 이벤트 루프가 호출되지 않아도 대기열의 이벤트 항목이 올바르게 처리되고 있습니다. 나는 스레딩에 대해 알고 있으며, 귀하의 의견에 감사드립니다. 당신은 나에게 몇 가지 좋은 통찰력을주었습니다. 나는 오류가 with의 코드 줄에서 직접적으로 발생한다면 오류가 단지'with' 문을 깨뜨린다는 것을 알았다. 내 접근법이 틀렸다는 것이 맞습니다. – HashSplat

0

당신은 그냥이 루프 자체가 취소 된 것 있는지 확인할 수 있습니다 : 그것이 완전히 다른 맥락에서 호출되고 있기 때문에, 사용해서 원하는 영향이 없습니다

class MyClass(QtGui.QProgressDialog): 

    def __init__(self, *args, **kwargs): 
     super().__init__(*args, **kwargs) 

     self.cancelled = False 
     self.setWindowFlags(QtCore.Qt.Dialog | QtCore.Qt.WindowTitleHint) 
     self.setWindowModality(QtCore.Qt.WindowModal) 
     self.setMinimumDuration(2000) # if progress is finished in 2 sec it wont show 
     self.setRange(0, 99) 

    def breakOut(self): 
     print("break out") 
     self.cancelled = True 

    def __enter__(self): 
     self.canceled.connect(self.breakOut) 
     return self 

    def __exit__(self, etype, value, traceback): 
     self.canceled.disconnect(self.breakOut) 


if __name__ == "__main__": 
    app = QtGui.QApplication(sys.argv) 

    window = QtGui.QMainWindow() 
    window.show() 

    myinst = MyClass() 
    window.setCentralWidget(myinst) 

    val = 0 
    with myinst: 
     for i in range(100): 
      if myinst.cancelled: 
       break 
      myinst.setLabelText(str(i)) 
      myinst.setValue(i) 
      val = i+1 
      time.sleep(0.1) 

breakOut에 예외를 발생 for 루프. __exit__을 호출하면 PySide의 오류 처리에 관계없이 with 문이 작성되지 않습니다. 나는 당신이 뒷쪽으로 causa을 가지고 있다고 생각한다. __exit__은 with 블록을 떠날 때 호출된다. __exit__을 호출하면 with 블록이 강제로 중단되지 않는다.

+0

예외 발생은 pyside에 의해 처리됩니다. "with"문에 예외 발생이있을 때마다 코드를 종료하고 (for 루프 또한) exit 메소드로갑니다. PySide가 이것을 막고 있습니다. 불행히도 이것은 매우 큰 작업을 처리하기위한 것이며, 설정되는 값은 항상 그렇게 빠르게 발생합니다. 취소 버튼을 즉시 사용하고 싶습니다. 시간 내 주셔서 감사합니다. – HashSplat

관련 문제