Line 143: |
Line 143: |
| self.gplayWin.resize( 300, 400 ) | | self.gplayWin.resize( 300, 400 ) |
| self.gplayWin.show_all( ) | | self.gplayWin.show_all( ) |
| + | </pre> |
| + | |
| + | === How can I add play/pause buttons and a scrubber to my audio or video? === |
| + | <pre> |
| + | import gtk |
| + | import pygtk |
| + | pygtk.require('2.0') |
| + | import pygst |
| + | pygst.require('0.10') |
| + | import gst |
| + | import gst.interfaces |
| + | import gobject |
| + | |
| + | import sugar.graphics.style |
| + | |
| + | class GScrub(gtk.Window): |
| + | |
| + | def __init__(self, gplay, playImg, pauseImg): |
| + | gtk.Window.__init__(self) |
| + | self._gplay = gplay |
| + | self._playImg = playImg |
| + | self._pauseImg = pauseImg |
| + | |
| + | self.UPDATE_INTERVAL = 500 |
| + | self.UPDATE_SCALE_ID = 0 |
| + | self.CHANGED_ID = 0 |
| + | self.was_playing = False |
| + | self.p_position = gst.CLOCK_TIME_NONE |
| + | self.p_duration = gst.CLOCK_TIME_NONE |
| + | |
| + | self.hbox = gtk.HBox() |
| + | self.hbox.modify_bg( gtk.STATE_NORMAL, sugar.graphics.style.COLOR_BLACK.get_gdk_color() ) |
| + | self.hbox.modify_bg( gtk.STATE_INSENSITIVE, sugar.graphics.style.COLOR_BLACK.get_gdk_color() ) |
| + | self.add( self.hbox ) |
| + | |
| + | self.button = gtk.Button() |
| + | buttBox = gtk.EventBox() |
| + | buttBox.add(self.button) |
| + | buttBox.modify_bg( gtk.STATE_NORMAL, sugar.graphics.style.COLOR_BLACK.get_gdk_color() ) |
| + | self.button.set_image( self._playImg ) |
| + | self.button.set_property('can-default', True) |
| + | self.button.set_relief(gtk.RELIEF_NONE) |
| + | self.button.set_size_request( 55, 55 ) |
| + | buttBox.set_size_request( 55, 55 ) |
| + | self.button.show() |
| + | |
| + | buttBox.modify_bg( gtk.STATE_NORMAL, sugar.graphics.style.COLOR_BLACK.get_gdk_color() ) |
| + | self.button.modify_bg( gtk.STATE_ACTIVE, sugar.graphics.style.COLOR_BLACK.get_gdk_color() ) |
| + | |
| + | self.button.connect('clicked', self._buttonClickedCb) |
| + | self.hbox.pack_start(buttBox, expand=False) |
| + | |
| + | self.adjustment = gtk.Adjustment(0.0, 0.00, 100.0, 0.1, 1.0, 1.0) |
| + | self.hscale = gtk.HScale(self.adjustment) |
| + | self.hscale.set_draw_value(False) |
| + | self.hscale.set_update_policy(gtk.UPDATE_CONTINUOUS) |
| + | hscaleBox = gtk.EventBox() |
| + | hscaleBox.modify_bg( gtk.STATE_NORMAL, sugar.graphics.style.COLOR_BLACK.get_gdk_color() ) |
| + | hscaleBox.add( self.hscale ) |
| + | self.hscale.connect('button-press-event', self._scaleButtonPressCb) |
| + | self.hscale.connect('button-release-event', self._scaleButtonReleaseCb) |
| + | self.hbox.pack_start(hscaleBox, expand=True) |
| + | |
| + | |
| + | def removeCallbacks( self ): |
| + | if (self.UPDATE_SCALE_ID != 0): |
| + | gobject.source_remove(self.UPDATE_SCALE_ID) |
| + | self.UPDATE_SCALE_ID = 0 |
| + | if (self.CHANGED_ID != 0): |
| + | gobject.source_remove(self.CHANGED_ID) |
| + | self.CHANGED_ID = 0 |
| + | |
| + | |
| + | def reset(self): |
| + | self.adjustment.set_value(0) |
| + | |
| + | |
| + | def _buttonClickedCb(self, widget): |
| + | self.play_toggled() |
| + | |
| + | |
| + | def set_button_play(self): |
| + | self.button.set_image(self._playImg) |
| + | |
| + | |
| + | def set_button_pause(self): |
| + | self.button.set_image(self._pauseImg) |
| + | |
| + | |
| + | def play_toggled(self): |
| + | self.p_position, self.p_duration = self._gplay.queryPosition() |
| + | if (self.p_position == self.p_duration): |
| + | self._gplay.seek(0) |
| + | self._gplay.pause() |
| + | |
| + | if self._gplay.is_playing(): |
| + | self._gplay.pause() |
| + | self.set_button_play() |
| + | else: |
| + | #if self._gplay.error: |
| + | # #todo: check if we have "error", and also to disable everything |
| + | # self.button.set_disabled() |
| + | #else: |
| + | self.doPlay() |
| + | |
| + | |
| + | def doPlay(self): |
| + | self._gplay.play() |
| + | if self.UPDATE_SCALE_ID == 0: |
| + | self.UPDATE_SCALE_ID = gobject.timeout_add(self.UPDATE_INTERVAL, self._updateScaleCb) |
| + | self.set_button_pause() |
| + | |
| + | |
| + | def _scaleButtonPressCb(self, widget, event): |
| + | #self.button.set_sensitive(False) |
| + | self.was_playing = self._gplay.is_playing() |
| + | if self.was_playing: |
| + | self._gplay.pause() |
| + | |
| + | # don't timeout-update position during seek |
| + | if self.UPDATE_SCALE_ID != 0: |
| + | gobject.source_remove(self.UPDATE_SCALE_ID) |
| + | self.UPDATE_SCALE_ID = 0 |
| + | |
| + | # make sure we get changed notifies |
| + | if self.CHANGED_ID == 0: |
| + | self.CHANGED_ID = self.hscale.connect('value-changed', self._scaleValueChangedCb) |
| + | |
| + | |
| + | def _scaleButtonReleaseCb(self, widget, event): |
| + | # see seek.cstop_seek |
| + | widget.disconnect(self.CHANGED_ID) |
| + | self.CHANGED_ID = 0 |
| + | |
| + | #self.button.set_sensitive(True) |
| + | if self.was_playing: |
| + | self._gplay.play() |
| + | |
| + | if self.UPDATE_SCALE_ID != 0: |
| + | pass |
| + | #print('Had a previous update timeout id') |
| + | else: |
| + | self.UPDATE_SCALE_ID = gobject.timeout_add(self.UPDATE_INTERVAL, self._updateScaleCb) |
| + | |
| + | |
| + | def _scaleValueChangedCb(self, scale): |
| + | real = long(scale.get_value() * self.p_duration / 100) # in ns |
| + | self._gplay.seek(real) |
| + | # allow for a preroll |
| + | self._gplay.get_state(timeout=50*gst.MSECOND) # 50 ms |
| + | |
| + | |
| + | def _updateScaleCb(self): |
| + | self.p_position, self.p_duration = self._gplay.queryPosition() |
| + | if self.p_position != gst.CLOCK_TIME_NONE: |
| + | value = self.p_position * 100.0 / self.p_duration |
| + | if (value > 99): |
| + | value = 99 |
| + | elif (value < 0): |
| + | value = 0 |
| + | |
| + | self.adjustment.set_value(value) |
| + | |
| + | if self._gplay.is_playing() and (self.p_position == self.p_duration): |
| + | self._gplay.pause() |
| + | self.set_button_play() |
| + | |
| + | return True |
| </pre> | | </pre> |