尽管Gdk.threads_enter / leave(),线程冻结整个应用程序中的对话框

时间:2014-10-14 13:45:51

标签: python gtk gtk3 python-multithreading pygobject

我有一个应用程序可以在后台创建一些东西。为了通知用户,它会更新一些小部件及其进度。这很有效。

但有些事情在这个后台操作中有错误或其他东西,所以它必须显示一个对话框。虽然我使用Threading-Lock处理所有内容,但这会冻结我的整个应用程序。完全是我的问题的代码示例如下:

import threading, time
from gi.repository import Gtk, Gdk

def background(label, parent):
    for t in range(5):
        label.set_text(str(t))
        time.sleep(1)
    Gdk.threads_enter()
    dlg = Gtk.MessageDialog(
                type=Gtk.MessageType.INFO,
                buttons=Gtk.ButtonsType.OK,
                message_format="Time is gone.",
                title="Info")
    dlg.run()
    dlg.destroy()
    Gdk.threads_leave()

def main():
    window = Gtk.Window()
    window.connect("delete-event", Gtk.main_quit)
    label = Gtk.Label()
    window.add(label)
    window.show_all()
    thread = threading.Thread(target=background, args=(label, window))
    Gdk.threads_init()
    Gdk.threads_enter()
    thread.start()
    Gtk.main()
    Gdk.threads_leave()

if __name__=="__main__":
    main()

1 个答案:

答案 0 :(得分:3)

在gtk3中,所有gtk函数(如添加/删除/更改小部件)必须由gtk线程(运行Gtk.main()的线程)执行。

固定代码:

import threading, time
from gi.repository import Gtk, Gdk, GLib # need GLib for GLib.PRIORITY_DEFAULT

# a short utility function that I like to use.
# makes the Gtk thread execute the given callback.
def add_mainloop_task(callback, *args):
    def cb(args):
        args[0](*args[1:])
        return False
    args= [callback]+list(args)
    Gdk.threads_add_idle(GLib.PRIORITY_DEFAULT, cb, args)

def background(label, parent):
    for t in range(5):
        #~ label.set_text(str(t)) # let the gtk thread do this.
        add_mainloop_task(label.set_text, str(t))
        time.sleep(1)
    #~ Gdk.threads_enter() # don't need this.
    dlg = Gtk.MessageDialog(
                type=Gtk.MessageType.INFO,
                buttons=Gtk.ButtonsType.OK,
                message_format="Time is gone.",
                title="Info")

    # put these two functions calls inside a little function, and let the gtk thread execute it.
    def run_dialog(dlg):
        dlg.run()
        dlg.destroy()
    add_mainloop_task(run_dialog, dlg)
    #~ Gdk.threads_leave() # don't need this.

def main():
    window = Gtk.Window()
    window.connect("delete-event", Gtk.main_quit)
    label = Gtk.Label()
    window.add(label)
    window.show_all()
    thread = threading.Thread(target=background, args=(label, window))
    Gdk.threads_init()
    #~ Gdk.threads_enter() # don't need this.
    thread.start()
    Gtk.main()
    #~ Gdk.threads_leave() # don't need this.

if __name__=="__main__":
    main()