gtkidle_add 没有运行?
我有一个双线程应用程序:GUI 和一些后台工作。我试图向主线程发送请求以进行 GUI 更新(移动进度条),但它似乎不起作用。我将其归结为一个非常小的示例:
import pygtk
pygtk.require('2.0')
import glib
import gtk
import threading
import sys
import time
def idle():
sys.stderr.write('Hello from another world.\n')
sys.stderr.flush()
gtk.main_quit()
def another_thread():
time.sleep(1)
glib.idle_add(idle)
thread = threading.Thread(target=another_thread)
thread.start()
gtk.main()
我认为,这应该从主/GUI 线程打印一些内容到标准错误,但什么也没有发生。而且它也不会退出,因此 gtk.main_quit
不会被调用。
此外,向 stderr
添加更多输出的行为很奇怪。如果我将线程的函数更改为:
sys.stderr.write('----\n')
sys.stderr.write('----\n')
sys.stderr.flush()
sys.stderr.write('After.\n')
sys.stderr.flush()
我会看到 1 行,有时 2 行输出。它看起来像是主线程进入 gtk.main 的某种竞争条件,但我不知道为什么会这样。
I have a two-thread application: GUI, and some background work. I'm trying to send requests to the main thread to do GUI updates (move a progress bar), but it doesn't seem to work. I've boiled it down to a really minimal example:
import pygtk
pygtk.require('2.0')
import glib
import gtk
import threading
import sys
import time
def idle():
sys.stderr.write('Hello from another world.\n')
sys.stderr.flush()
gtk.main_quit()
def another_thread():
time.sleep(1)
glib.idle_add(idle)
thread = threading.Thread(target=another_thread)
thread.start()
gtk.main()
This should, I thought, print something to standard error from the main/GUI thread, but nothing happens. And it doesn't quit, either, so gtk.main_quit
isn't being called.
Also, adding more output to stderr
acts weirdly. If I change the thread's function to:
sys.stderr.write('----\n')
sys.stderr.write('----\n')
sys.stderr.flush()
sys.stderr.write('After.\n')
sys.stderr.flush()
I see 1, sometimes 2 lines out output. It looks like some kind of race condition with the main thread entering gtk.main
, but I don't know why this would be.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
在多线程环境中使用 glib 之前,需要初始化 glib 的线程支持。只需调用:
在调用 glib 函数之前。
You need to init glib's thread support before using glib in a multi-threaded environment. Just call:
Before calling into glib functions.
为什么不使用
glib.timeout_add_seconds(1,idle)
并从idle()
返回False
而不是启动一个线程然后休眠1秒?从另一个线程启动空闲函数是相当多余的,因为空闲函数已经在另一个线程中运行。编辑:
“从另一个线程启动空闲函数是多余的”,我的意思是您不必启动空闲函数来扰乱 GUI 并更新进度条。你不能在其他线程中弄乱 GUI,这是一个神话。
让我在这里重申一下从其他线程进行 GTK 调用所需的内容:
glib.threads_init()
或gobject.threads_init()
,无论您有什么,如 vanza 中所述回答。gtk.main()
之前调用。 C 文档建议在 gtk_init() 之前调用它,但如果我没记错的话,它是在 PyGTK 导入时调用的。gtk.main()
的调用括在gtk.gdk.threads_enter()
和gtk.gdk.threads_leave()
之间。将来自以下位置的任何 GTK 函数调用括起来:
在
gtk.gdk.threads_enter()
和gtk.gdk.threads_leave()
之间。。请注意,您还可以使用
with gtk.gdk.lock:
并在该内进行调用
-块。这里有一些资源也解释了这个问题:
Why not use
glib.timeout_add_seconds(1, idle)
and returnFalse
fromidle()
instead of starting a thread and then sleeping 1 second? Starting an idle function from another thread is quite redundant, since idle functions already run in another thread.EDIT:
By "starting an idle function from another thread is redundant", I meant that you don't have to start an idle function in order to mess with the GUI and update the progress bar. It is a myth that you can't mess with the GUI in other threads.
Let me restate here what is needed to do GTK calls from other threads:
glib.threads_init()
orgobject.threads_init()
, whichever you have, as discussed in vanza's answer.gtk.gdk.threads_init()
. I am pretty sure you are right in your answer, this only has to be called beforegtk.main()
. The C docs suggest calling it beforegtk_init()
, but that's called at import time in PyGTK if I'm not mistaken.gtk.main()
betweengtk.gdk.threads_enter()
andgtk.gdk.threads_leave()
.Bracket any calls to GTK functions from:
between
gtk.gdk.threads_enter()
andgtk.gdk.threads_leave()
.Note that instead of surrounding your calls with
enter
/leave
pairs, you can also usewith gtk.gdk.lock:
and do your calls within thatwith
-block.Here are some resources which also explain the matter:
以下内容使上述内容对我有用:
在执行其他操作之前调用
gtk.gdk.threads_init()
:在启动线程之前、在进入gtk.main()
之前。我认为实际上,这只需要在进入 gtk.main() 之前调用,但调用它很容易,因为一切都是单线程且简单的。在空闲回调中(示例中为
idle
),在 GTK 内容之前调用gtk.gdk.threads_enter()
(只需在函数顶部即可轻松完成) ),并在函数结束之前调用gtk.gdk.threads_leave()
。gtk.gdk.threads_init() 似乎还告诉 PyGTK 在进入睡眠状态时不要保留 GIL - 我认为我丢失了 aux 的一些输出。示例中的线程只是因为休眠主线程(在
gtk.main()
中休眠)仍然持有 GIL。据我所知,gtk.gdk.threads_init() 为 PyGTK 和 GTK+ 注入了良好的魔力。因此,即使您启动一个不接触 GTK+、不使用 glib、gobject 等执行任何操作、仅进行基本计算的线程,也需要 gtk.gdk.threads_init() 。The following makes the above work for me:
A call to
gtk.gdk.threads_init()
before doing anything else: before starting threads, before enteringgtk.main()
. I think in reality, this only needs to be called before enteringgtk.main()
, but it is easy enough to call it while everything is single threaded and simple.In the idle callback (
idle
, in the example), a call togtk.gdk.threads_enter()
before GTK stuff (easy to do just at the top of the function), and a call togtk.gdk.threads_leave()
, before the end of the function.gtk.gdk.threads_init()
seems to also tell PyGTK not hold the GIL when it goes to sleep - I think I was missing some output from the aux. thread in the example just because the sleeping main thread (sleeping ingtk.main()
) was still holding the GIL.gtk.gdk.threads_init()
, as far as I can tell, instills good mojo into PyGTK and GTK+. Because of this,gtk.gdk.threads_init()
is needed even if you launch a thread that doesn't touch GTK+, doesn't do anything with glib, gobject, etc., just does basic computation.