PyGObject - left-click menu on a status icon - python

I'm starting to write a small panel applet for Gnome and I'd like the user to be able to left-click on the status icon to see some options and information e.g. similar to sound icon in Gnome 3, where you can set volume via left-click while set preferences via right-click.
Right-click code is this:
statusicon.connect("popup-menu", right_button_click)
where right_button_click is the name of the function that gets called on right-click event. The important part is "popup-menu". What would be alternative for setting left-click event?

This is the tiny example showing how it works.
#!/usr/bin/python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class TrayIcon(Gtk.StatusIcon):
def __init__(self):
Gtk.StatusIcon.__init__(self)
self.set_from_icon_name('help-about')
self.set_has_tooltip(True)
self.set_visible(True)
self.connect("popup_menu", self.on_secondary_click)
def on_secondary_click(self, widget, button, time):
menu = Gtk.Menu()
menu_item1 = Gtk.MenuItem("First Entry")
menu.append(menu_item1)
menu_item2 = Gtk.MenuItem("Quit")
menu.append(menu_item2)
menu_item2.connect("activate", Gtk.main_quit)
menu.show_all()
menu.popup(None, None, None, self, 3, time)
if __name__ == '__main__':
tray = TrayIcon()
Gtk.main()

First thing is to look into the gnome code for the volume control, and that's is this
Second, you should look into the API documentation for GtkStatusIcon, and that one is here
That should be enough.

This is a late response, but i'm just posting this in case someone else ever looks for left click control over gtkstatusicon.
The direct alternative is
statusicon.connect("activate", left_button_click)
This is a sample for trayicon popup menu working with left click instead of the (common) right click.
#!/usr/bin/env python
import pygtk
pygtk.require('2.0')
import gtk
class TrayIcon(gtk.StatusIcon):
def __init__(self):
gtk.StatusIcon.__init__(self)
self.set_from_icon_name('help-about')
self.set_has_tooltip(True)
self.set_visible(True)
self.connect("activate", self.on_click)
def greetme(self,data=None):
msg=gtk.MessageDialog(None, gtk.DIALOG_MODAL,gtk.MESSAGE_INFO, gtk.BUTTONS_OK, "Greetings")
msg.run()
msg.destroy()
def on_click(self,data):
event=gtk.get_current_event()
btn=event.button #this gets the button value of gtk event.
time=gtk.get_current_event_time() # required by menu popup.
menu = gtk.Menu()
menu_item1 = gtk.MenuItem("First Entry")
menu.append(menu_item1)
menu_item1.connect("activate", self.greetme)
menu_item2 = gtk.MenuItem("Quit")
menu.append(menu_item2)
menu_item2.connect("activate", gtk.main_quit)
menu.show_all()
menu.popup(None, None, None, btn, time)
#button can be hardcoded (i.e 1) but time must be correct.
if __name__ == '__main__':
tray = TrayIcon()
gtk.main()
Also , there is this alternative :
statusicon.connect("button-press-event", button_click)
Bellow sample code raise the same popup menu in gtktrayicon in BOTH right and left click.
#!/usr/bin/env python
import pygtk
pygtk.require('2.0')
import gtk
class TrayIcon(gtk.StatusIcon):
def __init__(self):
gtk.StatusIcon.__init__(self)
self.set_from_icon_name('help-about')
self.set_has_tooltip(True)
self.set_visible(True)
self.connect("button-press-event", self.on_click)
def greetme(self,data=None):
msg=gtk.MessageDialog(None, gtk.DIALOG_MODAL,gtk.MESSAGE_INFO, gtk.BUTTONS_OK, "Greetings")
msg.run()
msg.destroy()
def on_click(self,data,event):
#event in this case is sent by the status icon connect.
btn=event.button
#By controlling this event.button value (1-2-3 for left-middle-right click) you can call other functions.
time=gtk.get_current_event_time() # required by the popup.
menu = gtk.Menu()
menu_item1 = gtk.MenuItem("First Entry")
menu.append(menu_item1)
menu_item1.connect("activate", self.greetme)
menu_item2 = gtk.MenuItem("Quit")
menu.append(menu_item2)
menu_item2.connect("activate", gtk.main_quit)
menu.show_all()
menu.popup(None, None, None, btn, time)
if __name__ == '__main__':
tray = TrayIcon()
gtk.main()
Hope above code helps.
George V.

