title: Condense Datetime Code, Hook Appindicator3 Menu Call + Relative Icon Path, Code Review
body: <p><img src="https://i.stack.imgur.com/akbif.png" alt="Image required for Q1"></p>
  
  <p>Hello,</p>
  
  <p>I am a novice to this (please answer like so for me). All of the code should run easily after fixing my (second) question. It actually runs great on my machine, but probably not yours yet. I have tried to comment everywhere for you to make it easier for someone to read. This runs on an Ubuntu 12.10 machine, but you don't need to be running Linux to help my issues!</p>
  
  <p>SOLVED 1. Code Review: As you go through the code, I would appreciate any input on how to condense or do things in a better, more appropriate way. The rest of my questions are really just the things I already know should be worked on. But if there's something else you find in my coding style, et al, <em>please</em> be candid. Upvotes to all good comments.</p>
  
  <p>SOLVED: 2. Relative Icon Path: At the following: </p>
  
  <pre><code>/home/mh00h/workspace/issindicator/src/International_Space_Station_clip_art.svg
  </code></pre>
  
  <p>This is an absolute path to the same folder as this script. I don't want that,, I want this script to work on anybody's machine. I tried these:</p>
  
  <pre><code>$HOME/workspace...
  (nothing in front) International_Space_Station_clip_art.svg
  ./International_Space_Station_clip_art.svg
  </code></pre>
  
  <p>but those didn't work. The image above is what I am trying to use (yes, I know I have an SVG instead of png listed, imgur limitation). <a href="http://developer.ubuntu.com/api/ubuntu-12.04/python/AppIndicator3-0.1.html" rel="nofollow noreferrer" title="Here">Here</a> is the documentation. It talks about an ""icon-theme-path"... maybe that would do it somehow? Or perhaps there is a standard directory all programmers are expected to store icons?</p>
  
  <p><strong>3. Concentrate my datetime functions</strong>: Really, I was fortunate to get this to work at all. My way is roundabout, but as far as I can tell, it works. I'm pretty confident that there is a better way to fix that mess though!! You'll find a bunch of datetime stuff at the bottom of the script.</p>
  
  <p>SOLVED: 4. Appindicator3 Hook: I would love to have GTK refresh only when the menu has been called instead of running every second regardless. This was partially answered <a href="https://stackoverflow.com/questions/14983279/update-gtk-menuitem-countdown-each-second-and-periodic-function-call" title="here">here</a>, but I don't really understand how to implement "realize." (Hopefully this is the right place to be asking this?)</p>
  
  <p><strong><em>Thank you!</em></strong></p>
  
  <pre><code>#!/usr/bin/env python
  import json, urllib2, time, math, datetime, os, webbrowser
  from dateutil import tz
  
  #indicator
  from gi.repository import Gtk, GObject
  from gi.repository import AppIndicator3 as appindicator
  
  class indicator():
      def __init__(self):
  
          #######Set this to "False" if IssIndicator should hide it's icon during normal runtime (default = True)
          self.isiconhidden = True
          #
  
          #create indicator
          self.ind = appindicator.Indicator.new (
                          "issindicator",
                          "/home/mh00h/workspace/issindicator/src/International_Space_Station_clip_art.svg",
                          #"indicator-messages",
                          appindicator.IndicatorCategory.APPLICATION_STATUS)
          if self.isiconhidden == True:
              self.ind.set_status (appindicator.IndicatorStatus.PASSIVE)
          else:
              self.ind.set_status (appindicator.IndicatorStatus.ACTIVE)
  
          #this is used to keep track of the gtk refresh period
          self.refreshvalue = False
  
          #dropdown menu
          #current pass menu items
          self.menu = Gtk.Menu()
          self.curpass = Gtk.MenuItem("not refreshed")
          self.curpass.connect("activate", self.checkiss)
          self.menu.append(self.curpass)
  
          self.curpassdur = Gtk.MenuItem(" ")
          self.menu.append(self.curpassdur)
  
          self.curpassrise = Gtk.MenuItem(" ")
          self.menu.append(self.curpassrise)
  
          self.curpassset = Gtk.MenuItem(" ")
          self.menu.append(self.curpassset)
  
          self.sep1 = Gtk.SeparatorMenuItem()
          self.menu.append(self.sep1)
  
          #future pass items
          self.futpass = Gtk.MenuItem(" ")
          self.futpass.connect("activate", self.onurl)
          self.menu.append(self.futpass)
  
          self.sep2 = Gtk.SeparatorMenuItem()
          self.menu.append(self.sep2)
  
          #Options items
  
          self.aboutmenu = Gtk.MenuItem("About")
          self.aboutmenu.connect("activate", self.onabout)
          self.menu.append(self.aboutmenu)
  
          self.quit = Gtk.MenuItem("Quit")
          self.quit.connect("activate", self.quitnow)
          self.menu.append(self.quit)
  
          self.curpass.show()
          self.sep1.show()
          self.futpass.show()
          self.sep2.show()
          self.aboutmenu.show()
          self.quit.show()
          self.ind.set_menu(self.menu)
  
          #get iss data at first run
          self.updatecache()
          self.checkiss()
  
          Gtk.main()
  
  
      #functions
      def hideicon(self, w=None):
          self.ind.set_status (appindicator.IndicatorStatus.PASSIVE)
  
      def showicon(self, w=None):
          self.ind.set_status (appindicator.IndicatorStatus.ACTIVE)
  
      def quitnow(self, w=None):
          Gtk.main_quit()
  
      #open browser for more tracking info
      def onurl(self, w=None):
          webbrowser.open("http://www.n2yo.com/passes/")
  
      def onabout(self,widget):
          widget.set_sensitive(False)
          ad=Gtk.AboutDialog()
          ad.set_name("aboutdialog")
          ad.set_version("0.1")
          ad.set_copyright('Copyrignt (c) 2013 mh00h')
          ad.set_comments('Indicating ISS Zarya')
          ad.set_license(''+
          'This program is free software: you can redistribute it and/or modify it\n'+
          'under the terms of the GNU General Public License as published by the\n'+
          'Free Software Foundation, either version 3 of the License, or (at your option)\n'+
          'any later version.\n\n'+
          'This program is distributed in the hope that it will be useful, but\n'+
          'WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\n'+
          'or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for\n'+
          'more details.\n\n'+
          'You should have received a copy of the GNU General Public License along with\n'+
          'this program.  If not, see &lt;http://www.gnu.org/licenses/&gt;.')
          ad.set_website('https://launchpad.net/~mh00h/+archive/issindicator')
          ad.set_website_label('ISSIndicator Homepage')
          ad.set_authors(['mh00h &lt;abcd@abcd.com'])
          ad.run()
          ad.destroy()
          widget.set_sensitive(True)
  
      #how often to run checkiss
      def setrefresh(self, r):
          #this clause is required to keep script from hanging
          if r != self.refreshvalue:
              self.refreshvalue = r
              try:
                  self.reftime = GObject.source_remove(True)
              except:
                  pass        
              try:
                  self.reftime = GObject.timeout_add(r, self.checkiss)
              except:
                  pass
  
      #
      def updatecache(self, w=None):
          #this will show in the menu until the update process completes
          self.passingstatus = 'not updated yet'
          #get ISS data from api
          self.ip = urllib2.urlopen("http://api.exip.org/?call=ip").read()
          self.geoip = json.load(urllib2.urlopen("http://freegeoip.net/json/"+self.ip))
          self.data = json.load(urllib2.urlopen("http://api.open-notify.org/iss/?lat="+str(self.geoip["latitude"])+"&amp;lon="+str(self.geoip["longitude"])+"&amp;alt=280&amp;n=47"))
          self.data = {"message": "success", "request": {"latitude": 45.0, "passes": 3, "altitude": 280, "longitude": -81.0, "datetime": 1361502063}, 
                   "response": [{"duration": 542, "risetime": time.time()+10}, {"duration": 642, "risetime": 1361560774}, {"duration": 593, "risetime": 1361566621}]}
  
  
      def checkiss(self, w=None):
          #so as to not overload api servers, this runs as a separate process
          #this updates the timers
          self.n = 0
          self.passingstatus = "ISS Zarya is below the horizon"
  
          #check if we've gone through cached iss passings and update api if needed
          try:
              #ignore errors in case internet is not accessible
              #have a buffer of 5 passes remaining before updating cache
              #at 2 passes left, stop the program to prevent the rest of the program from throwing codes
              if time.time() &gt; self.data['response'][len(self.data['response'])-5]['risetime']:
                  self.updatecache
          except:
              if time.time() &gt; self.data['response'][len(self.data['response'])-2]['risetime']:
                  os.system("notify-send 'ISS Indicator tried multiple times to update its satellite cache but has run out of its cached track.' 'This may be due to a bad internet connection. The application will now quit.'")
                  Gtk.main_quit()
  
          #get current time
          current_utc = datetime.datetime.utcnow()
          current_utc = current_utc.replace(tzinfo=tz.gettz('UTC'))
  
          #iterate over all iss passes
          for k in self.data['response']:
              duration = self.data['response'][self.n]['duration']
              risetime = self.data['response'][self.n]['risetime']
              settime = risetime + duration
  
              #if this iteration matches with the current time, do...
              if risetime &lt;= time.time() &lt;= settime:
                  #make the countdown values for the current pass tick
                  #rise time calculations and date conversions to string format
                  currisetime_utc = datetime.datetime.utcfromtimestamp(self.data['response'][self.n]['risetime'])
                  currisetime_utc = currisetime_utc.replace(tzinfo=tz.gettz('UTC'))
                  currisetime_tz = currisetime_utc.astimezone(tz.tzlocal())
                  currisetime_tzstr = str("%02d" % (currisetime_tz.hour))+':'+str("%02d" % (currisetime_tz.minute))+':'+str("%02d" % (currisetime_tz.second))
                  #set time calculations and durations
                  cursettime_utc = datetime.datetime.utcfromtimestamp(self.data['response'][self.n]['risetime']+self.data['response'][self.n]['duration'])
                  cursettime_utc = cursettime_utc.replace(tzinfo=tz.gettz('UTC'))
                  cursettime_tz = cursettime_utc.astimezone(tz.tzlocal())
                  curremainingtimeleft = cursettime_utc - current_utc
                  curduration = cursettime_utc - currisetime_utc
  
                  z= curremainingtimeleft.seconds
                  zhours = z/60/60
                  zminutes = z/60-zhours*60
                  zseconds = z-zhours*60*60-zminutes*60
                  curremainingtimeleftstr = str(zhours)+':'+str("%02d" % (zminutes))+':'+str("%02d" % (zseconds))
  
                  z= curduration.seconds
                  zhours = z/60/60
                  zminutes = z/60-zhours*60
                  zseconds = z-zhours*60*60-zminutes*60
                  curdurationstr = str(zhours)+':'+str("%02d" % (zminutes))+':'+str("%02d" % (zseconds))
  
                  cursettime_tzstr = str("%02d" % (cursettime_tz.hour))+':'+str("%02d" % (cursettime_tz.minute))+':'+str("%02d" % (cursettime_tz.second))
  
                  #since the ISS is presently overhead, show the icon and update GTK menuitems to show timers on the ISS pass
                  self.showicon()
                  self.passingstatus = "ISS Zarya is above the horizon!"
                  self.curpass.get_child().set_text(self.passingstatus)
                  self.curpassdur.get_child().set_text("Duration: "+curdurationstr+" ("+curremainingtimeleftstr+" remaining)")
                  self.curpassdur.show()
                  self.curpassrise.get_child().set_text("Rise time: "+currisetime_tzstr)
                  self.curpassrise.show()
                  self.curpassset.get_child().set_text("Set time: "+cursettime_tzstr)
                  self.curpassset.show()
                  break
              else:
                  #if this iteration of ISS passes does not match with current time, then increase self.n
                  self.n += 1
  
          #regardless of results show the next pass time
          #if the ISS is overhead, use the next dictionary key for data 
          if self.n != len(self.data['response']):
              nextrisetime_utc = datetime.datetime.utcfromtimestamp(self.data['response'][self.n+1]['risetime'])
          else:
              #if the ISS is not overhead, use the first key in the dictionary
              nextrisetime_utc = datetime.datetime.utcfromtimestamp(self.data['response'][0]['risetime'])
          #calculate the next rise time and make timers
          nextrisetime_utc = nextrisetime_utc.replace(tzinfo=tz.gettz('UTC'))
          nextrisetime_tz = nextrisetime_utc.astimezone(tz.tzlocal())
          remainingtimeleft = nextrisetime_utc - current_utc
          z= remainingtimeleft.seconds
          zhours = z/60/60
          zminutes = z/60-zhours*60
          zseconds = z-zhours*60*60-zminutes*60
          remainingtimeleftstr = str(zhours)+':'+str("%02d" % (zminutes))+':'+str("%02d" % (zseconds))
          nextrisetime_tzstr = str("%02d" % (nextrisetime_tz.hour))+':'+str("%02d" % (nextrisetime_tz.minute))+':'+str("%02d" % (nextrisetime_tz.second))
  
          #update GTK menuitem
          self.futpass.get_child().set_text("Next Pass: "+nextrisetime_tzstr+" ("+remainingtimeleftstr+")")
  
          #if the ISS is not above the horizon, refresh GTK only once its time for the icon to be visible 
          if self.passingstatus != "ISS Zarya is above the horizon!":
              self.setrefresh(remainingtimeleft.seconds*1000+100)
              #self.setrefresh(1000)
              self.curpass.get_child().set_text(self.passingstatus)
              self.curpassdur.hide()
              self.curpassrise.hide()
              self.curpassset.hide()
              if self.isiconhidden == True:
                  self.hideicon()
          else:
              #otherwise, refresh once a second to show the timers ticking in the menu
              #test if the menu is active instead of always running like in this example
              ####MISSING CODE HERE#####  DONT KNOW HOW TO DO IT, SO JUST SETTING TO 1 SEC
              self.setrefresh(1000)
  
          #for when setrefresh calls this function
          return True
  
  if __name__ == '__main__':
      issindicator = indicator()
  </code></pre>