扭曲+ qtreactor:最后一个窗口关闭后如何清理?
我有一个 Twisted/PyQt 应用程序(除其他外)连接到一堆远程资源。当用户关闭窗口时,我想关闭所有连接,如果可能的话干净地关闭,否则强制关闭。
问题是,当我去关闭连接时,反应堆似乎不再允许我这样做。
这是我的应用程序代码:
# Create app and connect the Twisted/Qt reactors
app = QApplication(sys.argv)
qtreactor.qt4reactor.install()
# Shutdown Twisted when window is closed
@defer.inlineCallbacks
def stop():
print "="*40, "Closing connections..."
yield closeConnections()
print "="*40, "closed."
print "="*40, "Stopping reactor..."
reactor.stop()
print "="*40, "stopped."
app.connect(app, SIGNAL("lastWindowClosed()"), stop)
reactor.runReturn()
rc = app.exec_()
exit(rc)
这是我的清理代码的精简版本:
@defer.inlineCallbacks
def closeConnections():
for connection in connections:
print "Closing connection #%s" % connection
yield threads.deferToThread(popen("/foo/bar/cleanup %s" % connection))
print "Connection closed."
到达第一个打印语句,并且执行命令,但我从未得到第二个,也没有多次执行 for 循环。
我的分析正确吗?问题是反应堆已经关闭了,所以我再也没有收到threads.deferToThread 的回复吗?或者还有其他问题吗?此外,我该如何解决它?
谢谢, 乔纳森
I have a Twisted/PyQt application that (among other things) connects to a bunch of remote resources. When the user closes the window I want to shut down all the connections, cleanly if possible, forcibly if not.
The problem is that by the time I go to close the connections, it appears that the reactor is no longer alive to let me do so.
Here's my app code:
# Create app and connect the Twisted/Qt reactors
app = QApplication(sys.argv)
qtreactor.qt4reactor.install()
# Shutdown Twisted when window is closed
@defer.inlineCallbacks
def stop():
print "="*40, "Closing connections..."
yield closeConnections()
print "="*40, "closed."
print "="*40, "Stopping reactor..."
reactor.stop()
print "="*40, "stopped."
app.connect(app, SIGNAL("lastWindowClosed()"), stop)
reactor.runReturn()
rc = app.exec_()
exit(rc)
And here's a stripped down version of my cleanup code:
@defer.inlineCallbacks
def closeConnections():
for connection in connections:
print "Closing connection #%s" % connection
yield threads.deferToThread(popen("/foo/bar/cleanup %s" % connection))
print "Connection closed."
The first print statement is reached, and the command is executed, but I never get the second one, nor do I go through the for loop multiple times.
Is my analysis correct? Is the problem that the reactor is already down, so I never hear back from threads.deferToThread? Or is there some other problem? Furthermore, how do I fix it?
Thanks,
Jonathan
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我不知道 lastWindowClosed() 信号何时触发。然而,即使它足够早地触发,在反应堆关闭之前(阻止你做你想做的事情),我确信 PyQt 不知道如何处理你的
停止
功能。这意味着当您的异步清理代码尝试运行时,关闭过程将顺利进行。 GUI 关闭可能会在网络关闭之前完成。因此,请改用reactor.addSystemEventTrigger('before', 'shutdown', stop)。我不知道这是否会比 lastWindowClosed() 稍早或稍晚运行,但它会运行得足够早,以便反应堆仍然可用,并且<将会注意您的函数返回的 Deferred。事实上,关闭将被暂停,直到 Deferred 触发。这为您提供了进行清理所需的所有时间。
除此之外,您不应该执行
threads.deferToThread(popen("/foo/bar/cleanup %s" % connection))
:deferToThread< /code>,不是调用可调用对象的结果。正如所写,您的代码在反应器线程中运行 popen 并将文件对象传递给要调用的线程(当然,这没有意义)
spawnProcess
的所有功能(您似乎不需要),另请参阅twisted.internet.utils.getProcessOutput
。I don't know exactly when that lastWindowClosed() signal fires. However, even if it fires early enough, before the reactor has shut down (preventing you from doing what you want to do), I'm sure that PyQt doesn't know what to do with the Deferred that is returned by your
stop
function. This means that the shutdown process will proceed merrily onward while your asynchronous cleanup code tries to run. Likely the GUI shutdown will finish before your network shutdown gets anywhere.So, use
reactor.addSystemEventTrigger('before', 'shutdown', stop)
instead. I don't know if this will run slightly earlier or slightly later than lastWindowClosed(), but it will run early enough that the reactor will still be usable, and it will pay attention to the Deferred your function returns. Shutdown will be suspended, in fact, until that Deferred fires. This gives you all the time you need to do your cleanup.Separately from all that, you shouldn't do
threads.deferToThread(popen("/foo/bar/cleanup %s" % connection))
:deferToThread
, not the result of calling the callable. As written, your code runs popen in the reactor thread and passes a file object to the thread to be called (which makes no sense, of course)reactor.spawnProcess
will let you run a child process without blocking, without threads, and without worrying about mixing threads and processes. See alsotwisted.internet.utils.getProcessOutput
if you don't need all the features ofspawnProcess
(which you appear not to).