Related

How to Pygtk WindowTypeHint to DOCK on XFCE and have it visible on the desktop

So I've run into an issue where whenever I set the WindowTypeHint to anything other than normal the window just disappears. I've verified the hint type with print.Below is my sample code
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk
# Create the main window
class MyWindow(Gtk.Window):
def __init__(self):
super().__init__(title="Hello World")
# Innitially setting the bar to off
self.set_decorated(False)
# Attempting to set window type to prevent minimizing when show desktop is hit and to stay behind other objects etc.
self.set_type_hint(Gdk.WindowTypeHint.DOCK)
# Defining the button and drawing it
self.button = Gtk.Button(label="Click Here")
self.button.connect("clicked", self.on_button_clicked)
self.add(self.button)
# define the button functions
def on_button_clicked(self, widget):
if win.props.decorated == False:
win.set_decorated(True)
else:
win.set_decorated(False)
print("Hello World")
print(win.props.decorated)
print(self.props.type_hint)
# Set alias for the window
win = MyWindow()
# Testing. Print the hint type to console
print(win.props.type_hint)
# Window settings.
win.set_keep_below(True)
win.connect("destroy", Gtk.main_quit)
win.show_all()
print(win.props.type_hint)
Gtk.main()
Window to appear as a dock on the desktop where the mouse pointer is or in the corner of one of the monitors the same way glava works with it's settings.
Item was drawing off screen. Multiple monitors had it throwing the item into the corner off screen between them. If anyone else comes across this I resolved it by setting the coordinates with self.move(x, y) in the initialisation. Found where I wanted it to root with win.get_position() on button press to find where I wanted it.

How to keep PyGTK AppIndicator menu open after click?

I was trying to toy around with the pygtk module to make an app indicator in my top bar in Ubuntu 17.10.
With a little research, I have successfully made a menu where you can pause the main thread (which just prints "Update" to the console currently.)
Now I've stumbled across a problem and couldn't find an answer.
I plan to make a radio streamer for the top bar and the menu closing on every click would be very undesirable for navigation when searching.
How can I make the menu stay open when clicking an item?
Here is my code:
import os
import signal
import time
from threading import Thread
import gi
gi.require_version("Gtk", "3.0")
gi.require_version("AppIndicator3", "0.1")
from gi.repository import Gtk as gtk
from gi.repository import AppIndicator3 as appindicator
from gi.repository import GObject as gobject
class Indicator:
def __init__(self, name=None, icon=None):
self.path = os.path.abspath(os.path.dirname(__file__))
signal.signal(signal.SIGINT, signal.SIG_DFL)
if name is None:
self.name = "MyApp"
else:
self.name = name
if icon is None:
self.icon = gtk.STOCK_INFO
else:
self.icon = icon
self.indicator = appindicator.Indicator.new(
self.name, self.icon,
appindicator.IndicatorCategory.SYSTEM_SERVICES
)
self.indicator.set_status(appindicator.IndicatorStatus.ACTIVE)
self.indicator.set_menu(self.create_menu())
self.running = True
self.paused = False
self.thread = Thread(target=self.update)
self.thread.setDaemon(True)
self.thread.start()
def create_menu(self):
menu = gtk.Menu()
pause_entry = gtk.MenuItem("Pause")
pause_entry.connect("activate", self.pause)
menu.append(pause_entry)
quit_entry = gtk.MenuItem("Quit")
quit_entry.connect("activate", self.quit)
menu.append(quit_entry)
menu.show_all()
return menu
def update(self):
while self.running:
time.sleep(1)
if not self.paused:
print("Update", flush=True)
def start(self):
gobject.threads_init()
gtk.main()
def pause(self, menu):
self.paused = not self.paused
text = "Resume" if self.paused else "Pause"
for widget in menu.get_children():
print(widget.get_text())
widget.set_text(text)
def quit(self, menu=None):
self.running = False
gtk.main_quit()
if __name__ == "__main__":
indicator = Indicator()
indicator.start()
Unfortunately, I don't think you can make the menu stay open when clicking on an item.
This was an intentional design decision taken by the AppIndicator developers in order to ensure that the indicator would function in a consistent and uniform way and that no custom actions(e.g. right-click, mouseover etc) would be possible to be applied on by end users.
I have created an appindicator that displays news in real time and when I researched the related pygtk/appindicator API reference material; the only allowed supported menu action that I came across was that of scrolling.
With regards to your radio streamer project I suggest have the menu as the main focal point of your application and have the menu items appropriately bidden to pygtk windows. Then use callbacks and signals to perform your necessary actions. This is the approach I undertook in my little project too and turned out pretty nicely.

