class PlayerClassUI(ManagerUI): """ Graphic User Interface for Listing Player and Player Alone """ __gtype_name__ = 'PlayerClass' def __init__(self,package=None): ManagerUI.__init__(self,1) builder = gtk.Builder() builder.add_from_file(get_ui_path('player.glade')) self.gui=builder # BUILD GUI self.playerui = builder.get_object("playerbox") self.main_area = builder.get_object("videobox") self.player = None # Seek Bar self.duration = 0 self.seeking = False self.jump=0 # seek value self.jump_id=0 # seek signal id self.correct=False # To correct SCROLL_JUMP, after release it self.seek_bar=self.gui.get_object("seekbar") self.seek_bar.add_events(gtk.gdk.SCROLL_MASK) self.seek_bar.connect("change-value", self.on_seek) # VUMETER self.audiobar=AudioBarClass(True) self.volume_bar = self.audiobar.volume self.volume_bar.connect("value-changed", self.on_volume2) self.vubox = builder.get_object("vubox") self.vubox.add(self.audiobar.bar) # STATUSBAR self.statusbar= StatusBarClass() sbox = builder.get_object("statusbox") sbox.add(self.statusbar.bar) self.playerui.pack_start(self.strip,False,False,0) self.playerui.reorder_child(self.strip,0) self.pack_start(self.playerui,True,True,0) self.status=GC_INIT self.previous=None self.change_state(GC_INIT) self.mediapackage=None # The Mediapackage being reproduced self.thread_id=None builder.connect_signals(self) self.dispatcher.connect("update-play-vumeter", self.audiobar.SetVumeter) self.dispatcher.connect("play-stopped", self.change_state_bypass, GC_READY) self.dispatcher.connect("galicaster-status", self.event_change_mode) self.dispatcher.connect("galicaster-notify-quit", self.close) #-------------------------- INIT PLAYER----------------------- def init_player(self,element,mp): """Send absolute file names and Drawing Areas to the player.""" self.mediapackage = mp tracks = OrderedDict() videos = OrderedDict() index = 0 for t in mp.getTracks(): if not t.getFlavor().count('other'): tracks[t.getIdentifier()] = t.getURI() if (t.getMimeType().count("video") and not t.getFlavor().count('other')): index+=1 videos[t.getIdentifier()] = index areas = self.create_drawing_areas(videos) self.seek_bar.set_value(0) if self.player: self.player.quit() self.player = Player(tracks, areas) self.change_state(GC_READY) self.statusbar.SetVideo(None, self.mediapackage.title) self.statusbar.SetPresenter(None, self.mediapackage.getCreators()) #self.dispatcher.emit def play_from_list(self,package): """Takes a MP from the listing area and plays it""" if self.status == GC_PAUSE: self.on_stop_clicked() self.statusbar.ClearTimer() self.ready_id = self.dispatcher.connect('player-ready',self.on_player_ready) self.init_player(None, package) def on_player_ready(self, origin): """Auto-starts the player once ready""" self.dispatcher.disconnect(self.ready_id) self.init_timer() return True #------------------------- PLAYER ACTIONS ------------------------ def on_play_clicked(self, button): """Starts the reproduction""" self.player.play() self.init_timer() return True def init_timer(self): """Starts the thread handling the timer for visualiztion and seeking feature""" self.change_state(GC_PLAY) self.timer_thread = threading.Thread(target=self.timer_launch_thread) self.thread_id = 1 self.timer_thread.daemon = True self.timer_thread.start() def on_pause_clicked(self, button=None): """Pauses the reproduction""" self.player.pause() self.change_state(GC_PAUSE) return True def on_stop_clicked(self, button=None): """Stops the reproduction and send the seek bar to the start""" self.thread_id = None self.player.stop() self.seek_bar.set_value(0) self.statusbar.SetTimer2(0,self.duration) self.change_state(GC_STOP) return True def on_quit_clicked(self, button): """Stops the preview and close the player""" gui = gtk.Builder() gui.add_from_file(get_ui_path("quit.glade")) dialog = gui.get_object("dialog") response = dialog.run() if response == gtk.RESPONSE_OK: dialog.destroy() if self.status > 0: self.player.quit() self.change_state(GC_EXIT) self.emit("delete_event", gtk.gdk.Event(gtk.gdk.DELETE)) else: dialog.destroy() return True def focus_out(self, button, event): """Stop the player when focus is lost""" self.player.pause() self.change_state(GC_STOP) def on_seek(self, button, scroll_type, new_value): """Move to the new position""" if new_value>100: new_value=100; temp=new_value*self.duration*gst.SECOND/100 # FIXME get_duration propertly if scroll_type == gtk.SCROLL_JUMP and not self.correct: self.seeking = True if self.player.is_playing(): self.player.pause() value=new_value * self.duration // 100 self.statusbar.SetTimer2(value,self.duration) self.jump=temp if not self.jump_id: log.warning("Handling Seek Jump") self.jump_id=self.seek_bar.connect("button-release-event",self.on_seek,0)# FIXME ensure real release, not click if self.correct: self.correct=False if scroll_type != gtk.SCROLL_JUMP: # handel regular scroll if scroll_type == gtk.SCROLL_PAGE_FORWARD or scroll_type == gtk.SCROLL_PAGE_BACKWARD: self.player.seek(temp, False) else: # handle jump self.player.seek(self.jump, True) # jump to the position where the cursor was released self.seek_bar.disconnect(self.jump_id) self.jump_id=0 # jump finished and disconnected self.correct=True # correction rutine activated self.seeking= False def on_volume(self, button, scroll_type, new_value): """Changes the player value""" value = 120 if new_value > 120 else 0 if new_value < 0 else new_value self.player.set_volume(value/100.0) def on_volume2(self, button, new_value): self.player.set_volume(new_value*2.0) def create_drawing_areas(self, source): # TODO refactorize, REC """Creates the preview areas depending on the video tracks of a mediapackage""" main = self.main_area for child in main.get_children(): main.remove(child) child.destroy() areas = None areas = dict() for key in source.keys(): new_area = gtk.DrawingArea() new_area.set_name("playerarea "+str(key)) areas[key]=new_area areas[key].modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("black")) main.pack_start(new_area,True,True,3)#TODO editable padding=3 for child in main.get_children(): child.show() return areas #------------------------- PACKAGE ACTIONS ------------------------ def on_edit_meta(self,button): """Pop ups the Medatada Editor for the current Mediapackage""" key = self.mediapackage.identifier self.edit(key) self.statusbar.SetVideo(None, self.mediapackage.title) self.statusbar.SetPresenter(None, self.mediapackage.getCreators()) return True def on_question(self,button): """Pops up a dialog with the available operations""" package = self.mediapackage self.ingest_question(package) def on_delete(self, button): """ Pops up a dialog. If response is positive the mediapackage is deleted and the focus goes to the previous area""" key = self.mediapackage.identifier response=self.delete(key) if response: self.thread_id = None self.player.stop() self.statusbar.SetVideo(None, "") self.statusbar.SetPresenter(None, "") self.statusbar.ClearTimer() self.change_state(GC_INIT) self.mediapackage = None self.dispatcher.emit("change-mode", 1) return True #-------------------------- UI ACTIONS ----------------------------- def timer_launch_thread(self): """Thread handling the timer, provides base time for seeking""" thread_id= self.thread_id self.initial_time=self.player.get_time() self.duration = self.player.get_duration() gtk.gdk.threads_enter() self.statusbar.SetTimer2(0,self.duration) gtk.gdk.threads_leave() self.volume_bar.set_value(0.5) while thread_id == self.thread_id: if not self.seeking : if not self.duration: actual_time=self.player.get_time() timer=(actual_time-self.initial_time)/gst.SECOND else: try: actual_time, format_type =self.player.get_position() except: actual_time = 0 log.warning("Query position failed") timer = actual_time / gst.SECOND self.seek_bar.set_value(timer*100/self.duration) if thread_id==self.thread_id: gtk.gdk.threads_enter() self.statusbar.SetTimer2(timer,self.duration) gtk.gdk.threads_leave() time.sleep(0.2) return True def resize(self): """Adapts GUI elements to the screen size""" size = context.get_mainwindow().get_size() buttonlist = ["playbutton", "pausebutton", "stopbutton"] secondarylist = ["editbutton", "ingestbutton", "deletebutton"] self.do_resize(buttonlist, secondarylist) vubox = self.gui.get_object("vubox") calign = self.gui.get_object("c_align") k = self.proportion vubox.set_padding(0,int(k*10),int(k*20),int(k*40)) calign.set_padding(int(k*20),int(k*10),0,0) self.statusbar.resize(size) self.audiobar.resize(size) return True def event_change_mode(self, orig, old_state, new_state): """Pause a recording in the event of a change of mode""" if old_state == 2 and self.status == GC_PLAY: self.on_pause_clicked() def change_state_bypass(self,origin,state): """To handles change state through signal""" self.change_state(state) return True def change_state(self,state): """Activates or deactivates the buttons depending on the new state""" play=self.gui.get_object("playbutton") pause=self.gui.get_object("pausebutton") stop=self.gui.get_object("stopbutton") editb=self.gui.get_object("editbutton") deleteb=self.gui.get_object("deletebutton") self.previous,self.status = self.status,state if state==GC_INIT: play.set_sensitive(False) pause.set_sensitive(False) stop.set_sensitive(False) editb.set_sensitive(False) deleteb.set_sensitive(False) if state==GC_READY: play.set_sensitive(True) pause.set_sensitive(False) stop.set_sensitive(False) editb.set_sensitive(True) deleteb.set_sensitive(True) if state==GC_PLAY: play.set_sensitive(False) pause.set_sensitive(True) stop.set_sensitive(True) if state==GC_PAUSE: play.set_sensitive(True) pause.set_sensitive(False) stop.set_sensitive(True) if state==GC_STOP: play.set_sensitive(True) pause.set_sensitive(False) stop.set_sensitive(False) if state==GC_BLOCKED: play.set_sensitive(False) pause.set_sensitive(False) stop.set_sensitive(False) def close(self, signal): """Close player UI, stopping threads and reproduction""" self.thread_id=None if self.status in [GC_PLAY, GC_PAUSE]: self.player.quit() return True
class PlayerClassUI(ManagerUI): """ Graphic User Interface for Listing Player and Player Alone """ __gtype_name__ = 'PlayerClass' def __init__(self, package=None): ManagerUI.__init__(self, 1) builder = Gtk.Builder() builder.add_from_file(get_ui_path('player.glade')) release = builder.get_object("release_label") release.set_label(get_footer()) self.gui = builder # BUILD GUI self.playerui = builder.get_object("playerbox") self.main_area = builder.get_object("videobox") self.player = None # Seek Bar self.duration = 0 self.seeking = False self.jump = 0 # seek value self.jump_id = 0 # seek signal id self.seek_bar = self.gui.get_object("seekbar") self.seek_bar.add_events(Gdk.EventMask.SCROLL_MASK) self.seek_bar.connect("change-value", self.on_seek) # VUMETER self.vumeterL = builder.get_object("progressbarL") self.vumeterR = builder.get_object("progressbarR") self.label_channels_player = builder.get_object( "label_channels_player") self.rangeVum = 50 self.stereo = True # STATUSBAR self.statusbar = builder.get_object("statusbar") self.status = builder.get_object("status") self.timer = builder.get_object("timer") self.video = builder.get_object("video") self.presenter = builder.get_object("presenter") self.playerui.pack_start(self.strip, False, False, 0) self.playerui.reorder_child(self.strip, 0) self.pack_start(self.playerui, True, True, 0) self.status = INIT self.previous = None self.change_state(INIT) self.mediapackage = None # The Mediapackage being reproduced self.thread_id = None builder.connect_signals(self) self.dispatcher.connect_ui("player-vumeter", self.set_vumeter) self.dispatcher.connect("player-status", self.change_state_bypass) self.dispatcher.connect_ui('play-list', self.play_from_list) self.dispatcher.connect_ui("view-changed", self.event_change_mode) self.dispatcher.connect_ui("quit", self.close) #-------------------------- INIT PLAYER----------------------- def init_player(self, element, mp): """Send absolute file names and Drawing Areas to the player.""" if (self.mediapackage != mp): if self.status == PAUSED: self.on_stop_clicked() self.clear_timer() self.mediapackage = mp tracks = OrderedDict() videos = OrderedDict() index = 0 for t in mp.getTracks(): if not t.getFlavor().count('other') and not t.getFlavor( ).count("delivery") and not t.getFlavor().count("composition"): tracks[t.getIdentifier()] = t.getURI() if t.getMimeType().count("video") and t.getFlavor().count( "source"): index += 1 videos[t.getIdentifier()] = index areas = self.create_drawing_areas(videos) self.seek_bar.set_value(0) if self.player: self.player.quit() self.player = Player(tracks, areas) self.change_state(READY) self.setVideo(None, self.mediapackage.title) self.setPresenter(None, self.mediapackage.getCreator()) self.on_play_clicked(None) def play_from_list(self, origin, package): """Takes a MP from the listing area and plays it""" self.dispatcher.emit("action-view-change", 2) self.init_player(None, package) #------------------------- PLAYER ACTIONS ------------------------ def on_play_clicked(self, button=None): """Starts the reproduction""" self.change_state(PLAYING) self.player.play() self.init_timer() return True def init_timer(self): """Starts the thread handling the timer for visualiztion and seeking feature""" self.timer_thread = threading.Thread(target=self.timer_launch_thread) self.thread_id = 1 self.timer_thread.daemon = True self.timer_thread.start() def on_pause_clicked(self, button=None): """Pauses the reproduction""" if not button or button.get_active(): self.player.pause() self.change_state(PAUSED) else: self.on_play_clicked() return True def on_stop_clicked(self, button=None): """Stops the reproduction and send the seek bar to the start""" self.thread_id = None self.player.stop() self.seek_bar.set_value(0) self.set_timer(0, self.duration) self.change_state(STOPPED) return True def on_quit_clicked(self, button): """Stops the preview and close the player""" gui = Gtk.Builder() gui.add_from_file(get_ui_path("quit.glade")) dialog = gui.get_object("dialog") response = dialog.run() if response == Gtk.ResponseType.OK: dialog.destroy() if self.status > 0: self.player.quit() self.change_state(ERRORED) self.emit("delete_event", Gdk.Event(Gdk.DELETE)) else: dialog.destroy() return True def focus_out(self, button, event): """Stop the player when focus is lost""" self.player.pause() self.change_state(STOPPED) def on_seek(self, button, scroll_type, new_value): """Move to the new position""" if new_value > 100: new_value = 100 temp = new_value * self.duration * Gst.SECOND / 100 # FIXME get_duration propertly if scroll_type == Gtk.ScrollType.JUMP: self.seeking = True if self.player.is_playing(): self.player.pause() value = new_value * self.duration // 100 self.set_timer(value, self.duration) self.jump = temp if not self.jump_id: log.warning("Handling Seek Jump") self.jump_id = self.seek_bar.connect( "button-release-event", self.on_seek, 0) # FIXME ensure real release, not click if scroll_type != Gtk.ScrollType.JUMP: # handel regular scroll if scroll_type == Gtk.ScrollType.PAGE_FORWARD or scroll_type == Gtk.ScrollType.PAGE_BACKWARD: self.player.seek(temp, False) else: # handle jump self.player.seek( self.jump, True) # jump to the position where the cursor was released self.seek_bar.disconnect(self.jump_id) self.jump_id = 0 # jump finished and disconnected self.seeking = False def create_drawing_areas(self, source): # TODO refactorize, REC """Creates the preview areas depending on the video tracks of a mediapackage""" main = self.main_area for child in main.get_children(): main.remove(child) child.destroy() areas = dict() for key in list(source.keys()): new_area = Gtk.DrawingArea() new_area.set_name("playerarea " + str(key)) areas[key] = new_area areas[key].modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse("black")) main.pack_start(new_area, True, True, 3) #TODO editable padding=3 for child in main.get_children(): child.show() return areas #------------------------- PACKAGE ACTIONS ------------------------ def on_edit_meta(self, button): """Pop ups the Medatada Editor for the current Mediapackage""" key = self.mediapackage.identifier self.edit(key) self.setVideo(None, self.mediapackage.title) self.setPresenter(None, self.mediapackage.getCreator()) return True def on_question(self, button): """Pops up a dialog with the available operations""" package = self.mediapackage self.ingest_question(package) def on_delete(self, button): """ Pops up a dialog. If response is positive the mediapackage is deleted and the focus goes to the previous area""" key = self.mediapackage.identifier self.delete(key, self.create_delete_dialog_response(key)) return True def create_delete_dialog_response(self, key): def on_delete_dialog_response(response_id, **kwargs): if response_id in message.POSITIVE: self.repository.delete(self.repository.get(key)) self.thread_id = None self.player.stop() self.setVideo(None, "") self.setPresenter(None, "") self.clear_timer() self.change_state(INIT) self.mediapackage = None self.dispatcher.emit("action-view-change", 1) return on_delete_dialog_response #-------------------------- UI ACTIONS ----------------------------- def timer_launch_thread(self): """Thread handling the timer, provides base time for seeking""" thread_id = self.thread_id self.initial_time = self.player.get_time() self.duration = self.player.get_duration() GObject.idle_add(self.set_timer, 0, self.duration) while thread_id == self.thread_id: if not self.seeking: if not self.duration: actual_time = self.player.get_time() timer = (actual_time - self.initial_time) / Gst.SECOND else: try: format_type, actual_time = self.player.get_position() except Exception: actual_time = 0 log.warning("Query position failed") timer = actual_time / Gst.SECOND self.seek_bar.set_value(timer * 100 / self.duration) if thread_id == self.thread_id: GObject.idle_add(self.set_timer, timer, self.duration) time.sleep(0.2) return True def resize(self): """Adapts GUI elements to the screen size""" buttonlist = ["playbutton", "pausebutton", "stopbutton"] secondarylist = ["editbutton", "ingestbutton", "deletebutton"] self.do_resize(buttonlist, secondarylist) calign = self.gui.get_object("c_align") k = self.proportion calign.set_padding(int(k * 20), int(k * 10), 0, 0) return True def event_change_mode(self, orig, old_state, new_state): """Pause a recording in the event of a change of mode""" if old_state == 2 and self.status == PLAYING: self.on_pause_clicked() def change_state_bypass(self, origin, state): """To handles change state through signal""" self.change_state(state) return True def change_state(self, state): """Activates or deactivates the buttons depending on the new state""" play = self.gui.get_object("playbutton") pause = self.gui.get_object("pausebutton") stop = self.gui.get_object("stopbutton") editb = self.gui.get_object("editbutton") deleteb = self.gui.get_object("deletebutton") self.previous, self.status = self.status, state if state == INIT: play.set_sensitive(False) pause.set_sensitive(False) stop.set_sensitive(False) editb.set_sensitive(False) deleteb.set_sensitive(False) if state == READY: play.set_sensitive(True) pause.set_sensitive(False) stop.set_sensitive(False) editb.set_sensitive(True) deleteb.set_sensitive(True) if state == PLAYING: play.set_sensitive(False) pause.set_sensitive(True) pause.set_active(False) stop.set_sensitive(True) if state == PAUSED: play.set_sensitive(True) pause.set_sensitive(True) stop.set_sensitive(True) if state == STOPPED: play.set_sensitive(True) pause.set_sensitive(False) stop.set_sensitive(False) if state == ERRORED: play.set_sensitive(False) pause.set_sensitive(False) stop.set_sensitive(False) def close(self, signal): """Close player UI, stopping threads and reproduction""" self.thread_id = None if self.status in [PLAYING, PAUSED]: self.player.quit() return True #-------------------------- AUDIOBAR ----------------------------- def set_vumeter(self, element, data, data2, stereo): value, value2 = self.scale_data(data, data2) self.vumeterL.set_fraction(value) self.vumeterR.set_fraction(value2) if not stereo and self.stereo: self.stereo = False self.label_channels_player.set_text("Mono") elif stereo and not self.stereo: self.stereo = True self.label_channels_player.set_text("Stereo") def scale_data(self, data, data2): if data == "Inf": data = -200 if data2 == "Inf": data2 = -200 if data < -100: valor = 1 else: valor = 1 - ((data + self.rangeVum) / float(self.rangeVum)) if data2 < -100: valor2 = 1 else: valor2 = 1 - ((data2 + self.rangeVum) / float(self.rangeVum)) return valor, valor2 #-------------------------- STATUSBAR ----------------------------- def clear_timer(self): """Empties the timer""" self.timer.set_text("") def setVideo(self, element, value=None): if value != None: self.video.set_text(value) self.video.set_property("tooltip-text", value) def setPresenter(self, element, value): self.presenter.set_text(value or '') self.presenter.set_property("tooltip-text", value or '') def set_timer(self, value, duration): """Sets the timer on reproduction environments""" self.timer.set_text(self.time_readable2(value, duration)) def time_readable(self, seconds): """ Generates date hour:minute:seconds from seconds""" iso = int(seconds) return "{}:{:02d}:{:02d}".format(iso // 3600, (iso % 3600) // 60, iso % 60) def time_readable2(self, s1, s2): """ Generates date hour:minute:seconds from seconds """ t1 = self.time_readable(s1) t2 = self.time_readable(s2) timing = t1 + " / " + t2 return timing
class PlayerClassUI(ManagerUI): """ Graphic User Interface for Listing Player and Player Alone """ __gtype_name__ = 'PlayerClass' def __init__(self, package=None): ManagerUI.__init__(self, 1) builder = gtk.Builder() builder.add_from_file(get_ui_path('player.glade')) self.gui = builder # BUILD GUI self.playerui = builder.get_object("playerbox") self.main_area = builder.get_object("videobox") self.player = None # Seek Bar self.duration = 0 self.seeking = False self.jump = 0 # seek value self.jump_id = 0 # seek signal id self.correct = False # To correct SCROLL_JUMP, after release it self.seek_bar = self.gui.get_object("seekbar") self.seek_bar.add_events(gtk.gdk.SCROLL_MASK) self.seek_bar.connect("change-value", self.on_seek) # VUMETER self.audiobar = AudioBarClass() self.volume_bar = self.audiobar.volume self.volume_bar.connect("value-changed", self.on_volume2) self.vubox = builder.get_object("vubox") self.vubox.add(self.audiobar.bar) # STATUSBAR self.statusbar = StatusBarClass() sbox = builder.get_object("statusbox") sbox.add(self.statusbar.bar) self.playerui.pack_start(self.strip, False, False, 0) self.playerui.reorder_child(self.strip, 0) self.pack_start(self.playerui, True, True, 0) self.status = GC_INIT self.previous = None self.change_state(GC_INIT) self.mediapackage = None # The Mediapackage being reproduced self.thread_id = None builder.connect_signals(self) self.dispatcher.connect("update-play-vumeter", self.audiobar.SetVumeter) self.dispatcher.connect("play-stopped", self.change_state_bypass, GC_READY) self.dispatcher.connect('play-list', self.play_from_list) self.dispatcher.connect("galicaster-status", self.event_change_mode) self.dispatcher.connect("galicaster-notify-quit", self.close) #-------------------------- INIT PLAYER----------------------- def init_player(self, element, mp): """Send absolute file names and Drawing Areas to the player.""" if (self.mediapackage != mp): if self.status == GC_PAUSE: self.on_stop_clicked() self.statusbar.ClearTimer() self.mediapackage = mp tracks = OrderedDict() videos = OrderedDict() index = 0 for t in mp.getTracks(): if not t.getFlavor().count('other'): tracks[t.getIdentifier()] = t.getURI() if (t.getMimeType().count("video") and not t.getFlavor().count('other')): index += 1 videos[t.getIdentifier()] = index areas = self.create_drawing_areas(videos) self.seek_bar.set_value(0) if self.player: self.player.quit() self.player = Player(tracks, areas) self.change_state(GC_READY) self.statusbar.SetVideo(None, self.mediapackage.title) self.statusbar.SetPresenter(None, self.mediapackage.getCreator()) self.on_play_clicked(None) def play_from_list(self, origin, package): """Takes a MP from the listing area and plays it""" self.dispatcher.emit("change-mode", 2) self.init_player(None, package) #------------------------- PLAYER ACTIONS ------------------------ def on_play_clicked(self, button): """Starts the reproduction""" self.change_state(GC_PLAY) self.player.play() self.init_timer() return True def init_timer(self): """Starts the thread handling the timer for visualiztion and seeking feature""" self.timer_thread = threading.Thread(target=self.timer_launch_thread) self.thread_id = 1 self.timer_thread.daemon = True self.timer_thread.start() def on_pause_clicked(self, button=None): """Pauses the reproduction""" self.player.pause() self.change_state(GC_PAUSE) return True def on_stop_clicked(self, button=None): """Stops the reproduction and send the seek bar to the start""" self.thread_id = None self.player.stop() self.seek_bar.set_value(0) self.statusbar.SetTimer2(0, self.duration) self.change_state(GC_STOP) return True def on_quit_clicked(self, button): """Stops the preview and close the player""" gui = gtk.Builder() gui.add_from_file(get_ui_path("quit.glade")) dialog = gui.get_object("dialog") response = dialog.run() if response == gtk.RESPONSE_OK: dialog.destroy() if self.status > 0: self.player.quit() self.change_state(GC_EXIT) self.emit("delete_event", gtk.gdk.Event(gtk.gdk.DELETE)) else: dialog.destroy() return True def focus_out(self, button, event): """Stop the player when focus is lost""" self.player.pause() self.change_state(GC_STOP) def on_seek(self, button, scroll_type, new_value): """Move to the new position""" if new_value > 100: new_value = 100 temp = new_value * self.duration * gst.SECOND / 100 # FIXME get_duration propertly if scroll_type == gtk.SCROLL_JUMP and not self.correct: self.seeking = True if self.player.is_playing(): self.player.pause() value = new_value * self.duration // 100 self.statusbar.SetTimer2(value, self.duration) self.jump = temp if not self.jump_id: log.warning("Handling Seek Jump") self.jump_id = self.seek_bar.connect( "button-release-event", self.on_seek, 0) # FIXME ensure real release, not click if self.correct: self.correct = False if scroll_type != gtk.SCROLL_JUMP: # handel regular scroll if scroll_type == gtk.SCROLL_PAGE_FORWARD or scroll_type == gtk.SCROLL_PAGE_BACKWARD: self.player.seek(temp, False) else: # handle jump self.player.seek( self.jump, True) # jump to the position where the cursor was released self.seek_bar.disconnect(self.jump_id) self.jump_id = 0 # jump finished and disconnected self.correct = True # correction rutine activated self.seeking = False def on_volume(self, button, scroll_type, new_value): """Changes the player value""" value = 120 if new_value > 120 else 0 if new_value < 0 else new_value self.player.set_volume(value / 100.0) def on_volume2(self, button, new_value): self.player.set_volume(new_value * 2.0) def create_drawing_areas(self, source): # TODO refactorize, REC """Creates the preview areas depending on the video tracks of a mediapackage""" main = self.main_area for child in main.get_children(): main.remove(child) child.destroy() areas = None areas = dict() for key in source.keys(): new_area = gtk.DrawingArea() new_area.set_name("playerarea " + str(key)) areas[key] = new_area areas[key].modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("black")) main.pack_start(new_area, True, True, 3) #TODO editable padding=3 for child in main.get_children(): child.show() return areas #------------------------- PACKAGE ACTIONS ------------------------ def on_edit_meta(self, button): """Pop ups the Medatada Editor for the current Mediapackage""" key = self.mediapackage.identifier self.edit(key) self.statusbar.SetVideo(None, self.mediapackage.title) self.statusbar.SetPresenter(None, self.mediapackage.getCreator()) return True def on_question(self, button): """Pops up a dialog with the available operations""" package = self.mediapackage self.ingest_question(package) def on_delete(self, button): """ Pops up a dialog. If response is positive the mediapackage is deleted and the focus goes to the previous area""" key = self.mediapackage.identifier response = self.delete(key) if response: self.thread_id = None self.player.stop() self.statusbar.SetVideo(None, "") self.statusbar.SetPresenter(None, "") self.statusbar.ClearTimer() self.change_state(GC_INIT) self.mediapackage = None self.dispatcher.emit("change-mode", 1) return True #-------------------------- UI ACTIONS ----------------------------- def timer_launch_thread(self): """Thread handling the timer, provides base time for seeking""" thread_id = self.thread_id self.initial_time = self.player.get_time() self.duration = self.player.get_duration() gtk.gdk.threads_enter() self.statusbar.SetTimer2(0, self.duration) gtk.gdk.threads_leave() self.volume_bar.set_value(0.5) while thread_id == self.thread_id: if not self.seeking: if not self.duration: actual_time = self.player.get_time() timer = (actual_time - self.initial_time) / gst.SECOND else: try: actual_time, format_type = self.player.get_position() except: actual_time = 0 log.warning("Query position failed") timer = actual_time / gst.SECOND self.seek_bar.set_value(timer * 100 / self.duration) if thread_id == self.thread_id: gtk.gdk.threads_enter() self.statusbar.SetTimer2(timer, self.duration) gtk.gdk.threads_leave() time.sleep(0.2) return True def resize(self): """Adapts GUI elements to the screen size""" size = context.get_mainwindow().get_size() buttonlist = ["playbutton", "pausebutton", "stopbutton"] secondarylist = ["editbutton", "ingestbutton", "deletebutton"] self.do_resize(buttonlist, secondarylist) vubox = self.gui.get_object("vubox") calign = self.gui.get_object("c_align") k = self.proportion vubox.set_padding(0, int(k * 10), int(k * 20), int(k * 40)) calign.set_padding(int(k * 20), int(k * 10), 0, 0) self.statusbar.resize(size) self.audiobar.resize(size) return True def event_change_mode(self, orig, old_state, new_state): """Pause a recording in the event of a change of mode""" if old_state == 2 and self.status == GC_PLAY: self.on_pause_clicked() def change_state_bypass(self, origin, state): """To handles change state through signal""" self.change_state(state) return True def change_state(self, state): """Activates or deactivates the buttons depending on the new state""" play = self.gui.get_object("playbutton") pause = self.gui.get_object("pausebutton") stop = self.gui.get_object("stopbutton") editb = self.gui.get_object("editbutton") deleteb = self.gui.get_object("deletebutton") self.previous, self.status = self.status, state if state == GC_INIT: play.set_sensitive(False) pause.set_sensitive(False) stop.set_sensitive(False) editb.set_sensitive(False) deleteb.set_sensitive(False) if state == GC_READY: play.set_sensitive(True) pause.set_sensitive(False) stop.set_sensitive(False) editb.set_sensitive(True) deleteb.set_sensitive(True) if state == GC_PLAY: play.set_sensitive(False) pause.set_sensitive(True) stop.set_sensitive(True) if state == GC_PAUSE: play.set_sensitive(True) pause.set_sensitive(False) stop.set_sensitive(True) if state == GC_STOP: play.set_sensitive(True) pause.set_sensitive(False) stop.set_sensitive(False) if state == GC_BLOCKED: play.set_sensitive(False) pause.set_sensitive(False) stop.set_sensitive(False) def close(self, signal): """Close player UI, stopping threads and reproduction""" self.thread_id = None if self.status in [GC_PLAY, GC_PAUSE]: self.player.quit() return True
class PlayerClassUI(ManagerUI): """ Graphic User Interface for Listing Player and Player Alone """ __gtype_name__ = 'PlayerClass' def __init__(self,package=None): ManagerUI.__init__(self,1) builder = Gtk.Builder() builder.add_from_file(get_ui_path('player.glade')) release = builder.get_object("release_label") release.set_label(get_footer()) self.gui=builder # BUILD GUI self.playerui = builder.get_object("playerbox") self.main_area = builder.get_object("videobox") self.player = None # Seek Bar self.duration = 0 self.seeking = False self.jump=0 # seek value self.jump_id=0 # seek signal id self.seek_bar=self.gui.get_object("seekbar") self.seek_bar.add_events(Gdk.EventMask.SCROLL_MASK) self.seek_bar.connect("change-value", self.on_seek) # VUMETER self.vumeterL = builder.get_object("progressbarL") self.vumeterR = builder.get_object("progressbarR") self.label_channels_player = builder.get_object("label_channels_player") self.rangeVum = 50 self.stereo = True # STATUSBAR self.statusbar = builder.get_object("statusbar") self.status=builder.get_object("status") self.timer=builder.get_object("timer") self.video=builder.get_object("video") self.presenter=builder.get_object("presenter") self.playerui.pack_start(self.strip,False,False,0) self.playerui.reorder_child(self.strip,0) self.pack_start(self.playerui,True,True,0) self.status=INIT self.previous=None self.change_state(INIT) self.mediapackage=None # The Mediapackage being reproduced self.thread_id=None builder.connect_signals(self) self.dispatcher.connect_ui("player-vumeter", self.set_vumeter) self.dispatcher.connect("player-status", self.change_state_bypass) self.dispatcher.connect_ui('play-list', self.play_from_list) self.dispatcher.connect_ui("view-changed", self.event_change_mode) self.dispatcher.connect_ui("quit", self.close) #-------------------------- INIT PLAYER----------------------- def init_player(self, element, mp): """Send absolute file names and Drawing Areas to the player.""" if (self.mediapackage != mp): if self.status == PAUSED: self.on_stop_clicked() self.clear_timer() self.mediapackage = mp tracks = OrderedDict() videos = OrderedDict() index = 0 for t in mp.getTracks(): if not t.getFlavor().count('other') and not t.getFlavor().count("delivery") and not t.getFlavor().count("composition"): tracks[t.getIdentifier()] = t.getURI() if t.getMimeType().count("video") and t.getFlavor().count("source"): index+=1 videos[t.getIdentifier()] = index areas = self.create_drawing_areas(videos) self.seek_bar.set_value(0) if self.player: self.player.quit() self.player = Player(tracks, areas) self.change_state(READY) self.setVideo(None, self.mediapackage.title) self.setPresenter(None, self.mediapackage.getCreator()) self.on_play_clicked(None) def play_from_list(self, origin, package): """Takes a MP from the listing area and plays it""" self.dispatcher.emit("action-view-change", 2) self.init_player(None, package) #------------------------- PLAYER ACTIONS ------------------------ def on_play_clicked(self, button=None): """Starts the reproduction""" self.change_state(PLAYING) self.player.play() self.init_timer() return True def init_timer(self): """Starts the thread handling the timer for visualiztion and seeking feature""" self.timer_thread = threading.Thread(target=self.timer_launch_thread) self.thread_id = 1 self.timer_thread.daemon = True self.timer_thread.start() def on_pause_clicked(self, button=None): """Pauses the reproduction""" if not button or button.get_active(): self.player.pause() self.change_state(PAUSED) else: self.on_play_clicked() return True def on_stop_clicked(self, button=None): """Stops the reproduction and send the seek bar to the start""" self.thread_id = None self.player.stop() self.seek_bar.set_value(0) self.set_timer(0,self.duration) self.change_state(STOPPED) return True def on_quit_clicked(self, button): """Stops the preview and close the player""" gui = Gtk.Builder() gui.add_from_file(get_ui_path("quit.glade")) dialog = gui.get_object("dialog") response = dialog.run() if response == Gtk.ResponseType.OK: dialog.destroy() if self.status > 0: self.player.quit() self.change_state(ERRORED) self.emit("delete_event", Gdk.Event(Gdk.DELETE)) else: dialog.destroy() return True def focus_out(self, button, event): """Stop the player when focus is lost""" self.player.pause() self.change_state(STOPPED) def on_seek(self, button, scroll_type, new_value): """Move to the new position""" if new_value>100: new_value=100; temp=new_value*self.duration*Gst.SECOND/100 # FIXME get_duration propertly if scroll_type == Gtk.ScrollType.JUMP: self.seeking = True if self.player.is_playing(): self.player.pause() value=new_value * self.duration // 100 self.set_timer(value,self.duration) self.jump=temp if not self.jump_id: log.warning("Handling Seek Jump") self.jump_id=self.seek_bar.connect("button-release-event",self.on_seek,0)# FIXME ensure real release, not click if scroll_type != Gtk.ScrollType.JUMP: # handel regular scroll if scroll_type == Gtk.ScrollType.PAGE_FORWARD or scroll_type == Gtk.ScrollType.PAGE_BACKWARD: self.player.seek(temp, False) else: # handle jump self.player.seek(self.jump, True) # jump to the position where the cursor was released self.seek_bar.disconnect(self.jump_id) self.jump_id=0 # jump finished and disconnected self.seeking= False def create_drawing_areas(self, source): # TODO refactorize, REC """Creates the preview areas depending on the video tracks of a mediapackage""" main = self.main_area for child in main.get_children(): main.remove(child) child.destroy() areas = dict() for key in source.keys(): new_area = Gtk.DrawingArea() new_area.set_name("playerarea "+str(key)) areas[key]=new_area areas[key].modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse("black")) main.pack_start(new_area,True,True,3)#TODO editable padding=3 for child in main.get_children(): child.show() return areas #------------------------- PACKAGE ACTIONS ------------------------ def on_edit_meta(self,button): """Pop ups the Medatada Editor for the current Mediapackage""" key = self.mediapackage.identifier self.edit(key) self.setVideo(None, self.mediapackage.title) self.setPresenter(None, self.mediapackage.getCreator()) return True def on_question(self,button): """Pops up a dialog with the available operations""" package = self.mediapackage self.ingest_question(package) def on_delete(self, button): """ Pops up a dialog. If response is positive the mediapackage is deleted and the focus goes to the previous area""" key = self.mediapackage.identifier self.delete(key,self.create_delete_dialog_response(key)) return True def create_delete_dialog_response(self, key): def on_delete_dialog_response(response_id, **kwargs): if response_id in message.POSITIVE: self.repository.delete(self.repository.get(key)) self.thread_id = None self.player.stop() self.setVideo(None, "") self.setPresenter(None, "") self.clear_timer() self.change_state(INIT) self.mediapackage = None self.dispatcher.emit("action-view-change", 1) return on_delete_dialog_response #-------------------------- UI ACTIONS ----------------------------- def timer_launch_thread(self): """Thread handling the timer, provides base time for seeking""" thread_id= self.thread_id self.initial_time=self.player.get_time() self.duration = self.player.get_duration() GObject.idle_add(self.set_timer, 0, self.duration) while thread_id == self.thread_id: if not self.seeking : if not self.duration: actual_time=self.player.get_time() timer=(actual_time-self.initial_time)/Gst.SECOND else: try: format_type, actual_time =self.player.get_position() except Exception: actual_time = 0 log.warning("Query position failed") timer = actual_time / Gst.SECOND self.seek_bar.set_value(timer*100/self.duration) if thread_id==self.thread_id: GObject.idle_add(self.set_timer, timer, self.duration) time.sleep(0.2) return True def resize(self): """Adapts GUI elements to the screen size""" buttonlist = ["playbutton", "pausebutton", "stopbutton"] secondarylist = ["editbutton", "ingestbutton", "deletebutton"] self.do_resize(buttonlist, secondarylist) calign = self.gui.get_object("c_align") k = self.proportion calign.set_padding(int(k*20),int(k*10),0,0) return True def event_change_mode(self, orig, old_state, new_state): """Pause a recording in the event of a change of mode""" if old_state == 2 and self.status == PLAYING: self.on_pause_clicked() def change_state_bypass(self,origin,state): """To handles change state through signal""" self.change_state(state) return True def change_state(self,state): """Activates or deactivates the buttons depending on the new state""" play=self.gui.get_object("playbutton") pause=self.gui.get_object("pausebutton") stop=self.gui.get_object("stopbutton") editb=self.gui.get_object("editbutton") deleteb=self.gui.get_object("deletebutton") self.previous,self.status = self.status,state if state==INIT: play.set_sensitive(False) pause.set_sensitive(False) stop.set_sensitive(False) editb.set_sensitive(False) deleteb.set_sensitive(False) if state==READY: play.set_sensitive(True) pause.set_sensitive(False) stop.set_sensitive(False) editb.set_sensitive(True) deleteb.set_sensitive(True) if state==PLAYING: play.set_sensitive(False) pause.set_sensitive(True) pause.set_active(False) stop.set_sensitive(True) if state==PAUSED: play.set_sensitive(True) pause.set_sensitive(True) stop.set_sensitive(True) if state==STOPPED: play.set_sensitive(True) pause.set_sensitive(False) stop.set_sensitive(False) if state==ERRORED: play.set_sensitive(False) pause.set_sensitive(False) stop.set_sensitive(False) def close(self, signal): """Close player UI, stopping threads and reproduction""" self.thread_id=None if self.status in [PLAYING, PAUSED]: self.player.quit() return True #-------------------------- AUDIOBAR ----------------------------- def set_vumeter(self,element,data, data2, stereo): value,value2 = self.scale_data(data,data2) self.vumeterL.set_fraction(value) self.vumeterR.set_fraction(value2) if not stereo and self.stereo: self.stereo = False self.label_channels_player.set_text("Mono") elif stereo and not self.stereo: self.stereo = True self.label_channels_player.set_text("Stereo") def scale_data(self,data,data2): if data == "Inf": data = -200 if data2 == "Inf": data2 = -200 if data < -100: valor = 1 else: valor=1 - ((data + self.rangeVum)/float(self.rangeVum)) if data2 < -100: valor2 = 1 else: valor2=1 - ((data2 + self.rangeVum)/float(self.rangeVum)) return valor, valor2 #-------------------------- STATUSBAR ----------------------------- def clear_timer(self): """Empties the timer""" self.timer.set_text("") def setVideo(self, element, value = None): if value != None: self.video.set_text(value) self.video.set_property("tooltip-text",value) def setPresenter(self,element, value): self.presenter.set_text(value or '') self.presenter.set_property("tooltip-text",value or '') def set_timer(self,value,duration): """Sets the timer on reproduction environments""" self.timer.set_text(self.time_readable2(value,duration)) def time_readable(self,seconds): """ Generates date hour:minute:seconds from seconds""" iso = int(seconds) return "{}:{:02d}:{:02d}".format(iso//3600, (iso%3600)//60, iso%60) def time_readable2(self,s1,s2): """ Generates date hour:minute:seconds from seconds """ t1=self.time_readable(s1) t2=self.time_readable(s2) timing = t1+" / "+t2 return timing