PySide ожидает сигнала от основного потока в рабочем потоке

Я решил добавить графический интерфейс к одному из своих скриптов. Скрипт представляет собой простой веб-скребок. Я решил использовать рабочий поток, так как загрузка и анализ данных может занять некоторое время. Я решил использовать 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 отвечает так же, как и слоты класса рабочего потока.Я вижу текст, отправляемый в рабочий поток, статус цикла событий и сам поток, но ничего после выполнения вышеупомянутой строки.

Код немного запутан, поэтому я добавляю немного псевдокода-python-mix, опуская неважное:

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_()

7
задан Peter Throwson 15 March 2012 в 00:39
поделиться