Python : Add url to menu in appindicator

I'm trying to make a little application with appindicator and Gtk. My goal is to display a list of server with link to url of them.
Here is what I try :
from gi.repository import Gtk as gtk
from gi.repository import AppIndicator3 as appindicator
def main():
indicator = appindicator.Indicator.new(APPINDICATOR_ID, img, appindicator.IndicatorCategory.SYSTEM_SERVICES)
indicator.set_status(appindicator.IndicatorStatus.ACTIVE)
indicator.set_menu(build_menu())
gtk.main()
def build_menu():
menu = gtk.Menu()
value = "label"
item = gtk.MenuItem()
button = gtk.LinkButton("http://url/host/id", label=value)
button.show()
item.add(button)
item.show()
menu.append(item)
menu.show_all()
return menu
if __name__ == "__main__":
main()
That's working and I have no errors. But wen I launch application, I've only menu, with items but no link.
I've seen many exemple with gtk.Window but nothing with a menu for appindicator.
Is there a way to have link in this menu ?
Thanks
I've found a way to do that. I'm not sure it's the best way, but it works.
Instead of create a LinkItem, I've made a function for open url:
def open_url(source):
webbrowser.open("http://url/host/id")
And I call it after, with connect :
item.connect("activate", open_url)
When I run my app and click on my item, it opens url as expected. Here is part of code working:
def build_menu():
menu = gtk.Menu()
value = "label"
item = gtk.MenuItem(value)
item.connect("activate", open_url)
menu.append(item)
menu.show_all()
return menu
As I see in many post on web, appindicator has limited functions compared to normal Gtk application.

Tray icon application in Python, icon is gone

