Difference between revisions of "Mythstatus.py"
(→The Icons) |
|||
Line 351: | Line 351: | ||
= The Icons = | = The Icons = | ||
− | [[ | + | [[file:tv_broken.svg]] |
(I'm not sure how to add the icons yet, they are .svg files) | (I'm not sure how to add the icons yet, they are .svg files) | ||
[[Category:Python Scripts]] | [[Category:Python Scripts]] | ||
[[Category:Miscellaneous_Status_Information_Scripts]] | [[Category:Miscellaneous_Status_Information_Scripts]] |
Revision as of 01:34, 18 December 2010
Note: The correct title of this article is mythstatus.py. It appears incorrectly here due to technical restrictions.
Instructions
Place the various files in the places described by the comments at the beginning of the .py file
You may need to log out and log back in before the panel applet will show up in the "Add to Panel" window accessed by right clicking on the gnome panel.
The icon displayed in the Gnome Panel will change with the state of the back end.
When idle it will show a picture of a TV that is off
When recording it will show color bars with a red circle on it.
When recording more than one show, it will show the number of current recordings in the red circle.
If the applet can not communicate with the back end, it will show a broken television.
The Script
#!/usr/bin/env python # copyright Douglas Peale 2010 # released under GPLv3 #This is a Gnome Pannel Application. #It displays the number of currently recording tuners on a MythTV system. #Tooltip shows currently recording programs, when they will end, and when the next recordings will begin #mythstatus.py should be put in /usr/bin #mythstatus.server should be put in /usr/lib/bonobo/servers #icons should be put in /usr/share/pixmaps/mythstatus #Currently used icons: tv_color_bars.svg, tv_snow.svg, tv_broken.svg, tv_off.svg import sys import os import MythTV import pygtk pygtk.require('2.0') import gtk import gnomeapplet import gobject # Changing debug to a path/filename will cause the applet to remap stdout & stderr to that file # It appears that when run as a panel applet, no path is configured, and it is run as the logged # in user, so an absolute path to a directory you have write access to is required debug=None #debug="/home/user/mythstatus.log" class MythStatus: #################################################### def myth_status_update(self): try: #If the connection to the backend is not already establised, attempt to establish the connection. if not self.backend: self.backend=MythTV.MythBE() #get a list of recorders (tuners) recorders=self.backend.getRecorderList() #count how many of those recorders are busy recording something recording=0 for recorder in recorders: if self.backend.isRecording(recorder): recording+=1 if recording > 0: #change the icon to indicate that the system is recording and set lable to number of recorders if self.state != 'TV_color_bars': self.state='TV_color_bars' self.image.set_from_pixbuf(self.icon[self.state]) if self.rec_state != 'TV_recording': self.rec_state='TV_recording' self.rec_image.set_from_pixbuf(self.icon[self.rec_state]) if recording > 1 : self.set_label(str(recording)) else: #don't bother with number if only one recorder self.set_label("") else: self.set_label('') if self.state != 'TV_off': self.state='TV_off' self.image.set_from_pixbuf(self.icon[self.state]) if self.rec_state != 'TV_blank': self.rec_state='TV_blank' self.rec_image.set_from_pixbuf(self.icon[self.rec_state]) except BaseException as err: #Something went wrong. Display broken TV icon since we can't talk to the backend print(str(err)) self.errstr=str(err) self.set_label('') self.backend=None if self.state != 'TV_broken': self.state='TV_broken' self.image.set_from_pixbuf(self.icon[self.state]) self.tooltipuptodate=False return True #################################################### def myth_status_tooltip(self,widget,x,y,keyboard_mode,tooltip): #Unfortunately, this gets called for every mouse event while the mouse is over the widget. #What I was looking for and did not find was a way to change the tooltip just before it is displayed. #This is the closest thing I could find. To prevent unnecessary communication with the backend, only update the tooltip #if the panel app has been updated since this function was last called. #Only do this once per update if not self.tooltipuptodate: try: #make the tooltip an empty string self.tooltiptext="" #Avoid creating a new exception that would overwrite an existing one. if self.backend: #get a list of recorders (tuners) recorders=self.backend.getRecorderList() isrecording=False for recorder in recorders: if self.backend.isRecording(recorder): #add a title if you found a recording recorder if not isrecording: self.tooltiptext="Currently Recording:\n" isrecording=True else: self.tooltiptext+='\n' #add the title & subtitle of the activerecording recording=self.backend.getCurrentRecording(recorder) self.tooltiptext+=str(recording.title)+': '+str(recording.subtitle)+'\n' #add the time the recording will end if recording.recendts.hour>12: hour=recording.recendts.hour-12 ampm=' PM' else: if recording.recendts.hour == 0: hour=12 else: hour=recording.recendts.hour ampm=' AM' self.tooltiptext+='ends at %d:%02d%s'%(hour,recording.recendts.minute,ampm) #Set tool tip to display the next upcoming recording #This function returns a list of recordings. I'm not sure what it does if there is nothing scheduled. recordings=self.backend.getUpcomingRecordings() if recordings!=[]: if isrecording: self.tooltiptext+='\n\n' self.tooltiptext+='Next Recordings:\n' #list all the recordings starting at the same time for recording in recordings: if recording.starttime != recordings[0].starttime: break self.tooltiptext+=str(recording.title)+': '+str(recording.subtitle)+'\n' #build a nicly formatted start time string self.tooltiptext+='at %4d/%02d/%02d '%(recordings[0].recstartts.year,recordings[0].recstartts.month,recordings[0].recstartts.day) if recordings[0].recstartts.hour>12: hour=recordings[0].recstartts.hour-12 ampm=' PM' else: if recordings[0].recstartts.hour == 0: hour=12 else: hour=recordings[0].recstartts.hour ampm=' AM' self.tooltiptext+='%d:%02d%s'%(hour,recordings[0].recstartts.minute,ampm) else: self.tooltiptext="Nothing is scheduled to record" else: self.tooltiptext="Not communicating with back end" except BaseException as err: #Disconnect from the backend so we will attempt to re-establish communication on the next update print(str(err)) self.errstr=str(err) self.tooltiptext="Can't communicate with back end" self.backend=None if self.state != 'TV_broken': self.state='TV_broken' self.image.set_from_pixbuf(self.icon[self.state]) if self.errstr != "": self.tooltiptext+="\n\nLast Exception:\n"+self.errstr # I must set this every time, even if most of the time it is never used because if mouse is moved since this was set, the # tooltip is discarded and a new empty tooltip created. tooltip.set_text(self.tooltiptext) self.tooltipuptodate=True return True #################################################### #If the button is clicked, fork the process, and have the child start mythfrontend def start_front_end(self,widget,event): if event.type == gtk.gdk.BUTTON_PRESS : if event.button == 1 : if not os.fork(): os.execlp("mythfrontend","mythfrontend","--service") #################################################### def change_size(self,size_orient,user=0): #Calculate size of icons based on orientation and size hint such that the 16x9 aspect ratio is preserved and the result will fit in the panel. if (self.applet.get_orient() == gnomeapplet.ORIENT_UP) or (self.applet.get_orient() == gnomeapplet.ORIENT_DOWN): self.height=self.applet.get_size()-2 self.width=(self.height*16)//9 else: self.width=self.applet.get_size()-2 self.height=self.width*9//16 self.rec_size=min([(gtk.Label('0').size_request()[1]*5)//4,self.height]) try: self.icon['TV_color_bars']=gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/pixmaps/mythstatus/tv_color_bars.svg",self.width,self.height) self.icon['TV_off']=gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/pixmaps/mythstatus/tv_off.svg",self.width,self.height) self.icon['TV_broken']=gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/pixmaps/mythstatus/tv_broken.svg",self.width,self.height) self.icon['TV_snow']=gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/pixmaps/mythstatus/tv_snow.svg",self.width,self.height) self.icon['TV_blank']=gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/pixmaps/mythstatus/tv_blank.svg",self.rec_size,self.rec_size) self.icon['TV_recording']=gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/pixmaps/mythstatus/tv_recording.svg",self.rec_size,self.rec_size) self.image.set_from_pixbuf(self.icon[self.state]) self.rec_image.set_from_pixbuf(self.icon[self.rec_state]) self.fixed.move(self.rec_image,max([0,(self.width-self.rec_size)//2]),max([0,(self.height-self.rec_size)//2])) except BaseException as err: self.errstr=str(err) print(self.errstr) #################################################### def set_label(self,label): self.label.set_text(label) size=self.label.size_request() self.fixed.move(self.label,max([0,(self.width-size[0])//2]),max([0,(self.height-size[1])//2])) #################################################### def __init__(self,applet,iid): #I don't know what to do with these yet, but I'll keep a copy so the garbage collector can't eat them self.applet=applet self.iid=iid self.errstr="" #If debugging, hijack stdout & stderr so error messages & debug prints can actually be looked at. if debug: try: #Panel app runs as user, but does not have users '~/' directory defined, so absolute path must be used. dbgfile=open(debug,'w') sys.stdout=dbgfile sys.stderr=dbgfile except BaseException as err: self.errstr=str(err) #Create the variable, but don't Connect to MythBackend here. self.backend=None #set up timer so I can update the count every 10 seconds. Could be longer, but that makes debugging very annoying. gobject.timeout_add_seconds(10,self.myth_status_update) #Enable tool tip, and connect handler so I can change tool tip before it is displayed. self.applet.set_property("has-tooltip",True) self.applet.connect("query-tooltip",self.myth_status_tooltip) self.applet.connect("button-press-event",self.start_front_end) self.applet.connect("change-size",self.change_size) self.applet.connect("change-orient",self.change_size) #use change size function to import properly sized icons self.state='TV_snow' self.rec_state='TV_blank' self.icon={} self.image=gtk.Image() self.rec_image=gtk.Image() self.fixed=gtk.Fixed() self.applet.add(self.fixed) self.fixed.put(self.image,0,0) self.fixed.put(self.rec_image,0,0) self.change_size(0) #Put a label in the window self.label = gtk.Label("test") self.fixed.put(self.label,0,0) self.set_label("test") self.tooltipuptodate=False self.applet.show_all() #################################################### def mythstatus_factory(applet, iid): MythStatus(applet,iid) return True if len(sys.argv) == 2 and sys.argv[1] == "run-in-window": # user wants to run in a window so he can see error messages in the consol he ran from. print("Running in window") main_window = gtk.Window(gtk.WINDOW_TOPLEVEL) main_window.set_title("MythTV Status Debug Window") main_window.connect("destroy", gtk.main_quit) app = gnomeapplet.Applet() mythstatus_factory(app, None) app.reparent(main_window) main_window.show_all() gtk.main() sys.exit() else: gnomeapplet.bonobo_factory("OAFIID:GNOME_MythstatusApplet_Factory", gnomeapplet.Applet.__gtype__, "MythStatus", "0", mythstatus_factory)
The Gnome Server File
<oaf_info> <oaf_server iid="OAFIID:GNOME_MythstatusApplet_Factory" type="exe" location="/usr/bin/mythstatus.py"> <oaf_attribute name="repo_ids" type="stringv"> <item value="IDL:Bonobo/GenericFactory:1.0"/> <item value="IDL:Bonobo/Unknown:1.0"/> </oaf_attribute> <oaf_attribute name="name" type="string" value="MythTV Status"/> <oaf_attribute name="description" type="string" value="MythTV Backend Status Monitor"/> </oaf_server> <oaf_server iid="OAFIID:GNOME_MythstatusApplet" type="factory" location="OAFIID:GNOME_MythstatusApplet_Factory"> <oaf_attribute name="repo_ids" type="stringv"> <item value="IDL:GNOME/Vertigo/PanelAppletShell:1.0"/> <item value="IDL:Bonobo/Control:1.0"/> <item value="IDL:Bonobo/Unknown:1.0"/> </oaf_attribute> <oaf_attribute name="name" type="string" value="MythTV Status"/> <oaf_attribute name="description" type="string" value="MythTV Backend Status Monitor"/> <oaf_attribute name="panel:category" type="string" value="Utility"/> <oaf_attribute name="panel:icon" type="string" value="/usr/share/pixmaps/mythstatus/tv_color_bars.svg"/> </oaf_server> </oaf_info>
The Icons
File:Tv broken.svg (I'm not sure how to add the icons yet, they are .svg files)