如何刷新 GTK 的 DrawingArea 以避免"X Window Server 0.0", "Fatal Error IO 11"错误?

98 阅读1分钟

在使用 PyGTK 创建的图形界面中,利用线程来刷新 DrawingArea 时,可能会遇到诸如 "X Window Server 0.0" 和 "Fatal Error IO 11" 等错误。下面的代码示例中,我们创建了一个包含 DrawingArea 和按钮的界面,并创建了一个线程来每隔一段时间刷新 DrawingArea,但运行一段时间后就会出现上述错误:

import pygtk
pygtk.require("2.0")
import gtk
import Canvas
import threading as T
import time
import Map

gtk.gdk.threads_init()

class Interface(object):
    class ThreadCanvas(T.Thread):
        """Thread to display the map"""
        def __init__(self, interface):
            T.Thread.__init__(self)
            self.interface = interface
            self.started = True
            self.start()

        def run(self):
            while self.started:
                time.sleep(2)
                self.interface.on_canvas_expose_event()

        def stop(self):
            self.started = False

    def __init__(self):
        self.interface = gtk.Builder()
        self.interface.add_from_file("interface.glade")

    #Map
    self.map = Map.Map(2,2)

        #Canva
        self.canvas = Canvas.MyCanvas(self.interface.get_object("canvas"),self.game)
        self.interface.connect_signals(self)

        #Thread Canvas
        self.render = self.ThreadCanvas(self)

    def on_btnChange_clicked(self, widget):
    #Change map
        self.map.change()

    def on_interface_destroy(self, widget):
        self.render.stop()
        self.render.join()
        self.render._Thread__stop()
        gtk.main_quit()

    def on_canvas_expose_event(self):
        st = time.time()
        self.canvas.update(self.map)
        et = time.time()
        print "Canvas refresh in : %f times" %(et-st)

    def main(self):
        gtk.main()

2、解决方案

为了解决这个问题,我们需要在访问共享的 GTK 对象时使用 gtk.gdk.threads_enter()gtk.gdk.threads_leave() 来确保线程安全。另外,我们也可以使用 GTK 提供的定时调用函数,例如 timeout_add(...)timeout_add_seconds(...)。此外,我们应该避免使用 _Thread__stop(),而应该使用线程安全的对象或将线程设置为守护线程,以便在程序退出时自动结束线程。

下面是我们修复后的代码示例:

import pygtk
pygtk.require("2.0")
import gtk
import Canvas
import threading as T
import time
import Map

gtk.gdk.threads_init()

class Interface(object):
    class ThreadCanvas(T.Thread):
        """Thread to display the map"""
        def __init__(self, interface):
            T.Thread.__init__(self)
            self.interface = interface
            self.started = True
            self.start()

        def run(self):
            while self.started:
                time.sleep(2)
                gtk.gdk.threads_enter()
                self.interface.on_canvas_expose_event()
                gtk.gdk.threads_leave()

        def stop(self):
            self.started = False

    def __init__(self):
        self.interface = gtk.Builder()
        self.interface.add_from_file("interface.glade")

    #Map
    self.map = Map.Map(2,2)

        #Canva
        self.canvas = Canvas.MyCanvas(self.interface.get_object("canvas"),self.game)
        self.interface.connect_signals(self)

        #Thread Canvas
        self.render = self.ThreadCanvas(self)

    def on_btnChange_clicked(self, widget):
    #Change map
        self.map.change()

    def on_interface_destroy(self, widget):
        self.render.stop()
        self.render.join()
        self.render._Thread__stop()
        gtk.main_quit()

    def on_canvas_expose_event(self):
        st = time.time()
        self.canvas.update(self.map)
        et = time.time()
        print "Canvas refresh in : %f times" %(et-st)

    def main(self):
        gtk.gdk.threads_enter()
        try:
            gtk.main()
        finally:
            gtk.gdk.threads_leave()

通过这些改进,我们就可以避免出现 "X Window Server 0.0" 和 "Fatal Error IO 11" 等错误,并在 PyGTK 中安全地使用线程来刷新 DrawingArea。