0

In my program, I've got a menu with a group of RadioMenuItem entries. Choosing one of them should trigger a function which can either succeed or fail. If it fails, this RadioMenuItem shouldn't be marked chosen (the previous one should persist). Besides, sometimes I want to set marked item without running the choice processing function. Here is my current code:

# Update seat menu list
def update_seat_menu(self, seats, selected_seat=None):
    seat_menu = self.builder.get_object('seat_menu')
    # Delete seat menu items
    for menu_item in seat_menu:
        # TODO: is it a good way? does remove() delete obsolete menu_item from memory?
        if menu_item.__class__.__name__ == 'RadioMenuItem': seat_menu.remove(menu_item)
    # Fill menu with new items
    group = []
    for seat in seats:
        menu_item = Gtk.RadioMenuItem.new_with_label(group, str(seat[0]))
        group = menu_item.get_group()
        seat_menu.append(menu_item)
        if str(seat[0]) == selected_seat: menu_item.activate()
        menu_item.connect("activate", self.choose_seat, str(seat[0]))
        menu_item.show()

# Process item choice
def choose_seat(self, entry, seat_name):
    # Looks like this is called when item is deselected, too; must check if active
    if entry.get_active():
        # This can either succeed or fail
        self.logind.AttachDevice(seat_name, '/sys'+self.device_syspath, True)

Chosen RadioMenuItem gets marked irrespective of the choose_seat() execution result; and the only way to set marked item without triggering choose_seat() is to re-run update_seat_menu() with selected_seat argument, which is an overkill.

I tried to connect choose_seat() with 'button-release-event' instead of 'activate' and call entry.activate() in choose_seat() if AttachDevice() succeeds, but this resulted in whole X desktop lockup until AttachDevice() timed out, and chosen item still got marked.

Eugene Shatsky
  • 401
  • 4
  • 8

2 Answers2

0

In my opinion a different approach would be better. When the user clicks an item, display a spinner or something to let the user know an operation is going on, then do your long-running choose_seat() in the background. If it fails, then change the radio button to non-active and set it to be insensitive (since you now know it doesn't work.)

ptomato
  • 56,175
  • 13
  • 112
  • 165
  • > _When the user clicks an item, display a spinner or something to let the user know an operation is going on_ Doing it asynchronously? I don't think I really need it at this stage. But, I still wonder why does `'button-release-event'` signal handling block the whole desktop (I thought X would never allow a regular client to block events for screen area outside its window), while `'active'` blocks program's UI only (as expected). – Eugene Shatsky Aug 09 '14 at 20:05
  • > _If it fails, then change the radio button to non-active_ That's the problem. I did't see a way to switch it to previous one without triggering `choose_seat` for it. – Eugene Shatsky Aug 09 '14 at 20:08
0

Thanks to help from people on irc.gnome.org/gtk+, I've got this with handler_block_by_func()/handler_unblock_by_func():

# Update chosen seat menu item (without triggering choose_seat)
def update_chosen_seat(self, seat):
    self.seat_menu_items[seat].handler_block_by_func(self.choose_seat)
    self.seat_menu_items[seat].activate()
    self.seat_menu_items[seat].handler_unblock_by_func(self.choose_seat)

And in choose_seat() I use the same approach to activate previously chosen item, then run actual handler code and activate chosen item if it succeeds.

Eugene Shatsky
  • 401
  • 4
  • 8