[英]PySide wait for signal from main thread in a worker thread
I decided to add a GUI to one of my scripts. 我决定在我的一个脚本中添加一个GUI。 The script is a simple web scraper.
该脚本是一个简单的Web scraper。 I decided to use a worker thread as downloading and parsing the data can take a while.
我决定使用工作线程作为下载和解析数据可能需要一段时间。 I decided to use PySide, but my knowledge of Qt in general is quite limited.
我决定使用PySide,但我对Qt的了解非常有限。
As the script is supposed to wait for user input upon coming across a captcha I decided it should wait until a QLineEdit
fires returnPressed
and then send it's content to the worker thread so it can send it for validation. 由于脚本应该在遇到验证码时等待用户输入,我决定它应该等到
QLineEdit
触发returnPressed
,然后将其内容发送到工作线程,以便它可以发送它进行验证。 That should be better than busy-waiting for the return key to be pressed. 这应该比忙碌更好 - 等待按下返回键。
It seems that waiting for a signal isn't as straight forward as I thought it would be and after searching for a while I came across several solutions similar to this . 似乎等待信号并不像我想象的那样直截了当,在搜索了一段时间后,我遇到了几个类似于此的解决方案。 Signaling across threads and a local event loop in the worker thread make my solution a bit more complicated though.
跨线程的信令和工作线程中的本地事件循环使我的解决方案有点复杂。
After tinkering with it for several hours it still won't work. 经过几个小时的修补后,它仍然无法正常工作。
What is supposed to happen: 应该发生什么:
QEventLoop
by calling self.loop.exec_()
self.loop.exec_()
启动QEventLoop
QEventLoop
by calling loop.quit()
in a worker threads slot which is connected via self.line_edit.returnPressed.connect(self.worker.stop_waiting)
in the main_window
class loop.quit()
来退出QEventLoop
,该槽通过main_window
类中的self.line_edit.returnPressed.connect(self.worker.stop_waiting)
连接 What happens: 怎么了:
...see above... ...往上看...
Exiting QEventLoop
doesn't work. 退出
QEventLoop
不起作用。 self.loop.isRunning()
returns False
after calling its exit()
. self.loop.isRunning()
在调用exit()
后返回False
。 self.isRunning
returns True
, as such the thread didn't seem to die under odd circumstances. self.isRunning
返回True
,因此线程在奇怪的情况下似乎没有死。 Still the thread halts at the self.loop.exec_()
line. 线程仍在
self.loop.exec_()
行停止。 As such the thread is stuck executing the event loop even though the event loop tells me it is not running anymore. 因此,即使事件循环告诉我它不再运行,线程也会执行事件循环。
The GUI responds as do the slots of the worker thread class. GUI响应工作线程类的插槽。 I can see the text beeing send to the worker thread, the status of the event loop and the thread itself, but nothing after the above mentioned line gets executed.
我可以看到文本beeing发送到工作线程,事件循环的状态和线程本身,但在上面提到的行执行后没有任何内容。
The code is a bit convoluted, as such I add a bit of pseudo-code-python-mix leaving out the unimportant: 代码有点复杂,因此我添加了一些伪代码-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_()
What you are describing looks ideal for QWaitCondition
. 您所描述的内容看起来非常适合
QWaitCondition
。
Simple example: 简单的例子:
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_())
The slot is executed inside the thread which created the QThread
, and not in the thread that the QThread
controls. 插槽在创建
QThread
的线程内执行,而不是在QThread
控制的线程中执行。
You need to move a QObject
to the thread and connect its slot to the signal, and that slot will be executed inside the thread: 您需要将
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()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.