dabo Commit
Revision 6300
Date: 2011-01-01 14:52:48 -0800 (Sat, 01 Jan 2011)
Author: Ed
Trac: http://trac.dabodev.com/changeset/6300
Changed:
U trunk/dabo/dEvents.py
U trunk/dabo/ui/uiwx/__init__.py
A trunk/dabo/ui/uiwx/dMediaControl.py
Log:
Wrapped the wx.media.MediaCtrl to create the Dabo dMediaControl.
While this seems to be working quite well, it's only been tested on a single
platform (OS X). Please test this out with any video (.mov, .avi, .wmv, .mpg,
.mp4, others?) or audio (.mp3, .aiff, .wav, others?), and let me know your
findings.
Diff:
Modified: trunk/dabo/dEvents.py
===================================================================
--- trunk/dabo/dEvents.py 2010-12-27 21:17:01 UTC (rev 6299)
+++ trunk/dabo/dEvents.py 2011-01-01 22:52:48 UTC (rev 6300)
@@ -230,6 +230,12 @@
appliesToClass = classmethod(appliesToClass)
+class MediaEvent(dEvent):
+ def appliesToClass(eventClass, objectClass):
+ return issubclass(objectClass, dabo.ui.dMediaControl)
+ appliesToClass = classmethod(appliesToClass)
+
+
class Activate(dEvent):
"""Occurs when the form or application becomes active."""
def appliesToClass(eventClass, objectClass):
@@ -982,3 +988,31 @@
pass
+class MediaFinished(MediaEvent):
+ """Occurs when the media has finished playing."""
+ pass
+
+
+class MediaLoaded(MediaEvent):
+ """Occurs when the media has been successfully loaded."""
+ pass
+
+
+class MediaPause(MediaEvent):
+ """Occurs when playback has been paused."""
+ pass
+
+
+class MediaPlay(MediaEvent):
+ """Occurs when playback has begun."""
+ pass
+
+
+class MediaStop(MediaEvent):
+ """Occurs when playback has been stopped."""
+ pass
+
+
+class MediaStateChanged(MediaEvent):
+ """Occurs when the playback status has changed from one state to
another."""
+ pass
Modified: trunk/dabo/ui/uiwx/__init__.py
===================================================================
--- trunk/dabo/ui/uiwx/__init__.py 2010-12-27 21:17:01 UTC (rev 6299)
+++ trunk/dabo/ui/uiwx/__init__.py 2011-01-01 22:52:48 UTC (rev 6300)
@@ -15,7 +15,7 @@
# Very VERY first thing: ensure a minimal wx is selected, but only if
# wx hasn't already been imported, and if we aren't running frozen:
-if 'wx' not in sys.modules and not getattr(sys, "frozen", False):
+if "wx" not in sys.modules and not getattr(sys, "frozen", False):
minWx = "2.8"
try:
import wxversion
@@ -137,6 +137,7 @@
from dListControl import dListControl
from dBaseMenuBar import dBaseMenuBar
from dMaskedTextBox import dMaskedTextBox
+from dMediaControl import dMediaControl
from dMenuBar import dMenuBar
from dMenu import dMenu
from dMenuItem import dMenuItem
Added: trunk/dabo/ui/uiwx/dMediaControl.py
===================================================================
--- trunk/dabo/ui/uiwx/dMediaControl.py (rev 0)
+++ trunk/dabo/ui/uiwx/dMediaControl.py 2011-01-01 22:52:48 UTC (rev 6300)
@@ -0,0 +1,331 @@
+# -*- coding: utf-8 -*-
+import wx
+import wx.media
+import dabo
+
+if __name__ == "__main__":
+ dabo.ui.loadUI("wx")
+
+import dControlMixin as cm
+# import dImageMixin as dim
+import dabo.dEvents as dEvents
+from dabo.dLocalize import _
+from dabo.ui import makeDynamicProperty
+
+
+def _timeConvertOut(fn):
+ """Takes millisecond values returned by wx and converts them to
+ fractional seconds if the current setting of TimeInSeconds is True.
+ """
+ def wrapper(instance):
+ ret = fn(instance)
+ if instance.TimeInSeconds:
+ ret = ret / 1000.0
+ return ret
+ return wrapper
+
+def _timeConvertIn(fn):
+ """Takes values set by the program and converts them to milliseconds
+ if the current setting of TimeInSeconds is True.
+ """
+ def wrapper(instance, val):
+ if instance.TimeInSeconds:
+ val = int(val * 1000)
+ fn(instance, val)
+ return wrapper
+
+
+
+class dMediaControl(cm.dControlMixin, wx.media.MediaCtrl):
+ """Wraps the wx MediaCtrl to display video and audio content."""
+ def __init__(self, parent, properties=None, attProperties=None, *args,
**kwargs):
+ self._baseClass = dMediaControl
+ preClass = wx.media.MediaCtrl
+ self._loop = False
+ self._source = None
+ self._timeInSeconds = True
+ self._playbackRate = 100
+ self._showControls = self._extractKey((properties, kwargs),
"ShowControls", True)
+ kwargs["ShowControls"] = self._showControls
+
+ cm.dControlMixin.__init__(self, preClass, parent, properties,
attProperties,
+ *args, **kwargs)
+ # Create lower-case method names to be consistent with Dabo
+# self.play = self.Play
+ self.pause = self.Pause
+ self.stop = self.Stop
+
+
+ def play(self, rate=None):
+ """Plays the content. By default, the content is played forward
at normal
+ speed. You can optionally pass a playback rate which will be
applied
+ to the content. To start the playback in reverse mode, pass in
-100.
+ """
+ self.Play()
+ if rate is not None:
+ self.PlaybackRate = rate
+
+
+ def moveToPct(self, pct):
+ """Moves the CurrentPosition to the specified percentage of the
content's
+ Duration. E.g., passing 50 moves to the middle; 75 to 3/4 of
the way through.
+ Negative values measure from the end; e.g., -10 will set the
CurrentPosition
+ to 90% through the content.
+ """
+ if not -100 <= pct <= 100:
+ raise ValueError(_("Cannot set percentages greater than
100."))
+ if pct < 0:
+ pct = 100 + pct
+ self.CurrentPosition = self.Duration * (pct / 100.0)
+
+
+ def moveByPct(self, pct):
+ """Moves the CurrentPosition by the specified percentage of the
content.
+ Negative values move backward.
+ """
+ fulltime = self.Duration
+ change = (pct / 100.0) * fulltime
+ newpos = self.CurrentPosition + change
+ self.CurrentPosition = max(0, min(newpos, fulltime))
+
+
+ def _initEvents(self):
+ self.Bind(wx.media.EVT_MEDIA_LOADED, self._onWxLoaded)
+ self.Bind(wx.media.EVT_MEDIA_FINISHED, self._onWxFinished)
+ self.Bind(wx.media.EVT_MEDIA_PAUSE, self._onWxPause)
+ self.Bind(wx.media.EVT_MEDIA_PLAY, self._onWxPlay)
+ self.Bind(wx.media.EVT_MEDIA_STATECHANGED,
self._onWxStateChanged)
+ self.Bind(wx.media.EVT_MEDIA_STOP, self._onWxStop)
+
+ #### Start event methods ####
+ # Note: I've left the debug print statements to help determine when
these
+ # events are actually raised. I've noticed that clicking the player
controls
+ # does not seem to cause these events to fire.
+ def _onWxLoaded(self, evt):
+ print "LOAD"
+ self.raiseEvent(dEvents.MediaLoaded, evt)
+ def _onWxFinished(self, evt):
+ print "FIN"
+ self.raiseEvent(dEvents.MediaFinished, evt)
+ def _onWxPause(self, evt):
+ print "PAUS"
+ self.raiseEvent(dEvents.MediaPause, evt)
+ def _onWxPlay(self, evt):
+ print "PLAY"
+ self.raiseEvent(dEvents.MediaPlay, evt)
+ def _onWxStateChanged(self, evt):
+ print "CHHG"
+ self.raiseEvent(dEvents.MediaStateChanged, evt)
+ def _onWxStop(self, evt):
+ print "STOP"
+ self.raiseEvent(dEvents.MediaStop, evt)
+ #### End event methods ####
+
+
+ def scale(self, prop=1.0):
+ """Size the control to the video's native size. By default, the
size is scaled
+ to the video's native size, but you can optionally pass a
proportion to
+ enlarge or reduce the size.
+ """
+ w, h = self.GetBestSize().Get()
+ self.Size = (w * prop, h * prop)
+
+
+ def reverse(self):
+ """Reverses the direction of the playing content stream. Has no
effect if the
+ content is not playing.
+ """
+ if not self.Status == "Playing":
+ return
+ # Since the internal attribute '_playbackRate' can get out of
sync with the actual
+ # rate if the video is stopped or paused, always grab the
current value.
+ self.PlaybackRate = -100 * self.GetPlaybackRate()
+
+
+ def __onLoop(self, evt):
+ """Handler for when the content finishes playing, and self.Loop
= True."""
+ self.play()
+
+
+ def _getContentDimensions(self):
+ sc = self.ShowControls
+ ret = self.GetBestSize().Get()
+ self.ShowControls = sc
+ return ret
+
+
+ @_timeConvertOut
+ def _getCurrentPosition(self):
+ return self.Tell()
+
+ @_timeConvertIn
+ def _setCurrentPosition(self, val):
+ if self._constructed():
+ val = max(0, min(val, self.Length()))
+ self.Seek(val)
+ else:
+ self._properties["CurrentPosition"] = val
+
+
+ def _getDisplayDimensions(self):
+ return self.GetBestSize().Get()
+
+
+ @_timeConvertOut
+ def _getDuration(self):
+ return self.Length()
+
+
+ def _getLoop(self):
+ return self._loop
+
+ def _setLoop(self, val):
+ if self._constructed():
+ self._loop = val
+ self.unbindEvent(dEvents.MediaFinished)
+ if val:
+ self.bindEvent(dEvents.MediaFinished,
self.__onLoop)
+ else:
+ self._properties["Loop"] = val
+
+
+ def _getPlaybackRate(self):
+ return self._playbackRate
+
+ def _setPlaybackRate(self, val):
+ if self._constructed():
+ if not self.Status == "Playing":
+ return
+ self._playbackRate = val
+ self.SetPlaybackRate(self._playbackRate / 100.0)
+ else:
+ self._properties["PlaybackRate"] = val
+
+
+ def _getShowControls(self):
+ return self._showControls
+
+ def _setShowControls(self, val):
+ if self._constructed():
+ self._showControls = val
+ self.ShowPlayerControls(val)
+ else:
+ self._properties["ShowControls"] = val
+
+
+ def _getSource(self):
+ return self._source
+
+ def _setSource(self, val):
+ if self._constructed():
+ if val.startswith("http:"):
+ success = self.LoadURI(val)
+ else:
+ success = self.Load(val)
+ if success:
+ self._source = val
+ else:
+ if dabo.ui.areYouSure(_("Could not load '%s'.
Try again?") % val,
+ title=_("Media Load Fail"),
defaultNo=True,
+ cancelButton=False):
+ dabo.ui.setAfterInterval(500, self,
"Source", val)
+ return
+ self._source = None
+ self.clear()
+ else:
+ self._properties["Source"] = val
+
+
+ def _getStatus(self):
+ states = {0: "Stopped", 1: "Paused", 2: "Playing"}
+ return states[self.GetState()]
+
+
+ def _getTimeInSeconds(self):
+ return self._timeInSeconds
+
+ def _setTimeInSeconds(self, val):
+ if self._constructed():
+ self._timeInSeconds = val
+ else:
+ self._properties["TimeInSeconds"] = val
+
+
+ def _getVolume(self):
+ return int(self.GetVolume() * 100)
+
+ def _setVolume(self, val):
+ if self._constructed():
+ self.SetVolume(val / 100.0)
+ else:
+ self._properties["Volume"] = val
+
+
+ ContentDimensions = property(_getContentDimensions, None, None,
+ _("""The native dimensions of the content, minus the
player controls, if any.
+ (read-only) (2-tuple of int)"""))
+
+ CurrentPosition = property(_getCurrentPosition, _setCurrentPosition,
None,
+ _("""The current playback position of the content in
either milliseconds (default)
+ or seconds, depending on the setting of TimeInSeconds.
(int or float)"""))
+
+ DisplayDimensions = property(_getDisplayDimensions, None, None,
+ _("""The native dimensions of the content and the
player controls, if any.
+ When ShowControls is False, or when audio content is
loaded, this is identical
+ to ContentDimensions. (read-only) (2-tuple of int)"""))
+
+ Duration = property(_getDuration, None, None,
+ _("""Duration of the content in either milliseconds
(default) or seconds, depending
+ on the value of TimeInSeconds. (read-only) (int or
float)"""))
+
+ Loop = property(_getLoop, _setLoop, None,
+ _("""Controls whether the content stops when it reaches
the end (False; default),
+ or whether it restarts at the beginning (True).
(bool)"""))
+
+ PlaybackRate = property(_getPlaybackRate, _setPlaybackRate, None,
+ _("""Controls the speed at which the content is played.
A rate of 100 (default)
+ plays at normal speed; a rate of 200 would play back at
double speed; 50 at
+ half-speed, etc. Note that this has undefined behavior
when the content is not
+ playing: it can either do nothing, or can start the
content playing immediately.
+ Once the content stops, though, this value does not
persist. (int)"""))
+
+ ShowControls = property(_getShowControls, _setShowControls, None,
+ _("""Determines if the player controls are visible.
Note that the specific controls
+ that are shown with the control depends on the platform
and the type of content.
+ Default=True. (bool)"""))
+
+ Source = property(_getSource, _setSource, None,
+ _("""This can be either a file path or a URI for the
content displayed in this
+ control. If the (str)"""))
+
+ Status = property(_getStatus, None, None,
+ _("""The current playback status. One of 'Playing',
'Paused', or 'Stopped'.
+ (read-only) (str)"""))
+
+ TimeInSeconds = property(_getTimeInSeconds, _setTimeInSeconds, None,
+ _("""Determines whether we specify content length and
position in
+ seconds (default), or milliseconds. Affects the
Duration and
+ CurrentPosition properties. Default=True (bool)"""))
+
+ Volume = property(_getVolume, _setVolume, None,
+ _("""Controls the sound level. 100 (default) is full
volume; 0 turns the
+ sound off. (int)"""))
+
+
+
+if __name__ == "__main__":
+ class MediaForm(dabo.ui.dForm):
+ def afterInit(self):
+ # Here's a sample movie URI; you can change this to
something local on
+ # your machine, or another URI.
+ uri =
"http://c0097282.cdn.cloudfiles.rackspacecloud.com/how_to_fold_a_shirt.mpg"
+ self.player = dMediaControl(self, Source=uri,
Loop=False)
+ # Change this to fill the form
+ show_native_size = True
+ if show_native_size:
+ self.Sizer.append(self.player)
+ else:
+ self.Sizer.append1x(self.player)
+
+ app = dabo.dApp(MainFormClass=MediaForm)
+ app.start()
\ No newline at end of file
_______________________________________________
Post Messages to: [email protected]
Subscription Maintenance: http://leafe.com/mailman/listinfo/dabo-dev
Searchable Archives: http://leafe.com/archives/search/dabo-dev
This message:
http://leafe.com/archives/byMID/[email protected]