Postanowiłem dodać GUI do jednego z moich skryptów. Skrypt to prosty skrobaczka do stron internetowych. Postanowiłem użyć wątku roboczego, ponieważ pobieranie i parsowanie danych może trochę potrwać. Postanowiłem użyć PySide, ale moja znajomość Qt w ogóle jest dość ograniczona.PySide czekać na sygnał z głównego wątku w wątku roboczy
Ponieważ skrypt powinien czekać na dane wprowadzone przez użytkownika po napotkaniu captcha, zdecydowałem, że powinien poczekać, aż QLineEdit
odpali returnPressed
, a następnie wyśle jego zawartość do wątku roboczego, aby mógł wysłać do sprawdzenia poprawności. To powinno być lepsze niż zajęte - oczekiwanie na naciśnięcie klawisza powrotu.
Wygląda na to, że czekanie na sygnał nie jest tak proste, jak myślałem, a po pewnym czasie natrafiłem na kilka rozwiązań podobnych do this. Sygnalizacja między wątkami i lokalna pętla zdarzeń w wątku roboczym sprawiają jednak, że moje rozwiązanie jest nieco bardziej skomplikowane.
Po kilku sekundach majsterkowania nadal nie będzie działać.
Co ma się wydarzyć:
- Pobierz dane aż skieruję do captcha i wprowadź pętli
- Pobierz captcha i wyświetlić go do użytkownika, start
QEventLoop
wywołującself.loop.exec_()
- Wyjdź
QEventLoop
wywołującloop.quit()
w gnieździe wątków roboczych połączonym przezself.line_edit.returnPressed.connect(self.worker.stop_waiting)
w klasiemain_window
- Sprawdź poprawność captcha i pętli, jeśli validati na nie, w przeciwnym razie powtórzyć ostatni adres URL, który powinien być teraz do pobrania, a następnie przejść do następnego url
co się dzieje:
... patrz wyżej ...
Opuszczanie
QEventLoop
nie działa.self.loop.isRunning()
zwracaFalse
po wywołaniu jejexit()
.self.isRunning
zwracaTrue
, ponieważ wątek nie zdawał się ginąć w dziwnych okolicznościach. Nadal wątek zatrzymuje się na liniiself.loop.exec_()
. W związku z tym wątek utknął podczas wykonywania pętli zdarzeń, mimo że pętla zdarzeń mówi mi, że nie jest już uruchomiona.Interfejs GUI odpowiada tak samo, jak otwory klasy wątków roboczych. Widzę tekst wysyłany do wątku roboczego, status pętli zdarzeń i samego wątku, ale nic po wykonaniu powyższej linii.
Kod jest nieco zawiłe, takich jak dodam trochę pseudo-kodu-python-mix pomijając nieistotne:
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_()
Rzeczywiście, które rozwiązać mój problem. Dzięki. –