使用PyGObject自定义Gtk.Container

时间:2014-01-19 08:42:14

标签: python gtk3 pygobject

我正在尝试使用PyGObject实现自定义Gtk.Container。但我遇到了问题,甚至不知道这个错误,或者我的实现是错误的。这是我的测试代码段:

from gi.repository import Gtk, Gdk
import sys

class TestContainer(Gtk.Container):
    __gtype_name__ = "TestContainer"

    def __init__(self, *args, **kwargs):
        self.children = None
        super().__init__()

    def do_add(self, widget):
        if not widget is None:
            if not self.children is None:
                self.children.append({ "widget" : widget })
            else:
                self.children = []
                self.children.append({ "widget" : widget })
            widget.set_parent(self)

    def do_remove(self, widget):
        print("do_remove()")
        if not widget is None:
            if not self.children is None:
                for item in self.children:
                    if item["widget"] == widget:
                        self.children.remove(item)
                        widget.unparent()
                        break

    def do_child_type(self):
        print("do_child_type()")
        return(Gtk.Widget.get_type())

    def do_forall(self, include_internals, callback, *callback_parameters):
        print("do_forall() self = %x" % id(self))
        if not self.children is None and not callback is None:
            for item in self.children:
                widget = item["widget"]
                callback(widget, *callback_parameters)

    def do_get_request_mode(self):
        print("do_get_request_mode()")
        return(Gtk.SizeRequestMode.CONSTANT_SIZE)

    def do_get_preferred_height(self):
        print("do_get_preferred_height()")
        result = (50, 50)
        return(result)

    def do_get_preferred_width(self):
        print("do_get_preferred_width()")
        min_width = 0
        nat_width = 0

        if not self.children is None:
            length = len(self.children)

            for item in self.children:
                widget = item["widget"]
                child_min_width, child_nat_width = widget.get_preferred_width()
                min_width = min_width + child_min_width
                nat_width = nat_width + child_nat_width

        return(min_width, nat_width)

    def do_size_allocate(self, allocation):
        print("do_size_allocate()")
        child_allocation = Gdk.Rectangle()

        self.set_allocation(allocation)

        if self.get_has_window():
            if self.get_realized():
                self.get_window().move_resize(allocation.x, allocation.y, allocation.width, allocation.height)

        if not self.children is None:
            for item in self.children:
                widget = item["widget"]
                if widget.get_visible():
                    min_size, nat_size = widget.get_preferred_size()

                    child_allocation.x = 0
                    child_allocation.y = 0

                    if not widget.get_has_window():
                        child_allocation.x = child_allocation.x + allocation.x
                        child_allocation.y = child_allocation.x + allocation.x

                    child_allocation.width = min_size.width
                    child_allocation.height = min_size.height

                    widget.size_allocate(child_allocation)

    def do_realize(self):
        print("do_realize()")
        allocation = self.get_allocation()

        attr = Gdk.WindowAttr()
        attr.window_type = Gdk.WindowType.CHILD
        attr.x = allocation.x
        attr.y = allocation.y
        attr.width = allocation.width
        attr.height = allocation.height
        attr.visual = self.get_visual()
        attr.event_mask = self.get_events() | Gdk.EventMask.EXPOSURE_MASK

        WAT = Gdk.WindowAttributesType
        mask = WAT.X | WAT.Y | WAT.VISUAL

        window = Gdk.Window(self.get_parent_window(), attr, mask);
        window.set_decorations(0)
        self.set_window(window)
        self.register_window(window)
        self.set_realized(True)

    def do_draw(self, cr):
        allocation = self.get_allocation()
        Gtk.render_background(self.get_style_context(), cr, 0, 0, allocation.width, allocation.height)

        for item in self.children:
            self.propagate_draw(item["widget"], cr)

class TestWindow(Gtk.Window):
    __gtype_name__ = "TestWindow"
    def __init__(self):
        Gtk.Window.__init__(self, title="GTK3 PyGObject Custom Container Test")

        label = Gtk.Label(label = "Text")

        self.area = TestContainer()
        self.area.add(label)
        self.add(self.area)
        self.show_all()

    def _on_quit(self, widget, event):
        Gtk.main_quit()


MainWindow = TestWindow()
MainWindow.connect("delete-event", MainWindow._on_quit)
MainWindow.show_all()
Gtk.main()

一切顺利,直到你关闭窗口。关闭窗口后我得到了这个:

Traceback (most recent call last):
  File "/home/cheatfate/Projects/test_container.py", line 36, in do_forall
    if not self.children is None and not callback is None:
AttributeError: 'TestContainer' object has no attribute 'children'
Error in sys.excepthook:
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/apport_python_hook.py", line 58, in apport_excepthook
    from cStringIO import StringIO
  File "<frozen importlib._bootstrap>", line 1565, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1523, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 1475, in _find_module
TypeError: 'NoneType' object is not iterable

Original exception was:
Traceback (most recent call last):
  File "/home/cheatfate/Projects/test_container.py", line 36, in do_forall
    if not self.children is None and not callback is None:
AttributeError: 'TestContainer' object has no attribute 'children'
Error in sys.excepthook:

Original exception was:

经过一些调查后我发现由于某些原因,我的TestContainer将内存移到其他地址并丢失了所有私有变量。如你所见,我们进入了stdout:

do_forall() self = 7f97be2c5140
do_forall() self = 7f97c7cc7820

所以问题是“我做错了什么?”

1 个答案:

答案 0 :(得分:4)

似乎是一个错误,所以我已经提交了https://bugzilla.gnome.org/show_bug.cgi?id=722562