Merge lp:~mjthompson/openlp/media_plugin into lp:openlp

Proposed by Martin Thompson
Status: Merged
Merged at revision: not available
Proposed branch: lp:~mjthompson/openlp/media_plugin
Merge into: lp:openlp
Diff against target: None lines
To merge this branch: bzr merge lp:~mjthompson/openlp/media_plugin

This proposal supersedes a proposal from 2009-06-30.

To post a comment you must log in.
Revision history for this message
Martin Thompson (mjthompson) wrote : Posted in a previous version of this proposal

Previews for video items - uses gstreamer as I can;t find a way to make Phonon do it. This may be unportable? But gets us through until Phonon gets the functionality.
No previews for audio files yet

Revision history for this message
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal

Sorry but this will break the other plugins.

adding self.ServiceItemIconName will be required by all plugins now

Also is incorrect.
        - service_item.addIcon(u':/media/media_image.png')
92 + service_item.addIcon(self.ServiceItemIconName)

should be
          service_item.addIcon(u':/media/media_' + 'self.ShortPluginName + u'image.png')
This does not work for bibles as the media_image for bibles is _verses instead of _bibles.

Use of assert causes problems and rejections on a number of boxes can we have throws error instead.

review: Needs Fixing
Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Please don't use this, it throws Eric4 out (and is incorrect Python):

  assert 0, 'This fn needs to be defined by the plugin'

Rather use:

  raise NotImplementedError(u'This function needs to be defined by the plugin')

Rather return None, and do a check on the other side... "image is not None":

264 + except:
265 + log.info("Can't generate video preview for some reason");
266 + import sys
267 + print sys.exc_info()
268 + return QtGui.QImage()

This should be in a docstring:
43 # self.ListViewWithDnD_class - there is a base list class with DnD assigned to it (openlp.core.lib.BaseListWithDnD())
44 # each plugin needs to inherit a class from this and pass that *class* (not an instance) to here
45 # via the ListViewWithDnD_class member
46 + # self.ServiceItemIconName - string referring to an icon file or a resource icon
47 + # self.PreviewFunction - a function which returns a QImage to represent the item (a preview usually) - no scaling required - that's done later
48 + # If this fn is not defined, a default will be used (treat the filename as an image)
49 +
50 # The assumption is that given that at least two plugins are of the form
51 # "text with an icon" then all this will help
52 # even for plugins of another sort, the setup of the right-click menu, common toolbar

review: Needs Fixing
Revision history for this message
Martin Thompson (mjthompson) wrote : Posted in a previous version of this proposal

On Wed, Jul 01, 2009 at 05:14:09AM -0000 or thereabouts, Tim Bentley wrote:
> Review: Needs Fixing
> Sorry but this will break the other plugins.
>
> adding self.ServiceItemIconName will be required by all plugins now
>

Should I add it to the other plugins then...?

> Also is incorrect.
> - service_item.addIcon(u':/media/media_image.png')
> 92 + service_item.addIcon(self.ServiceItemIconName)
>
> should be
> service_item.addIcon(u':/media/media_' + 'self.ShortPluginName + u'image.png')
> This does not work for bibles as the media_image for bibles is _verses instead of _bibles.

which would also fix this problem.

>
> Use of assert causes problems and rejections on a number of boxes can we have throws error instead.

OK.

Cheers,
Martin

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'openlp/core/lib/listwithpreviews.py'
2--- openlp/core/lib/listwithpreviews.py 2009-06-23 20:59:38 +0000
3+++ openlp/core/lib/listwithpreviews.py 2009-06-30 20:35:53 +0000
4@@ -35,14 +35,18 @@
5 self.items = []
6 self.rowheight = 50
7 self.maximagewidth = self.rowheight * 16 / 9.0;
8- if new_preview_function is not None:
9- self.make_preview=new_preview_function
10- else:
11- self.make_preview=self.preview_function
12+ self.preview_function = new_preview_function
13
14- def preview_function(self, filename):
15+ def make_preview(self, filename):
16 if os.path.exists(filename):
17- preview = QtGui.QImage(filename)
18+ if self.preview_function is not None:
19+ preview=self.preview_function(filename)
20+ else:
21+ preview = QtGui.QImage(filename)
22+ else:
23+ preview = None
24+
25+ if preview is not None:
26 w = self.maximagewidth;
27 h = self.rowheight
28 preview = preview.scaled(w, h, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
29
30=== modified file 'openlp/core/lib/mediamanageritem.py'
31--- openlp/core/lib/mediamanageritem.py 2009-06-25 20:03:41 +0000
32+++ openlp/core/lib/mediamanageritem.py 2009-07-01 20:21:13 +0000
33@@ -18,7 +18,7 @@
34 Place, Suite 330, Boston, MA 02111-1307 USA
35 """
36 import types
37-
38+import os
39 from PyQt4 import QtCore, QtGui
40 from openlp.core.lib.toolbar import *
41 from openlp.core.lib import translate
42@@ -27,7 +27,32 @@
43 class MediaManagerItem(QtGui.QWidget):
44 """
45 MediaManagerItem is a helper widget for plugins.
46- """
47+
48+ None of the following *need* to be used, feel free to override
49+ them cmopletely in your plugin's implementation. Alternatively, call them from your
50+ plugin before or after you've done etra things that you need to.
51+
52+ The plugin will be assigned an icon called
53+ u':/media/media_' + 'self.ShortPluginName + u'image.png'
54+ which needs to be available in the main resources
55+
56+ in order for them to work, you need to have setup
57+
58+ self.TranslationContext
59+ self.PluginTextShort # eg 'Image' for the image plugin
60+ self.ConfigSection - where the items in the media manager are stored
61+ this could potentially be self.PluginTextShort.lower()
62+
63+ self.OnNewPrompt=u'Select Image(s)'
64+ self.OnNewFileMasks=u'Images (*.jpg *jpeg *.gif *.png *.bmp)'
65+ assumes that the new action is to load a file. If not, override onnew
66+ self.ListViewWithDnD_class - there is a base list class with DnD assigned to it (openlp.core.lib.BaseListWithDnD())
67+ each plugin needs to inherit a class from this and pass that *class* (not an instance) to here
68+ via the ListViewWithDnD_class member
69+ self.PreviewFunction - a function which returns a QImage to represent the item (a preview usually) - no scaling required - that's done later
70+ If this fn is not defined, a default will be used (treat the filename as an image)
71+
72+"""
73 global log
74 log = logging.getLogger(u'MediaManagerItem')
75 log.info(u'Media Item loaded')
76@@ -106,49 +131,23 @@
77 QtCore.QObject.connect(action, QtCore.SIGNAL(u'triggered()'), slot)
78 return action
79
80-####################################################################################################
81- ### None of the following *need* to be used, feel free to override
82- ### them cmopletely in your plugin's implementation. Alternatively, call them from your
83- ### plugin before or after you've done etra things that you need to.
84- ### in order for them to work, you need to have setup
85- # self.TranslationContext
86- # self.PluginTextShort # eg "Image" for the image plugin
87- # self.ConfigSection - where the items in the media manager are stored
88- # this could potentially be self.PluginTextShort.lower()
89- #
90- # self.OnNewPrompt=u'Select Image(s)'
91- # self.OnNewFileMasks=u'Images (*.jpg *jpeg *.gif *.png *.bmp)'
92- # assumes that the new action is to load a file. If not, override onnew
93- # self.ListViewWithDnD_class - there is a base list class with DnD assigned to it (openlp.core.lib.BaseListWithDnD())
94- # each plugin needs to inherit a class from this and pass that *class* (not an instance) to here
95- # via the ListViewWithDnD_class member
96- # The assumption is that given that at least two plugins are of the form
97- # "text with an icon" then all this will help
98- # even for plugins of another sort, the setup of the right-click menu, common toolbar
99- # will help to keep things consistent and ease the creation of new plugins
100-
101- # also a set of completely consistent action anesm then exist
102- # (onPreviewClick() is always called that, rather than having the
103- # name of the plugin added in as well... I regard that as a
104- # feature, I guess others might differ!)
105-
106 def setupUi(self):
107 # Add a toolbar
108 self.addToolbar()
109 # Create buttons for the toolbar
110- ## New Song Button ##
111+ ## New Button ##
112 self.addToolbarButton(
113 translate(self.TranslationContext, u'Load '+self.PluginTextShort),
114 translate(self.TranslationContext, u'Load item into openlp.org'),
115- u':/images/image_load.png', self.onNewClick, u'ImageNewItem')
116- ## Delete Song Button ##
117+ u':/images/image_load.png', self.onNewClick, u'NewItem')
118+ ## Delete Button ##
119 self.addToolbarButton(
120 translate(self.TranslationContext, u'Delete '+self.PluginTextShort),
121 translate(self.TranslationContext, u'Delete the selected item'),
122 u':/images/image_delete.png', self.onDeleteClick, u'DeleteItem')
123 ## Separator Line ##
124 self.addToolbarSeparator()
125- ## Preview Button ##
126+ ## Preview ##
127 self.addToolbarButton(
128 translate(self.TranslationContext, u'Preview '+self.PluginTextShort),
129 translate(self.TranslationContext, u'Preview the selected item'),
130@@ -158,7 +157,7 @@
131 translate(self.TranslationContext, u'Go Live'),
132 translate(self.TranslationContext, u'Send the selected item live'),
133 u':/system/system_live.png', self.onLiveClick, u'LiveItem')
134- ## Add Button ##
135+ ## Add to service Button ##
136 self.addToolbarButton(
137 translate(self.TranslationContext, u'Add '+self.PluginTextShort+u' To Service'),
138 translate(self.TranslationContext, u'Add the selected item(s) to the service'),
139@@ -166,7 +165,10 @@
140 #Add the List widget
141 self.ListView = self.ListViewWithDnD_class()
142 self.ListView.uniformItemSizes = True
143- self.ListData = ListWithPreviews()
144+ try:
145+ self.ListData = ListWithPreviews(self.PreviewFunction)
146+ except AttributeError:
147+ self.ListData = ListWithPreviews(None)
148 self.ListView.setModel(self.ListData)
149 self.ListView.setGeometry(QtCore.QRect(10, 100, 256, 591))
150 self.ListView.setSpacing(1)
151@@ -203,7 +205,7 @@
152 log.info(u'New files(s)', unicode(files))
153 if len(files) > 0:
154 self.loadList(files)
155- dir, filename = os.path.split(unicode(files[0]))
156+ dir, filename = os.path.split(uniClickcode(files[0]))
157 self.parent.config.set_last_dir(dir)
158 self.parent.config.set_list(self.ConfigSection, self.ListData.getFileList())
159
160@@ -219,25 +221,26 @@
161 self.parent.config.set_list(self.ConfigSection, self.ListData.getFileList())
162
163 def generateSlideData(self):
164- assert (0, 'This fn needs to be defined by the plugin');
165+ raise NotImplementedError(u'This function needs to be defined by the plugin')
166
167 def onPreviewClick(self):
168 log.debug(self.PluginTextShort+u'Preview Requested')
169 service_item = ServiceItem(self.parent)
170- service_item.addIcon(u':/media/media_image.png')
171+ service_item.addIcon(self.ServiceItemIconName)
172+
173 self.generateSlideData(service_item)
174 self.parent.preview_controller.addServiceItem(service_item)
175
176 def onLiveClick(self):
177 log.debug(self.PluginTextShort+u' Live Requested')
178 service_item = ServiceItem(self.parent)
179- service_item.addIcon(u':/media/media_image.png')
180+ service_item.addIcon(self.ServiceItemIconName)
181 self.generateSlideData(service_item)
182 self.parent.live_controller.addServiceItem(service_item)
183
184 def onAddClick(self):
185 log.debug(self.PluginTextShort+u' Add Requested')
186 service_item = ServiceItem(self.parent)
187- service_item.addIcon(u':/media/media_image.png')
188+ service_item.addIcon(self.ServiceItemIconName)
189 self.generateSlideData(service_item)
190 self.parent.service_manager.addServiceItem(service_item)
191
192=== modified file 'openlp/plugins/images/lib/mediaitem.py'
193--- openlp/plugins/images/lib/mediaitem.py 2009-06-25 20:01:02 +0000
194+++ openlp/plugins/images/lib/mediaitem.py 2009-06-25 21:09:52 +0000
195@@ -49,7 +49,8 @@
196 self.OnNewFileMasks = u'Images (*.jpg *jpeg *.gif *.png *.bmp)'
197 # this next is a class, not an instance of a class - it will
198 # be instanced by the base MediaManagerItem
199- self.ListViewWithDnD_class = ImageListView
200+ self.ListViewWithDnD_class = ImageListView
201+ self.ServiceItemIconName = u':/media/media_image.png'
202 MediaManagerItem.__init__(self, parent, icon, title)
203
204
205
206=== modified file 'openlp/plugins/media/lib/mediaitem.py'
207--- openlp/plugins/media/lib/mediaitem.py 2009-06-16 18:21:24 +0000
208+++ openlp/plugins/media/lib/mediaitem.py 2009-07-01 20:21:13 +0000
209@@ -19,13 +19,25 @@
210 """
211 import logging
212 import os
213-
214+import tempfile
215+try:
216+ import gst
217+except:
218+ log = logging.getLogger(u'MediaMediaItemSetup')
219+ log.warning(u'Can\'t generate Vidoe previews - import gst failed');
220+
221 from PyQt4 import QtCore, QtGui
222
223 from openlp.core.lib import MediaManagerItem, translate
224
225 from openlp.plugins.media.lib import MediaTab
226 from openlp.plugins.media.lib import FileListData
227+# from listwithpreviews import ListWithPreviews
228+from openlp.core.lib import MediaManagerItem, ServiceItem, translate, BaseListWithDnD
229+class MediaListView(BaseListWithDnD):
230+ def __init__(self, parent=None):
231+ self.PluginName = u'Media'
232+ BaseListWithDnD.__init__(self, parent)
233
234 class MediaMediaItem(MediaManagerItem):
235 """
236@@ -36,100 +48,76 @@
237 log.info(u'Media Media Item loaded')
238
239 def __init__(self, parent, icon, title):
240+ self.TranslationContext = u'MediaPlugin'
241+ self.PluginTextShort = u'Media'
242+ self.ConfigSection = u'images'
243+ self.OnNewPrompt = u'Select Media(s)'
244+ self.OnNewFileMasks = u'Videos (*.avi *.mpeg *.mpg *.mp4);;Audio (*.ogg *.mp3 *.wma);;All files (*)'
245+ # this next is a class, not an instance of a class - it will
246+ # be instanced by the base MediaManagerItem
247+ self.ListViewWithDnD_class = MediaListView
248+ self.ServiceItemIconName = u':/media/media_image.png'
249+ self.PreviewFunction = self.video_get_preview
250 MediaManagerItem.__init__(self, parent, icon, title)
251
252- def setupUi(self):
253- # Add a toolbar
254- self.addToolbar()
255- # Create buttons for the toolbar
256- ## New Media Button ##
257- self.addToolbarButton(
258- translate(u'MediaMediaItem',u'New Media'),
259- translate(u'MediaMediaItem',u'Load Media into openlp.org'),
260- ':/videos/video_load.png', self.onMediaNewClick, 'MediaNewItem')
261- ## Delete Media Button ##
262- self.addToolbarButton(
263- translate(u'MediaMediaItem',u'Delete Media'),
264- translate(u'MediaMediaItem',u'Delete the selected Media item'),
265- ':/videos/video_delete.png', self.onMediaDeleteClick, 'MediaDeleteItem')
266- ## Separator Line ##
267- self.addToolbarSeparator()
268- ## Preview Media Button ##
269- self.addToolbarButton(
270- translate(u'MediaMediaItem',u'Preview Media'),
271- translate(u'MediaMediaItem',u'Preview the selected Media item'),
272- ':/system/system_preview.png', self.onMediaPreviewClick, 'MediaPreviewItem')
273- ## Live Media Button ##
274- self.addToolbarButton(
275- translate(u'MediaMediaItem',u'Go Live'),
276- translate(u'MediaMediaItem',u'Send the selected Media item live'),
277- ':/system/system_live.png', self.onMediaLiveClick, 'MediaLiveItem')
278- ## Add Media Button ##
279- self.addToolbarButton(
280- translate(u'MediaMediaItem',u'Add Media To Service'),
281- translate(u'MediaMediaItem',u'Add the selected Media items(s) to the service'),
282- ':/system/system_add.png',self.onMediaAddClick, 'MediaAddItem')
283- ## Add the Medialist widget ##
284-
285- self.MediaListView = QtGui.QListView()
286- self.MediaListView.setAlternatingRowColors(True)
287- self.MediaListData = FileListData()
288- self.MediaListView.setModel(self.MediaListData)
289-
290- self.PageLayout.addWidget(self.MediaListView)
291-
292- #define and add the context menu
293- self.MediaListView.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
294-
295- self.MediaListView.addAction(self.contextMenuAction(
296- self.MediaListView, ':/system/system_preview.png',
297- translate(u'MediaMediaItem',u'&Preview Media'), self.onMediaPreviewClick))
298- self.MediaListView.addAction(self.contextMenuAction(
299- self.MediaListView, ':/system/system_live.png',
300- translate(u'MediaMediaItem',u'&Show Live'), self.onMediaLiveClick))
301- self.MediaListView.addAction(self.contextMenuAction(
302- self.MediaListView, ':/system/system_add.png',
303- translate(u'MediaMediaItem',u'&Add to Service'), self.onMediaAddClick))
304-
305- def initialise(self):
306- list = self.parent.config.load_list(u'Media')
307- self.loadMediaList(list)
308-
309- def onMediaNewClick(self):
310- files = QtGui.QFileDialog.getOpenFileNames(None,
311- translate(u'MediaMediaItem', u'Select Media(s) items'),
312- self.parent.config.get_last_dir(),
313- u'Videos (*.avi *.mpeg);;Audio (*.mp3 *.ogg *.wma);;All files (*)')
314- if len(files) > 0:
315- self.loadMediaList(files)
316- dir, filename = os.path.split(unicode(files[0]))
317- self.parent.config.set_last_dir(dir)
318- self.parent.config.set_list(u'media', self.MediaListData.getFileList())
319-
320- def getFileList(self):
321- filelist = [item[0] for item in self.MediaListView];
322- return filelist
323-
324- def loadMediaList(self, list):
325- for files in list:
326- self.MediaListData.addRow(files)
327-
328- def onMediaDeleteClick(self):
329- indexes = self.MediaListView.selectedIndexes()
330+ def video_get_preview(self, filename):
331+
332+ """Gets a preview of the first frame of a video file using
333+ GSTREAMER (non-portable??? - Can't figure out how to do with
334+ Phonon - returns a QImage"""
335+
336+ try:
337+ # Define your pipeline, just as you would at the command prompt.
338+ # This is much easier than trying to create and link each gstreamer element in Python.
339+ # This is great for pipelines that end with a filesink (i.e. there is no audible or visual output)
340+ log.info ("Video preview %s"%( filename))
341+ outfile=tempfile.NamedTemporaryFile(suffix='.png')
342+ cmd=u'filesrc location="%s" ! decodebin ! ffmpegcolorspace ! pngenc ! filesink location="%s"'% (filename, outfile.name)
343+ pipe = gst.parse_launch(cmd)
344+ # Get a reference to the pipeline's bus
345+ bus = pipe.get_bus()
346+
347+ # Set the pipeline's state to PLAYING
348+ pipe.set_state(gst.STATE_PLAYING)
349+
350+ # Listen to the pipeline's bus indefinitely until we receive a EOS (end of stream) message.
351+ # This is a super important step, or the pipeline might not work as expected. For example,
352+ # in my example pipeline above, the pngenc will not export an actual image unless you have
353+ # this line of code. It just exports a 0 byte png file. So... don't forget this step.
354+ bus.poll(gst.MESSAGE_EOS, -1)
355+ img=QtGui.QImage(outfile.name)
356+ outfile.close()
357+# os.unlink(outfile.name)
358+ pipe.set_state(gst.STATE_NULL)
359+ return img
360+ except:
361+ log.info("Can't generate video preview for some reason");
362+ import sys
363+ print sys.exc_info()
364+ return None
365+
366+
367+ def generateSlideData(self, service_item):
368+ indexes = self.ListView.selectedIndexes()
369+ service_item.title = u'Media'
370 for index in indexes:
371- current_row = int(index.row())
372- self.MediaListData.removeRow(current_row)
373- self.parent.config.set_list(u'media', self.MediaListData.getFileList())
374-
375- def onMediaPreviewClick(self):
376+ filename = self.ListData.getFilename(index)
377+ frame = QtGui.QImage(unicode(filename))
378+ (path, name) = os.path.split(filename)
379+ service_item.add_from_image(path, name, frame)
380+
381+
382+ def onPreviewClick(self):
383 log.debug(u'Media Preview Button pressed')
384- items = self.MediaListView.selectedIndexes()
385+ items = self.ListView.selectedIndexes()
386 for item in items:
387- text = self.MediaListData.getValue(item)
388+ text = self.ListData.getValue(item)
389 print text
390
391 def onMediaLiveClick(self):
392+ log.debug(u'Media Live Button pressed')
393 pass
394
395- def onMediaAddClick(self):
396- pass
397\ No newline at end of file
398+# def onMediaAddClick(self):
399+# log.debug(u'Media Add Button pressed')
400+# pass
401
402=== modified file 'openlp/plugins/media/mediaplugin.py'
403--- openlp/plugins/media/mediaplugin.py 2009-06-16 18:21:24 +0000
404+++ openlp/plugins/media/mediaplugin.py 2009-07-01 20:21:13 +0000
405@@ -22,7 +22,6 @@
406
407 from openlp.core.lib import Plugin, MediaManagerItem, SettingsTab
408 from openlp.plugins.media.lib import MediaTab,MediaMediaItem
409-
410 class MediaPlugin(Plugin):
411
412 def __init__(self, plugin_helpers):
413
414=== removed file 'openlp/plugins/media/video_render.py'
415--- openlp/plugins/media/video_render.py 2009-06-22 20:44:35 +0000
416+++ openlp/plugins/media/video_render.py 1970-01-01 00:00:00 +0000
417@@ -1,70 +0,0 @@
418-# -*- coding: utf-8 -*-
419-# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
420-"""
421-OpenLP - Open Source Lyrics Projection
422-Copyright (c) 2008 Raoul Snyman
423-Portions copyright (c) 2008 Martin Thompson, Tim Bentley,
424-
425-This program is free software; you can redistribute it and/or modify it under
426-the terms of the GNU General Public License as published by the Free Software
427-Foundation; version 2 of the License.
428-
429-This program is distributed in the hope that it will be useful, but WITHOUT ANY
430-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
431-PARTICULAR PURPOSE. See the GNU General Public License for more details.
432-
433-You should have received a copy of the GNU General Public License along with
434-this program; if not, write to the Free Software Foundation, Inc., 59 Temple
435-Place, Suite 330, Boston, MA 02111-1307 USA
436-"""
437-import os
438-from PyQt4 import QtCore, QtGui
439-
440-# xxx this needs a try, except once we've decided what to do if it fails
441-from PyQt4.phonon import Phonon
442-
443-# from openlp.core.lib import Plugin, MediaManagerItem, SettingsTab
444-# from openlp.plugins.media.lib import MediaTab,MediaMediaItem
445-
446-"""Renders a video to some surface or other """
447-
448-class w(QtGui.QMainWindow):
449- def __init__(self, parent=None):
450- super(QtGui.QMainWindow, self).__init__(parent)
451- self.resize(640,480)
452- self.setWindowTitle(u'simple media player')
453- self.show()
454-
455-if __name__==u'__main__':
456- app = QtGui.QApplication([])
457-# widget = QtGui.QWidget()
458-# widget.resize(320, 240)
459-# widget.setWindowTitle(u'simple')
460-# widget.show()
461-# QCore.QCoreApplication.setApplicationName(u'OpenLP')
462- mainwindow=w()
463- widget=QtGui.QWidget(mainwindow)
464- mainwindow.setCentralWidget(widget)
465- widget.setLayout(QtGui.QVBoxLayout(widget))
466-# videofile=u'r-128.rm'
467- videofile=u'/extra_space/Download/coa360download56Kbps240x160.mpg'
468- source=Phonon.MediaSource(videofile)
469-
470- media=Phonon.MediaObject(widget)
471- media.setCurrentSource(source)
472-
473- video=Phonon.VideoWidget(widget)
474- audio=Phonon.AudioOutput(Phonon.MusicCategory)
475-# controller=Phonon.MediaController(media)
476- Phonon.createPath(media, video);
477- Phonon.createPath(media, audio);
478-# player=Phonon.VideoPlayer(Phonon.VideoCategory, widget)
479- slider=Phonon.SeekSlider(media, mainwindow)
480- widget.layout().addWidget(slider)
481- widget.layout().addWidget(video)
482- slider.show()
483-
484- video.show()
485- media.play()
486- app.exec_()
487-