Порядок исполнения с потоками и PyGTK на Windows - PullRequest
1 голос
/ 17 июня 2009

У меня проблемы с потоками и PyGTK в Windows. Согласно PyGTK FAQ (и моим собственным экспериментам), единственный способ надежно обновить GUI из дочернего потока - использовать функцию gobject.idle_add. Тем не менее, это не может быть гарантировано, когда эта функция будет вызвана. Как я могу гарантировать, что строка, следующая за gobject.idle_add, будет вызвана после функции, на которую она указывает?

Очень простой и надуманный пример:

<code>import gtk
import gobject
from threading import Thread<br>
class Gui(object):
    def __init__(self):
        self.button = gtk.Button("Click")
        self.button.connect("clicked", self.onButtonClicked)
        self.textEntry = gtk.Entry()
        self.content = gtk.HBox()
        self.content.pack_start(self.button)
        self.content.pack_start(self.textEntry)
        self.window = gtk.Window()
        self.window.connect("destroy", self.quit)
        self.window.add(self.content)
        self.window.show_all()<br>
    def onButtonClicked(self, button):
        Thread(target=self.startThread).start()<br>
    def startThread(self):
        #I want these next 2 lines to run in order
        gobject.idle_add(self.updateText)
        print self.textEntry.get_text()<br>
    def updateText(self):
        self.textEntry.set_text("Hello!")<br>
    def quit(self, widget):
        gtk.main_quit()</p>

<p>gobject.threads_init()
x = Gui()
gtk.main()

Ответы [ 2 ]

2 голосов
/ 18 июня 2009

Не пытайтесь обновить или получить доступ к вашему графическому интерфейсу из потока. Вы просто напрашиваетесь на неприятности. Например, тот факт, что "get_text" работает на всех в потоке, является почти случайностью. Возможно, вы сможете положиться на него в GTK - хотя я даже не уверен в этом - но вы не сможете сделать это в других инструментах GUI.

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

import time
import gtk
import gobject
from threading import Thread

w = gtk.Window()
h = gtk.HBox()
v = gtk.VBox()
addend1 = gtk.Entry()
h.add(addend1)
h.add(gtk.Label(" + "))
addend2 = gtk.Entry()
h.add(addend2)
h.add(gtk.Label(" = "))
summation = gtk.Entry()
summation.set_text("?")
summation.set_editable(False)
h.add(summation)
v.add(h)
progress = gtk.ProgressBar()
v.add(progress)
b = gtk.Button("Do It")
v.add(b)
w.add(v)
status = gtk.Statusbar()
v.add(status)
w.show_all()

def hardWork(a1, a2):
    messages = ["Doing the hard work to add %s to %s..." % (a1, a2),
                "Oof, I'm working so hard...",
                "Almost done..."]
    for index, message in enumerate(messages):
        fraction = index / float(len(messages))
        gobject.idle_add(progress.set_fraction, fraction)
        gobject.idle_add(status.push, 4321, message)
        time.sleep(1)
    result = a1 + a2
    gobject.idle_add(summation.set_text, str(result))
    gobject.idle_add(status.push, 4321, "Done!")
    gobject.idle_add(progress.set_fraction, 1.0)


def addthem(*ignored):
    a1 = int(addend1.get_text())
    a2 = int(addend2.get_text())
    Thread(target=lambda : hardWork(a1, a2)).start()

b.connect("clicked", addthem)
gtk.gdk.threads_init()
gtk.main()

Если вам действительно необходимо прочитать данные из графического интерфейса в середине потока (это действительно плохая идея, не делайте этого - вы можете попасть в действительно удивительные тупики, особенно когда программа закрывается ) в Twisted есть утилита blockingCallFromThread , которая сделает за вас тяжелую работу. Вы можете использовать это так:

from twisted.internet.gtk2reactor import install
install()
from twisted.internet import reactor

from twisted.internet.threads import blockingCallFromThread
from threading import Thread

import gtk

w = gtk.Window()
v = gtk.VBox()
e = gtk.Entry()
b = gtk.Button("Get Text")

v.add(e)
v.add(b)
w.add(v)

def inThread():
    print 'Getting value'
    textValue = blockingCallFromThread(reactor, e.get_text)
    print 'Got it!', repr(textValue)

def kickOffThread(*ignored):
    Thread(target=inThread).start()

b.connect("clicked", kickOffThread)
w.show_all()

reactor.run()

Если вы хотите увидеть, как работает магия, вы всегда можете прочитать источник .

1 голос
/ 17 июня 2009

Вы можете заключить две функции в другую функцию и вызвать idle_add для этой функции:

def update_and_print(self):
        self.updateText()
        print self.textEntry.get_text()

def startThread(self):
        gobject.idle_add(self.update_and_print)
...