11

im trying to port some small examples from PyGTK to the new PyGobject bindings, but ive hit a roadblock with a popupmenu, despite getting no errors, no menu is being shown on rightclick, here is the code,

from gi.repository import Gtk
class aStatusIcon:
    def __init__(self):
        self.statusicon = Gtk.StatusIcon()
        self.statusicon.set_from_stock(Gtk.STOCK_HOME) 
        self.statusicon.connect("popup-menu", self.right_click_event)

        window = Gtk.Window()
        window.connect("destroy", lambda w: Gtk.main_quit())
        window.show_all()

    def right_click_event(self, icon, button, time):
        menu = Gtk.Menu()

        about = Gtk.MenuItem()
        about.set_label("About")
        quit = Gtk.MenuItem()
        quit.set_label("Quit")

        about.connect("activate", self.show_about_dialog)
        quit.connect("activate", Gtk.main_quit)

        menu.append(about)
        menu.append(quit)

        menu.show_all()

        #menu.popup(None, None, gtk.status_icon_position_menu, button, time, self.statusicon) # previous working pygtk line
        menu.popup(None, None, None, Gtk.StatusIcon.position_menu, button, time) #i assume this is problem line

    def show_about_dialog(self, widget):
        about_dialog = Gtk.AboutDialog()

        about_dialog.set_destroy_with_parent(True)
        about_dialog.set_name("StatusIcon Example")
        about_dialog.set_version("1.0")
        about_dialog.set_authors(["Andrew Steele"])

        about_dialog.run()
        about_dialog.destroy()

aStatusIcon()
Gtk.main()

i assume the problem is im not telling the menu about self.statusicon in there, but it doesnt work in any of the args since they all want a widget arg or none, not a statusicon, any smart ppl here got an idea where im going wrong?

Mike
  • 400
  • 2
  • 12
  • I don't even see `StatusIcon.position_menu` in the documentation for `Gtk.StatusIcon`. I see [`gtk.status_icon_position_menu`](http://www.pygtk.org/docs/pygtk/class-gtkstatusicon.html#function-gtk--status-icon-position-menu), which clearly accepts a `StatusIcon`. Does that not work anymore? (Related question: are you [Hairy_Palms](http://ubuntuforums.org/showthread.php?t=1789358)? You don't have to answer that.) – senderle Jun 26 '11 at 17:46
  • StatusIcon.position_menu is the new gtk3 introspection way of calling gtk.status_icon_position_menu, which complains if i try and give it a statusicon the way i did for the old method. (Related Answer: Yes :) ) – Mike Jun 26 '11 at 18:29
  • @Mike, ok, sorry. It's been a while since I used pygtk... I guess my approach would be to write my own positioning function that would accept a `StatusIcon`, call [`StatusIcon.get_geometry()`](http://www.pygtk.org/docs/pygtk/class-gtkstatusicon.html#method-gtkstatusicon--get-geometry), and return a [`(x, y, push_in)`](http://www.pygtk.org/docs/pygtk/class-gtkmenu.html#method-gtkmenu--popup) tuple. But that's a WAG, and assumes that these functions haven't changed. (BTW, has the `menu.popup` signature really changed, as your code suggests? That seems like some serious API breakage if so.) – senderle Jun 26 '11 at 19:11
  • yea i did think i could do something like that, but i was sure there must be a 'correct; way to do it, menu.popup is sortof "gone" its mapped in gi to `self.popup_for_device(None, parent_menu_shell, parent_menu_item, func, data, button, activate_time)` compared to the old `self.popup(parent_menu_shell, parent_menu_item, func, button, activate_time, data=None)` or at least it says it is, which is the problem :( since the arguments dont seem to match to what they are supposed to according to the reference. – Mike Jun 26 '11 at 19:35
  • @Mike, yeah, relative positioning of popups is kind of a pain -- I remember it gave me some trouble back when I was using pygtk 2.12. If there's a better way, I'm not smart enough to figure it out! Sorry not to be more helpful. – senderle Jun 26 '11 at 19:59
  • no problem, thanks for taking time to try and help me senderle! :) got it fixed now :) – Mike Jun 27 '11 at 19:45
  • Hey great, glad you found the solution! – senderle Jun 27 '11 at 20:32

2 Answers2

16

