为什么在窗口中删除窗口小部件然后再次创建窗口小部件会在PyGObject中产生奇怪的错误消息?

时间:2017-06-28 09:30:01

标签: python gtk pygobject

我有一个窗口,我希望更改一个窗口小部件中的值以更改另一个窗口小部件;因此在小部件的事件处理程序中,我"刷新"该窗口删除其小部件并再次创建它们。在我的实际代码中,我有几个小部件,这是我的解决方法,以及#34;更新"他们。如果有更好的方法来更新其他小部件,如果用于生成它们的其中一个值发生更改,请随时告诉我。

但是,在下面的示例中,小部件似乎变为空白,我收到此消息:

(test4.py:19274): Gtk-CRITICAL **: gtk_container_remove: assertion 
'_gtk_widget_get_parent (widget) == GTK_WIDGET (container) || 
GTK_IS_ASSISTANT (container) || GTK_IS_ACTION_BAR (container) || 
GTK_IS_POPOVER_MENU (container)' failed

(test4.py:19274): Gtk-CRITICAL **: gtk_adjustment_get_value: assertion 
'GTK_IS_ADJUSTMENT (adjustment)' failed

(test4.py:19274): Gtk-CRITICAL **: gtk_widget_has_focus: assertion 
'GTK_IS_WIDGET (widget)' failed

(test4.py:19274): Gtk-CRITICAL **: gtk_widget_has_focus: assertion 
'GTK_IS_WIDGET (widget)' failed
test4.py:69: Warning: g_object_ref: assertion 'G_IS_OBJECT (object)' failed 
Gtk.main()
[...]

我在这里附上了我的代码:

import sys
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk

# Documentation: https://graph-tool.skewed.de/static/doc/index.html

class SpecialBox(Gtk.Box):

    def __init__(self, GUI):
        Gtk.Box.__init__(self)

        self.GUI = GUI

        self.liststore = Gtk.ListStore(str, int, int)
        self.liststore.append(["Apple", 0, 100])
        self.liststore.append(["Pear", 0, 100])
        self.liststore.append(["Orange", 0, 100])

        treeview = Gtk.TreeView(model=self.liststore)

        filter_name = Gtk.CellRendererText()
        column_text = Gtk.TreeViewColumn("Fruit is good", filter_name, text=0)
        treeview.append_column(column_text)

        self.filter_low = Gtk.CellRendererSpin()
        self.filter_low.connect("edited", self.low_on_amount_edited)
        self.filter_low.set_property("editable", True)

        low_adjustment = Gtk.Adjustment(0, 0, 99, 1, 10, 0)
        self.filter_low.set_property("adjustment", low_adjustment)

        low_spin = Gtk.TreeViewColumn("Random Number", self.filter_low, text=1)
        treeview.append_column(low_spin)

        self.add(treeview)

    def low_on_amount_edited(self, widget, path, value):
        value = int(value)
        self.liststore[path][1] = value
        self.GUI.restart_window(str(value))

class GUI:

    def __init__(self):
        self.win = Gtk.Window()
        self.window_grid = Gtk.Grid()
        self.special_box = Gtk.Box(spacing=10)
        self.label = Gtk.Label("Number label")
        self.win.connect("delete-event", Gtk.main_quit)
        self.start_window()

    def start_window(self):
        self.special_box.pack_start(SpecialBox(self), True, True, 0)
        self.window_grid.add(self.special_box)
        self.window_grid.add(self.label)
        self.win.add(self.window_grid)
        self.win.show_all()

    def restart_window(self, label="Number"):
        self.window_grid.destroy()
        self.window_grid = Gtk.Grid()
        self.special_box = Gtk.Box(spacing=10)
        self.label = Gtk.Label(label)
        self.start_window()

def main():
    app = GUI()
    Gtk.main()

if __name__ == "__main__":
    sys.exit(main())

上面的代码生成错误,但由于某种原因,下面的代码工作正常(我基本上只是用一个按钮替换了上面的小部件):

import sys
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk

# Documentation: https://graph-tool.skewed.de/static/doc/index.html

class SpecialButton(Gtk.Box):

    def __init__(self, GUI):
        Gtk.Box.__init__(self)

        self.GUI = GUI
        self.set_border_width(10)
        self.message = "Special Text"

        button = Gtk.Button.new_with_label(self.message)
        button.connect("clicked", self.on_click)
        self.pack_start(button, True, True, 0)

    def on_click(self, widget):
        self.GUI.restart_window(self.message)

class GUI:

    def __init__(self):
        self.win = Gtk.Window()
        self.window_grid = Gtk.Grid()
        self.box = Gtk.Box(spacing=10)
        self.label = Gtk.Label("Default label")
        self.win.connect("delete-event", Gtk.main_quit)
        self.start_window()

    def start_window(self):
        self.box.pack_start(SpecialButton(self), True, True, 0)
        self.window_grid.add(self.box)
        self.window_grid.add(self.label)
        self.win.add(self.window_grid)
        self.win.show_all()

    def restart_window(self, label="Default label"):
        self.window_grid.destroy()
        self.window_grid = Gtk.Grid()
        self.box = Gtk.Box(spacing=10)
        self.label = Gtk.Label(label)
        self.start_window()

def main():
    app = GUI()
    Gtk.main()

if __name__ == "__main__":
    sys.exit(main())

1 个答案:

答案 0 :(得分:0)

这里的问题是,当该窗口小部件的事件处理程序正在运行时,您正在销毁窗口小部件。该小部件是您的CellRendererSpin

当此Spinbutton的值发生变化时,它会触发low_on_amount_edited)

self.filter_low.connect("edited", self.low_on_amount_edited)

调用GUI.restart_window

self.GUI.restart_window(str(value))

破坏SpecialBox小部件及其所有子小部件,包括Spinbutton:

self.window_grid.destroy()

在小部件做某事时摧毁小部件并不奇怪。

要解决此问题,您只需将调用推迟到GUI.restart_window,直到Spinbutton的事件处理程序完成。为此,您可以使用idle_add,它将回调排队以由Gtk主循环执行:

from gi.repository import Gtk, Gdk, GLib

class SpecialBox(Gtk.Box):
    def low_on_amount_edited(self, widget, path, value):
        value = int(value)
        self.liststore[path][1] = value
        Gdk.threads_add_idle(GLib.PRIORITY_DEFAULT, self.GUI.restart_window, str(value))