twisted + gtk:我应该在线程或反应器线程中运行GUI吗?

时间:2010-08-17 16:29:48

标签: python multithreading gtk twisted pygtk

根据我对扭曲的理解,反应堆线程中运行的任何东西都不应该阻塞。应该将所有阻塞活动委托给其他线程,以便在完成后将回调激活回反应器线程。

那么这也适用于gtk的东西吗?例如,如果连接...失败,我想显示“连接失败”消息。我这样做:

def connectionFailed(self, reason):
    dlg = gtk.MessageDialog(type=gtk.MESSAGE_ERROR,
                      buttons=gtk.BUTTONS_CLOSE,
                      message_format="Could not connect to server:\n%s" % (
                          reason.getErrorMessage()))
    dlg.run()

或:

def connectionFailed(self, reason):
    dlg = gtk.MessageDialog(type=gtk.MESSAGE_ERROR,
                      buttons=gtk.BUTTONS_CLOSE,
                      message_format="Could not connect to server:\n%s" % (
                          reason.getErrorMessage()))
    reactor.callInThread(dlg.run)

或:

def connectionFailed(self, reason):
    def bloogedy():
        dlg = gtk.MessageDialog(type=gtk.MESSAGE_ERROR,
                          buttons=gtk.BUTTONS_CLOSE,
                          message_format="Could not connect to server:\n%s" % (
                              reason.getErrorMessage()))
        dlg.run()
    reactor.callInThread(bloogedy)

编辑:好吧,后两者真的搞砸了。所以我猜答案是第一个。那我的问题是:为什么?看起来这会阻塞反应堆线程。

3 个答案:

答案 0 :(得分:10)

您的问题实际上与线程和GUI无关。你应该总是在同一个线程中使用Twisted和GTK:没有必要这样做。

您的问题是您使用的是gtk.Dialog.run()。这是一个你永远不应该使用的API,Twisted与否。它运行一个可重入的主循环,它会导致当前的事件处理程序阻塞,但允许其他事件处理程序在堆栈中执行一个层。 GTK对重入主循环有很好的支持,但Twisted没有(这没关系,因为就像我说的那样,你不应该使用它们。)

MessageDialog.run无论如何都不会在一个线程中工作,所以你实际上没有那个选项。它将导致不可预测的行为,这可能导致您的应用程序表现奇怪或崩溃。 GTK对线程有很好的支持,但有些东西你从来不应该对一个线程做,因为它没有任何意义,这就是其中之一。

如果您处理的代码没有进行任何处理,只是想等待某些事情发生(比如等待用户按下对话框上的按钮),您应该使用返回{{1 s,而不是线程。当它发生时,Deferred将在响应它们的位置发出信号:“response”。您可以使用它来连接一个非常简单的函数,该函数通过对话框显示您的消息,并在完成后返回gtk.Dialog。这是一个例子:

Deferred

答案 1 :(得分:0)

根据我对Gtk +的经验,最好的选择是在单独的线程中运行GUI。您可以通过在Gtk +主循环中运行函数来通过GUI线程进行通信(通过idle_add函数)。我不知道reactor,但从你的例子来看,似乎可以通过GUI进行相同的通信方式。

就像这样(对不起,我还没有测试过代码):

def connectionFailed(self, reason):
    def frob():
        dlg = gtk.MessageDialog(type=gtk.MESSAGE_ERROR,
                          buttons=gtk.BUTTONS_CLOSE,
                          message_format="Could not connect to server:\n%s" % (
                              reason.getErrorMessage()))
        dlg.run()
    gobject.idle_add(frob)

(除了这段代码,gtk.main必须在自己的线程上运行)

这将在Gtk +线程中运行frob函数,并且不会阻塞反应器线程。

这将在单独的线程中启动Gtk +:

import threading
import pygtk
pygtk.require('2.0')
import gtk
import gobject
def gtk_thread():
    gtk.main()
threading.Thread(target=gtk_thread)

如果您需要一些复杂的GUI交互,则必须使用continuation-passing style

进行编程

修改:添加了在单独的帖子中运行Gtk +的示例

答案 2 :(得分:0)

虽然不推荐和不支持,但使用Twisted 10.x时,以下代码似乎可以继续使用gtk.main()/ dialog.run()

  gobject.idle_add(lambda *x: reactor.runUntilCurrent())
  reactor.startRunning()    
  dialog.run()