你好,
我是这个新手(请像我这样回答)。修复我的(第二个)问题后,所有代码都应该可以轻松运行。它实际上在我的机器上运行良好,但可能还不是你的。我试图在任何地方为您发表评论,以使某人更容易阅读。这在 Ubuntu 12.10 机器上运行,但您不需要运行 Linux 来解决我的问题!
已解决 1. 代码审查:当您浏览代码时,我将不胜感激任何有关如何以更好、更合适的方式浓缩或做事的意见。我剩下的问题实际上只是我已经知道应该解决的问题。但是,如果您在我的编码风格等中发现了其他东西,请坦率地说。为所有好的评论点赞。
已解决:2.相对图标路径:在以下位置:
/home/mh00h/workspace/issindicator/src/International_Space_Station_clip_art.svg
这是与此脚本相同的文件夹的绝对路径。我不希望这样,我希望这个脚本可以在任何人的机器上运行。我试过这些:
$HOME/workspace...
(nothing in front) International_Space_Station_clip_art.svg
./International_Space_Station_clip_art.svg
但那些没有用。上面的图片是我正在尝试使用的(是的,我知道我列出了 SVG 而不是 png,imgur 限制)。这是文档。它谈到了“图标主题路径”......也许会以某种方式做到这一点?或者也许有一个标准目录,所有程序员都应该存储图标?
3. 集中我的日期时间功能:真的,我很幸运能够让它工作。我的方式是迂回,但据我所知,它有效。我非常有信心有更好的方法来解决这个烂摊子!你会在脚本的底部找到一堆日期时间的东西。
已解决:4. Appindicator3 Hook:我希望 GTK 仅在调用菜单时刷新,而不是每秒运行一次。这部分回答here,但我真的不明白如何实现“实现”。(希望这是问这个问题的正确地方吗?)
谢谢!
#!/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 <http://www.gnu.org/licenses/>.')
ad.set_website('https://launchpad.net/~mh00h/+archive/issindicator')
ad.set_website_label('ISSIndicator Homepage')
ad.set_authors(['mh00h <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"])+"&lon="+str(self.geoip["longitude"])+"&alt=280&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() > self.data['response'][len(self.data['response'])-5]['risetime']:
self.updatecache
except:
if time.time() > 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 <= time.time() <= 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()