Re: [pygtk] freeze when trying to dynamically update menus
Abel Daniel wrote: I was trying to implement dynamic menus. The idea is that the menu-items are created when the menu is activated (shown). This would be for an undo-menu where the menu-items would name the previously done editing actions, so dynamically creating the menu is vital. With a bit of help from #pygtk (on gimpnet) I came up with the attached code. (Most of the code is the itemfactory example from the pygtk tutorial.) The problem appears if the dynamically created menu is the first sub-menu from the menubar. When activating this menu the second time, the app freezes and I get a lot of the following messages on stderr: (itemfactory.py:2223): Gtk-CRITICAL **: file gtkwidget.c: line 3359 (gtk_widget_event): assertion `WIDGET_REALIZED_FOR_EVENT (widget, event)' failed (It looked like one for each redraw.) (This means that I have to go to a console with Ctrl-Alt-F2 and kill the app. X is unusable after triggering the bug, as the app won't give up focus, so mouseclicks go nowhere.) If the dynamically created menu is the second sub-menu from the menubar, (which means the menuitem which's 'activated' signal triggers the re-creation of the menu is not on the menubar, but in a submenu) it works fine. In the attached code, the menu Options->Test works, but the top-level Test does not. Is this a bug in my code? I don't know what the problem is with your code but maybe my mods may be useful. I think the "select" signal is more useful for dynamically updating the submenus. In the update_menu() method remove the current menuitems and add the new ones (or whatever changes you want). Spec '/Options/Test' and '/Text' as "" items. Modified program attached. Hope this helps. John #!/usr/bin/env python # example itemfactory.py import gtk import time class ItemFactoryExample: # Obligatory basic callback def print_hello(self, widget, data): print "Hello, World!" def update_menu(self, widget): submenu = widget.get_submenu() # remove previous submenu items for c in submenu.get_children(): submenu.remove(c) t=time.time() menu_item = gtk.MenuItem(label=str(t)) menu_item.show() #?? # add new menuitem submenu.append(menu_item) # This is the ItemFactoryEntry structure used to generate new menus. # Item 1: The menu path. The letter after the underscore indicates an # accelerator key once the menu is open. # Item 2: The accelerator key for the entry # Item 3: The callback. # Item 4: The callback action. This changes the parameters with # which the callback is called. The default is 0. # Item 5: The item type, used to define what kind of an item it is. # Here are the possible values: # NULL -> "" # "" -> "" # "" -> create a title item # "" -> create a simple item # "" -> create a check item # "" -> create a toggle item # "" -> create a radio item #-> path of a radio item to link against # "" -> create a separator # "" -> create an item to hold sub items (optional) # "" -> create a right justified branch def get_main_menu(self, window): accel_group = gtk.AccelGroup() # This function initializes the item factory. # Param 1: The type of menu - can be MenuBar, Menu, # or OptionMenu. # Param 2: The path of the menu. # Param 3: A reference to an AccelGroup. The item factory sets up # the accelerator table while generating menus. item_factory = gtk.ItemFactory(gtk.MenuBar, "", accel_group) # This method generates the menu items. Pass to the item factory # the list of menu items item_factory.create_items(self.menu_items) # connect to the "select" signal to update submenus menuitem = item_factory.get_item('/Test') # <-- SWITCH HERE !!! menuitem.connect('select', self.update_menu) menuitem = item_factory.get_item('/Options/Test') menuitem.connect('select', self.update_menu) # Attach the new accelerator group to the window. window.add_accel_group(accel_group) # need to keep a reference to item_factory to prevent its destruction self.item_factory = item_factory # Finally, return the actual menu bar created by the item factory. return item_factory.get_widget("") def __init__(self): self.menu_items = ( ( "/_File", None, None, 0, "" ), ( "/File/_New", "N", self.print_hello, 0, None ), ( "/File/_Open","O", self.print_hello, 0, None ), ( "/File/_Save","S", self.print_hello, 0, None ), ( "/File/Save _As", None, None, 0, None ),
[pygtk] freeze when trying to dynamically update menus
I was trying to implement dynamic menus. The idea is that the menu-items are created when the menu is activated (shown). This would be for an undo-menu where the menu-items would name the previously done editing actions, so dynamically creating the menu is vital. With a bit of help from #pygtk (on gimpnet) I came up with the attached code. (Most of the code is the itemfactory example from the pygtk tutorial.) The problem appears if the dynamically created menu is the first sub-menu from the menubar. When activating this menu the second time, the app freezes and I get a lot of the following messages on stderr: (itemfactory.py:2223): Gtk-CRITICAL **: file gtkwidget.c: line 3359 (gtk_widget_event): assertion `WIDGET_REALIZED_FOR_EVENT (widget, event)' failed (It looked like one for each redraw.) (This means that I have to go to a console with Ctrl-Alt-F2 and kill the app. X is unusable after triggering the bug, as the app won't give up focus, so mouseclicks go nowhere.) If the dynamically created menu is the second sub-menu from the menubar, (which means the menuitem which's 'activated' signal triggers the re-creation of the menu is not on the menubar, but in a submenu) it works fine. In the attached code, the menu Options->Test works, but the top-level Test does not. Is this a bug in my code? Thanks in advance, Abel Daniel #!/usr/bin/env python # example itemfactory.py import gtk import time class ItemFactoryExample: # Obligatory basic callback def print_hello(self, widget, data): print "Hello, World!" def update_menu(self, widget): #print 'updating' t=time.time() menu = gtk.Menu() menu_item = gtk.MenuItem(label=str(t)) menu.append(menu_item) menu_item.show() #?? widget.set_submenu(menu) menu.show() #widget.show() #print 'updated:', t # This is the ItemFactoryEntry structure used to generate new menus. # Item 1: The menu path. The letter after the underscore indicates an # accelerator key once the menu is open. # Item 2: The accelerator key for the entry # Item 3: The callback. # Item 4: The callback action. This changes the parameters with # which the callback is called. The default is 0. # Item 5: The item type, used to define what kind of an item it is. # Here are the possible values: # NULL -> "" # "" -> "" # "" -> create a title item # "" -> create a simple item # "" -> create a check item # "" -> create a toggle item # "" -> create a radio item #-> path of a radio item to link against # "" -> create a separator # "" -> create an item to hold sub items (optional) # "" -> create a right justified branch def get_main_menu(self, window): accel_group = gtk.AccelGroup() # This function initializes the item factory. # Param 1: The type of menu - can be MenuBar, Menu, # or OptionMenu. # Param 2: The path of the menu. # Param 3: A reference to an AccelGroup. The item factory sets up # the accelerator table while generating menus. item_factory = gtk.ItemFactory(gtk.MenuBar, "", accel_group) # This method generates the menu items. Pass to the item factory # the list of menu items item_factory.create_items(self.menu_items) menuitem = item_factory.get_widget('/Test') # <-- SWITCH HERE !!! menuitem.connect('activate', self.update_menu) menuitem = item_factory.get_widget('/Options/Test') menuitem.connect('activate', self.update_menu) # Attach the new accelerator group to the window. window.add_accel_group(accel_group) # need to keep a reference to item_factory to prevent its destruction self.item_factory = item_factory # Finally, return the actual menu bar created by the item factory. return item_factory.get_widget("") def __init__(self): self.menu_items = ( ( "/_File", None, None, 0, "" ), ( "/File/_New", "N", self.print_hello, 0, None ), ( "/File/_Open","O", self.print_hello, 0, None ), ( "/File/_Save","S", self.print_hello, 0, None ), ( "/File/Save _As", None, None, 0, None ), ( "/File/sep1", None, None, 0, "" ), ( "/File/Quit", "Q", gtk.mainquit, 0, None ), ( "/_Options", None, None, 0, "" ), ( "/Options/Test", None, None, 0, None ), ( "/Test", None, None, 0, None ), ( "/_Help", None, None, 0, "" ), ( "/_Help/About", None, None, 0, None