tags:

views:

323

answers:

2

Hey everyone,

I'm currently working on a small script that needs to use gtk.StatusIcon(). For some reason, I'm getting some weird behavior with it. If I go into the python interactive shell and type:

>> import gtk
>> statusIcon = gtk.status_icon_new_from_file("img/lin_idle.png")

Pygtk does exactly what it should do, and shows an icon (lin_idle.png) in the system tray:

This works fine

However, if I try to do the same task in my script:

def gtkInit(self):
    self.statusIcon = gtk.status_icon_new_from_file("img/lin_idle.png")

When gtkInit() gets called, I see this instead:

WHY WON'T YOU WORK GSFKLJFSGDJKHSFDGHJKL

I made I ran the script in the same working directory as the interactive python shell, so I'm pretty sure it's finding the image, so I'm stumped... Any ideas anyone? Thanks in advance.

Update: For some reason or another, after calling gtk.status_icon_new_from_file() a few times in the script, it does eventually create the icon, but this issue still remains unfortunately. Does anyone at all have any ideas as to what could be going wrong?

As requested: Here's the full script. This is actually an application that I'm in the very early stages of making, but it does work at the moment if you get it setup correctly, so feel free to play around with it if you want (and also help me!), you just need to get an imgur developer key and put it in linup_control.py

Linup.py

#
# Linup - A dropbox alternative for Linux!
# Written by Nakedsteve
# Released under the MIT License
#

import os
import time
import ConfigParser
from linup_control import Linup

cfg = ConfigParser.RawConfigParser()
# See if we have a .linuprc file
home = os.path.expanduser("~")

if not os.path.exists(home+"/.linuprc"):
    # Nope, so let's make one
    cfg.add_section("paths")
    cfg.set("paths","watch_path", home+"/Desktop/screenshot1.png")

    # Now write it to the file
    with open(home+"/.linuprc","wb") as configfile:
     cfg.write(configfile)
else:
    cfg.read(home+"/.linuprc")

linup = Linup()

# Create the GUI (status icon, menus, etc.)
linup.gtkInit()

# Enter the main loop, where we check to see if there's a shot to upload
# every 1 second
path = cfg.get("paths","watch_path")
while 1:
    if(os.path.exists(path)):
     linup.uploadImage(path)
     url = linup.getURL()
     linup.toClipboard(url)
     linup.json = ""

     print "Screenshot uploaded!"
     os.remove(path)
    else:
     # If you're wondering why I'm using time.sleep()
     # it's because I found that without it, my CPU remained
     # at 50% at all times while running linup. If you have a better
     # method for doing this, please contact me about it (I'm relatively new at python)
     time.sleep(1)

linup_control.py

import gtk
import json
import time
import pycurl
import os

class Linup:
    def __init__(self):
     self.json = ""

    def uploadImage(self, path):
     # Set the status icon to busy
     self.statusIcon.set_from_file("img/lin_busy.png")

     # Create new pycurl instance
     cu = pycurl.Curl()

     # Set the POST variables to the image and dev key
     vals = [
      ("key","*************"),
      ("image", (cu.FORM_FILE, path))
     ]

     # Set the URL to send to
     cu.setopt(cu.URL, "http://imgur.com/api/upload.json")
     # This lets us get the json returned by imgur
     cu.setopt(cu.WRITEFUNCTION, self.resp_callback)
     cu.setopt(cu.HTTPPOST, vals)

     # Do eet!
     cu.perform()
     cu.close()

     # Set the status icon to done...
     self.statusIcon.set_from_file("img/lin_done.png")
     # Wait 3 seconds
     time.sleep(3)
     # Set the icon to idle
     self.statusIcon.set_from_file("img/lin_idle.png")

    # Used for getting the response json from imgur
    def resp_callback(self, buff):
     self.json += buff

    # Extracts the image URL from the json data
    def getURL(self):
     js = json.loads(self.json)
     return js['rsp']['image']['original_image']

    # Inserts the text variable into the clipboard
    def toClipboard(self, text):
     cb = gtk.Clipboard()
     cb.set_text(text)
     cb.store()

    # Initiates the GUI elements of Linup
    def gtkInit(self):
     self.statusIcon = gtk.StatusIcon()
     self.statusIcon.set_from_file("img/lin_idle.png")
+1  A: 

You made two mistakes. One is important one is not.

At first if you want to use stock icon use .set_from_stock( stock_id ) method. If you want to use your own icon then the .set_from_file(/path/to/img.png) is ok.

The other think witch is the probably the main problem is that when you write gtk application you have to call gtk.main() function. This is main gtk loop where all signal handling/window drawing and all other gtk stuff is done. If you don't do this, simply your icon is not drawing.

The solution in your case is to make two threads - one for gui, second for your app. In the first one you simply call gtk.main(). In second you put your main program loop. Of course when you call python program you have one thread already started:P

If you aren't familiar whit threads there is other solution. Gtk have function which calls function specified by you with some delay:

def call_me:
    print "Hello World!"
    gtk.timeout_add( 1000 , call_me )

gtk.timeout_add( 1000 , call_me )
gtk.main()

But it seems to be deprecated now. Probably they have made a better solution.

qba
+4  A: 

You need to call the gtk.main function like qba said, however the correct way to call a function every N milliseconds is to use the gobject.timeout_add function. In most cases you would want to have anything that could tie up the gui in a separate thread, however in your case where you just have an icon you don't need to. Unless you are planning on making the StatusIcon have a menu. Here is the part of Linup.py that I changed:

# Enter the main loop, where we check to see if there's a shot to upload
# every 1 second
path = cfg.get("paths","watch_path")
def check_for_new():

    if(os.path.exists(path)):
        linup.uploadImage(path)
        url = linup.getURL()
        linup.toClipboard(url)
        linup.json = ""

        print "Screenshot uploaded!"
        os.remove(path)
    # Return True to keep calling this function, False to stop.  
    return True

if __name__ == "__main__":

    gobject.timeout_add(1000, check_for_new)

    gtk.main()

You will have to import gobject somewhere too.

I don't know for sure if this works because I can't get pycurl installed.

EDIT: In linup_control.py, I would try changing

# Wait 3 seconds
time.sleep(3)
# Set the icon to idle
self.statusIcon.set_from_file("img/lin_idle.png")

to

gobject.timeout_add(3000, self.statusIcon.set_from_file, "img/lin_idle.png")
DoR
This is the solution that I managed to get working, however when I'm now a bit stuck with changing the icon to a different image, as seen in `Linup.uploadImage()`. Is this a mistake of my own or do I need to make a separate thread in order to modify the `gtk.StatusIcon()`?
Vestonian
See my edit... [15 chars]
DoR