#!/usr/bin/env python import pygtk pygtk.require('2.0') import gtk import commands import sys from popen2 import Popen3 from os.path import basename # The /dev/video* device to use DEVICE = '/dev/video0' # The media player you wish to use to play the stream VIDEO_PLAYER = '/usr/bin/mplayer' # Arguments to pass to the video player executable PLAYER_ARGUMENTS = '-msglevel all=-1 -nolirc -cache 4096 -vf pp=lb ' + DEVICE class RemoteGui(object): """ RemoteGui(device, [ video_player, args ] """ def __init__(self, device, video_player): ###################################################### # GUI Code # ###################################################### # Create the main window self.main_window = gtk.Window(gtk.WINDOW_TOPLEVEL) self.main_window.set_position(gtk.WIN_POS_MOUSE) self.main_window.set_title('PVRmote') self.main_window.set_default_size(165, 0) # Add an icon to the main window from stock icon = self.main_window.render_icon(gtk.STOCK_ABOUT, gtk.ICON_SIZE_MENU) self.main_window.set_icon(icon) # Connect the signal handler to destroy the window self.main_window.connect("destroy", gtk.main_quit) # Create the table to hold the buttons and add it to self.main_window self.main_table = gtk.Table(rows=7, columns=3, homogeneous=True) # Add the table to the window self.main_window.add(self.main_table) # Create a HBox to hold the channel: [###] info self.top_hbox = gtk.HBox(False, 2) # Add the box to the top of the table self.main_table.attach(self.top_hbox,0,3,0,1) # Show the new hbox self.top_hbox.show() # Create the 'Channel:' label, attach it to the hbox, and show it self.lbl_channel = gtk.Label() self.lbl_channel.set_markup('Channel:') self.top_hbox.pack_start(self.lbl_channel, False, False) self.lbl_channel.show() # Create the gtk.Entry to display the current channel self.ent_channel = gtk.Entry() self.top_hbox.pack_end(self.ent_channel, False, False) self.ent_channel.set_width_chars(4) # Connect a handler to track changes in the ent_channel Entry self.ent_channel.connect('changed', self.on_ent_channel_changed) # If the user presses enter in the ent_channel Entry run on_watched_clicked self.ent_channel.connect('activate', self.on_watched_clicked) self.ent_channel.show() # Create an array of buttons #1 through #9 self.btn_numbers = [ [gtk.Button(str(i)), i] for i in range(1,10) ] # Add 0, Composite and S-video buttons self.btn_numbers += [[gtk.Button('Comp'),-2],[gtk.Button('0'),0],[gtk.Button('Svid'),-1]] # Add Channel +, Set (change channel without opening a video player), Channel - self.btn_numbers += [ [ gtk.Button('Ch-'), -1, self.on_channel_step ], [ gtk.Button('Set'), False, self.on_watched_clicked ], [ gtk.Button('Ch+'), 1, self.on_channel_step ] ] # Explain what 'Set', 'Comp' and 'Svid' buttons do with tooltips self.btn_numbers[13][0].set_tooltip_text('Change the channel without trying to open a video player') self.btn_numbers[9][0].set_tooltip_text('Switch to the Composite input') self.btn_numbers[11][0].set_tooltip_text('Switch to the S-Video input') for i in range(len(self.btn_numbers)): # Attach the number buttons to the table self.main_table.attach(self.btn_numbers[i][0], i%3, i%3+1, i/3+1, i/3+2) # Add a signal handler for each button if len(self.btn_numbers[i]) <= 2: self.btn_numbers[i][0].connect('clicked', self.button_handler, self.btn_numbers[i][1]) else: self.btn_numbers[i][0].connect('clicked', self.btn_numbers[i][2], self.btn_numbers[i][1]) # Make the button visible self.btn_numbers[i][0].show() # Create a 'Watch!' button self.btn_watch = gtk.Button('Watch!') # Attach btn_watch to the bottom of the table self.main_table.attach(self.btn_watch,0,3,6,7) # Add it's signal handler self.btn_watch.connect('clicked', self.on_watched_clicked) # Show the 'Watch!' button self.btn_watch.show() # Make the table visible self.main_table.show() # Show the main window self.main_window.show() ###################################################### # non-GUI Initialization Code # ###################################################### self.video_player = video_player # Have we clicked 'watch' since the last text entry? self.watch_clicked = True channel = self.get_channel(device) self.current_channel = 0 self.special_text = { 'Svid' : -1, 'Comp' : -2, -1 : 'Svid', -2 : 'Comp' } self.device = device self.video_player_process = Popen3(None) self.ent_channel.set_text(self.special_text.get(channel, channel)) self.watch_clicked = True def button_handler(self, sender, number): if ( self.watch_clicked and number >= 0 ) or self.current_channel < 0: self.ent_channel.set_text(self.special_text.get(number, str(number))) elif not self.watch_clicked and number >= 0: self.ent_channel.set_text(self.ent_channel.get_text() + str(number)) elif number < 0: self.ent_channel.set_text(self.special_text[number]) # If the user presses the 'Comp' or 'Svid' button, switch to it right away if number < 0: self.on_watched_clicked(None, False) def on_watched_clicked(self, sender, play_video = True): self.watch_clicked = True if self.current_channel != int(self.get_channel(self.device)): print 'switch', self.current_channel, self.get_channel(self.device) if self.current_channel >= 0: if self.get_current_input(self.device) != 0: self.set_input(0, self.device) self.set_channel(self.current_channel, self.device) elif self.current_channel < 0: if self.get_current_input(self.device) != abs(self.current_channel): self.set_input(abs(self.current_channel), self.device) if play_video: self.start_video_player(self.video_player) def on_ent_channel_changed(self, sender): self.watch_clicked = False text = self.ent_channel.get_text() if self.special_text.has_key(text): self.current_channel = self.special_text[text] else: try: self.current_channel = int(text) except ValueError: self.current_channel = 0 def on_channel_step(self, sender, arg): if self.current_channel >= 0 and self.current_channel + arg >= 0: self.ent_channel.set_text(str(self.current_channel + arg)) self.on_watched_clicked(None, False) def get_current_input(self, device): return int(commands.getoutput('v4l2-ctl -I -d ' + device).split(':')[1].split(' ')[1]) def set_input(self, ninput, device): return commands.getstatusoutput('v4l2-ctl -i %d -d %s' % (ninput, device))[0] def set_channel(self, channel, device): return commands.getstatusoutput('ivtv-tune -c %d -d %s' % (channel, device)) def get_channel(self,device): """ Finds the channel that the card is currently tuned to Warning! returns strings for channel numbers and ints for other inputs""" cinput = self.get_current_input(device) if cinput != 0: channel = cinput * -1 else: raw_channel_list = [ channel.split('\t') for channel in commands.getoutput('ivtv-tune -l -d ' + device).split('\n')[1:]] channel_list = dict() for channel_num, freq in raw_channel_list: channel_list[float(freq)] = channel_num current_freq = float(commands.getoutput('v4l2-ctl -F -d ' + device).split('(')[1].split(' ')[0]) channel = channel_list[current_freq] return channel def start_video_player(self, video_player): player = basename(video_player[0]) # Check if the video player is already running if self.video_player_process.poll() != -1 and commands.getstatusoutput('ps -A | grep -E %s$' % player)[0] != 0: self.video_player_process = Popen3('%s %s' % (video_player[0], video_player[1])) if __name__ == '__main__': needed_apps = [ 'ivtv_tune', 'v4l2-ctl' ] for app in needed_apps: if commands.getstatusoutput('which ' + app)[0] != 0: print 'ERROR: Missing ' + app sys.exit(1) RemoteGui(DEVICE, [ VIDEO_PLAYER, PLAYER_ARGUMENTS]) gtk.main()