Twisted + GTK: я должен запускать вещи GUI в потоках, или в потоке реактора? - PullRequest
7 голосов
/ 17 августа 2010

Из того, что я понимаю о скрученном, ничто, работающее в потоке реактора, не должно блокировать. Все действия по блокировке должны быть делегированы другим потокам, чтобы после завершения вызывать обратные вызовы в потоке реактора.

Так это же относится и к 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 ]

10 голосов
/ 18 августа 2010

Ваша проблема на самом деле не имеет ничего общего с потоками и графическим интерфейсом. Вы всегда должны использовать Twisted и GTK из одного потока: в противном случае нет необходимости.

Ваша проблема в том, что вы используете gtk.Dialog.run(). Это API, который вы никогда не должны использовать, Twisted или нет. Он запускает повторяющийся основной цикл, который вызывает блокировку текущего обработчика событий, но позволяет другим обработчикам событий выполнять один уровень вниз по стеку. GTK имеет отличную поддержку для повторных входных главных циклов, но Twisted нет (и это нормально, потому что, как я уже сказал, вы не должны их использовать).

MessageDialog.run не будет работать в потоке, так что у вас фактически нет этой опции. Это приведет к непредсказуемому поведению, которое может привести к тому, что ваше приложение будет вести себя странно или зависать. GTK прекрасно поддерживает потоки, но есть вещи, которые вы никогда не должны делать с потоками, потому что это не имеет никакого смысла, и это один из них.

Если вы имеете дело с кодом, который не выполняет никакой обработки, но просто хочет дождаться, чтобы что-то произошло (например, ожидание нажатия пользователем кнопки в диалоговом окне), вам следует использовать функции, которые возвращают Deferred с, а не темы. Как это происходит, gtk.Dialog s будет излучать сигнал в точке, на которую они отвечают: "response". Вы можете использовать это, чтобы подключить очень простую функцию, которая отображает ваше сообщение с диалоговым окном и возвращает Deferred после его завершения. Вот пример:

def showMessage(text):
    mdlg = gtk.MessageDialog(type=gtk.MESSAGE_INFO,
                             buttons=gtk.BUTTONS_CLOSE,
                             message_format=text)
    result = Deferred()
    def response(dialog, response_id):
        mdlg.destroy()
        result.callback(response_id)
        return False
    mdlg.connect("response", response)
    mdlg.show_all()
    return result
0 голосов
/ 26 июля 2011

Хотя это и не рекомендуется и не поддерживается, с Twisted 10.x кажется, что следующий код позволит продолжать использовать gtk.main () / dialog.run ()

  gobject.idle_add(lambda *x: reactor.runUntilCurrent())
  reactor.startRunning()    
  dialog.run()
0 голосов
/ 17 августа 2010

Из моего опыта работы с Gtk +, лучший вариант - запускать графический интерфейс в отдельном потоке.Вы можете общаться с потоком GUI, запустив функции в главном цикле Gtk + (с помощью функции idle_add ).Я не знаю о реакторе, но из ваших примеров кажется, что такой же способ связи с 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 должен запускаться в своем собственном потоке)

Это запустит функцию frob в потоке Gtk + и не заблокирует поток реактора.

Это запустит Gtk + в отдельном потоке:

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

Если вам нужны сложные взаимодействия с графическим интерфейсом, вам придется программировать, используя стиль передачи продолжения

Редактировать : добавлен пример запуска Gtk + в отдельном потоке

...