ah finally, if anyone else has this problem, it got solved thanks to some awesome help from one of the guys on gimpnet#python youve got to keep your menu in scope or it gets garbage collected hence no errors but no menu either this is the working code

from gi.repository import Gtk

class aStatusIcon:
    def __init__(self):
        self.statusicon = Gtk.StatusIcon()
        self.statusicon.set_from_stock(Gtk.STOCK_HOME)
        self.statusicon.connect("popup-menu", self.right_click_event)

        window = Gtk.Window()
        window.connect("destroy", lambda w: Gtk.main_quit())
        window.show_all()

    def right_click_event(self, icon, button, time):
        self.menu = Gtk.Menu()

        about = Gtk.MenuItem()
        about.set_label("About")
        quit = Gtk.MenuItem()
        quit.set_label("Quit")

        about.connect("activate", self.show_about_dialog)
        quit.connect("activate", Gtk.main_quit)

        self.menu.append(about)
        self.menu.append(quit)

        self.menu.show_all()

        def pos(menu, icon):
                return (Gtk.StatusIcon.position_menu(menu, icon))

        self.menu.popup(None, None, pos, self.statusicon, button, time)

    def show_about_dialog(self, widget):
        about_dialog = Gtk.AboutDialog()

        about_dialog.set_destroy_with_parent(True)
        about_dialog.set_name("StatusIcon Example")
        about_dialog.set_version("1.0")
        about_dialog.set_authors(["Andrew Steele"])

        about_dialog.run()
        about_dialog.destroy()

aStatusIcon()
Gtk.main()
Laurence Gonsalves
  • 137,896
  • 35
  • 246
  • 299
Mike
  • 400
  • 2
  • 12
  • 1
    seems some things have changed in pygobject, the pos function now only receives two arguments from the event, ill put an updated answer at http://pastebin.com/Rzek336p since stackoverflow seems to screw the indentation too – Mike Oct 10 '11 at 16:54
  • I edited the inline code to modify your pastebin. It looks like you may have been using a mix of tabs and spaces, which might be why SO was messing up your formatting. – Laurence Gonsalves Jul 11 '14 at 18:50
  • Two things about this code I found when I was using it: First, `self.menu` has a reference to your StatusIcon. This means that if the user opens the context menu and then closes it, you won't be able to remove the icon. This can be fixed by breaking the reference (eg: `self.menu = None`) when you get a "deactivate" event on the menu. Second, you can just pass `Gtk.StatusIcon.position_menu` as the third arg to `self.menu.popup`. The `pos` wrapper you have takes the exact same parameters in the same order and just delegates, so it isn't necessary. – Laurence Gonsalves Jul 11 '14 at 18:58
  • @LaurenceGonsalves I can't explain why but for some reason it works with the pos wrapper but not without. – erb Sep 08 '15 at 20:31
0

Copying Mike's solution from above with some minor cleanups and fixes for newer gtk3:

#!/usr/bin/python3
import gi
gi.require_version('Gtk', '3.0')

from gi.repository import Gtk

class MyStatusIconApp:
    def __init__(self):
        self.status_icon = Gtk.StatusIcon()
        self.status_icon.set_from_stock(Gtk.STOCK_HOME)
        self.status_icon.connect("popup-menu", self.right_click_event)

    def right_click_event(self, icon, button, time):
        self.menu = Gtk.Menu()

        about = Gtk.MenuItem()
        about.set_label("About")
        about.connect("activate", self.show_about_dialog)
        self.menu.append(about)

        quit = Gtk.MenuItem()
        quit.set_label("Quit")
        quit.connect("activate", Gtk.main_quit)
        self.menu.append(quit)

        self.menu.show_all()

        self.menu.popup(None, None, None, self.status_icon, button, time)

    def show_about_dialog(self, widget):
        about_dialog = Gtk.AboutDialog()

        about_dialog.set_destroy_with_parent(True)
        about_dialog.set_name("StatusIcon Example")
        about_dialog.set_version("1.0")
        about_dialog.set_authors(["Andrew Steele"])

        about_dialog.run()
        about_dialog.destroy()

app = MyStatusIconApp()
Gtk.main()

(Feel free to update if gtk changes again)

Community
  • 1
  • 1
Udi
  • 29,222
  • 9
  • 96
  • 129