I whipped up a little wallpaper-changing application for myself in Python. It makes an icon in the notification tray, which can be used to switch modes between "nice" and "naughty" depending on who's around :P and to force a wallpaper change. If left alone, it still changes to a random wallpaper once every 10 minutes, selecting randomly from a directory full of images that I continually add to. Everything was working great, until I upgraded from Ubuntu 14.04 "Trusty" to Ubuntu 15.10 "Wily." Now, the application still runs, and will change wallpapers once every 10 minutes like it should, but the icon is gone. It makes a space in the tray for the icon, but the icon no longer appears in it, nor does the empty space respond to any mouse clicks, left or right (left click used to force a wallpaper change, right click used to give me a menu of the two modes). No warning or error messages appear on the console when I run the application. I'm not too experienced with Python, and can't figure out what the hell is the problem. Following is the (very short) code for the applet. Sorry if there are any awful practices in the code, like I said I'm really not a python guy, it just seemed the easiest way to do what I wanted to do so I went with it. If anyone can help me figure out the problem I'd appreciate it!
PS "./change_wall" is just a bash script which does some other stuff besides just changing the wallpaper. I know the problem isn't there because the automatic wallpaper changes are still working, it's just the tray icon / control interface that's FUBAR.
#!/usr/bin/python
import os
import wx
import time
import thread
class TaskBarIcon(wx.TaskBarIcon):
def __init__(self):
super(TaskBarIcon, self).__init__()
os.chdir("/home/caleb/walls")
self.SetIcon(wx.Icon('walls.xpm', wx.BITMAP_TYPE_XPM), "Wallpaper switcher")
self.set_nice(None)
self.Bind(wx.EVT_TASKBAR_LEFT_UP, self.set_new_wall_x)
def CreatePopupMenu(self):
menu = wx.Menu()
nice_item = menu.AppendRadioItem(-1, "Nice")
naughty_item = menu.AppendRadioItem(-1, "Naughty")
if self.type == 'nice':
nice_item.Check()
elif self.type == 'naughty':
naughty_item.Check()
menu.Bind(wx.EVT_MENU, self.set_nice, nice_item)
menu.Bind(wx.EVT_MENU, self.set_naughty, naughty_item)
return menu
def set_nice(self, event):
self.type = 'nice'
self.set_new_wall()
def set_naughty(self, event):
self.type = 'naughty'
self.set_new_wall()
def set_new_wall(self):
os.system("./change_wall " + self.type)
self.last_changed_time = time.time()
def set_new_wall_x(self, event):
self.set_new_wall()
def main():
app = wx.App(False)
the_icon = TaskBarIcon()
thread.start_new_thread(app.MainLoop, ())
while 1:
if (time.time() > the_icon.last_changed_time + 600):
the_icon.set_new_wall()
if __name__ == '__main__':
main()
Other than checking the things in my comment, see if this code functions, the icon used should exist on your machine.
import wx
TRAY_TOOLTIP = 'System Tray Demo'
TRAY_ICON = '/usr/share/icons/gnome/16x16/emotes/face-cool.png'
def create_menu_item(menu, label, func):
item = wx.MenuItem(menu, -1, label)
menu.Bind(wx.EVT_MENU, func, id=item.GetId())
menu.AppendItem(item)
return item
class TaskBarIcon(wx.TaskBarIcon):
def __init__(self):
wx.TaskBarIcon.__init__(self)
self.set_icon(TRAY_ICON)
self.Bind(wx.EVT_TASKBAR_LEFT_DOWN, self.on_left_down)
self.Bind(wx.EVT_TASKBAR_RIGHT_DOWN, self.on_right_down)
def CreatePopupMenu(self):
menu = wx.Menu()
create_menu_item(menu, 'Say Hello', self.on_hello)
menu.AppendSeparator()
create_menu_item(menu, 'Exit', self.on_exit)
return menu
def set_icon(self, path):
icon = wx.IconFromBitmap(wx.Bitmap(path))
self.SetIcon(icon, TRAY_TOOLTIP)
def on_left_down(self, event):
print 'Tray icon was left-clicked.'
def on_right_down(self, event):
print 'Tray icon was right-clicked.'
event.Skip()
def on_hello(self, event):
print 'Hello, world!'
def on_exit(self, event):
wx.CallAfter(self.Destroy)
def main():
app = wx.App()
TaskBarIcon()
app.MainLoop()
if __name__ == '__main__':
main()
Well, rather than continue to muck around with wxPython and this Project Phoenix nonsense, I've switched to just using PyGTK and I have to say, overall I'm liking this a lot better. My code is now behaving exactly as I want it to, as follows:
#!/usr/bin/python
import gobject
import gtk
import os
import thread
import time
last_changed_time = 0
mode = "nice"
def set_mode (new_mode):
global mode
mode = new_mode
change_wall()
def make_menu(event_button, event_time, data=None):
menu = gtk.Menu()
nice_item = gtk.CheckMenuItem("Nice")
naughty_item = gtk.CheckMenuItem("Naughty")
kill_item = gtk.MenuItem("Quit")
if mode == "nice":
nice_item.set_active(True)
if mode == "naughty":
naughty_item.set_active(True)
menu.append(nice_item)
menu.append(naughty_item)
menu.append(kill_item)
nice_item.connect_object("activate", set_mode, ("nice"))
naughty_item.connect_object("activate", set_mode, ("naughty"))
kill_item.connect_object("activate", gtk.main_quit, ())
nice_item.show()
naughty_item.show()
kill_item.show()
menu.popup(None, None, None, event_button, event_time)
def change_wall():
global last_changed_time
os.system("./change_wall " + mode)
last_changed_time = time.time()
def on_right_click(data, event_button, event_time):
make_menu(event_button, event_time)
def on_left_click(event):
change_wall()
def auto_update():
while 1:
time.sleep(1)
if time.time() > last_changed_time + 600:
change_wall()
if __name__ == '__main__':
gobject.threads_init()
os.chdir("/home/caleb/walls")
icon = gtk.status_icon_new_from_file("walls.xpm")
icon.connect('popup-menu', on_right_click)
icon.connect('activate', on_left_click)
change_wall()
thread.start_new_thread(auto_update, ())
gtk.main()
I had to insert the time.sleep(1) call before every check to see whether it's time to auto-update in order to prevent the icon/menu itself from becoming rather laggy. I never ran into that problem when I was using wx; is there a more elegant way to solve this?
Thank you again for the help!
Because you are running wx.python 3 it looks like you will need to change a few of your calls:
IconFromBitmap becomes Icon
TaskBarIcon becomes adv.TaskBarIcon
There may be some other minor discrepancies but those appear to be the obvious ones.
Edit:
The other option is to load a previous version of wxpython.
For this, you can use wxversion like so, in your imports:
import wxversion
wxversion.select("2.8-gtk2-unicode")
import wx

