English 中文(简体)
Add a changing icon to Ubuntu Panel
原标题:

What would be the most simple way of adding and changing an icon in the Ubuntu (Gnome) Panel? I m looking for something as simple as shell scripting, but I m not restricted to that. Will write a program for it if that s a better or simpler approach.

The idea is to create a script/program to monitor some condition (e.g. availability of a mount point, availability of internet connection), and change the state of the icon to reflect the current status.

最佳回答

One easy way is to make it in Python. See this blog post for example.

问题回答

I found YAD (Yet Another Dialog) to provide the simplest solution. See webupd8 s short description. However, integration into Unity appears to be slightly broken at the moment. I mention a work-around below, but if you really care about Unity you should probably look at the other answers.

Note: While I m led to believe that YAD works in a wide range of environments, I ve only tested the instructions below using Lubuntu 15.10 (LXDE desktop) and Ubuntu 14.04 (Unity desktop).

Installation

I obtained a working installation with:

sudo apt-add-repository ppa:webupd8team/y-ppa-manager 
sudo apt-get update
sudo apt-get install yad

(In fact, I didn t need the first two lines in Lubuntu 15.10, but that may have been a coincidence.)
In LXDE, calling

yad --notification --listen

then brought up a tray icon which I could change by typing (for example): icon:gtk-help. In Unity, nothing appeared, so I needed the following ...

Work-around for Unity: The following instructions are again taken from webupd8. The problem is that a "system tray" does no longer officially exist in Unity. One possible solution for running programs like YAD that haven t caught up with this change is to install a "system tray emulator":

sudo apt-add-repository ppa:fixnix/indicator-systemtray-unity
sudo apt-get update
sudo apt-get install indicator-systemtray-unity

To obtain icons directly in the Unity panel, I used the following settings:

gsettings set net.launchpad.indicator.systemtray tray-is-static true
gsettings set net.launchpad.indicator.systemtray show-background-static false

Once I d log out and back in again, yad --notification worked as expected. (Moreover, the "systemtray" displayed some additional icons that I had previously been searching in vain.) The position of the icons on the panel can be adapted via:

gsettings set net.launchpad.indicator.systemtray static-x 1500

(where 1500 may be replaced by any reasonable value). I do not know how to get the icons to be displayed flush-right. If you ever feel like uninstalling the "system tray emulator" again, webupd8 recommend:

sudo apt-get purge indicator-systemtray-unity

Demo

Here s a simplistic demo that might help illustrate how to use YAD in real-world scenarios. I m assuming YAD itself is already installed as described above. Suppose we would like to watch the output of some program running on the command line and update a tray icon accordingly. For the purposes of this demo, let s just take this "program" to be the following script "dummyprogram.sh":

#! /bin/bash
i=1
while [ $i -ne 3 ]
do
  let "i=((i+1)%2)"
echo $i
  sleep 1
done

Copying the above lines to a file "dummyprogram.sh", making it executable with "chmod +x dummyprogram.sh" and calling "./dummyprogram.sh" should result in the following output:

0
1
0
1
...

(one line every second). Now for the actual task at hand. To get an "iconified" version of the above output in the tray area, we use the following script "demo.sh":

#! /bin/bash
while read x
do
  if  [ $x -eq 0 ]
  then
    echo icon:gtk-home
  else
    echo icon:gtk-help
  fi
done

Again, copy the lines to a file "demo.sh" and make it executable. Calling

./dummyprogram.sh | ./demo.sh | yad --notification --listen

should now lead to the desired result: an icon in the tray area which changes back and forth between two different icons each second.

You can end the demo by typing Ctrl-C in the terminal.

For those who care about Unity, a simple solution may not currently exist. So perhaps writing a custom Python script that uses Unity s AppIndicator-API is indeed the way to go. There are already lots of examples of this on the web, but it still took me a fair amount of effort to arrive at a script that implements roughly the same functionality as the solution using YAD described in my other answer: a script that will listen to the output of a program and update a "tray icon" accordingly. Here s my solution:

#!/usr/bin/python
import os, threading, signal
from subprocess import Popen, PIPE, STDOUT
from gi.repository import Gtk
from gi.repository import AppIndicator3 as AppIndicator

class my_indicator:
    ICON_0 = "gtk-home" 
    ICON_1 = "gtk-help"       

    def __init__(self, wrapper):
        self.ind = AppIndicator.Indicator.new("my-app-indicator", Gtk.STOCK_INFO, AppIndicator.IndicatorCategory.SYSTEM_SERVICES)
        self.ind.set_status(AppIndicator.IndicatorStatus.ACTIVE)
        self.wrapper = wrapper
        self.build_menu()

    def build_menu(self):
        item_quit = Gtk.MenuItem( Quit )
        item_quit.connect( activate , self.wrapper.quit)
        menu = Gtk.Menu()
        self.ind.set_menu(menu)
        menu.append(item_quit)
        menu.show_all()

    def update_icon(self,icon):
        self.ind.set_icon(icon)

class dummy_wrapper:
    PROGRAM = "./dummyprogram.sh"

    def __init__(self):
        self.indicator = my_indicator(self)

    def main(self):
        self.process = Popen(self.PROGRAM, stdout=PIPE, stderr=STDOUT, close_fds=True, shell=True, preexec_fn=os.setsid)
        self.thread = threading.Thread(target=self.watch_pipe, args=(self.process.stdout,self.indicator))
        self.thread.daemon = True
        self.thread.start() 
        Gtk.main()

    def quit(self, source):
        if self.process:
            os.killpg(self.process.pid, signal.SIGTERM)               
        if self.thread:
            self.thread.join() 
        Gtk.main_quit()

    @staticmethod
    def watch_pipe(pipe, indicator): 
        while 1:  
            line = pipe.readline().strip() 
            # The thread waits here until a line appears in the pipe.
            if not line: 
                # This happens exactly once, namely when the process ends.
                break
            else:
                print line
                if line=="0": 
                    indicator.update_icon(my_indicator.ICON_0)
                else:
                    indicator.update_icon(my_indicator.ICON_1)  

dummy_wrapper().main()

Some brief notes on the code:

  • The syntax of the lines calling and closing the program I want to listen to (./dummyprogram.sh), i.e. the lines self.process = Popen(... and os.killpg(... are taken from this answer. This seems to be the only reliable way to terminate a shell (sub)process.
  • The script cannot be terminated with Ctrl-C -- use the menu entry Quit instead). It should be possible to add an event-handler for Ctrl-C using the signals library, but this appears to be broken due to a bug in Gtk. See this answer for a possible work-around.

Demo: Paste the above code into a file my-indicator.py and make it executable with chmod +x my-indicator.py. Do the same for the file dummy-programm.sh described in my answer proposing YAD. Call ./my-indicator.py, and you should see an icon in the Unity panel which changes back and forth between two different icons each second. (This should also work on gnome and KDE-desktops provided Ubuntu s app-indicator-library is installed.)





相关问题
Signed executables under Linux

For security reasons, it is desirable to check the integrity of code before execution, avoiding tampered software by an attacker. So, my question is How to sign executable code and run only trusted ...

encoding of file shell script

How can I check the file encoding in a shell script? I need to know if a file is encoded in utf-8 or iso-8859-1. Thanks

How to write a Remote DataModule to run on a linux server?

i would like to know if there are any solution to do this. Does anyone? The big picture: I want to access data over the web, using my delphi thin clients. But i´would like to keep my server/service ...

How can I use exit codes to run shell scripts sequentially?

Since cruise control is full of bugs that have wasted my entire week, I have decided the existing shell scripts I have are simpler and thus better. Here is what I have so far svn update /var/www/...

Good, free, easy-to-use C graphics libraries? [closed]

I was wondering if there were any good free graphics libraries for C that are easy to use? It s for plotting 2d and 3d graphs and then saving to a file. It s on a Linux system and there s no gnuplot ...

热门标签