Gtk.StatusIcon PopupMenu in python
Asked Answered
T

2

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?

Thaumatrope answered 26/6, 2011 at 16:45 Comment(7)
I don't even see StatusIcon.position_menu in the documentation for Gtk.StatusIcon. I see gtk.status_icon_position_menu, which clearly accepts a StatusIcon. Does that not work anymore? (Related question: are you Hairy_Palms? You don't have to answer that.)Latish
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 :) )Thaumatrope
@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(), and return a (x, y, push_in) 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.)Latish
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.Thaumatrope
@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.Latish
no problem, thanks for taking time to try and help me senderle! :) got it fixed now :)Thaumatrope
Hey great, glad you found the solution!Latish
T
17

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()
Thaumatrope answered 27/6, 2011 at 19:43 Comment(4)
seems some things have changed in pygobject, the pos function now only receives two arguments from the event, ill put an updated answer at pastebin.com/Rzek336p since stackoverflow seems to screw the indentation tooThaumatrope
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.Julianejuliann
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.Julianejuliann
@LaurenceGonsalves I can't explain why but for some reason it works with the pos wrapper but not without.Issuant
R
1

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)

Rave answered 1/11, 2015 at 21:15 Comment(2)
I did copy and paste but didn't work. python 3.7 and gtk 3Craiova
@Craiova Works for me on Python 3.8.10.Colleen

© 2022 - 2024 — McMap. All rights reserved.