Merge lp:~samuel-buffet/entertainer/reactive_videoOSD into lp:entertainer
- reactive_videoOSD
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Matt Layman |
Approved revision: | 391 |
Merged at revision: | not available |
Proposed branch: | lp:~samuel-buffet/entertainer/reactive_videoOSD |
Merge into: | lp:entertainer |
Diff against target: | None lines |
To merge this branch: | bzr merge lp:~samuel-buffet/entertainer/reactive_videoOSD |
Related bugs: | |
Related blueprints: |
Touchscreen support
(Essential)
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Matt Layman | Approve | ||
Review via email: mp+7767@code.launchpad.net |
Commit message
The video screen is now reactive to pointer events.
Description of the change
Samuel Buffet (samuel-buffet) wrote : | # |
- 390. By Samuel Buffet
-
Merged with trunk 386.
Matt Layman (mblayman) wrote : | # |
I really love the one click pause feature. That's definitely my favorite part about this branch. Anyway, time for some review comments:
main.py:
* The connect to 'refresh' could now be one line.
progress_bar.py:
* I found the two lines `self._progess = None; self.progress = 0` to be a bit confusing. I realize now that the second line is actually calling the property setter function, but it wasn't very clear to me that that was happening until I hunted around. Could you please add a comment there to help remove that confusion? Something like "# Call the property setter." between the two lines.
* _reset_
* The progress bar does not update its position after a seek while the media was paused. I mentioned this to you on IRC.
media_player.py:
* _internal_
* The amount of calls to _internal_
* Is the is_reactive property logically backwards? I must confess that I am confused by it. In the init, self.is_reactive is set to False, then a few lines later, self.video_
* The video_texture connects mix " and ' quote marks. Could you pick one or the other?
* position += self._seek_step -> What if you're seeking backwards? Should it decrement instead? Also, what is position was 1 and then you just added the seek_step to it. Would gstreamer complain?
General:
* Can you think of any worthwhile tests for some of this?
- 391. By Samuel Buffet
-
Modifications according to Matt's comments.
Samuel Buffet (samuel-buffet) wrote : | # |
Matt,
> main.py:
> * The connect to 'refresh' could now be one line.
Modified.
> progress_bar.py:
> * I found the two lines `self._progess = None; self.progress = 0` to be a bit
> confusing. I realize now that the second line is actually calling the property
> setter function, but it wasn't very clear to me that that was happening until
> I hunted around. Could you please add a comment there to help remove that
> confusion? Something like "# Call the property setter." between the two lines.
I have added a comment there to explain that I set the progress property to initialize the widget.
> * _reset_
> period.
Yes, Fixed.
> * The progress bar does not update its position after a seek while the media
> was paused. I mentioned this to you on IRC.
Yes you're right. Unfortunatly, fix is not as trivial as expected at the widget level. I think that's because we do to much gstreamer queries. I plan to make another branch for that if you're okay.
> media_player.py:
> * _internal_
> (because it's not just emitting a position-changed event).
Right, I've modified that docstring.
> * The amount of calls to _internal_
> they occur kind of bothers me. A media player object is created when
> Entertainer starts and the way this init is written means that we *never* stop
> calling that timer (which fires every 1/5 of a second). That seems pretty
> resource intensive to me. Any ideas of how to make this timer only run when
> media is *actually* playing (because I think that is the only time when it is
> really needed)?
Well, I don't really see the point here because I think we don't have to be afraid of timers like that. The tasks done here are very*100 limited for a CPU. But the fix is trivial so I've done the modification (but it's an added complexity!)
> * Is the is_reactive property logically backwards? I must confess that I am
> confused by it. In the init, self.is_reactive is set to False, then a few
> lines later, self.video_
Yeah, I've renamed it to is_reactive_allowed to limit confusion. This variable is used to neutralize the reactive commands when the video texture of the player is on the background on other screen but video_osd screen.
Indeed, it would be a very bad user experience imho if we were able pause/seek on other screens.
> * The video_texture connects mix " and ' quote marks. Could you pick one or
> the other?
Yep, it's fixed.
> * position += self._seek_step -> What if you're seeking backwards? Should it
> decrement instead? Also, what is position was 1 and then you just added the
> seek_step to it. Would gstreamer complain?
No in fact because I've protected it against bad values. Check the set_media_position method, I've added test on the position parameter.
> General:
> * Can you think of any worthwhile tests for some of this?
Yes, that is really something I have in mind. The main issue is that we need to start a clutter main loop during our tests. Otherwise events won't work. Even timeout...
Matt Layman (mblayman) wrote : | # |
>> * The progress bar does not update its position after a seek while the media
>> was paused. I mentioned this to you on IRC.
>
>Yes you're right. Unfortunatly, fix is not as trivial as expected at the widget level. I think >that's because we do to much gstreamer queries. I plan to make another branch for that if you're >okay.
Okay, that's fine with me. I'd rather see this branch land in a mostly working state with a couple of minor issues than to hold it back until it's absolutely perfect.
>> * The amount of calls to _internal_
>> they occur kind of bothers me. A media player object is created when
>> Entertainer starts and the way this init is written means that we *never* stop
>> calling that timer (which fires every 1/5 of a second). That seems pretty
>> resource intensive to me. Any ideas of how to make this timer only run when
>> media is *actually* playing (because I think that is the only time when it is
>> really needed)?
>
>Well, I don't really see the point here because I think we don't have to be afraid of timers like >that. The tasks done here are very*100 limited for a CPU. But the fix is trivial so I've done the >modification (but it's an added complexity!)
Thanks for applying this fix anyway. It may not seem like much, but since the level of effort to fix it was minimal, I think it was worth it in this case.
>> * Is the is_reactive property logically backwards? I must confess that I am
>> confused by it. In the init, self.is_reactive is set to False, then a few
>> lines later, self.video_
>
>Yeah, I've renamed it to is_reactive_allowed to limit confusion. This variable is used to >neutralize the reactive commands when the video texture of the player is on the background on >other screen but video_osd screen.
>
>Indeed, it would be a very bad user experience imho if we were able pause/seek on other screens.
Okay, that helped a lot. I agree that it would be bad if users were able to pause/seek on other screens (outside of keyboard methods, of course).
>> * position += self._seek_step -> What if you're seeking backwards? Should it
>> decrement instead? Also, what is position was 1 and then you just added the
>> seek_step to it. Would gstreamer complain?
>
>No in fact because I've protected it against bad values. Check the set_media_position method, >I've added test on the position parameter.
Ah, sorry, you're right. For some reason, I thought the position checking stuff was just a few lines above this section of code. I still don't know what happens in the seek backwards case (it seems to me like you'd want to decrement instead of increment), but I'm fine with this.
I understand about not being able to get tests in place. I wonder if there is any way we could simulate/mock events in a future branch.
Paul Hummer (rockstar) wrote : | # |
`which trial` entertainerlib.
entertainerlib.
BaseTest
testCreate ... [OK]
testGetAbsX ... [OK]
testGetAbsY ... [OK]
entertainerlib.
ConfigurationTest
testBorg ... [OK]
testCreate ... [OK]
testGetCfgDir ... [OK]
testGetSlid
testGetStag
testGetStag
testGetThem
testSetStag
testSetStag
testStartAu
testTrayIco
test_create_dir ... [OK]
test_
test_sanitize ... [OK]
test_taint ... [OK]
test_
test_
test_
entertainerlib.
ConnectionSer
testPortBinding ... [OK]
entertainerlib.
DatabaseTest
testCreate ... [OK]
testUseExisting ... [OK]
entertainerlib.
FeedConfigToo
testAddFeed
testAddFeed
testAddFile
testAddFile
entertainerlib.
FeedEntryPars
testConvert ... [OK]
testStripNo
testStripTa
testStripTa
entertainerlib.
FrontendFeedTest
test_date ... [OK]
test_
test_entries ... ...
Preview Diff
1 | === modified file 'entertainerlib/frontend/gui/screens/main.py' |
2 | --- entertainerlib/frontend/gui/screens/main.py 2009-05-27 10:33:21 +0000 |
3 | +++ entertainerlib/frontend/gui/screens/main.py 2009-05-28 19:44:40 +0000 |
4 | @@ -31,7 +31,7 @@ |
5 | self.media_player = media_player |
6 | self.media_player.connect("stop", self.update) |
7 | self.media_player.connect("play", self.update) |
8 | - self.media_player.connect('position_changed', |
9 | + self.media_player.connect('refresh', |
10 | self._update_preview_title) |
11 | |
12 | self.theme = self.config.theme |
13 | @@ -244,10 +244,10 @@ |
14 | Update screen widgets. This is called always when screen is poped from |
15 | the screen history. Updates main menu widget. |
16 | """ |
17 | - if self.media_player.is_playing() and \ |
18 | + if self.media_player.is_playing and \ |
19 | (self.menu.get_index("playing") == -1): |
20 | self.menu.add_item(_("Playing now..."), "playing") |
21 | - elif not self.media_player.is_playing() and \ |
22 | + elif not self.media_player.is_playing and \ |
23 | (self.menu.get_index("playing") != -1): |
24 | self.menu.remove_item("playing") |
25 | |
26 | |
27 | === modified file 'entertainerlib/frontend/gui/screens/video_osd.py' |
28 | --- entertainerlib/frontend/gui/screens/video_osd.py 2009-05-16 19:14:01 +0000 |
29 | +++ entertainerlib/frontend/gui/screens/video_osd.py 2009-05-25 11:07:55 +0000 |
30 | @@ -59,13 +59,13 @@ |
31 | self.pause_texture.hide() |
32 | self.add(self.pause_texture) |
33 | |
34 | - pause_timeline = clutter.Timeline(fps=120, duration=2000) |
35 | + pause_timeline = clutter.Timeline(fps=20, duration=2000) |
36 | pause_timeline.set_loop(True) |
37 | alpha_pause = clutter.Alpha(pause_timeline, clutter.sine_func) |
38 | |
39 | self.pause_opacity = clutter.BehaviourOpacity(alpha=alpha_pause, |
40 | - opacity_start=0, opacity_end=255) |
41 | - self.pause_scale = clutter.BehaviourScale(1.0, 1.0, 1.3, 1.3, |
42 | + opacity_start=100, opacity_end=255) |
43 | + self.pause_scale = clutter.BehaviourScale(1.0, 1.0, 1.4, 1.4, |
44 | alpha_pause) |
45 | |
46 | self.pause_opacity.apply(self.pause_texture) |
47 | @@ -154,7 +154,7 @@ |
48 | |
49 | def _handle_play_pause(self, event=None): |
50 | '''Handle UserEvent.PLAYER_PLAY_PAUSE.''' |
51 | - if self.media_player.is_playing(): |
52 | + if self.media_player.is_playing: |
53 | self.pause_texture.hide() |
54 | else: |
55 | self.pause_texture.show() |
56 | |
57 | === modified file 'entertainerlib/frontend/gui/user_interface.py' |
58 | --- entertainerlib/frontend/gui/user_interface.py 2009-06-03 01:54:03 +0000 |
59 | +++ entertainerlib/frontend/gui/user_interface.py 2009-06-05 05:12:59 +0000 |
60 | @@ -94,10 +94,9 @@ |
61 | self.config.get_stage_width(), self.config.get_stage_height()) |
62 | |
63 | # Initialize menu overlay texture |
64 | - self.overlay_status = False |
65 | + self.is_overlay = False |
66 | self.menu_overlay = MenuOverlay(self.config.theme) |
67 | self.menu_overlay.set_opacity(0) |
68 | - self.menu_overlay.set_name("overlay") |
69 | self.menu_overlay.set_size( |
70 | self.config.get_stage_width(), self.config.get_stage_height()) |
71 | self.stage.add(self.menu_overlay) |
72 | @@ -258,36 +257,27 @@ |
73 | a video playing and menu showing at the same time. Overlay is not part |
74 | of any specific screen. It is used for all screens when neccesary. |
75 | """ |
76 | - self.overlay_status = True |
77 | - if self.config.show_effects(): |
78 | + if not self.is_overlay: |
79 | + self.is_overlay = True |
80 | self.menu_overlay.fade_in() |
81 | - else: |
82 | - self.menu_overlay.set_opacity(255) |
83 | + self.player.is_reactive = False |
84 | |
85 | def disable_menu_overlay(self): |
86 | """ |
87 | Disable menu overlay. Overlay should be disabled when current screen is |
88 | a type of Screen.OSD. |
89 | """ |
90 | - self.overlay_status = False |
91 | - if self.config.show_effects(): |
92 | + if self.is_overlay: |
93 | + self.is_overlay = False |
94 | self.menu_overlay.fade_out() |
95 | - else: |
96 | - self.menu_overlay.set_opacity(0) |
97 | - |
98 | - def is_overlay(self): |
99 | - """ |
100 | - Get menu overlay status. |
101 | - @return boolean value. True if menu overlay is active, otherwise False. |
102 | - """ |
103 | - return self.overlay_status |
104 | + self.player.is_reactive = True |
105 | |
106 | def change_screen(self, screen, direction): |
107 | '''Transition the given screen in the direction provided.''' |
108 | # Enable/Disable menu overlay |
109 | - if screen.kind == Screen.OSD and self.is_overlay(): |
110 | + if screen.kind == Screen.OSD: |
111 | self.disable_menu_overlay() |
112 | - elif screen.kind != Screen.OSD and not self.is_overlay(): |
113 | + else: |
114 | self.enable_menu_overlay() |
115 | |
116 | # Add current screen to screen history |
117 | @@ -367,7 +357,7 @@ |
118 | if self.current.is_interested_in_play_action(): |
119 | self.current.execute_play_action() |
120 | else: |
121 | - if self.player.is_playing(): |
122 | + if self.player.is_playing: |
123 | self.player.pause() |
124 | self.current.handle_user_event(event) |
125 | else: |
126 | @@ -390,7 +380,7 @@ |
127 | |
128 | def _handle_player_stop(self, event): |
129 | '''Handle UserEvent.PLAYER_STOP.''' |
130 | - if self.player.is_playing(): |
131 | + if self.player.is_playing: |
132 | self.player.stop() |
133 | self.current.handle_user_event(event) |
134 | |
135 | |
136 | === modified file 'entertainerlib/frontend/gui/widgets/menu_overlay.py' |
137 | --- entertainerlib/frontend/gui/widgets/menu_overlay.py 2009-05-10 06:24:43 +0000 |
138 | +++ entertainerlib/frontend/gui/widgets/menu_overlay.py 2009-05-25 10:22:25 +0000 |
139 | @@ -1,5 +1,5 @@ |
140 | # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 |
141 | -'''Texture that is displayed over video when menu is active''' |
142 | +'''Texture that is displayed over video when menu is active.''' |
143 | |
144 | import clutter |
145 | |
146 | @@ -18,25 +18,17 @@ |
147 | """Initialize overlay texture.""" |
148 | Texture.__init__(self, filename = theme.getImage("menu_overlay")) |
149 | self.timeline = clutter.Timeline(13, 26) |
150 | - self.timeline.set_loop(False) |
151 | + self.alpha = clutter.Alpha(self.timeline, clutter.smoothstep_inc_func) |
152 | + self.behaviour = clutter.BehaviourOpacity(255, 0, self.alpha) |
153 | + self.behaviour.apply(self) |
154 | |
155 | def fade_in(self): |
156 | - """ |
157 | - Fade texture in. Animate texture's opacity from 0 to 255 |
158 | - """ |
159 | - self.alpha = clutter.Alpha(self.timeline, clutter.smoothstep_dec_func) |
160 | - self.behaviour = clutter.BehaviourOpacity(255, 0, self.alpha) |
161 | - self.behaviour.apply(self) |
162 | - self.timeline.rewind() |
163 | + """Fade texture in.""" |
164 | + self.behaviour.set_bounds(0, 255) |
165 | self.timeline.start() |
166 | |
167 | def fade_out(self): |
168 | - """ |
169 | - Fade texture out. Animate texture's opacity from 255 to 0 |
170 | - """ |
171 | - self.alpha = clutter.Alpha(self.timeline, clutter.smoothstep_inc_func) |
172 | - self.behaviour = clutter.BehaviourOpacity(255, 0, self.alpha) |
173 | - self.behaviour.apply(self) |
174 | - self.timeline.rewind() |
175 | + """Fade texture out.""" |
176 | + self.behaviour.set_bounds(255, 0) |
177 | self.timeline.start() |
178 | |
179 | |
180 | === modified file 'entertainerlib/frontend/gui/widgets/progress_bar.py' |
181 | --- entertainerlib/frontend/gui/widgets/progress_bar.py 2009-05-27 10:33:21 +0000 |
182 | +++ entertainerlib/frontend/gui/widgets/progress_bar.py 2009-05-28 19:44:40 +0000 |
183 | @@ -33,7 +33,6 @@ |
184 | |
185 | self.set_position(self.get_abs_x(x), self.get_abs_y(y)) |
186 | |
187 | - self._progress = 0 |
188 | self._color = self._color_to_cairo_color( |
189 | self.config.theme.get_color(color)) |
190 | |
191 | @@ -65,6 +64,9 @@ |
192 | self._behaviour = clutter.BehaviourOpacity(0, 255, self._alpha) |
193 | self._behaviour.apply(self) |
194 | |
195 | + self._progress = None |
196 | + self.progress = 0 |
197 | + |
198 | # Preparation to pointer events handling. |
199 | self._motion_handler = 0 |
200 | self.set_reactive(True) |
201 | @@ -81,9 +83,10 @@ |
202 | def _set_media_player(self, media_player): |
203 | '''media_player property setter.''' |
204 | self._media_player = media_player |
205 | - self._media_player.connect('position_changed', |
206 | - self._update_media_position) |
207 | + self._media_player.connect('refresh', self._update_media_position) |
208 | self._media_player.connect('stop', self._update_media_position) |
209 | + self._media_player.connect('position-changed', |
210 | + self._on_player_position_changed) |
211 | |
212 | media_player = property(_get_media_player, _set_media_player) |
213 | |
214 | @@ -137,6 +140,13 @@ |
215 | self.visible = False |
216 | return False |
217 | |
218 | + def _reset_auto_display_timeout(self): |
219 | + '''Reset the timeout if auto_display = True''' |
220 | + if self._hide_timeout_key is not None: |
221 | + gobject.source_remove(self._hide_timeout_key) |
222 | + self._hide_timeout_key = gobject.timeout_add(3000, |
223 | + self._hide_progress_bar) |
224 | + |
225 | def _update_media_position(self, event=None): |
226 | '''Update the media's position.''' |
227 | if not self._progress_bar_moving: |
228 | @@ -268,12 +278,18 @@ |
229 | return False |
230 | |
231 | def _on_enter_event(self, stage, clutter_event): |
232 | - '''Shows the button.''' |
233 | + '''Shows the progress bar.''' |
234 | if self.auto_display and not self._progress_bar_moving: |
235 | self.visible = True |
236 | |
237 | def _on_leave_event(self, stage, clutter_event): |
238 | - '''Hides the button.''' |
239 | + '''Hides the progress bar.''' |
240 | if self.auto_display and not self._progress_bar_moving: |
241 | self.visible = False |
242 | |
243 | + def _on_player_position_changed(self, event=None): |
244 | + '''Shows the progress bar.''' |
245 | + if self.auto_display and not self._progress_bar_moving: |
246 | + self.visible = True |
247 | + self._reset_auto_display_timeout() |
248 | + |
249 | |
250 | === modified file 'entertainerlib/frontend/media_player.py' |
251 | --- entertainerlib/frontend/media_player.py 2009-05-26 17:32:56 +0000 |
252 | +++ entertainerlib/frontend/media_player.py 2009-06-22 19:12:37 +0000 |
253 | @@ -9,6 +9,7 @@ |
254 | import gobject |
255 | import gst |
256 | |
257 | +from entertainerlib.frontend.gui.widgets.motion_buffer import MotionBuffer |
258 | from entertainerlib.frontend.gui.widgets.texture import Texture |
259 | from entertainerlib.frontend.medialibrary.playable import Playable |
260 | from entertainerlib.utils.logger import Logger |
261 | @@ -31,7 +32,8 @@ |
262 | 'stop' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ), |
263 | 'skip-forward' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ), |
264 | 'skip-backward' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ), |
265 | - 'position_changed' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ), |
266 | + 'position-changed' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ), |
267 | + 'refresh' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ), |
268 | } |
269 | |
270 | # Ratio constants |
271 | @@ -42,9 +44,17 @@ |
272 | ZOOM = 4 |
273 | INTELLIGENT = 5 |
274 | |
275 | + MODE_NONE = 0 |
276 | + MODE_PLAYPAUSE = 1 |
277 | + MODE_SEEK = 2 |
278 | + |
279 | def __init__(self, stage, width, height): |
280 | gobject.GObject.__init__(self) |
281 | |
282 | + self._motion_buffer = MotionBuffer() |
283 | + self._event_mode = self.MODE_NONE |
284 | + self._motion_handler = 0 |
285 | + |
286 | self.stage = stage # Stage that displays textures |
287 | # Stage background color when not playing |
288 | self.bgcolor = stage.get_color() |
289 | @@ -58,7 +68,8 @@ |
290 | self.media = None # Current media (Playable object) |
291 | self.shuffle = False # Shuffle mode |
292 | self.repeat = False # Repeat mode |
293 | - self.playing = False # Is media player currently playing |
294 | + self.is_playing = False # Is media player currently playing |
295 | + self.is_reactive = False # Is the video_texture reactive |
296 | |
297 | self.logger = Logger().getLogger('frontend.MediaPlayer') |
298 | |
299 | @@ -68,10 +79,15 @@ |
300 | self.bus.add_signal_watch() |
301 | self.bus.connect('message', self._on_gst_message) |
302 | |
303 | - self.video_texture.connect("size-change", self.change_ratio_callback) |
304 | - self.video_texture.show() |
305 | + gobject.timeout_add(200, self._internal_timer_callback) |
306 | |
307 | - gobject.timeout_add(500, self._emit_position_changed) |
308 | + self.video_texture.set_reactive(True) |
309 | + self.video_texture.connect("size-change", self._on_size_change) |
310 | + self.video_texture.connect('scroll-event', self._on_scroll_event) |
311 | + self.video_texture.connect("button-press-event", |
312 | + self._on_button_press_event) |
313 | + self.video_texture.connect('button-release-event', |
314 | + self._on_button_release_event) |
315 | |
316 | def _on_gst_message(self, bus, message): |
317 | ''' |
318 | @@ -113,7 +129,7 @@ |
319 | self.playlist = None |
320 | |
321 | # If player is currently playing then we stop it |
322 | - if self.is_playing(): |
323 | + if self.is_playing: |
324 | self.stop() |
325 | |
326 | # Update media information |
327 | @@ -173,7 +189,7 @@ |
328 | return |
329 | |
330 | if self.media.get_type() == Playable.AUDIO_STREAM: |
331 | - self.playing = True |
332 | + self.is_playing = True |
333 | self.video_texture.set_playing(True) |
334 | self.emit('play') |
335 | |
336 | @@ -182,20 +198,20 @@ |
337 | if (self.video_texture.get_parent() == None): |
338 | self.stage.add(self.video_texture) |
339 | self.video_texture.lower_bottom() |
340 | - self.playing = True |
341 | + self.is_playing = True |
342 | self.stage.set_color((0, 0, 0, 0)) |
343 | self.video_texture.set_playing(True) |
344 | self.emit('play') |
345 | |
346 | def pause(self): |
347 | '''Pause media player.''' |
348 | - self.playing = False |
349 | + self.is_playing = False |
350 | self.video_texture.set_playing(False) |
351 | self.emit('pause') |
352 | |
353 | def stop(self): |
354 | '''Stop media player.''' |
355 | - self.playing = False |
356 | + self.is_playing = False |
357 | if self.media.get_type() == Playable.VIDEO_STREAM: |
358 | self.stage.set_color(self.bgcolor) |
359 | self.stage.remove(self.video_texture) |
360 | @@ -248,10 +264,6 @@ |
361 | seek_ns) |
362 | self.emit('skip-backward') |
363 | |
364 | - def is_playing(self): |
365 | - '''Is media player currently playing.''' |
366 | - return self.playing |
367 | - |
368 | def get_media_position(self): |
369 | '''Get current position of the play back.''' |
370 | try: |
371 | @@ -277,6 +289,12 @@ |
372 | |
373 | def set_media_position(self, position): |
374 | '''Set position of the current media.''' |
375 | + if position < 0.0: |
376 | + position = 0.0 |
377 | + |
378 | + if position > 1.0: |
379 | + position = 1.0 |
380 | + |
381 | if (self.media.get_type() == Playable.AUDIO_STREAM) or \ |
382 | (self.media.get_type() == Playable.VIDEO_STREAM): |
383 | dur = self.playbin.query_duration(gst.FORMAT_TIME, None)[0] |
384 | @@ -331,7 +349,7 @@ |
385 | time_str = time_str + "0" + str(time_int) |
386 | return time_str |
387 | |
388 | - def change_ratio_callback(self, texture, width, height): |
389 | + def _on_size_change(self, texture, width, height): |
390 | ''' |
391 | Callback for changing video texture's aspect ratio. This is called when |
392 | video texture size changes. |
393 | @@ -467,9 +485,76 @@ |
394 | else: |
395 | return None |
396 | |
397 | - def _emit_position_changed(self): |
398 | + def _internal_timer_callback(self): |
399 | '''A `position-changed` event is regulary emited if media is playing.''' |
400 | - if self.is_playing(): |
401 | - self.emit('position_changed') |
402 | + if self.is_playing: |
403 | + self.emit('refresh') |
404 | + |
405 | + if self._event_mode == self.MODE_SEEK: |
406 | + position = self.get_media_position() |
407 | + position += self._seek_step |
408 | + self.set_media_position(position) |
409 | + self.emit('position-changed') |
410 | return True |
411 | |
412 | + def _on_button_press_event(self, actor, event): |
413 | + """`button-press` event handler.""" |
414 | + if not self.is_reactive: |
415 | + return |
416 | + |
417 | + clutter.grab_pointer(self.video_texture) |
418 | + if not self.video_texture.handler_is_connected(self._motion_handler): |
419 | + self._motion_handler = self.video_texture.connect('motion-event', |
420 | + self._on_motion_event) |
421 | + |
422 | + self._motion_buffer.start(event) |
423 | + self._event_mode = self.MODE_PLAYPAUSE |
424 | + |
425 | + def _on_button_release_event(self, actor, event): |
426 | + """`button-press` event handler.""" |
427 | + if not self.is_reactive: |
428 | + return |
429 | + |
430 | + clutter.ungrab_pointer() |
431 | + if self.video_texture.handler_is_connected(self._motion_handler): |
432 | + self.video_texture.disconnect_by_func(self._on_motion_event) |
433 | + |
434 | + if self._event_mode == self.MODE_PLAYPAUSE: |
435 | + if self.is_playing: |
436 | + self.pause() |
437 | + else: |
438 | + self.play() |
439 | + |
440 | + self._event_mode = self.MODE_NONE |
441 | + |
442 | + def _on_motion_event(self, actor, event): |
443 | + """`motion-event` event handler.""" |
444 | + # threshold in pixels = the minimum distance we have to move before we |
445 | + # consider a motion has started |
446 | + motion_threshold = 20 |
447 | + |
448 | + self._motion_buffer.compute_from_start(event) |
449 | + if self._motion_buffer.distance_from_start > motion_threshold: |
450 | + self._motion_buffer.take_new_motion_event(event) |
451 | + self._event_mode = self.MODE_SEEK |
452 | + self._seek_step = float(self._motion_buffer.dx_from_start) |
453 | + self._seek_step /= self.video_texture.get_width() |
454 | + self._seek_step *= 0.01 |
455 | + |
456 | + return False |
457 | + |
458 | + def _on_scroll_event(self, actor, event): |
459 | + '''`scroll-event` event handler (mouse's wheel).''' |
460 | + # +/- 2% per scroll event on the position of the media stream. |
461 | + scroll_progress_ratio = 0.02 |
462 | + |
463 | + position = self.get_media_position() |
464 | + |
465 | + if event.direction == clutter.SCROLL_DOWN: |
466 | + position -= scroll_progress_ratio |
467 | + else: |
468 | + position += scroll_progress_ratio |
469 | + |
470 | + self.set_media_position(position) |
471 | + self.emit('position-changed') |
472 | + |
473 | |
474 | === modified file 'entertainerlib/tests/mock.py' |
475 | --- entertainerlib/tests/mock.py 2009-05-27 10:27:48 +0000 |
476 | +++ entertainerlib/tests/mock.py 2009-05-28 19:44:40 +0000 |
477 | @@ -136,21 +136,19 @@ |
478 | 'stop' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ), |
479 | 'skip-forward' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ), |
480 | 'skip-backward' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ), |
481 | - 'position_changed' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ), |
482 | + 'position-changed' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ), |
483 | + 'refresh' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ), |
484 | } |
485 | |
486 | def __init__(self, stage=None, width=None, height=None): |
487 | '''Override any actual media player set up.''' |
488 | gobject.GObject.__init__(self) |
489 | + self.is_playing = False |
490 | |
491 | def has_media(self): |
492 | '''See `MediaPlayer.has_media`.''' |
493 | return False |
494 | |
495 | - def is_playing(self): |
496 | - '''See `MediaPlayer.is_playing`.''' |
497 | - return False |
498 | - |
499 | def play(self): |
500 | '''See `MediaPlayer.play`.''' |
501 | |
502 | |
503 | === modified file 'entertainerlib/tests/test_progressbar.py' |
504 | --- entertainerlib/tests/test_progressbar.py 2009-05-17 12:26:38 +0000 |
505 | +++ entertainerlib/tests/test_progressbar.py 2009-06-06 15:45:10 +0000 |
506 | @@ -16,10 +16,6 @@ |
507 | self.media_player = MockMediaPlayer() |
508 | self.progress_bar.media_player = self.media_player |
509 | |
510 | - def tearDown(self): |
511 | - '''Clean up after the test.''' |
512 | - EntertainerTest.tearDown(self) |
513 | - |
514 | def test_create(self): |
515 | '''Test correct ProgressBar initialization.''' |
516 | self.assertTrue(isinstance(self.progress_bar, ProgressBar)) |
Matt,
Here is a branch to give the videoOSD texture reactive capacities.
By a single click on it, it's possible to play/pause a video.
It is also possible to seek forward or backward through a video by "click slide and release".
The seek progression is a function of the horizontal distance since the first click.
Some related modification have been done to the player and a simplification of the videoOSD texture as well.
Samuel-