Detect scroll wheel signal in system tray using Python AppIndicator in Ubuntu 12.04

I'm trying to get this python code to react when the mouse hovers over the tray icon and scrolls the mouse wheel, I can't find any examples online. This is what I have so far, it doesn't react to scrolling the wheel...
#!/usr/bin/python
APPNAME = "My App"
ICON = "/usr/share/pixmaps/firefox.png"
import appindicator as AI
import gtk
def sayhello(item):
print "menu item selected"
def scroll(aai, ind, steps):
print "hello" # doesn't print anything
def makemenu():
' Set up the menu '
menu = gtk.Menu()
check = gtk.MenuItem('Check')
exit = gtk.MenuItem('Quit')
check.connect('activate', sayhello)
exit.connect('activate', gtk.main_quit)
menu.append(check)
menu.append(exit)
return menu
def startapp():
ai = AI.Indicator(APPNAME, ICON, AI.CATEGORY_APPLICATION_STATUS)
ai.set_status(AI.STATUS_ACTIVE)
ai.connect("scroll-event", scroll)
ai.set_menu(makemenu())
gtk.main()
startapp()
How can I detect scroll wheel movements?
That is the correct way to connect to the mouse scroll event and the code does work, tested on two 12.04 systems. There might be a bug however as the first few test runs on one of them did not work either but then was fine.
If you are starting from scratch I would recommend using pygobject (Gtk3) instead of pygtk (Gtk2) as it is no longer developed. As part of testing I did convert your script to pygobject and fixed showing the menu:
#!/usr/bin/env python
APPNAME = "My App"
ICON = "/usr/share/pixmaps/firefox.png"
from gi.repository import AppIndicator3 as AI
from gi.repository import Gtk
def sayhello(item):
print "menu item selected"
def scroll(aai, ind, steps):
print "hello" # doesn't print anything
def makemenu():
' Set up the menu '
menu = Gtk.Menu()
check_item = Gtk.MenuItem('Check')
exit_item = Gtk.MenuItem('Quit')
check_item.connect('activate', sayhello)
check_item.show()
exit_item.connect('activate', Gtk.main_quit)
exit_item.show()
menu.append(check_item)
menu.append(exit_item)
menu.show()
return menu
def startapp():
ai = AI.Indicator.new(APPNAME, ICON, AI.IndicatorCategory.HARDWARE)
ai.set_status(AI.IndicatorStatus.ACTIVE)
ai.set_menu(makemenu())
ai.connect("scroll-event", scroll)
Gtk.main()
startapp()

Categories