문제

내 스크립트 중 하나에 GUI를 추가하기로 결정했습니다.스크립트는 간단한 웹 스크레이퍼입니다.데이터를 다운로드하고 구문 분석하는 데 시간이 걸릴 수 있으므로 작업자 스레드를 사용하기로 결정했습니다.나는 PySide를 사용하기로 결정했지만 일반적으로 Qt에 대한 지식은 상당히 제한되어 있습니다.

스크립트는 보안 문자를 발견할 때 사용자 입력을 기다려야 하기 때문에 나는 보안 문자가 나타날 때까지 기다려야 한다고 결정했습니다. QLineEdit 화재 returnPressed 그런 다음 유효성 검사를 위해 보낼 수 있도록 해당 내용을 작업자 스레드로 보냅니다.이는 리턴 키가 눌릴 때까지 바쁘게 기다리는 것보다 낫습니다.

신호를 기다리는 것이 생각만큼 간단하지 않은 것 같고 잠시 검색한 후 다음과 유사한 몇 가지 솔루션을 발견했습니다. 이것.하지만 스레드 간 신호 전달과 작업자 스레드의 로컬 이벤트 루프로 인해 내 솔루션이 좀 더 복잡해졌습니다.

몇 시간 동안 만지작거려도 여전히 작동하지 않습니다.

무슨 일이 일어날까요?

  • 보안 문자가 참조될 때까지 데이터를 다운로드하고 루프에 들어갑니다.
  • 보안 문자를 다운로드하여 사용자에게 표시하고 시작하세요. QEventLoop 전화로 self.loop.exec_()
  • 출구 QEventLoop 전화로 loop.quit() 다음을 통해 연결된 작업자 스레드 슬롯에서 self.line_edit.returnPressed.connect(self.worker.stop_waiting) 에서 main_window 수업
  • 보안 문자를 검증하고 검증이 실패하면 반복합니다. 그렇지 않으면 지금 다운로드할 수 있는 마지막 URL을 다시 시도한 후 다음 URL로 이동하세요.

무슨 일이야:

  • ...위 참조...

  • 종료 QEventLoop 작동하지 않습니다. self.loop.isRunning() 보고 False 전화 한 후 exit(). self.isRunning 보고 True, 따라서 스레드는 이상한 상황에서도 죽지 않는 것 같습니다.여전히 스레드는 다음에서 멈춥니다. self.loop.exec_() 선.따라서 이벤트 루프가 더 이상 실행되지 않는다고 알려주더라도 스레드는 이벤트 루프 실행을 중단합니다.

  • GUI는 작업자 스레드 클래스의 슬롯처럼 응답합니다.작업자 스레드로 전송되는 텍스트, 이벤트 루프 및 스레드 자체의 상태를 볼 수 있지만 위에서 언급한 줄 이후에는 아무 것도 실행되지 않습니다.

코드는 약간 복잡하므로 중요하지 않은 부분을 제외하고 약간의 의사 코드-파이썬 혼합을 추가합니다.

class MainWindow(...):
    # couldn't find a way to send the text with the returnPressed signal, so I
    # added a helper signal, seems to work though. Doesn't work in the
    # constructor, might be a PySide bug?
    helper_signal = PySide.QtCore.Signal(str)
    def __init__(self):
        # ...setup...
        self.worker = WorkerThread()
        self.line_edit.returnPressed.connect(self.helper_slot)
        self.helper_signal.connect(self.worker.stop_waiting)

    @PySide.QtCore.Slot()
    def helper_slot(self):
        self.helper_signal.emit(self.line_edit.text())

class WorkerThread(PySide.QtCore.QThread):
    wait_for_input = PySide.QtCore.QEventLoop()

    def run(self):
        # ...download stuff...
        for url in list_of_stuff:
            self.results.append(get(url))

    @PySide.QtCore.Slot(str)
    def stop_waiting(self, text):
        self.solution = text
        # this definitely gets executed upon pressing return
        self.wait_for_input.exit()

    # a wrapper for requests.get to handle captcha
    def get(self, *args, **kwargs):
        result = requests.get(*args, **kwargs)
        while result.history: # redirect means captcha
            # ...parse and extract captcha...
            # ...display captcha to user via not shown signals to main thread...

            # wait until stop_waiting stops this event loop and as such the user
            # has entered something as a solution
            self.wait_for_input.exec_()

            # ...this part never get's executed, unless I remove the event
            # loop...

            post = { # ...whatever data necessary plus solution... }
            # send the solution
            result = requests.post('http://foo.foo/captcha_url'), data=post)
        # no captcha was there, return result
        return result

frame = MainWindow()
frame.show()
frame.worker.start()
app.exec_()
도움이 되었습니까?

해결책

슬롯은 생성된 스레드 내부에서 실행됩니다. QThread, 스레드에는 없습니다. QThread 통제 수단.

당신은 이동해야 QObject 스레드에 연결하고 해당 슬롯을 신호에 연결하면 해당 슬롯이 스레드 내부에서 실행됩니다.

class SignalReceiver(QtCore.QObject):
    def __init__(self):
        self.eventLoop = QEventLoop(self)             

    @PySide.QtCore.Slot(str)
    def stop_waiting(self, text):                   
        self.text = text
        eventLoop.exit()

    def wait_for_input(self):
        eventLoop.exec()
        return self.text

class MainWindow(...):
     ...
     def __init__(self):
        ...
        self.helper_signal.connect(self.worker.signalReceiver.stop_waiting)

class WorkerThread(PySide.QtCore.QThread): 
    def __init__(self):
        self.signalReceiver = SignalReceiver() 
        # After the following call the slots will be executed in the thread             
        self.signalReceiver.moveToThread(self)    

    def get(self,  *args, **kwargs):
        result = requests.get(*args, **kwargs)
        while result.history:
            ...
            self.result = self.signalReceiver.wait_for_input()   

다른 팁

당신이 설명하는 것은 이상적으로 보입니다. QWaitCondition.

간단한 예:

import sys
from PySide import QtCore, QtGui

waitCondition = QtCore.QWaitCondition()
mutex = QtCore.QMutex()

class Main(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(Main, self).__init__()

        self.text = QtGui.QLineEdit()
        self.text.returnPressed.connect(self.wakeup)

        self.worker = Worker(self)
        self.worker.start()

        self.setCentralWidget(self.text)

    def wakeup(self):
        waitCondition.wakeAll()

class Worker(QtCore.QThread):
    def __init__(self, parent=None):
        super(Worker, self).__init__(parent)

    def run(self):
        print "initial stuff"

        mutex.lock()
        waitCondition.wait(mutex)
        mutex.unlock()

        print "after returnPressed"

if __name__=="__main__":      
    app = QtGui.QApplication(sys.argv)
    m = Main()
    m.show()
    sys.exit(app.exec_())
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top