问题
This is my International Space Station indicator. I am trying to create a GTK MenuItem that displays a countdown. This wil be in an Ubuntu Unity indicator using AppIndicator3. I want this MenuItem (futpass
)to refresh each second (i.e., countdown appears in real time), but also want it to stay inactive when the menu is not selected so as to not consume system resources. Problem is, I can't get it to refresh on its own.
Because there is a long wait period between each time checkiss
needs to be run, I also need to know the proper approach to calling this function at a set time with minimal CPU wakeups.
Here is my (rough) code so far:
#!/usr/bin/env python
#print time.localtime( time.time() )
import json, urllib2, time, math
#indicator
from gi.repository import Gtk
from gi.repository import AppIndicator3 as appindicator
class indicator():
def __init__(self):
#create indicator
self.ind = appindicator.Indicator.new (
"issindicator",
"indicator-messages",
#"indicator-messages",
appindicator.IndicatorCategory.APPLICATION_STATUS)
self.ind.set_status (appindicator.IndicatorStatus.ACTIVE)
self.ind.set_attention_icon ("indicator-messages-new")
#dropdown menu
#now 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 items
self.futpass = Gtk.MenuItem(" ")
self.menu.append(self.futpass)
self.sep2 = Gtk.SeparatorMenuItem()
self.menu.append(self.sep2)
#Options
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
self.updatecache()
self.checkiss()
Gtk.main()
#define code to hide or show icon
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()
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'])
ad.run()
ad.destroy()
widget.set_sensitive(True)
def updatecache(self, w=None):
self.passingstatus = 'not set 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=27"))
def checkiss(self, w=None):
self.n = 0
self.passingstatus = "The ISS is not overhead."
#check if cache is out of date and update if needed
if time.time() > self.data['response'][len(self.data['response'])-1]['risetime']:
self.updatecache
for k in self.data['response']:
duration = self.data['response'][self.n]['duration']
risetime = self.data['response'][self.n]['risetime']
settime = risetime + duration
#print risetime, time.time(), settime
if risetime <= time.time() <= settime:
self.showicon()
self.passingstatus = "The ISS is overhead"
self.curpass.get_child().set_text(self.passingstatus)
self.curpassdur.get_child().set_text("Duration: "+
str(time.strftime('%M:%S', time.gmtime(duration)))+" ("+
str(time.strftime('%M:%S', time.gmtime(time.time()-duration)))+" remaining)")
self.curpassdur.show()
self.curpassrise.get_child().set_text("Rise time: "+time.strftime('%H:%M:%S', time.gmtime(risetime)))
self.curpassrise.show()
self.curpassset.get_child().set_text("Set time: "+time.strftime('%H:%M:%S', time.gmtime(settime)))
self.curpassset.show()
break
else:
self.n += 1
if self.passingstatus != "The ISS is overhead":
self.curpass.get_child().set_text(self.passingstatus)
self.curpassdur.hide()
self.curpassrise.hide()
self.curpassset.hide()
#self.hideicon()
#regardless of isspass, show the next pass time
if self.n != len(self.data['response']):
self.futpass.get_child().set_text("Next Pass: "+str(time.strftime('%H:%M:%S', time.gmtime(self.data['response'][self.n]['risetime'])))+" ("+str(time.strftime('%H:%M:%S', time.gmtime(self.data['response'][self.n+1]['risetime']-time.time())))+")")
else:
self.futpass.get_child().set_text("Next Pass: "+str(time.strftime('%H:%M:%S', time.gmtime(self.data['response'][0]['risetime'])))+" ("+str(time.strftime('%H:%M:%S', time.gmtime(self.data['response'][0]['risetime']-time.time())))+")")
if __name__ == '__main__':
issindicator = indicator()
回答1:
Use gobject.timeout_add
to schedule the callback to happen at regular intervals. For example:
gobject.timeout_add(3600 * 1000, self.checkiss)
The function will be called from the main loop at regular intervals as long as it returns a true value. If you want a one-shot function, simply return False
, or allow it to implicitly return None
. To schedule a function at a specific point in time, calculate the time between now and that point, and invoke timeout_add
with that period.
When the menu is activated, reschedule the function to happen quicker; when deactivated, schedule it slow again. To reschedule the function, call gobject.source_remove
with the value returned by gobject.timeout_add
, then call timeout_add
with the new timeout value.
The gtk main loop will make sure that the process is not woken up unless it receives events, or the timeout expires.
来源:https://stackoverflow.com/questions/14983279/update-gtk-menuitem-countdown-each-second-and-periodic-function-call