当用户移动主窗口时,以下代码会冻结,并弹出一个对话框。
https://i.stack.imgur.com/Y1n87.jpg
以前有人注意到这个问题吗?
我使用idle_add
来显示消息对话框,但这并不能解决问题。
from time import sleep
import gtk
import pygtk
pygtk.require("2.0")
from threading import Thread
import gobject
class PyApp(gtk.Window):
def __init__(self):
super(PyApp, self).__init__()
self.connect("destroy", gtk.main_quit)
self.set_size_request(250, 100)
self.set_position(gtk.WIN_POS_CENTER)
self.set_title("Test")
btn = gtk.Button("Click Here")
btn.connect("clicked", self.on_click)
self.add(btn)
self.show_all()
def decorator_threaded(func):
def wrapper(*args, **kwargs):
gtk.gdk.threads_enter()
thread = Thread(target=func, args=args, kwargs=kwargs)
thread.start()
return thread
return wrapper
@decorator_threaded
def running_on_another_thread(self):
sleep(2) # Heavy task
gobject.idle_add(self.error_message)
def on_click(self, widget):
self.running_on_another_thread()
def error_message(self):
md = gtk.MessageDialog(self,
gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_ERROR,
gtk.BUTTONS_CLOSE, "Error")
md.run()
md.destroy()
PyApp()
gtk.gdk.threads_init()
gtk.main()
我也尝试使用Gtk 3.0,但注意到了同样的错误。
import threading
import time
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import GLib, Gtk
def app_main():
win = Gtk.Window(default_height=100, default_width=250)
win.connect("destroy", Gtk.main_quit)
win.set_position(Gtk.WindowPosition.CENTER)
def error_message():
md = Gtk.MessageDialog(
transient_for=win,
flags=0,
message_type=Gtk.MessageType.ERROR,
buttons=Gtk.ButtonsType.CLOSE,
text="Error",
)
md.run()
md.destroy()
def example_target():
time.sleep(2) # Heavy task
GLib.idle_add(error_message)
win.show_all()
thread = threading.Thread(target=example_target)
thread.daemon = True
thread.start()
if __name__ == "__main__":
app_main()
Gtk.main()
解决方案是在线程上调用timeout_add
,以调度MessageDialog
在主GUI线程上运行。
类MessageBox
也使用pygtk
在Gtk2中工作。
import threading
import time
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import GLib, Gtk
class MessageBox(Gtk.MessageDialog):
def __init__(self, parent, message):
Gtk.MessageDialog.__init__(self, transient_for=parent,
flags=0,
message_type=Gtk.MessageType.ERROR,
buttons=Gtk.ButtonsType.CLOSE,
text=message)
self.set_default_response(Gtk.ResponseType.OK)
self.connect('response', self._handle_clicked)
def _handle_clicked(self, *args):
self.destroy()
def show_dialog(self):
GLib.timeout_add(0, self._do_show_dialog)
def _do_show_dialog(self):
self.show_all()
return False
def app_main():
win = Gtk.Window(default_height=100, default_width=250)
win.connect("destroy", Gtk.main_quit)
win.set_position(Gtk.WindowPosition.CENTER)
def error_message():
dialog = MessageBox(win, "Error")
dialog.show_dialog()
def example_target():
time.sleep(2) # Heavy task
error_message()
win.show_all()
thread = threading.Thread(target=example_target)
thread.daemon = True
thread.start()
if __name__ == "__main__":
app_main()
Gtk.main()
注意:此问题无法在Linux上复制。
参考文献:
- https://bytes.com/topic/python/answers/717463-showing-message-dialog-thread-using-gtk#post2858146
- https://mail.gnome.org/archives/gtk-app-devel-list/2007-October/msg00034.html.