twisted + gtk: мне запускать вещи с GUI в потоках или в потоке реактора?

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

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

?

РЕДАКТИРОВАТЬ: О, хорошо, последние два действительно испорчены. поэтому я думаю, что ответ является первым. Тогда мой вопрос: почему? Похоже, это заблокирует поток реактора.

7
задан Claudiu 17 August 2010 в 16:36
поделиться

2 ответа

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

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

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

Если вы имеете дело с кодом, который не выполняет никакой обработки, а просто хочет подождать, пока что-то произойдет (например, ожидая, пока пользователь нажмет кнопку в диалоговом окне), вам следует использовать функции, которые возвращают Отложенные s, а не потоки. Так получилось, что gtk.Dialog будут выдавать сигнал в том месте, где они получат ответ: « 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
10
ответ дан 7 December 2019 в 01:14
поделиться

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

Если вам нужны сложные взаимодействия с GUI, вам придется программировать, используя стиль continuation-passing

Edit: added example of running Gtk+ in separate thread

0
ответ дан 7 December 2019 в 01:14
поделиться
Другие вопросы по тегам:

Похожие вопросы: