0

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()
Caleb
  • 11
  • 2
  • Just 2 initial thoughts, 1 did you python version change? and 2 have you tested using another image for the icon? You code is working on Mint 17.2 – Rolf of Saxony Dec 09 '15 at 09:50

3 Answers3

0

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()
Rolf of Saxony
  • 21,661
  • 5
  • 39
  • 60
  • Thank you for the reply. I'm not sure what Python version I was on before, but as of now, 'python -V' reports 2.7.10. I have tried using another icon with the same results. Re: your code, I don't have that icon in that place, so I changed it to the following, also a 16x16 PNG, which does exist on my system: '/usr/share/icons/Adwaita/16x16/status/battery-full-charged.png' When I run the code, however, nothing happens. I get no tray icon, no console output at all, and execution stops as soon as it was started. 'ps aux' tells me that no process related to the script remains running... – Caleb Dec 09 '15 at 17:38
  • Nasty! run python, `import wx` and then type `wx__version__`, that will tell you what version you have of wxpython. If it's 3 you're going to have to read the `Project Phoenix` manuals. I assume that you are running this from the command line, if not, do so. It may be that you have something missing on your new OS. – Rolf of Saxony Dec 09 '15 at 18:26
  • wx__version__ did not work (NameError: name 'wx__version__' is not defined), but 'print wx.version()' reports "3.0.2.0 gtk2 (classic)". I will check out Project Phoenix manuals. Thank you! – Caleb Dec 09 '15 at 20:27
0

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!

Caleb
  • 11
  • 2
0

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
Rolf of Saxony
  • 21,661
  • 5
  • 39
  • 60