Merge lp:~nico-inattendu/luciole/luciole-with-sound into lp:luciole

Proposed by NicoInattendu
Status: Merged
Approved by: NicoInattendu
Approved revision: 177
Merged at revision: 125
Proposed branch: lp:~nico-inattendu/luciole/luciole-with-sound
Merge into: lp:luciole
Diff against target: 63657 lines (+57987/-2079)
218 files modified
Examples_launch.txt (+5/-0)
WebcamProps.py (+390/-0)
luciole/base/options.py (+2/-3)
luciole/conf.py (+21/-11)
luciole/constants.py (+44/-4)
luciole/ctrl/base.py (+105/-0)
luciole/ctrl/constants.py (+2/-1)
luciole/ctrl/ctrl.py (+78/-71)
luciole/ctrl/ctrl_acq.py (+634/-0)
luciole/ctrl/ctrl_app.py (+1050/-0)
luciole/ctrl/ctrl_img_vw.py (+82/-0)
luciole/ctrl/ctrl_no_project.py (+60/-0)
luciole/ctrl/ctrl_project.py (+505/-293)
luciole/ctrl/ctrl_timeline.py (+953/-0)
luciole/ctrl/import_image.py (+24/-8)
luciole/ctrl/load_rush.py (+12/-7)
luciole/ctrl/viewer_ctrl.py (+8/-1)
luciole/data/templates/project_template.xml (+4/-2)
luciole/data/themes/Tropical.rc (+10/-0)
luciole/gui/actions.py (+787/-0)
luciole/gui/base/builder.py (+23/-17)
luciole/gui/base/signals.py (+33/-4)
luciole/gui/constants.py (+88/-1)
luciole/gui/fpi_widget.py (+103/-0)
luciole/gui/gui_ctrl.py (+52/-12)
luciole/gui/main_window.py (+178/-0)
luciole/gui/project_modes.py (+443/-0)
luciole/gui/treeviews/luciole_tree.py (+145/-91)
luciole/gui/treeviews/treeviews.py (+188/-8)
luciole/gui/viewer_widget.py (+543/-128)
luciole/gui/webcam_properties_widget.py (+236/-0)
luciole/gui/windows/assistant_new_project.py (+215/-30)
luciole/gui/windows/dialog.py (+42/-0)
luciole/gui/windows/dialog_project_properties.py (+280/-112)
luciole/gui/windows/export_video_window.py (+13/-3)
luciole/gui/windows/webcam_detection_widget.py (+374/-117)
luciole/gui/windows/windows.py (+6/-0)
luciole/main.py (+3/-3)
luciole/media/acquisition.py (+33/-4)
luciole/media/image.py (+9/-8)
luciole/media/lgst/acq.py (+33/-4)
luciole/media/lgst/common.py (+150/-0)
luciole/media/lgst/gst_base.py (+75/-25)
luciole/media/lgst/image_save_bin.py (+295/-0)
luciole/media/lgst/image_src.py (+176/-0)
luciole/media/lgst/mix_stream_img_bin.py (+284/-0)
luciole/media/lgst/play.py (+70/-19)
luciole/media/lgst/play_sound.py (+31/-5)
luciole/media/lgst/scale_bin.py (+157/-0)
luciole/media/lgst/videotest_src.py (+138/-0)
luciole/media/lgst/webcam_bin.py (+165/-0)
luciole/media/lgst/webcam_caps.py (+245/-0)
luciole/media/lgst/webcam_factory.py (+357/-0)
luciole/media/player.py (+8/-0)
luciole/media/webcam_detect/webcam_detection.py (+444/-412)
luciole/project/export/export_tool_base.py (+1/-1)
luciole/project/export/export_video.py (+34/-16)
luciole/project/project_etree.py (+218/-69)
luciole/ui/luciole.glade (+319/-589)
luciole/ui/no_project_mode.glade (+88/-0)
luciole/ui/viewer.ui (+394/-0)
luciole/ui/webcam_properties.glade (+22/-0)
pitivi/.gitignore (+4/-0)
pitivi/Makefile (+686/-0)
pitivi/Makefile.am (+46/-0)
pitivi/Makefile.in (+686/-0)
pitivi/__init__.py (+3/-0)
pitivi/action.py (+770/-0)
pitivi/actioner.py (+224/-0)
pitivi/application.py (+428/-0)
pitivi/check.py (+154/-0)
pitivi/configure.py (+65/-0)
pitivi/configure.py.in (+65/-0)
pitivi/device.py (+331/-0)
pitivi/discoverer.py (+732/-0)
pitivi/effects.py (+364/-0)
pitivi/elements/.gitignore (+3/-0)
pitivi/elements/Makefile (+445/-0)
pitivi/elements/Makefile.am (+12/-0)
pitivi/elements/Makefile.in (+445/-0)
pitivi/elements/__init__.py (+3/-0)
pitivi/elements/arraysink.py (+78/-0)
pitivi/elements/mixer.py (+292/-0)
pitivi/elements/singledecodebin.py (+411/-0)
pitivi/elements/thumbnailsink.py (+91/-0)
pitivi/elements/videofade.py (+100/-0)
pitivi/encode.py (+405/-0)
pitivi/factories/Makefile (+445/-0)
pitivi/factories/Makefile.am (+12/-0)
pitivi/factories/Makefile.in (+445/-0)
pitivi/factories/base.py (+789/-0)
pitivi/factories/file.py (+80/-0)
pitivi/factories/operation.py (+212/-0)
pitivi/factories/test.py (+117/-0)
pitivi/factories/timeline.py (+183/-0)
pitivi/formatters/Makefile (+444/-0)
pitivi/formatters/Makefile.am (+11/-0)
pitivi/formatters/Makefile.in (+444/-0)
pitivi/formatters/__init__.py (+25/-0)
pitivi/formatters/base.py (+400/-0)
pitivi/formatters/etree.py (+828/-0)
pitivi/formatters/format.py (+78/-0)
pitivi/formatters/playlist.py (+96/-0)
pitivi/instance.py (+33/-0)
pitivi/log/Makefile (+443/-0)
pitivi/log/Makefile.am (+11/-0)
pitivi/log/Makefile.in (+443/-0)
pitivi/log/log.py (+976/-0)
pitivi/log/loggable.py (+40/-0)
pitivi/log/termcolor.py (+213/-0)
pitivi/log/test_log.py (+284/-0)
pitivi/pipeline.py (+948/-0)
pitivi/pitivigstutils.py (+72/-0)
pitivi/pixmaps/.gitignore (+2/-0)
pitivi/pixmaps/Makefile (+509/-0)
pitivi/pixmaps/Makefile.am (+95/-0)
pitivi/pixmaps/Makefile.in (+509/-0)
pitivi/pixmaps/pitivi-group-24.svg (+221/-0)
pitivi/pixmaps/pitivi-group.svg (+225/-0)
pitivi/pixmaps/pitivi-keyframe-24.svg (+485/-0)
pitivi/pixmaps/pitivi-keyframe.svg (+419/-0)
pitivi/pixmaps/pitivi-relink-24.svg (+666/-0)
pitivi/pixmaps/pitivi-relink.svg (+668/-0)
pitivi/pixmaps/pitivi-split-24.svg (+296/-0)
pitivi/pixmaps/pitivi-split.svg (+324/-0)
pitivi/pixmaps/pitivi-ungroup-24.svg (+184/-0)
pitivi/pixmaps/pitivi-ungroup.svg (+188/-0)
pitivi/pixmaps/pitivi-unlink-24.svg (+766/-0)
pitivi/pixmaps/pitivi-unlink.svg (+264/-0)
pitivi/pixmaps/processing-clip.svg (+271/-0)
pitivi/plugincore.py (+77/-0)
pitivi/pluginmanager.py (+453/-0)
pitivi/plumber.py (+178/-0)
pitivi/project.py (+208/-0)
pitivi/projectmanager.py (+301/-0)
pitivi/receiver.py (+64/-0)
pitivi/reflect.py (+151/-0)
pitivi/settings.py (+625/-0)
pitivi/signalgroup.py (+85/-0)
pitivi/signalinterface.py (+191/-0)
pitivi/sourcelist.py (+176/-0)
pitivi/sourcelist_undo.py (+79/-0)
pitivi/stream.py (+564/-0)
pitivi/threads.py (+113/-0)
pitivi/thumbnailcache.py (+64/-0)
pitivi/timeline/Makefile (+444/-0)
pitivi/timeline/Makefile.am (+11/-0)
pitivi/timeline/Makefile.in (+444/-0)
pitivi/timeline/__init__.py (+3/-0)
pitivi/timeline/gap.py (+145/-0)
pitivi/timeline/timeline.py (+2102/-0)
pitivi/timeline/timeline_undo.py (+471/-0)
pitivi/timeline/track.py (+1314/-0)
pitivi/ui/.gitignore (+3/-0)
pitivi/ui/Makefile (+513/-0)
pitivi/ui/Makefile.am (+60/-0)
pitivi/ui/Makefile.in (+513/-0)
pitivi/ui/__init__.py (+3/-0)
pitivi/ui/audiofxlist.py (+70/-0)
pitivi/ui/basetabs.py (+102/-0)
pitivi/ui/cam_capture.glade (+134/-0)
pitivi/ui/clipproperties.py (+423/-0)
pitivi/ui/common.py (+175/-0)
pitivi/ui/controller.py (+237/-0)
pitivi/ui/curve.py (+339/-0)
pitivi/ui/defaultpropertyeditor.py (+131/-0)
pitivi/ui/dnd.py (+46/-0)
pitivi/ui/dynamic.py (+677/-0)
pitivi/ui/effectlist.py (+353/-0)
pitivi/ui/effectsconfiguration.py (+110/-0)
pitivi/ui/elementsettingsdialog.glade (+325/-0)
pitivi/ui/encodingdialog.glade (+220/-0)
pitivi/ui/encodingdialog.py (+156/-0)
pitivi/ui/exportsettingswidget.glade (+452/-0)
pitivi/ui/exportsettingswidget.py (+463/-0)
pitivi/ui/filelisterrordialog.glade (+102/-0)
pitivi/ui/filelisterrordialog.py (+116/-0)
pitivi/ui/glade.py (+187/-0)
pitivi/ui/gstwidget.py (+206/-0)
pitivi/ui/mainwindow.py (+1185/-0)
pitivi/ui/mainwindow.xml (+69/-0)
pitivi/ui/net_capture.glade (+1073/-0)
pitivi/ui/netstream_managerdialog.py (+185/-0)
pitivi/ui/pathwalker.py (+62/-0)
pitivi/ui/pluginmanagerdialog.glade (+205/-0)
pitivi/ui/pluginmanagerdialog.py (+360/-0)
pitivi/ui/point.py (+45/-0)
pitivi/ui/prefs.py (+497/-0)
pitivi/ui/preview.py (+113/-0)
pitivi/ui/previewer.py (+563/-0)
pitivi/ui/projectsettings.glade (+175/-0)
pitivi/ui/projectsettings.py (+64/-0)
pitivi/ui/propertyeditor.py (+116/-0)
pitivi/ui/ruler.py (+350/-0)
pitivi/ui/screencast_manager.glade (+343/-0)
pitivi/ui/screencast_managerdialog.py (+101/-0)
pitivi/ui/sourcelist.py (+1074/-0)
pitivi/ui/timeline.py (+808/-0)
pitivi/ui/timelinecanvas.py (+335/-0)
pitivi/ui/timelinecontrols.py (+70/-0)
pitivi/ui/track.py (+132/-0)
pitivi/ui/trackobject.py (+426/-0)
pitivi/ui/videofxlist.py (+74/-0)
pitivi/ui/view.py (+24/-0)
pitivi/ui/viewer.py (+532/-0)
pitivi/ui/webcam_managerdialog.py (+261/-0)
pitivi/ui/zoominterface.py (+150/-0)
pitivi/undo.py (+255/-0)
pitivi/utils.py (+499/-0)
test/common.py (+45/-0)
test/luciole_project/luciole_project.xml (+40/-0)
test/test_assistant_new_project.py (+87/-0)
test/test_image.py (+100/-0)
test/test_import_image.py (+135/-0)
test/test_project_etree.py (+135/-0)
test/test_project_properties.py (+97/-0)
test/test_scale_bin.py (+93/-0)
test/test_webcam_viewer.py (+236/-0)
To merge this branch: bzr merge lp:~nico-inattendu/luciole/luciole-with-sound
Reviewer Review Type Date Requested Status
NicoInattendu Pending
Review via email: mp+51532@code.launchpad.net

Description of the change

Finally a merge of luciole with sound !
New features :
- Sound track can be added. Only wav files accepted
- Timeline management with Pitivi. implies huge changes of luciole
- Webcam detection improved. No more use of HAL replaced by udev
- Webcam definition / framerates / videotypes can now be selected
- Acquisition can now be made in full webcam definition or PAL definition

Know problems :
- Mixer features does not work
- Acqusisition from DV cam should be implemented ( urgent ! ). try with udev ?
- Only export to file is implemented with sound
- assistant page webcam when back/ rewind action the cam detection appears twice ...
- i18n with gettext to improve
- code cleaning needed (remove unused files and classes)
- GUI tuning needed : in particular the aspect ratio of viewer window
- Problems of display when no acquisition is set ( digicam mode)

For packaging :
- need to include all Pitivi files
- review files to include
- review files for i18n
- add-dependency to python-udev , and check gstreamer package depdendencies ( bad-plugins, etc ...)

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'Examples_launch.txt'
2--- Examples_launch.txt 1970-01-01 00:00:00 +0000
3+++ Examples_launch.txt 2011-02-28 13:21:06 +0000
4@@ -0,0 +1,5 @@
5+PITIVI_DEBUG=2,*action*:4,*ctrlp*:4 bin/luciole
6+
7+PITIVI_DEBUG=2,*webcam*:4,*tester:4* PYTHONPATH=".:$PYTHONPATH" python test/test_project_etree.py
8+
9+
10
11=== added file 'WebcamProps.py'
12--- WebcamProps.py 1970-01-01 00:00:00 +0000
13+++ WebcamProps.py 2011-02-28 13:21:06 +0000
14@@ -0,0 +1,390 @@
15+#!/usr/bin/env python
16+# -*- coding: utf-8 -*-
17+# -*- Mode: Python -*-
18+# vim:si:ai:et:sw=4:sts=4:ts=4
19+
20+import gst
21+
22+from pitivi.log.loggable import Loggable
23+from pitivi.signalinterface import Signallable
24+
25+from luciole.media.lgst.webcam_caps import WebcamCaps
26+
27+class WebcamProps(Loggable, Signallable):
28+
29+ __signals__ = {
30+ 'caps-selected' : ['caps'],
31+ }
32+
33+ BEST_RATIO = (720.0/576.0)
34+ BEST_VIDEOTYPES = ['video/x-raw-yuv', 'video/x-raw-rgb']
35+ BEST_YUV_FORMATS = [gst.Fourcc('I420'), gst.Fourcc('YUY2')]
36+
37+ VIDEOTYPE_PROPS = [
38+ 'red_mask',
39+ 'blue_mask',
40+ 'green_mask',
41+ 'bpp',
42+ 'depth',
43+ 'format'
44+ ]
45+
46+
47+ FIELDS_TYPE = {
48+ 'width' : 'int' ,
49+ 'height' : 'int' ,
50+ 'framerate' : 'fraction' ,
51+ 'definition' : 'definition',
52+ 'videotype' : 'videotype',
53+ 'red_mask' : 'int' ,
54+ 'blue_mask' : 'int' ,
55+ 'green_mask' : 'int' ,
56+ 'bpp' :'int',
57+ 'depth' :'int',
58+ 'format': 'fourcc',
59+ }
60+
61+
62+
63+ def __init__(self, widget) :
64+ Loggable.__init__(self)
65+ Signallable.__init__(self)
66+
67+ self.widget = widget
68+ self._filter_caps = gst.caps_new_any()
69+ self._webcam_caps = gst.caps_new_any()
70+ self._selected_props = {}
71+ self._selected_videotype = ""
72+
73+ self.PROPS_WITH_BEST_VALUE = {
74+ 'format' : self.get_best_yuv_format,
75+ 'definition' : self.get_best_definition,
76+ 'videotype' : self.get_best_videotype,
77+ }
78+
79+ self._active_videotype_props = []
80+ self._active_common_props = []
81+
82+
83+ def prepare(self, caps) :
84+ # store caps for webcam
85+ self._webcam_caps = WebcamCaps(caps)
86+
87+
88+ self.widget.connect('prop-changed', self._prop_changed_cb)
89+
90+ # first show definition
91+ self.manage_prop('definition', None)
92+
93+
94+
95+ def get_best_value(self, prop, prop_values) :
96+ _best = None
97+ if prop in self.PROPS_WITH_BEST_VALUE :
98+ _best = self.PROPS_WITH_BEST_VALUE[prop](prop_values)
99+ else :
100+ _best = prop_values[0]
101+ self.info('No best value algorithm for %s. Fist value taken %s',
102+ prop, _best)
103+ return _best
104+
105+ def get_best_definition(self, definitions) :
106+ """ algorithms who search first for best ratios,
107+ then for higher width
108+ """
109+ _def_dict = {}
110+ # Write algotithm
111+ _def_dict['width'] = definitions[-1]['width']
112+ _def_dict['height'] = definitions[-1]['height']
113+ return _def_dict
114+
115+ def get_best_videotype(self, videotypes) :
116+ """ parse BEST_VIDEOTYPES list in preference order.
117+ if videotype not in BEST_VIDEOTYPES return the first
118+ videotype in input list
119+ """
120+ _best_vtype = None
121+ for _vtype in self.BEST_VIDEOTYPES :
122+ if _vtype in videotypes :
123+ _best_vtype = _vtype
124+ break
125+ if _best_vtype == None :
126+ _best_vtype = videotypes[0]
127+ self.warning( ("No best video type founds. selelcted %s."
128+ "First in list") ) \
129+ % _best_vtype
130+ return _best_vtype
131+
132+ def get_best_yuv_format(self, yuv_formats) :
133+ """ parse BEST_YUV_FORMAT list in preference order.
134+ if videotype not in BEST_YUV_FORMAT return the first
135+ videotype in input list
136+ """
137+ _best = None
138+ self.debug('avlb values : "%s', yuv_formats)
139+ for _format in self.BEST_YUV_FORMATS :
140+ if _format in yuv_formats :
141+ _best = _format
142+ break
143+ if _best == None :
144+ _best = yuv_formats[0]
145+ print _best
146+ self.warning("No best yuv format found. selelcted %s. First in list", _best)
147+ self.debug("Best yuv fomat is %s",_best )
148+ return _best
149+
150+ def get_best_framerate(self, framerates) :
151+ """ take a framerate on the middle.
152+ Not too high, not too low
153+ """
154+ _len = len(framerates)
155+ _best = _len/2
156+ return _best
157+
158+
159+
160+ def _prop2str_single(self, name, value) :
161+ _prop_str = ""
162+ if self.FIELDS_TYPE[name] == 'fourcc' :
163+ _prop_str = "%s" % value.fourcc
164+ elif self.FIELDS_TYPE[name] == 'fraction' :
165+ _prop_str = "%s/%s" % (value.num, value.denom)
166+ elif self.FIELDS_TYPE[name] == 'definition' :
167+ _prop_str = "%sx%s" % (value['width'], value['height'])
168+ else :
169+ _prop_str = "%s" % (value)
170+ return _prop_str
171+
172+ def _prop2str(self, prop_name, values) :
173+ _prop_out = None
174+ if isinstance(values, list) :
175+ _prop_out = [ self._prop2str_single(prop_name, _val) \
176+ for _val in values ]
177+ else :
178+ _prop_out = self._prop2str_single(prop_name, values)
179+ return _prop_out
180+
181+ return _prop_out
182+
183+
184+ def _str2def(self, string) :
185+ if string is not None :
186+ _list = string.split('x')
187+ _def_dict = {
188+ 'width' : _list[0],
189+ 'height' : _list[1]
190+ }
191+ return _def_dict
192+ else :
193+ self.error('Impossible to understand/interpret %s'%string)
194+ return None
195+
196+
197+ def set_prop_widget(self, prop, prop_values , active_value, wdg_type = 'combo' ) :
198+ self.widget.set_prop(prop, prop_values , wdg_type )
199+
200+ # TODO : manage signal connect
201+
202+ self.debug("Active value for %s is %s", prop, active_value)
203+ self.widget.set_active_prop(prop, active_value)
204+
205+ def set_framerates_widget(self, framerates, best_framerate ) :
206+ pass
207+
208+
209+ def _prop_changed_cb(self, widget, prop, value) :
210+ if prop == 'definition' :
211+ self._definition_changed_cb(value)
212+ elif prop == 'videotype' :
213+ self._videotype_changed_cb(value)
214+ elif prop == 'framerate' :
215+ self._framerate_changed_cb(value)
216+ elif prop in self.VIDEOTYPE_PROPS :
217+ self._videotype_prop_changed_cb(prop, value)
218+
219+
220+
221+
222+ def _definition_changed_cb(self, str_definition) :
223+ """ definition changed cb :
224+ _ store definition
225+ _ look videotype
226+
227+ """
228+ _selected_def = self._str2def(str_definition)
229+ self._selected_props.update(_selected_def)
230+
231+ self._filter_caps = \
232+ self._webcam_caps.get_caps_from_definition( _selected_def)
233+
234+ # when defintion changed clear videotype ( if exists)
235+ self._clear_videotype()
236+ self._clear_framerate()
237+
238+ #now look for videotypes
239+ self.manage_prop('videotype', self._filter_caps)
240+
241+ def _videotype_changed_cb(self, videotype) :
242+ self._selected_videotype = videotype
243+ self.debug("RX videotype-changed : %s", self._selected_videotype)
244+
245+ _caps = self._make_caps(self._selected_videotype, self._selected_props)
246+ self._filter_caps = self._webcam_caps.intersect(_caps)
247+ _size = self._filter_caps.get_size()
248+ self.debug(" %s selected caps :%s",
249+ _size,
250+ self._filter_caps ,
251+ )
252+ if _size == 0 :
253+ self.error("No caps found for tpl %s ", _caps)
254+ return
255+ self._clear_videotype_props()
256+ self._clear_framerate()
257+ if _size == 1 :
258+ # no more selection of props needed
259+ # request for available framerates according definition
260+ _is_single = self.manage_prop('framerate', self._filter_caps)
261+ if _is_single == True :
262+ self._check_single_caps(self._filter_caps)
263+
264+
265+ else :
266+ # more than one struct in caps
267+ if self._selected_videotype == 'video/x-raw-yuv' :
268+ self.manage_prop('format', self._filter_caps)
269+
270+ if self._selected_videotype == 'video/x-raw-rgb' :
271+ self.manage_prop('red_mask', self._filter_caps)
272+
273+ def _videotype_prop_changed_cb(self, prop, value) :
274+
275+ self.debug("RX prop %s changed = %s", prop, value)
276+ self._selected_props[prop] = value
277+
278+ self._clear_framerate()
279+
280+ _caps = self._make_caps(self._selected_videotype, self._selected_props)
281+ self._filter_caps = self._webcam_caps.intersect(_caps)
282+
283+ _size = self._filter_caps.get_size()
284+ self.debug(" %s selected caps :%s",
285+ _size,
286+ self._filter_caps ,
287+ )
288+
289+
290+ if _size == 0 :
291+ self.error("No caps found for tpl %s ", _caps)
292+ return
293+ if _size > 1 :
294+ self.warning(" Now what we do ???. What prop to select ? take the fitst_srtuct")
295+ _single_caps = gst.Caps(self._filter_caps[0])
296+ self._filter_caps = _single_caps
297+
298+ # no more selection of props needed
299+ # request for available framerates according definition
300+ _is_single = self.manage_prop('framerate', self._filter_caps)
301+ if _is_single == True :
302+ self._check_single_caps(self._filter_caps)
303+
304+
305+ def _framerate_changed_cb(self, value) :
306+ self.debug("RX framerate changed = %s (%s)", value, type(value))
307+ if isinstance(value, float) :
308+ value = gst.Fraction(value)
309+ value = "%s/%s"%(int(value.num), int(value.denom))
310+ print value
311+ self._selected_props['framerate'] = value
312+
313+ _caps = self._make_caps(self._selected_videotype, self._selected_props)
314+ self._filter_caps = self._webcam_caps.intersect(_caps)
315+
316+ self._filter_caps.get_size()
317+ self.debug(" %s selected caps :%s",
318+ self._filter_caps.get_size(),
319+ self._filter_caps ,
320+ )
321+ # check if a single caps is set
322+ self._check_single_caps(self._filter_caps)
323+
324+
325+ def _check_single_caps(self, caps) :
326+ if caps.get_size() == 1 \
327+ and\
328+ caps.is_fixed() == True :
329+ self.debug("Hourra ! . caps %s can be applied to webcam", caps )
330+ self.emit('caps-selected', caps)
331+
332+
333+
334+ def manage_prop(self, prop_name, caps) :
335+ """ generic manage of a prop """
336+ _is_single_value = False
337+ is_range, _prop_values = \
338+ self._webcam_caps.get_prop_values(prop_name , caps)
339+ self.debug("%s = %s",prop_name, _prop_values)
340+ if len(_prop_values) > 1 or is_range == True :
341+ if is_range == False :
342+ _best = self.get_best_value(prop_name, _prop_values)
343+ _prop_values = self._prop2str(prop_name, _prop_values)
344+ _best = self._prop2str(prop_name, _best)
345+ self.set_prop_widget(prop_name, _prop_values , _best)
346+ else :
347+ _best = _prop_values[0]
348+ self.set_prop_widget(prop_name, _prop_values , _best, 'range')
349+
350+ else :
351+ self.info("Only one value %s for %s",_prop_values,prop_name )
352+ _is_single_value = True
353+ if prop_name in self.VIDEOTYPE_PROPS and _is_single_value == False :
354+ self._active_videotype_props.append(prop_name)
355+
356+ return _is_single_value
357+
358+
359+ def _clear_videotype(self) :
360+ self._clear_videotype_props()
361+ _prop = 'videotype'
362+ if self._selected_videotype != "" :
363+ self.widget.remove_prop(_prop)
364+ self._selected_videotype = ""
365+
366+ def _clear_videotype_props(self) :
367+ while self._active_videotype_props != [] :
368+ _prop = self._active_videotype_props.pop()
369+ self.widget.remove_prop(_prop)
370+ del self._selected_props[_prop]
371+
372+ def _clear_framerate(self) :
373+ _prop = 'framerate'
374+ if _prop in self._selected_props :
375+ self.widget.remove_prop(_prop)
376+ del self._selected_props[_prop]
377+
378+ def clear_all(self) :
379+ self.widget.remove_all_props()
380+ self._selected_props.clear()
381+ self._active_videotype_props = []
382+ self._selected_videotype = ""
383+
384+ try :
385+ self.widget.disconnect_by_function(self._prop_changed_cb)
386+ except :
387+ self.warning('Impossible to disconnect')
388+ pass
389+
390+ def _make_caps(self, mediatype, fields) :
391+ _caps_str = ""
392+ for _key, _value in fields.iteritems() :
393+ _caps_str = "%s, %s=(%s)%s" % ( \
394+ _caps_str,
395+ _key,
396+ self.FIELDS_TYPE[_key],
397+ _value
398+ )
399+
400+ _caps_str = "%s %s" % (mediatype, _caps_str)
401+ _caps = gst.Caps(_caps_str)
402+ self.debug(" Caps created : %s", _caps.to_string())
403+ return _caps
404+
405
406=== modified file 'luciole/base/options.py'
407--- luciole/base/options.py 2010-09-01 09:29:58 +0000
408+++ luciole/base/options.py 2011-02-28 13:21:06 +0000
409@@ -34,7 +34,7 @@
410
411 # local application/library specific imports
412
413-def options_parser() :
414+def options_parser(args) :
415 """ parse application options """
416 option_list = [
417 make_option("-f", "--file",
418@@ -50,6 +50,5 @@
419
420 usage = "usage: %prog [options] "
421 parser = OptionParser(option_list=option_list, usage=usage)
422- (options, args2) = parser.parse_args()
423- return options
424+ return parser.parse_args(args)
425
426
427=== modified file 'luciole/conf.py'
428--- luciole/conf.py 2010-09-06 05:04:33 +0000
429+++ luciole/conf.py 2011-02-28 13:21:06 +0000
430@@ -37,13 +37,14 @@
431 # related third party imports
432 import gtk
433
434+from pitivi.log.loggable import Loggable
435
436 # local application/library specific imports
437 import luciole.base.exceptions as LEXCEP
438 import luciole.base.tools as LT
439 import luciole.constants as LCONST
440 import luciole.base.lcl_et as LE
441-
442+import luciole.gui.constants as GUI_CONST
443
444 class RecentPjtMngr(object):
445 """ Manage recent projects, display
446@@ -114,7 +115,9 @@
447 """ Callback on menu activation. open selected project """
448 try :
449 # respect strucuture or emit signals
450- self.__cbs['open-project'](self, {'path':project})
451+ # TODO : emit open-project signal
452+ #self.__cbs['open-project'](self, {'path':project})
453+ pass
454 except LEXCEP.LucioException, _err :
455 _msg = _("Project %s no more exist"%project)
456 self.__gui_window.error_message(_msg)
457@@ -122,12 +125,11 @@
458
459
460
461-class LucioleConf(object):
462+class LucioleConf(Loggable):
463 """ Manage the configuration file of luciole """
464
465 __USER_LUCIOLE_DIR = ".luciole"
466 __CONF_FILE_NAME = "lucioleConf.xml"
467- __ORIGINAL_DIR = "templates"
468 __THEME_DIR = LCONST.THEMES_DIR
469
470 def __get_conf_options(self):
471@@ -148,8 +150,7 @@
472 - if conf file does not exist in user dir create it
473 - parse xml conf file
474 """
475-
476- self.logger = logging.getLogger('luciole')
477+ Loggable.__init__(self)
478 self._home_dir = os.path.expandvars('$HOME')
479 self._option_dict = dict()
480 self._option_dict["LastProjects"] = list()
481@@ -165,7 +166,7 @@
482 else :
483 try :
484 # copy file to local dir
485- self._copy_conf_file(os.path.join(self.__ORIGINAL_DIR,
486+ self._copy_conf_file(os.path.join(LCONST.TEMPLATE_DIR,
487 self.__CONF_FILE_NAME))
488 # and parse it
489 self._parse_conf_file()
490@@ -232,18 +233,27 @@
491
492 def load_theme(self) :
493 """ Load a gtk theme """
494- self.logger.debug('Entering theme load')
495+ self.debug('Entering theme load')
496 if self._option_dict.has_key('Theme') :
497 l_path = os.path.join(self.__THEME_DIR, self._option_dict['Theme'])
498 if os.path.exists(l_path) :
499 gtk.rc_parse(l_path)
500+ self.debug('Theme %s loaded', l_path)
501+ # load specidic icons
502+ factory = gtk.IconFactory()
503+ for _stock in GUI_CONST.STOCKS :
504+ gtk.stock_add([GUI_CONST.STOCK_ACQUISITION_ITEM])
505+ iconset = gtk.IconSet()
506+ factory.add(_stock, iconset)
507+ factory.add_default()
508+
509 else :
510 msg = _('Theme %s does not exist'%l_path)
511- self.logger.info(msg)
512+ self.info(msg)
513 else :
514 msg = _('Impossible to load theme')
515- self.logger.info(msg)
516- self.logger.debug('Exiting theme load')
517+ self.info(msg)
518+ self.debug('Exiting theme load')
519
520 #
521 # Private methods
522
523=== modified file 'luciole/constants.py'
524--- luciole/constants.py 2010-09-01 06:11:33 +0000
525+++ luciole/constants.py 2011-02-28 13:21:06 +0000
526@@ -31,7 +31,9 @@
527 import os.path
528
529 # related third party imports
530-# N/A
531+import pygst
532+pygst.require('0.10')
533+import gst
534
535 # local application/library specific imports
536 # N/A
537@@ -93,8 +95,9 @@
538 ########################################
539 # IMAGE FORMATS
540 ########################################
541-THUMB_RATIO = 4 # ration normal/thumbnail.
542- # To set size ot thumbnail images in treeview.
543+# Thumb width displayed in treeviews, the height is calcuated in app
544+# according image ratio
545+THUMB_WIDTH = 120
546 # clor in rgb format muliply by 255 each level to go in gtk.gdk.Color format
547 THUMB_COLOR_RATIO = 256
548 THUMB_TEXT_COLOR = (
549@@ -106,7 +109,6 @@
550 THUMB_TEXT_FAMILY = 'sans' # font family
551
552
553-ALPHA_DEFAULT = 0.4 # default alpha value used for mixer
554
555 ############################################
556 # PROGRAM_PATH
557@@ -117,9 +119,47 @@
558 TEMPLATE_DIR = os.path.join(BASE_DIR, 'data/templates')
559 IMAGE_DIR = os.path.join(BASE_DIR, 'data', 'images')
560 LOCALE_DIR = os.path.join(BASE_DIR, 'po')
561+SOUNDS_DIR = os.path.join(BASE_DIR, 'data', 'sounds')
562+
563 ###############################################
564 # For GLADE
565 ###############################################
566 MAIN_GLADE_FILE = "luciole.glade"
567 MAIN_GLADE_FILE_PATH = os.path.join(UI_DIR, MAIN_GLADE_FILE)
568+MAIN_WINDOW_NAME = 'window1'
569+
570+
571+
572+ACQ_MODE_GLADE_FILE = "acquisition_mode.glade"
573+ACQ_MODE_GLADE_PATH = os.path.join(UI_DIR, ACQ_MODE_GLADE_FILE)
574+
575+NO_PROJECT_MODE_GLADE_FILE = "no_project_mode.glade"
576+NO_PROJECT_MODE_GLADE_PATH = os.path.join(UI_DIR, NO_PROJECT_MODE_GLADE_FILE)
577+
578+VIEWER_GLADE_FILE = "viewer.ui"
579+VIEWER_GLADE_PATH = os.path.join(UI_DIR, VIEWER_GLADE_FILE)
580+
581+
582+MODE_MAIN_WINDOW = 'window_main'
583+
584+
585+###############################################
586+# GST constants
587+###############################################
588+CLOCK_TIME_NONE = gst.CLOCK_TIME_NONE
589+
590+###############################################
591+# SOUNDS_FILE
592+###############################################
593+SOUND_SNAPSHOT_FILE = 'camera.ogg'
594+SOUND_SNAPSHOT_PATH = os.path.join(SOUNDS_DIR, SOUND_SNAPSHOT_FILE)
595+
596+SOUND_TRACK_NAME = 'sound_track.wav'
597+#################################################
598+# For mixer
599+################################################
600+IMAGE2MIX_NAME = 'ToMix.jpeg'
601+DEFAULT_IMAGE2MIX = os.path.join(IMAGE_DIR,'white.jpg')
602+DEFAULT_ALPHA_IMAGE = 0.5
603+DEFAULT_ALPHA_STREAM = 1.0
604
605
606=== added file 'luciole/ctrl/base.py'
607--- luciole/ctrl/base.py 1970-01-01 00:00:00 +0000
608+++ luciole/ctrl/base.py 2011-02-28 13:21:06 +0000
609@@ -0,0 +1,105 @@
610+# -*- Mode: Python -*-
611+# vim:si:ai:et:sw=4:sts=4:ts=4
612+#
613+#
614+# Copyright Nicolas Bertrand (nico@inattendu.org), 2009-2010
615+#
616+# This file is part of Luciole.
617+#
618+# Luciole is free software: you can redistribute it and/or modify
619+# it under the terms of the GNU General Public License as published by
620+# the Free Software Foundation, either version 3 of the License, or
621+# (at your option) any later version.
622+#
623+# Luciole is distributed in the hope that it will be useful,
624+# but WITHOUT ANY WARRANTY; without even the implied warranty of
625+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
626+# GNU General Public License for more details.
627+#
628+# You should have received a copy of the GNU General Public License
629+# along with Luciole. If not, see <http://www.gnu.org/licenses/>.
630+#
631+#
632+"""
633+ctrl_base.py :
634+ Base class for ctrl class
635+"""
636+# standard library imports
637+
638+# related third party imports
639+from pitivi.signalinterface import Signallable
640+from pitivi.log.loggable import Loggable
641+# local application/library specific imports
642+
643+
644+class ProjectMode(Signallable, Loggable) :
645+ """ Base class """
646+ __signals__ = {}
647+
648+ def __init__(self, ctrl_project, mode_type = None) :
649+ Signallable.__init__(self)
650+ Loggable.__init__(self)
651+ self.mode_type = mode_type
652+ self.ctrl_project = ctrl_project
653+ self.gui = ctrl_project.gui
654+ self._viewer = None
655+
656+ self.project = None
657+ self._pipeline = None
658+
659+ self.connect_to_project()
660+
661+ def set_viewer(self, viewer) :
662+ """ set the gui viewer """
663+ self._viewer = viewer
664+
665+ def connect_to_project(self) :
666+ """ conecto project controller """
667+ self.debug('Connect to project')
668+ self.ctrl_project.connect('project-loaded', self.project_loaded_cb)
669+ self.ctrl_project.connect('project-closed', self.project_closed_cb)
670+
671+ def project_loaded_cb(self, project_ctrl, project) :
672+ """ project-loaded callback """
673+ self.debug('RX project-loaded')
674+ self.prepare(project)
675+
676+ def project_closed_cb(self, project_ctrl) :
677+ """ project-closed callback """
678+ self.debug('RX project-closed')
679+ self.release()
680+
681+ def connect_to_gui(self, gui) :
682+ """ Meta function.
683+ connect to gui signals """
684+ self.debug('Connect to gui')
685+
686+
687+ def active_viewer(self) :
688+ """ Meta function.
689+ active displays associted to mode """
690+ pass
691+
692+ def prepare(self, project) :
693+ """ Meta function.
694+ prepare the mode """
695+
696+ self.debug("project : %s", project)
697+ self.project = project
698+ if project == None :
699+ self.error('No project to prepare')
700+ else :
701+ self.project = project
702+
703+
704+ def release(self) :
705+ """ Meta function
706+ release """
707+ self.debug('Release')
708+ self._viewer.setPipeline(None)
709+ pass
710+
711+
712+
713+
714+
715
716=== modified file 'luciole/ctrl/constants.py'
717--- luciole/ctrl/constants.py 2010-04-06 13:57:15 +0000
718+++ luciole/ctrl/constants.py 2011-02-28 13:21:06 +0000
719@@ -43,7 +43,8 @@
720 "move-down-chrono",
721 "move-to-chrono",
722 "move-up-chrono",
723- "open-project",
724+# "open-project",
725+ "play-query-position",
726 "play-video",
727 "quit-app",
728 "save-as-project",
729
730=== modified file 'luciole/ctrl/ctrl.py'
731--- luciole/ctrl/ctrl.py 2010-09-03 12:44:41 +0000
732+++ luciole/ctrl/ctrl.py 2011-02-28 13:21:06 +0000
733@@ -28,13 +28,15 @@
734 """
735 # standard library imports
736 import sys
737+import os
738+import os.path
739 import locale
740 import gettext
741 _ = gettext.gettext
742
743 # related third party imports
744 import gobject
745-import os.path
746+from pitivi.log import log
747
748 # local application/library specific imports
749 import luciole.base.app_logging as LOGGING
750@@ -52,6 +54,8 @@
751 #####
752
753
754+
755+
756 class LucioleController(object):
757 """
758 Main luciole controller
759@@ -59,7 +63,12 @@
760
761 def __init__(self, args) :
762 """ Controller initialisation.
763+
764 Is the main appication intialisation """
765+ # init logging as early as possible so we can log startup code
766+ enable_color = os.environ.get('PITIVI_DEBUG_NO_COLOR', '0') in ('', '0')
767+ log.init('PITIVI_DEBUG', enable_color)
768+
769 #
770 # init attributes
771 #
772@@ -91,7 +100,7 @@
773
774
775 # load recent projects
776- _cbs = {'open-project' : self.on_open_project}
777+ _cbs = None
778 _recent_mnger = self.__configurer.init_recent_manager(
779 self.__gui.file_recent_menu,
780 self.__gui.windows,
781@@ -103,7 +112,7 @@
782
783
784 # start project controller
785- self.__project = \
786+ self._project = \
787 CTRL_PROJECT.ProjectController(self.__gui,_recent_mnger ,_cbs)
788
789 _cbs = {
790@@ -120,7 +129,12 @@
791
792 # conncet gui signals after the controller initialisation
793 self.__gui.connect_gui_signals()
794-
795+ # Connect GUI with app
796+ self.__gui.connect_to_app_ctrl(self)
797+
798+ # TODO : load timeline
799+ #TMLN.Timeline()
800+
801 def __connect_signals(self):
802 """ connect controller signals """
803 self.ctrl_signals = gobject.GObject()
804@@ -152,42 +166,42 @@
805 def on_change_framerate(self, *args) :
806 """ Request of framerate(fpi) change """
807 _args = self.__extract_signal_args(args)
808- self.__project.change_framerate(_args['framerate'])
809+ self._project.change_framerate(_args['framerate'])
810
811 def on_change_project(self, *args) :
812 """ Request for prohect change """
813 _args = self.__extract_signal_args(args)
814 for _key, _value in _args.iteritems() :
815- self.__project.change_project(_key, _value)
816+ self._project.change_project(_key, _value)
817
818 def on_close_project(self, *args ) :
819 """ Request for project close """
820 _args = self.__extract_signal_args(args)
821 # test if project exists
822- if self.__project.mode == CTRL_CONST.PROJECT_LOADED :
823+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
824 # check if project is modified
825- if self.__project.data['is_modified'] :
826+ if self._project.data['is_modified'] :
827 # ask for save it before close
828 _res = self.__gui.windows.\
829 question_message(_('Save Project before closing'))
830 if _res :
831- self.__project.save()
832+ self._project.save()
833
834 # set viewer in default mode
835 self._viewer.mode = self._viewer.DEFAULT
836
837 # clear project controller
838- self.__project.mode = CTRL_CONST.NO_PROJECT
839+ self._project.mode = CTRL_CONST.NO_PROJECT
840
841 # display close message
842- _msg = _('Project %s is closed' % self.__project.data['project_name'])
843+ _msg = _('Project %s is closed' % self._project.data['project_name'])
844 self.__gui.status_bar.display_message(_msg)
845
846
847 def on_create_project(self, *args) :
848 """ Request new project """
849 _args = self.__extract_signal_args(args)
850- self.__project.new()
851+ self._project.new()
852
853
854 def on_delete_capture(self, *args) :
855@@ -248,7 +262,7 @@
856 """ Request display project properties """
857 _args = self.__extract_signal_args(args)
858 # test if project exists
859- if self.__project.mode == CTRL_CONST.PROJECT_LOADED :
860+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
861 # go in default mode before dispalying window
862 # due to webcam detection and avoid confilcts with acquisition
863 self._viewer.mode = self._viewer.DEFAULT
864@@ -262,30 +276,30 @@
865 # launch project poperties window
866 _cbs = \
867 {'change-project-properties' : self.on_change_project_properties}
868- self.__gui.windows.project_properties(self.__project.data, _cbs)
869+ self.__gui.windows.project_properties(self._project.data, _cbs)
870
871
872 def on_export_to_tool(self, *args) :
873 """ Request export to tool """
874 _args = self.__extract_signal_args(args)
875 # test if project exists
876- if self.__project.mode == CTRL_CONST.PROJECT_LOADED :
877- self.__gui.windows.export_tool(self.__project.data)
878+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
879+ self.__gui.windows.export_tool(self._project.data)
880
881
882 def on_export_to_video(self, *args) :
883 """ Request export to video """
884 _args = self.__extract_signal_args(args)
885 # test if project exists
886- if self.__project.mode == CTRL_CONST.PROJECT_LOADED :
887- self.__gui.windows.export_video(self.__project.data)
888+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
889+ self.__gui.windows.export_video(self._project.data)
890
891
892 def on_import_image(self, *args) :
893 """ Request image import """
894 _args = self.__extract_signal_args(args)
895 # check if project exists
896- if self.__project.mode == CTRL_CONST.PROJECT_LOADED :
897+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
898 # open filename chooser dialog
899 _filenames = self.__gui.windows.import_dialog()
900
901@@ -293,7 +307,7 @@
902 _cbs = { 'done-import' : self.on_done_import}
903 # start import controller
904 CTRL_IMPORT.ImportController(_filenames,
905- self.__project.data,
906+ self._project.data,
907 self.__rusher,
908 self.__gui.status_bar,
909 _cbs)
910@@ -307,7 +321,7 @@
911 def on_move_down_chrono(self, *args) :
912 """ request to move down an image """
913 _args = self.__extract_signal_args(args)
914- if self.__project.mode == CTRL_CONST.PROJECT_LOADED :
915+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
916 self.__gui.treeviews[LCONST.CHRONO].move_down()
917
918
919@@ -326,40 +340,35 @@
920 def on_move_up_chrono(self, *args) :
921 """ request to move up an image """
922 _args = self.__extract_signal_args(args)
923- if self.__project.mode == CTRL_CONST.PROJECT_LOADED :
924+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
925 self.__gui.treeviews[LCONST.CHRONO].move_up()
926
927
928- def on_open_project(self, *args ) :
929- """ Request open project """
930+
931+
932+
933+ def on_play_query_position(self, *args) :
934+ """ Query for player position """
935 _args = self.__extract_signal_args(args)
936- if 'path' in _args :
937- if _args['path'] :
938- # path is givan as param
939- _path = _args['path']
940- else :
941- # launch open project window
942- _path = self.__gui.windows.open_project()
943- try :
944- self.__project.open(_path)
945- except LEXCEP.LucioException, _err_msg :
946- _msg = _("Project load impossible\n%s" % _err_msg)
947- self.__gui.windows.error_message( _msg)
948+ (_position, _duration) = self._viewer.play_query_position()
949+ if _args['cb'] :
950+ # callback call to gui
951+ _args['cb'](_position, _duration)
952
953
954 def on_play_video(self, *args) :
955 """ Play video request """
956 _args = self.__extract_signal_args(args)
957 # test if project exists
958- if self.__project.mode == CTRL_CONST.PROJECT_LOADED :
959+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
960 try :
961- self._viewer.init_video_player(self.__project.data)
962+ self._viewer.init_video_player(self._project.data)
963 except LEXCEP.LucioException, _err_msg :
964 raise LEXCEP.LucioException, _err_msg
965 else :
966 self._viewer.mode = self._viewer.PLAYER
967 try :
968- self._viewer.play_video(self.__project.data)
969+ self._viewer.play_video(self._project.data)
970 except LEXCEP.LucioException, _err_msg :
971 raise LEXCEP.LucioException, _err_msg
972 else :
973@@ -371,15 +380,15 @@
974 def on_quit_app(self, *args) :
975 """ Request quit application """
976 _args = self.__extract_signal_args(args)
977- if self.__project.mode == CTRL_CONST.PROJECT_LOADED and \
978- self.__project.data['is_modified'] :
979+ if self._project.mode == CTRL_CONST.PROJECT_LOADED and \
980+ self._project.data['is_modified'] :
981 _msg = _('Project modified. Save project before exit ?')
982 _res = self.__gui.windows.question_message(_msg)
983 # if response is not a bool : cancel clicked
984 if isinstance(_res, bool) :
985 if _res :
986 # if True save project before exit
987- self.__project.save()
988+ self._project.save()
989 GUI_CTRL.GuiController.quit()
990 else :
991 GUI_CTRL.GuiController.quit()
992@@ -387,45 +396,45 @@
993 def on_save_as_project(self, *args) :
994 """ save as project request """
995 _args = self.__extract_signal_args(args)
996- if self.__project.mode == CTRL_CONST.PROJECT_LOADED :
997+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
998 _dir = self.__gui.windows.dir_chooser()
999 if _dir != None :
1000 # set viewer in default mode due to project reload
1001 self._viewer.mode = self._viewer.DEFAULT
1002 try :
1003- self.__project.save_as(_dir)
1004+ self._project.save_as(_dir)
1005 except LEXCEP.LucioException, _err_msg :
1006 _msg = _('Impossible to save as project : %s' % _err_msg)
1007 self.__gui.windows.error_message(_msg)
1008 else :
1009 _msg = _('Project saved as %s' % \
1010- self.__project.data['project_name'])
1011+ self._project.data['project_name'])
1012 self.__gui.status_bar.display_message(_msg)
1013
1014
1015 def on_save_project(self, *args) :
1016 """ save project request """
1017 _args = self.__extract_signal_args(args)
1018- if self.__project.mode == CTRL_CONST.PROJECT_LOADED :
1019+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
1020 try :
1021- self.__project.save()
1022+ self._project.save()
1023 except LEXCEP.LucioException, _err_msg :
1024 _msg = _('Impossible to save project : %s' % _err_msg)
1025 self.__gui.windows.error_message(_msg)
1026 else :
1027 _msg = _('Project %s saved' % \
1028- self.__project.data['project_name'])
1029+ self._project.data['project_name'])
1030 self.__gui.status_bar.display_message(_msg)
1031 # update project name on Main bar
1032- self.__gui.set_program_bar(self.__project.data['project_name'],
1033- self.__project.data['is_modified'])
1034+ self.__gui.set_program_bar(self._project.data['project_name'],
1035+ self._project.data['is_modified'])
1036
1037
1038 def on_start_acquisition(self, *args ) :
1039 """Request for starting acqusisition """
1040 _args = self.__extract_signal_args(args)
1041 # check if a project is loaded
1042- if self.__project.mode == CTRL_CONST.PROJECT_LOADED :
1043+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
1044 self._viewer.mode = self._viewer.ACQUIRER
1045
1046 # change control widget in acquisition mode
1047@@ -441,7 +450,7 @@
1048 """ Request mixer start """
1049 _args = self.__extract_signal_args(args)
1050 # check if a project is loaded
1051- if self.__project.mode == CTRL_CONST.PROJECT_LOADED :
1052+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
1053 # switch to viewer with acqusisition and mixer
1054 self._viewer.mode = self._viewer.ACQUIRER_WITH_MIXER
1055 # change control widget in no acquisition mode
1056@@ -462,7 +471,7 @@
1057 """ Request to stop acquistion """
1058 _args = self.__extract_signal_args(args)
1059 # check if a project is loaded
1060- if self.__project.mode == CTRL_CONST.PROJECT_LOADED :
1061+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
1062 # switch to viewer default
1063 self._viewer.mode = self._viewer.DEFAULT
1064 # change control widget in no acquisition mode
1065@@ -478,7 +487,7 @@
1066 """ Request mixer stop """
1067 _args = self.__extract_signal_args(args)
1068 # check if a project is loaded
1069- if self.__project.mode == CTRL_CONST.PROJECT_LOADED :
1070+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
1071 # switch to viewer with acqusisition and mixer
1072 self._viewer.mode = self._viewer.ACQUIRER
1073 # change control widget in no acquisition mode
1074@@ -507,7 +516,7 @@
1075 def on_take_snapshot(self, *args) :
1076 """ take image snapshot request """
1077 _args = self.__extract_signal_args(args)
1078- if self.__project.mode == CTRL_CONST.PROJECT_LOADED :
1079+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
1080 self._viewer.take_snapshot()
1081 else :
1082 # robustness
1083@@ -558,10 +567,8 @@
1084 if self.__options.filename \
1085 and os.path.exists(self.__options.filename):
1086 # emit open project signal
1087- self.ctrl_signals.emit(
1088- "open-project",
1089- {'path' : self.__options.filename}
1090- )
1091+ self.__gui.emit(
1092+ "open-project", self.__options.filename)
1093
1094 GUI_CTRL.GuiController.main_loop()
1095 GUI_CTRL.GuiController.threads_leave()
1096@@ -572,32 +579,32 @@
1097 def on_done_rush(self, rusher) :
1098 """ callback : when tush images are loaded """
1099 if rusher != None \
1100- and self.__project.mode == CTRL_CONST.PROJECT_LOADED :
1101+ and self._project.mode == CTRL_CONST.PROJECT_LOADED :
1102 # load treeviews
1103 # prepare data
1104 _app_data = {}
1105- _app_data['capture_images'] = self.__project.data['capture_images']
1106- _app_data['chrono_images'] = self.__project.data['chrono_images']
1107+ _app_data['capture_images'] = self._project.data['capture_images']
1108+ _app_data['chrono_images'] = self._project.data['chrono_images']
1109 _app_data['rush'] = rusher
1110
1111 self.__gui.load_treeviews(_app_data)
1112
1113 # init acquisition
1114- self._viewer.init_acquisition(self.__project.data)
1115+ self._viewer.init_acquisition(self._project.data)
1116
1117
1118 # update fpi on gui and show it
1119- self.__gui.update_framerate(int(self.__project.data['fpi']))
1120+ self.__gui.update_framerate(int(self._project.data['fpi']))
1121
1122 # show project acquisition widgets
1123 self.__gui.control_widget.mode = GUI_CONST.ACQUISTION_INACTIVE
1124
1125 # update project name on Main bar
1126- self.__gui.set_program_bar(self.__project.data['project_name'],
1127- self.__project.data['is_modified'])
1128+ self.__gui.set_program_bar(self._project.data['project_name'],
1129+ self._project.data['is_modified'])
1130
1131 # finish project load (project aspect)
1132- self.__project.finish_project_load()
1133+ self._project.finish_project_load()
1134
1135 # store rusher
1136 self.__rusher = rusher
1137@@ -609,7 +616,7 @@
1138 callback : image snapshot is done.
1139 Now image can be processed into project
1140 """
1141- self.__project.append_snapshot(self.__rusher, self._viewer.acquirer)
1142+ self._project.append_snapshot(self.__rusher, self._viewer.acquirer)
1143
1144 def on_done_preferences(self , modified_options) :
1145 """ callback : preferences modified fome diaog """
1146@@ -632,17 +639,17 @@
1147 # Test if webcam data change key
1148 if key == 'webcam_data':
1149 # make a local copy of webcam_data
1150- webcam_dict = self.__project.data['webcam_data']
1151+ webcam_dict = self._project.data['webcam_data']
1152 #test if webcam dict key exists
1153 if webcam_dict.has_key(key_webcam) :
1154 # update webcam key and call project change
1155 webcam_dict[key_webcam] = data
1156- self.__project.change_project('webcam_data', webcam_dict)
1157+ self._project.change_project('webcam_data', webcam_dict)
1158
1159 def on_done_import(self, image_objs = [] ) :
1160 """ callback : image import (rush generation) is done """
1161 if image_objs != [] :
1162- self.__project.change_project( 'rush_images',
1163+ self._project.change_project( 'rush_images',
1164 self.__rusher.dump_image_name())
1165 # Not to be done in thread : interacts with gui
1166 for _image_obj in image_objs :
1167
1168=== added file 'luciole/ctrl/ctrl_acq.py'
1169--- luciole/ctrl/ctrl_acq.py 1970-01-01 00:00:00 +0000
1170+++ luciole/ctrl/ctrl_acq.py 2011-02-28 13:21:06 +0000
1171@@ -0,0 +1,634 @@
1172+#!/usr/bin/env python
1173+# -*- coding: utf-8 -*-
1174+# -*- Mode: Python -*-
1175+# vim:si:ai:et:sw=4:sts=4:ts=4
1176+#
1177+#
1178+# Copyright Nicolas Bertrand (nico@inattendu.org), 2009-2010
1179+#
1180+# This file is part of Luciole.
1181+#
1182+# Luciole is free software: you can redistribute it and/or modify
1183+# it under the terms of the GNU General Public License as published by
1184+# the Free Software Foundation, either version 3 of the License, or
1185+# (at your option) any later version.
1186+#
1187+# Luciole is distributed in the hope that it will be useful,
1188+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1189+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1190+# GNU General Public License for more details.
1191+#
1192+# You should have received a copy of the GNU General Public License
1193+# along with Luciole. If not, see <http://www.gnu.org/licenses/>.
1194+#
1195+#
1196+"""
1197+ctrl_acq.py :
1198+ manages acquisition
1199+"""
1200+# standard library imports
1201+import os.path
1202+
1203+# related third party imports
1204+import gst
1205+from pitivi.signalinterface import Signallable
1206+from pitivi.log.loggable import Loggable
1207+from pitivi.signalgroup import SignalGroup
1208+from pitivi.action import ViewAction
1209+from pitivi.pipeline import Pipeline
1210+
1211+# local application/library specific imports
1212+from luciole.media.lgst.webcam_factory import WebcamSourceFactory
1213+import luciole.constants as LCONST
1214+import luciole.base.exceptions as LEXCEP
1215+import luciole.base.tools as LT
1216+import luciole.media.image as LI
1217+
1218+from luciole.ctrl.base import ProjectMode
1219+
1220+class ProjectModeAcquisition(ProjectMode) :
1221+ MODES = ( "INVALID",
1222+ "NO_ACQUISITION",
1223+ "ACQUISITION",
1224+ "ACQUISITION_WITH_MIXER",
1225+ )
1226+
1227+ __signals__ = {
1228+ 'acquirer-ready' : [],
1229+ 'acquirer-error' : ['msg'],
1230+ 'acquisition-started' : ['with_mixer'],
1231+ 'acquisition-stopped' : [],
1232+ 'snapshot-on-progress' : [],
1233+ 'snapshot-taken' : ['rush_image'],
1234+ }
1235+
1236+ def __init__(self, ctrl_project) :
1237+ self._mode ="INVALID"
1238+ ProjectMode.__init__(self, ctrl_project, "ACQUISITION")
1239+ self.gui_signals = SignalGroup()
1240+
1241+ def prepare(self, project) :
1242+ """ prepare acquistion """
1243+ ProjectMode.prepare(self, project)
1244+ # prepare acqusisition
1245+ _project = project.props
1246+ if _project != None :
1247+ if _project['hardtype'] == LCONST.WEBCAM :
1248+ # create webcam source
1249+ _capture_path = os.path.join(
1250+ _project['project_dir'],
1251+ LCONST.ACQUIRED_IMAGE_NAME)
1252+ self.debug ("Image capture path : %s", _capture_path)
1253+ self._source_factory = \
1254+ WebcamSourceFactory(capture_path = _capture_path,
1255+ with_mixer = False)
1256+
1257+ self.debug(' webcam filter : %s',self.project.props['webcam_data'])
1258+ self._set_webcam_params(
1259+ self.project.props['webcam_data'],
1260+ self._source_factory)
1261+
1262+ # set video resize/scale mode (or not)
1263+ if _project['resize'] == 'yes' :
1264+ self._source_factory.with_scale = True
1265+ else :
1266+ self._source_factory.with_scale = False
1267+
1268+ self._connect_to_factory(self._source_factory, _capture_path )
1269+
1270+ # create pipeline and resource
1271+ self._pipeline = Pipeline()
1272+ self._view_action = ViewAction()
1273+ self._view_action.addProducers(self._source_factory)
1274+
1275+ self._mode = "NO_ACQUISITION"
1276+ self.debug('emit acquirer-ready, Webcam factory : %s, pipeline : %s',
1277+ self._source_factory, self._pipeline )
1278+ self.emit('acquirer-ready')
1279+
1280+
1281+ def release(self) :
1282+ ProjectMode.release(self)
1283+ self.debug('releasing acquisition')
1284+ self._disconnect_from_factory(self._source_factory)
1285+ self._mode = "INVALID"
1286+ self._pipeline.release()
1287+ self._pipeline = None
1288+ self._source_factory = None
1289+ self._view_action = None
1290+
1291+ def active_viewer(self) :
1292+ if self._viewer :
1293+ self._viewer.setPipeline(None)
1294+ self._viewer.hideSlider()
1295+ self.debug('active viewer - Pipeline : %s, action %s',
1296+ self._pipeline,
1297+ self._view_action)
1298+ self._viewer.setAction(self._view_action)
1299+ self._viewer.setPipeline(self._pipeline, "ACQUISITION")
1300+ else :
1301+ self.error("No viewer set")
1302+
1303+
1304+ def start(self, with_mixer = False) :
1305+ """ start acquisition """
1306+ # check if image view mode
1307+ # in that case restart acquisition mode
1308+ if self.ctrl_project.mode == 'IMAGE_VIEW' :
1309+ self.ctrl_project.acquistion_mode()
1310+ return
1311+
1312+ if self._mode == "NO_ACQUISITION" :
1313+ if with_mixer == True :
1314+ self._source_factory.active_mixer(True)
1315+ self._mode = "ACQUISITION_WITH_MIXER"
1316+ else :
1317+ #self._source_factory.active_mixer(False)
1318+ self._mode = "ACQUISITION"
1319+
1320+ self._pipeline.play()
1321+ self.debug('send acquisition-started - mode : %s',self._mode )
1322+ self.emit('acquisition-started', with_mixer)
1323+
1324+
1325+ else :
1326+ self.error("Cannot start acquisition. Controller in incorrect mode (%s)",
1327+ self._mode)
1328+
1329+ def stop(self) :
1330+ """ stop acquisition """
1331+ self.debug('stop acquisition - mode %s', self._mode)
1332+ if self._mode in ("ACQUISITION", "ACQUISITION_WITH_MIXER") :
1333+ if self._mode == "ACQUISITION_WITH_MIXER" :
1334+ pass
1335+ #self.acquirer.deactive_onion_skin()
1336+ self._pipeline.stop()
1337+ self._mode = "NO_ACQUISITION"
1338+ self.debug('emit acquisition-stopped')
1339+ self.emit('acquisition-stopped')
1340+ else :
1341+ self.error(
1342+ "Cannot stop acquistion. Controller in incorrect mode (%s)",
1343+ self._mode)
1344+
1345+
1346+ def do_snapshot(self) :
1347+ if self._mode in ("ACQUISITION", "ACQUISITION_WITH_MIXER") :
1348+ self._source_factory.capture()
1349+
1350+ else :
1351+ self.error(
1352+ "Cannot take snapshot. Controller in incorrect mode (%s)",
1353+ self._mode)
1354+
1355+ def active_mixer(self, is_active) :
1356+ # check with current mixer status
1357+ self.debug('active_mixer : %s', is_active)
1358+ if self.project.props['is-mixer-active'] == is_active :
1359+ self.info('No mixer status to change')
1360+ return
1361+
1362+
1363+
1364+ # stop pipeline, deactivate producer and remove old webcam factory
1365+ self._pipeline.stop()
1366+ self._view_action.deactivate()
1367+ self._view_action.removeProducers(self._source_factory)
1368+
1369+ # create new webcam factory
1370+ _capture_path = os.path.join(
1371+ self.project.props['project_dir'],
1372+ LCONST.ACQUIRED_IMAGE_NAME)
1373+ self.debug ("Image capture path : %s", _capture_path)
1374+
1375+ self._source_factory = WebcamSourceFactory(
1376+ capture_path = _capture_path,
1377+ with_mixer = is_active,
1378+ image2mix = self.project.props['image2mix'] )
1379+ # set video resize/scale mode (or not)
1380+ if self.project.props['resize'] == 'yes' :
1381+ self._source_factory.with_scale = True
1382+ else :
1383+ self._source_factory.with_scale = False
1384+
1385+
1386+ # add producer
1387+ self._view_action.addProducers(self._source_factory)
1388+ # activate it --> regeneration of pipeline bins
1389+ self._view_action.activate()
1390+ # set image alpha if webcam active
1391+ if is_active == True :
1392+ self._source_factory.alpha_value = self.project.props['alpha']
1393+
1394+ self.debug(' image to mix is %s with alpha = %s',
1395+ self._source_factory.image2mix,
1396+ self._source_factory.alpha_value)
1397+
1398+ # start acquisition
1399+ self._pipeline.play()
1400+
1401+ self.project.props['is-mixer-active'] = is_active
1402+
1403+ def _connect_to_factory(self, factory, _capture_path) :
1404+ factory.connect("image-capture-done", self._image_capture_done_cb, _capture_path )
1405+
1406+ def _disconnect_from_factory(self, factory) :
1407+ factory.disconnect_by_function(self._image_capture_done_cb)
1408+
1409+ def _image_capture_done_cb( self, factory, _capture_path) :
1410+ self.debug('RX image-capture-done')
1411+ _rush_image = self._process_snapshot(_capture_path)
1412+ self.debug('emit snapshot-taken : %s', _rush_image.name)
1413+ self.emit('snapshot-taken', _rush_image)
1414+
1415+ def _process_snapshot(self, capture_path) :
1416+ # get acquired image name
1417+ _acq_image = capture_path
1418+
1419+ # build temp impage path
1420+ _temp_dir = os.path.join(self.project.props['project_dir'], 'tmp')
1421+ # copy name
1422+ _ac_image_temp = os.path.join(_temp_dir, _acq_image)
1423+ # resized copy name
1424+ _ac_image_temp_rz = \
1425+ os.path.join(_temp_dir, LCONST.ACQUIRED_IMAGE_NAME_RZ)
1426+
1427+ # build rush image name
1428+ _basename = LCONST.RUSH_FILENAME_TPL % self.project.rush.rush_index
1429+ _rush_image = \
1430+ os.path.join(self.project.props['project_dir'],
1431+ self.project.props['rush_dir'],
1432+ _basename)
1433+
1434+ try :
1435+ # 1. move image acquired image to tmp dir
1436+ LT.movef(_acq_image, _ac_image_temp)
1437+ # 2. resize image result is in _ac_image_temp_rz
1438+ if self.project.props['resize'] == 'yes' :
1439+ _rz_obj = LI.ImageResize(_ac_image_temp, _ac_image_temp_rz )
1440+ _rz_obj.convert()
1441+ else :
1442+ _ac_image_temp_rz = _ac_image_temp
1443+
1444+ # 3. move resized image to rush dire
1445+ LT.movef(_ac_image_temp_rz, _rush_image)
1446+
1447+ except LEXCEP.LucioException, _err_msg :
1448+ self.error(_err_msg)
1449+ else :
1450+ self.debug("create rush image : %s", _basename)
1451+ # 4. append image to rush list
1452+ _rush_image = self.project.rush.append(_basename)
1453+
1454+ # always update the image 2 mix, even if mixer is not active
1455+ # used to memorize the last capture
1456+ #self.acquirer.Image2Mix = _rush_image.path
1457+
1458+ # project modified
1459+ self.project.props['rush_images'].append(_basename)
1460+ self.project.props['capture_images'].append(_basename)
1461+
1462+ self.ctrl_project.is_modified = True
1463+
1464+ self.debug("capture post-treatemnt done ")
1465+ return _rush_image
1466+
1467+ def _set_webcam_params(self, webcam_props, factory) :
1468+ """
1469+ set webcam parameters according project propperties
1470+ """
1471+ #retrieve params value
1472+ _wcam_params = {
1473+ 'wcam_driver': {
1474+ 'element': webcam_props['v4ldriver'] ,
1475+ 'properties' : {
1476+ 'device':webcam_props['device-file'],
1477+ },
1478+ },
1479+
1480+ 'wcam_filter' : {
1481+ 'media_type' :gst.Caps(webcam_props['caps']),
1482+ },
1483+ }
1484+
1485+ # set params on factory
1486+ factory.webcam_params = _wcam_params
1487+ self.debug(" webcam params set : %s", factory.webcam_params)
1488+
1489+
1490+
1491+
1492+
1493+
1494+# TODO : suppress CtrlAcq
1495+
1496+class CtrlAcq(Signallable, Loggable) :
1497+ MODES = ( "INVALID",
1498+ "NO_ACQUISITION",
1499+ "ACQUISITION",
1500+ "ACQUISITION_WITH_MIXER",
1501+ )
1502+
1503+
1504+ __signals__ = {
1505+ 'acquirer-ready' : ['acquirer'],
1506+ 'acquirer-error' : ['msg'],
1507+ 'acquisition-started' : ['with_mixer'],
1508+ 'acquisition-stopped' : [],
1509+ 'snapshot-on-progress' : [],
1510+ 'snapshot-taken' : ['rush_image'],
1511+ }
1512+
1513+
1514+ def __init__(self, ctrl_project) :
1515+ """ initialize the acquirer """
1516+ Signallable.__init__(self)
1517+ Loggable.__init__(self)
1518+ self.ctrl_project = ctrl_project
1519+
1520+ self.acquirer = None
1521+ self.project = None
1522+ self._mode = "INVALID"
1523+
1524+ # signal connections
1525+ self._connect_to_project(self.ctrl_project)
1526+
1527+ self.gui_signals = SignalGroup()
1528+
1529+ self.debug('CtrlAcq intialized : %s %s',self.ctrl_project, ctrl_project )
1530+
1531+
1532+ def init_acquirer(self, project) :
1533+ self.debug("Init acquirer")
1534+ _project = project.props
1535+ if _project != None :
1536+ # Create acquisition object
1537+ _acq_obj = None
1538+ # remark; acquisition object for webcam and dv cam
1539+ if _project['hardtype'] == LCONST.WEBCAM :
1540+ # init webcam factory
1541+ self.factory = WebcamSourceFactory()
1542+ self.debug('Webcam factory : %s', self.factory )
1543+ self.pipeline = PTV_PPLN.Pipeline()
1544+ self.view_action = ViewAction()
1545+ self.view_action.addProducers(self.factory)
1546+
1547+ self.viewer = self.ctrl_project.gui._get_drawarea()
1548+ self.viewer.hideSlider()
1549+ self.viewer.setPipeline(None)
1550+ self.viewer.setAction(self.view_action)
1551+ self.viewer.setPipeline(self.pipeline, "ACQUISITION")
1552+ #self.viewer.play()
1553+ # just for debug
1554+ _acq_obj =self.pipeline
1555+
1556+
1557+
1558+ elif _project['hardtype'] == LCONST.DVCAM :
1559+ # default acquisition load i.e. DVCAM
1560+ _acq_obj = LACQ.Acquisition(
1561+ self._get_drawarea(self.ctrl_project.gui),
1562+ False,
1563+ _project['hardtype'],
1564+ project_dir = _project['project_dir'],
1565+ cb_error = self._on_error_acqusition,
1566+ cb_capture_done = self._on_done_snaphot)
1567+ else :
1568+ # Nothing to do
1569+ self.emit('acquirer-error', 'No valid acquirer found' )
1570+ if _acq_obj != None :
1571+ self._mode = "NO_ACQUISITION"
1572+ # for mixer initialisation set image to mix with the last image
1573+ # of the capture view. only if capture image is not empty
1574+ if len(_project['capture_images'] ) > 0 :
1575+ _last_image_path = \
1576+ os.path.join( _project['project_dir'],
1577+ _project['rush_dir'],
1578+ _project['capture_images'][-1])
1579+ if os.path.exists(_last_image_path) :
1580+ _acq_obj.Image2Mix = _last_image_path
1581+
1582+ self.emit('acquirer-ready', _acq_obj )
1583+ self.acquirer = _acq_obj
1584+
1585+
1586+ def start_acquisition(self) :
1587+ if self._mode == "NO_ACQUISITION" :
1588+ self.pipeline.play()
1589+ self._mode = "ACQUISITION"
1590+ self.emit('acquisition-started', False)
1591+ else :
1592+ self.error(
1593+ "Cannot start acquisition. Controller in incorrect mode (%s)",
1594+ self._mode)
1595+
1596+ def stop_acquisition(self) :
1597+ if self._mode in ("ACQUISITION", "ACQUISITION_WITH_MIXER") :
1598+ if self._mode == "ACQUISITION_WITH_MIXER" :
1599+ pass
1600+ #self.acquirer.deactive_onion_skin()
1601+ self.pipeline.stop()
1602+
1603+ self._mode = "NO_ACQUISITION"
1604+ self.debug('emit acquisition-stopped')
1605+ self.emit('acquisition-stopped')
1606+ else :
1607+ self.error(
1608+ "Cannot stop acquistion. Controller in incorrect mode (%s)",
1609+ self._mode)
1610+
1611+ def active_mixer(self) :
1612+ if self._mode == "ACQUISITION" :
1613+ # the active onion skin stop the acquistion
1614+ self.acquirer.active_onion_skin()
1615+ # so start acqusition
1616+ self.acquirer.start_acqusition()
1617+
1618+ self._mode = "ACQUISITION_WITH_MIXER"
1619+ self.emit('acquisition-started', True)
1620+ else :
1621+ self.error(
1622+ "Cannot active mixer. Controller in incorrect mode (%s)",
1623+ self._mode)
1624+
1625+
1626+ def deactive_mixer(self) :
1627+ if self._mode == "ACQUISITION_WITH_MIXER" :
1628+ # deactivate onion skin stop the acquistion
1629+ self.acquirer.deactive_onion_skin()
1630+ # so start acqusition
1631+ self.acquirer.start_acqusition()
1632+
1633+ self._mode = "ACQUISITION"
1634+ self.emit('acquisition-started', False)
1635+ else :
1636+ self.error(
1637+ "Cannot deactive mixer. Controller in incorrect mode (%s)",
1638+ self._mode)
1639+
1640+ def take_snapshot(self) :
1641+ if self._mode in ("ACQUISITION", "ACQUISITION_WITH_MIXER") :
1642+ if self.acquirer.is_streaming_active == True :
1643+
1644+ self.debug('emit snapshot-on-progress')
1645+ self.emit('snapshot-on-progress')
1646+ self.acquirer.capture_image()
1647+
1648+
1649+
1650+ def release(self) :
1651+ # ensure acquisition stop
1652+ self.debug('Enterin release in mode=%s', self._mode)
1653+ if self._mode in ("ACQUISITION","ACQUISITION_WITH_MIXER") :
1654+ self.debug('Force acquistion stop : %s', self._mode)
1655+ self.stop_acquisition()
1656+ else :
1657+ self.debug('Bad state : %s', self._mode)
1658+ self.acquirer.release()
1659+
1660+ #self._disconnect_from_project(self.ctrl_project)
1661+ #self._disconnect_from_gui(self.ctrl_project.gui)
1662+ #self.gui_signals.disconnectAll()
1663+
1664+ def _connect_to_project(self, ctrl_project) :
1665+ ctrl_project.connect('project-loaded', self._project_loaded_cb)
1666+ ctrl_project.connect('project-closed', self._project_closed_cb)
1667+ """
1668+ def _disconnect_from_project(self, ctrl_project) :
1669+ ctrl_project.disconnect_by_function(self._project_loaded_cb)
1670+ ctrl_project.disconnect_by_function(self._project_closed_cb)
1671+ """
1672+ def _project_closed_cb(self, project_ctrl) :
1673+ self.debug('RX project-closed')
1674+ self.release()
1675+
1676+
1677+ def _project_loaded_cb(self, project_ctrl, project) :
1678+ self.debug('RX project-loaded')
1679+ self.init_acquirer(project)
1680+ self._connect_to_gui(self.ctrl_project.gui)
1681+ self.pipeline.play()
1682+ self.project = project
1683+
1684+ """"
1685+ def _connect_to_gui(self, gui) :
1686+ # connection GUI to ctrl_acq
1687+ gui.connect('start-acqusition', self._start_acq_cb)
1688+ gui.connect('stop-acqusition', self._stop_acq_cb)
1689+ gui.connect('take-snapshot', self._take_snapshot_cb)
1690+ gui.connect('active-mixer', self._active_mixer_cb)
1691+ gui.connect('deactive-mixer', self._deactive_mixer_cb)
1692+
1693+ # connection ctrl_acq to gui
1694+ gui._project_mode.connect_to_ctrl('ACQUISITION', self)
1695+ """
1696+
1697+ def _connect_to_gui(self, gui) :
1698+ self.gui_signals.connect(
1699+ gui, "start-acqusition", None, self._start_acq_cb)
1700+ self.gui_signals.connect(
1701+ gui, "stop-acqusition", None, self._stop_acq_cb)
1702+ self.gui_signals.connect(
1703+ gui, "take-snapshot", None, self._take_snapshot_cb)
1704+ self.gui_signals.connect(
1705+ gui, "active-mixer", None, self._active_mixer_cb)
1706+ self.gui_signals.connect(
1707+ gui, "deactive-mixer", None, self._deactive_mixer_cb)
1708+
1709+ # connection ctrl_acq to gui
1710+ gui._project_mode.connect_to_ctrl('ACQUISITION', self)
1711+
1712+
1713+ def _disconnect_from_gui(self,gui) :
1714+ # dicconnection GUI to ctrl_acq
1715+ gui.disconnect_by_function(self._start_acq_cb)
1716+ gui.disconnect_by_function(self._stop_acq_cb)
1717+ gui.disconnect_by_function(self._take_snapshot_cb)
1718+ gui.disconnect_by_function(self._active_mixer_cb)
1719+ gui.disconnect_by_function(self._deactive_mixer_cb)
1720+
1721+
1722+ def _start_acq_cb(self, gui) :
1723+ if self.ctrl_project.mode == 'ACQUISITION' :
1724+ self.start_acquisition()
1725+
1726+ def _stop_acq_cb(self, gui) :
1727+ if self.ctrl_project.mode == 'ACQUISITION' :
1728+ self.stop_acquisition()
1729+
1730+ def _take_snapshot_cb(self, gui) :
1731+ if self.ctrl_project.mode == 'ACQUISITION' :
1732+ self.take_snapshot()
1733+
1734+ def _active_mixer_cb(self, gui) :
1735+ self.active_mixer()
1736+
1737+ def _deactive_mixer_cb(self, gui) :
1738+ self.deactive_mixer()
1739+
1740+ def _on_error_acqusition(self, message) :
1741+ """
1742+ callback error
1743+ propagate it to main controller.
1744+ """
1745+ print " acqusistion error\n ", message
1746+ self.emit('acquirer-error', message)
1747+
1748+ def _on_done_snaphot(self, *unsued) :
1749+ """
1750+ callback when snaphot done.
1751+ """
1752+ _rush_image = self._process_snapshot()
1753+ self.debug('emit snapshot-taken : %s', _rush_image.name)
1754+ self.emit('snapshot-taken', _rush_image)
1755+
1756+ def _process_snapshot(self) :
1757+ # get acquired image name
1758+ _acq_image = self.acquirer.image2save
1759+
1760+ # build temp impage path
1761+ _temp_dir = os.path.join(self.project.props['project_dir'], 'tmp')
1762+ # copy name
1763+ _ac_image_temp = os.path.join(_temp_dir, _acq_image)
1764+ # resized copy name
1765+ _ac_image_temp_rz = \
1766+ os.path.join(_temp_dir, LCONST.ACQUIRED_IMAGE_NAME_RZ)
1767+
1768+ # build rush image name
1769+ _basename = LCONST.RUSH_FILENAME_TPL % self.project.rush.rush_index
1770+ _rush_image = \
1771+ os.path.join(self.project.props['project_dir'],
1772+ self.project.props['rush_dir'],
1773+ _basename)
1774+
1775+ try :
1776+ # 1. move image acquired image to tmp dir
1777+ LT.movef(_acq_image, _ac_image_temp)
1778+
1779+ # 2. resize image result is in _ac_image_temp_rz
1780+ _rz_obj = LI.ImageResize(_ac_image_temp, _ac_image_temp_rz )
1781+ _rz_obj.convert()
1782+
1783+ # 3. move resized image to rush dire
1784+ LT.movef(_ac_image_temp_rz, _rush_image)
1785+
1786+ except LEXCEP.LucioException, _err_msg :
1787+ self.error(_err_msg)
1788+ else :
1789+ # 4. append image to rush list
1790+ _rush_image = self.project.rush.append(_basename)
1791+
1792+ # always update the image 2 mix, even if mixer is not active
1793+ # used to memorize the last capture
1794+ self.acquirer.Image2Mix = _rush_image.path
1795+
1796+ # project modified
1797+ self.ctrl_project.is_modified = True
1798+
1799+ return _rush_image
1800+
1801+ def _get_drawarea(self, gui) :
1802+ _da = gui._get_drawarea()
1803+ self.debug(" Drawarea used : %s", _da)
1804+ return _da
1805+
1806
1807=== added file 'luciole/ctrl/ctrl_app.py'
1808--- luciole/ctrl/ctrl_app.py 1970-01-01 00:00:00 +0000
1809+++ luciole/ctrl/ctrl_app.py 2011-02-28 13:21:06 +0000
1810@@ -0,0 +1,1050 @@
1811+#!/usr/bin/env python
1812+# -*- coding: utf-8 -*-
1813+# -*- Mode: Python -*-
1814+# vim:si:ai:et:sw=4:sts=4:ts=4
1815+#
1816+#
1817+# Copyright Nicolas Bertrand (nico@inattendu.org), 2009-2010
1818+#
1819+# This file is part of Luciole.
1820+#
1821+# Luciole is free software: you can redistribute it and/or modify
1822+# it under the terms of the GNU General Public License as published by
1823+# the Free Software Foundation, either version 3 of the License, or
1824+# (at your option) any later version.
1825+#
1826+# Luciole is distributed in the hope that it will be useful,
1827+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1828+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1829+# GNU General Public License for more details.
1830+#
1831+# You should have received a copy of the GNU General Public License
1832+# along with Luciole. If not, see <http://www.gnu.org/licenses/>.
1833+#
1834+#
1835+"""
1836+ctrl.py :
1837+ Main application controller
1838+"""
1839+# standard library imports
1840+import sys
1841+import os
1842+import os.path
1843+import locale
1844+import gettext
1845+_ = gettext.gettext
1846+
1847+# related third party imports
1848+import gobject
1849+from pitivi.log import log
1850+
1851+from pitivi.signalinterface import Signallable
1852+from pitivi.log.loggable import Loggable
1853+from pitivi.log import log
1854+
1855+# local application/library specific imports
1856+import luciole.base.app_logging as LOGGING
1857+import luciole.base.exceptions as LEXCEP
1858+import luciole.base.options as OPTIONS
1859+import luciole.conf as LCONF
1860+import luciole.constants as LCONST
1861+import luciole.ctrl.constants as CTRL_CONST
1862+import luciole.ctrl.import_image as CTRL_IMPORT
1863+import luciole.ctrl.ctrl_project as CTRL_PROJECT
1864+import luciole.gui.constants as GUI_CONST
1865+import luciole.gui.main_window as GUI_MAIN
1866+import luciole.gui.gui_ctrl as GUI_CTRL
1867+import luciole.info as INFO
1868+import luciole.media.lgst.play_sound as LSOUND
1869+#####
1870+
1871+
1872+
1873+class CtrlAppGui(Signallable, Loggable) :
1874+ """ Interface between gui and App"""
1875+
1876+ def __init__(self, app) :
1877+ Signallable.__init__(self)
1878+ Loggable.__init__(self)
1879+ self.app = app
1880+ self.project_ctrl = self.app.project_ctrl
1881+ self.gui = self.app.gui
1882+ self._connect_gui_and_project()
1883+
1884+ def _connect_gui_and_project(self) :
1885+
1886+ # application high level actions
1887+ self.gui.gui_actions.connect('open-project', self._open_project_cb)
1888+ self.gui.gui_actions.connect('new-project', self._new_project_cb)
1889+ self.gui.gui_actions.connect('quit-app', self._quit_cb)
1890+
1891+ # Project level actions
1892+ self.gui.gui_actions.connect('save-project', self._save_project_cb)
1893+ self.gui.gui_actions.connect('save-as-project', self._save_as_project_cb)
1894+ self.gui.gui_actions.connect('close-project', self._close_project_cb)
1895+ self.gui.gui_actions.connect('acquisition-mode', self._acq_mode_cb)
1896+ self.gui.gui_actions.connect('timeline-mode', self._timeline_mode_cb)
1897+ self.gui.gui_actions.connect('import-sound', self._import_sound_cb)
1898+ self.gui.gui_actions.connect('image-view', self._image_view_cb)
1899+ self.gui.gui_actions.connect('import-images', self._import_images_cb)
1900+ self.gui.gui_actions.connect('show-project-properties', self._show_project_properties_cb)
1901+
1902+
1903+ # Acqusition actions
1904+ self.gui.gui_actions.connect('start-acquistion', self._start_acq_cb)
1905+ self.gui.gui_actions.connect('stop-acquistion', self._stop_acq_cb)
1906+ self.gui.gui_actions.connect('take-snapshot', self._take_snapshot_cb)
1907+ self.gui.gui_actions.connect('active-mixer', self._active_mixer_cb)
1908+
1909+ # Timeline actions
1910+ self.gui.gui_actions.connect('change-timeline-order', self._change_timeline_order_cb)
1911+ self.gui.gui_actions.connect('insert-image-on-timeline', self._insert_image_on_timeline_cb)
1912+ self.gui.gui_actions.connect('remove-image-on-timeline', self._remove_image_on_timeline_cb)
1913+ self.gui.gui_actions.connect('active-sound', self._active_sound_cb)
1914+ self.gui.gui_actions.connect('deactive-sound', self._deactive_sound_cb)
1915+ self.gui.gui_actions.connect('sound-stop-with-video', self._sound_stop_with_video_cb)
1916+ self.gui.gui_actions.connect('change-framerate', self._change_framerate_cb)
1917+
1918+
1919+
1920+
1921+ #
1922+ # application high level callbacks
1923+ #
1924+
1925+ def _open_project_cb(self, gui, path) :
1926+ self.debug('RX close-project signal')
1927+ self.project_ctrl.open(path)
1928+
1929+ def _new_project_cb(self, gui ) :
1930+ self.debug('RX new-project signal' )
1931+ self.project_ctrl.new_project()
1932+
1933+
1934+ def _quit_cb(self, gui):
1935+ self.app.quit()
1936+ return True
1937+ #
1938+ # Project level callbacks
1939+ #
1940+ def _save_project_cb(self, gui) :
1941+ self.debug('RX save-project signal')
1942+ self.project_ctrl.save()
1943+
1944+ def _save_as_project_cb(self, gui, path) :
1945+ self.debug('RX save-as-project signal')
1946+ self.project_ctrl.save(path)
1947+
1948+
1949+ def _close_project_cb(self, gui) :
1950+ self.debug('RX close-project signal')
1951+ self.project_ctrl.close()
1952+
1953+
1954+ def _acq_mode_cb(self, gui) :
1955+ self.debug('RX acquisition-mode signal')
1956+ self.project_ctrl.acquisition_mode()
1957+
1958+ def _timeline_mode_cb(self, gui) :
1959+ self.debug('RX timeline-mode signal')
1960+ self.project_ctrl.timleline_mode()
1961+
1962+ def _import_sound_cb(self, gui, path) :
1963+ self.debug('RX import-sound signal : %s', path)
1964+ self.project_ctrl.ctrl_tmln.add_sound_track(path)
1965+
1966+ def _image_view_cb(self, gui, origin, image_name, position = 0) :
1967+ self.debug('RX image-view signal : origin %s image %s', origin, image_name)
1968+ self.project_ctrl.img_vw_mode(origin, image_name, position)
1969+
1970+ def _import_images_cb(self, gui, paths) :
1971+ self.debug('RX import-images signal : %s',paths )
1972+ self.project_ctrl.import_images(paths)
1973+
1974+ def _show_project_properties_cb(self, gui) :
1975+ """ show project properties callback """
1976+ self.debug('RX show-project-properties' )
1977+ self.project_ctrl.show_project_properties()
1978+
1979+
1980+
1981+ #
1982+ # Acqusition callbacks
1983+ #
1984+
1985+ def _start_acq_cb(self, gui) :
1986+ self.debug(' RX start-acquistion signal')
1987+ self.project_ctrl.ctrl_acq.start()
1988+
1989+ def _stop_acq_cb(self, gui) :
1990+ self.debug(' RX stop-acquistion signal')
1991+ self.project_ctrl.ctrl_acq.stop()
1992+
1993+ def _take_snapshot_cb(self, gui) :
1994+ self.debug('RX take-snapshot signal')
1995+ self.project_ctrl.ctrl_acq.do_snapshot()
1996+
1997+ def _active_mixer_cb(self, gui, is_active) :
1998+ self.debug('RX active-mixer signal : %s', is_active)
1999+ self.project_ctrl.ctrl_acq.active_mixer(is_active)
2000+
2001+ #
2002+ # Timeline callbacks
2003+ #
2004+ def _change_timeline_order_cb(self, gui, objs, target):
2005+ self.debug('RX change-timeline-order signal - objs %s target %s', objs, target)
2006+ self.project_ctrl.ctrl_tmln.change_order(objs, target)
2007+
2008+ def _insert_image_on_timeline_cb(self, gui, objs, target):
2009+ self.debug('RX insert-image-on-timeline signal - objs %s target %s', objs, target)
2010+ self.project_ctrl.ctrl_tmln.insert(objs, target)
2011+
2012+ def _remove_image_on_timeline_cb(self, gui, objs):
2013+ self.debug('RX remove-image-on-timeline signal - objs %s', objs)
2014+ self.project_ctrl.ctrl_tmln.remove(objs)
2015+
2016+ def _active_sound_cb(self, gui) :
2017+ self.debug('RX active-sound signal')
2018+ self.project_ctrl.ctrl_tmln.add_sound_track_to_timeline()
2019+
2020+ def _deactive_sound_cb(self, gui) :
2021+ self.debug('RX deactive-sound signal')
2022+ self.project_ctrl.ctrl_tmln.remove_sound_track_from_timeline()
2023+
2024+ def _sound_stop_with_video_cb(self, gui, is_stop) :
2025+ self.debug('RX sound-stop-with-video, is_stop %s', is_stop)
2026+ self.project_ctrl.ctrl_tmln.stop_sound_with_video(is_stop)
2027+
2028+ def _change_framerate_cb(self, gui, fpi) :
2029+ self.debug('RXchange-framerate, fpi %s', fpi)
2030+ self.project_ctrl.set_framerate(fpi)
2031+
2032+class CtrlApp(Signallable, Loggable) :
2033+ # CONSTANTS
2034+ MODES = ("INIT", "RUN", "QUIT")
2035+
2036+ def __init__(self) :
2037+ """
2038+ Controller initialisation.
2039+ Is the main appication intialisation
2040+ """
2041+ # initialize threads
2042+ gobject.threads_init()
2043+
2044+ Loggable.__init__(self)
2045+
2046+ self.mode = "INIT"
2047+ # init logging as early as possible so we can log startup code
2048+ enable_color = os.environ.get('PITIVI_DEBUG_NO_COLOR', '0') in ('', '0')
2049+ log.init('PITIVI_DEBUG', enable_color)
2050+
2051+ self.mainloop = gobject.MainLoop()
2052+
2053+ # initializes i18n
2054+ self._init_i18n()
2055+
2056+ # load configuration : ie. saved user options
2057+ self.configuration = LCONF.LucioleConf()
2058+ # load GUI theme i.e parse rc file
2059+ self.configuration.load_theme()
2060+
2061+
2062+
2063+
2064+ def run(self, args) :
2065+
2066+ # TODO : seems no more needed : check
2067+ import gtk.gdk
2068+ gtk.gdk.threads_enter()
2069+
2070+
2071+ # intializes options :
2072+ (_options, _args) = OPTIONS.options_parser(args)
2073+
2074+ # initialize sound player i.e from snapshot sound
2075+ self.soundplayer = LSOUND.SoundPlayer(LCONST.SOUND_SNAPSHOT_PATH)
2076+
2077+ # start Project controller
2078+ self.project_ctrl = CTRL_PROJECT.CtrlProject()
2079+
2080+ # start GUI
2081+ self.gui = GUI_MAIN.LucioleMainWindow(self)
2082+
2083+
2084+ # connect project controller with GUI
2085+ self.project_ctrl.connect_to_gui(self.gui)
2086+
2087+ self.connect_to_project(self.project_ctrl)
2088+
2089+ self.gui_interface = CtrlAppGui(self)
2090+
2091+ # load a project ?
2092+ if _options.filename \
2093+ and os.path.exists(_options.filename):
2094+ self.project_ctrl.open(_options.filename)
2095+ else :
2096+ self.project_ctrl.no_project_mode()
2097+ self.mode = "RUN"
2098+
2099+
2100+
2101+ self.mainloop.run()
2102+
2103+ # TODO : seems no more needed : check
2104+ import gtk.gdk
2105+ gtk.gdk.threads_leave()
2106+
2107+ def quit(self) :
2108+ self.mainloop.quit()
2109+
2110+ def _init_i18n(self) :
2111+ """ intialize locales """
2112+ _locale_path = LCONST.LOCALE_DIR
2113+
2114+ # Init the list of languages to support
2115+ _langs = []
2116+ #Check the default locale
2117+ _lc, _encoding = locale.getdefaultlocale()
2118+ if (_lc):
2119+ #If we have a default, it's the first in the list
2120+ _langs = [_lc]
2121+ # Now lets get all of the supported languages on the system
2122+ _language = os.environ.get('LANGUAGE', None)
2123+ _msg = "language : " , _language
2124+ self.debug(_msg)
2125+ if (_language):
2126+ # langage comes back something like en_CA:en_US:en_GB:en
2127+ # on linuxy systems, on Win32 it's nothing, so we need to
2128+ # split it up into a list
2129+ _langs += _language.split(":")
2130+ # Now add on to the back of the list the translations that we
2131+ # know that we have, our defaults"""
2132+ _langs += ["en_US"]
2133+
2134+ # Now langs is a list of all of the languages that we are going
2135+ # to try to use. First we check the default, then what the system
2136+ # told us, and finally the 'known' list
2137+ _msg = "locale path", _locale_path
2138+ self.debug(_msg)
2139+
2140+ GUI_CTRL.GuiController.init_18n(_locale_path)
2141+ #import gtk.glade
2142+ #gtk.glade.bindtextdomain(LCONST.APP_NAME, _locale_path)
2143+ #gtk.glade.textdomain(LCONST.APP_NAME)
2144+
2145+ gettext.bindtextdomain(LCONST.APP_NAME, _locale_path)
2146+ gettext.textdomain(LCONST.APP_NAME)
2147+
2148+ # Get the language to use
2149+ _lang = gettext.translation(LCONST.APP_NAME, _locale_path
2150+ , languages=_langs, fallback = True)
2151+ # Install the language, map _() (which we marked our
2152+ # strings to translate with) to self.lang.gettext() which will
2153+ # translate them."""
2154+ global _
2155+ _ = _lang.gettext
2156+
2157+ def connect_to_project(self, project_ctrl) :
2158+ project_ctrl.connect('project-loaded', self._project_loaded_cb)
2159+ project_ctrl.connect('project-closed', self._project_closed_cb)
2160+ project_ctrl.connect('quit', self._quit_cb)
2161+
2162+ def disconnect_from_project(self, project_ctrl) :
2163+ project_ctrl.disconnect_by_function(self._project_loaded_cb)
2164+ project_ctrl.disconnect_by_function(self._project_closed_cb)
2165+
2166+ def _project_loaded_cb(self, project_ctrl, project) :
2167+ self.soundplayer.connect_to_app(project_ctrl)
2168+
2169+ def _project_closed_cb(self, project_ctrl) :
2170+ pass
2171+ #self.soundplayer.disconnect_from_app(project_ctrl)
2172+
2173+ def _quit_cb(self, project) :
2174+ self.quit()
2175+
2176+class LucioleController(object):
2177+ """
2178+ Main luciole controller
2179+ """
2180+
2181+ def __init__(self, args) :
2182+ """ Controller initialisation.
2183+
2184+ Is the main appication intialisation """
2185+ # init logging as early as possible so we can log startup code
2186+ enable_color = os.environ.get('PITIVI_DEBUG_NO_COLOR', '0') in ('', '0')
2187+ log.init('PITIVI_DEBUG', enable_color)
2188+
2189+ #
2190+ # init attributes
2191+ #
2192+ self.ctrl_signals = None
2193+ self.__rusher = None
2194+
2195+ # parse program options
2196+ self.__options = OPTIONS.options_parser()
2197+
2198+ # init logging
2199+ self.logger = LOGGING.init_logging(
2200+ self.__options.is_verbose,
2201+ log_to_file = self.__options.is_logfile
2202+ )
2203+ self.logger.info("Starting luciole")
2204+
2205+ # Init i18n
2206+ self.__init_i18n()
2207+
2208+ # load configuration : ie. saved user options
2209+ self.__configurer = LCONF.LucioleConf()
2210+ # load GUI theme
2211+ self.__configurer.load_theme()
2212+ # create controller signals
2213+ self.__connect_signals()
2214+ # initialize gui and BTW threads
2215+ self.__gui = GUI_CTRL.GuiController(self.ctrl_signals)
2216+
2217+
2218+
2219+ # load recent projects
2220+ _cbs = None
2221+ _recent_mnger = self.__configurer.init_recent_manager(
2222+ self.__gui.file_recent_menu,
2223+ self.__gui.windows,
2224+ _cbs)
2225+
2226+ # prepare callbacks for Project controller
2227+ _cbs = { 'done-rush' : self.on_done_rush,
2228+ 'close-project' : self.on_close_project }
2229+
2230+
2231+ # start project controller
2232+ self._project = \
2233+ CTRL_PROJECT.ProjectController(self.__gui,_recent_mnger ,_cbs)
2234+
2235+ _cbs = {
2236+ 'on-done-snapshot' : self.on_done_snapshot,
2237+ 'on-error':self.on_error
2238+ }
2239+ self._viewer = CTRL_VIEWER.Viewer(self.__gui, _cbs)
2240+
2241+ # display capture trash if option is set
2242+ if self.__configurer.conf_options['CaptureTrashDisplay'] == 'yes' :
2243+ self.__gui.capture_trash = True
2244+
2245+
2246+
2247+ # conncet gui signals after the controller initialisation
2248+ self.__gui.connect_gui_signals()
2249+ # Connect GUI with app
2250+ self.__gui.connect_to_app_ctrl(self)
2251+
2252+ # TODO : load timeline
2253+ #TMLN.Timeline()
2254+
2255+ def __connect_signals(self):
2256+ """ connect controller signals """
2257+ self.ctrl_signals = gobject.GObject()
2258+ for _signal in CTRL_CONST.CTRL_SIGNALS :
2259+
2260+ # declare signal
2261+ gobject.signal_new(_signal, self.ctrl_signals,
2262+ gobject.SIGNAL_RUN_LAST,
2263+ gobject.TYPE_NONE,
2264+ (gobject.TYPE_PYOBJECT,))
2265+
2266+ # Build signal callback function name from signal name
2267+ # example : signal "change-alpha-mixer"
2268+ # have callback self.on_change_alpha_mixer
2269+ _cb = 'self.on_' + _signal.replace('-', '_')
2270+ # connect signal
2271+ self.ctrl_signals.connect(_signal, eval(_cb))
2272+
2273+
2274+ def on_change_alpha_mixer(self, *args) :
2275+ """ Request change mixer alpha value """
2276+ _args = self.__extract_signal_args(args)
2277+ try :
2278+ self._viewer.change_mixer_alpha(_args['alpha'])
2279+ except LEXCEP.LucioException, _err_msg :
2280+ raise LEXCEP.LucioException, _err_msg
2281+
2282+
2283+ def on_change_framerate(self, *args) :
2284+ """ Request of framerate(fpi) change """
2285+ _args = self.__extract_signal_args(args)
2286+ self._project.change_framerate(_args['framerate'])
2287+
2288+ def on_change_project(self, *args) :
2289+ """ Request for prohect change """
2290+ _args = self.__extract_signal_args(args)
2291+ for _key, _value in _args.iteritems() :
2292+ self._project.change_project(_key, _value)
2293+
2294+ def on_close_project(self, *args ) :
2295+ """ Request for project close """
2296+ _args = self.__extract_signal_args(args)
2297+ # test if project exists
2298+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
2299+ # check if project is modified
2300+ if self._project.data['is_modified'] :
2301+ # ask for save it before close
2302+ _res = self.__gui.windows.\
2303+ question_message(_('Save Project before closing'))
2304+ if _res :
2305+ self._project.save()
2306+
2307+ # set viewer in default mode
2308+ self._viewer.mode = self._viewer.DEFAULT
2309+
2310+ # clear project controller
2311+ self._project.mode = CTRL_CONST.NO_PROJECT
2312+
2313+ # display close message
2314+ _msg = _('Project %s is closed' % self._project.data['project_name'])
2315+ self.__gui.status_bar.display_message(_msg)
2316+
2317+
2318+ def on_create_project(self, *args) :
2319+ """ Request new project """
2320+ _args = self.__extract_signal_args(args)
2321+ self._project.new()
2322+
2323+
2324+ def on_delete_capture(self, *args) :
2325+ """ Request delete on capture """
2326+ _args = self.__extract_signal_args(args)
2327+ # remove on treeview
2328+ self.__gui.treeviews[LCONST.CAPTURE].remove()
2329+
2330+ # The image selected for delete is displayed before delete
2331+ # so after go in default mode to nom more display the deleted image
2332+ self._viewer.mode = self._viewer.DEFAULT
2333+
2334+ # change control widget in no acquisition mode
2335+ if self.__gui.control_widget.mode in\
2336+ ( GUI_CONST.ACQUISITION_ACTIVE,
2337+ GUI_CONST.ACQUISITION_ACTIVE_WITH_MIXER) :
2338+ self.__gui.control_widget.mode = GUI_CONST.ACQUISTION_INACTIVE
2339+
2340+
2341+ def on_delete_chrono(self, *args) :
2342+ """ Request delete on capture """
2343+ _args = self.__extract_signal_args(args)
2344+ # remove on treeview
2345+ self.__gui.treeviews[LCONST.CHRONO].remove()
2346+
2347+ # The image selected for delete is displayed before delete
2348+ # so after go in default mode to nom more display the deleted image
2349+ self._viewer.mode = self._viewer.DEFAULT
2350+
2351+ # change control widget in no acquisition mode
2352+ if self.__gui.control_widget.mode in \
2353+ ( GUI_CONST.ACQUISITION_ACTIVE,
2354+ GUI_CONST.ACQUISITION_ACTIVE_WITH_MIXER) :
2355+ self.__gui.control_widget.mode = GUI_CONST.ACQUISTION_INACTIVE
2356+
2357+
2358+ def on_display_about(self, *args) :
2359+ """ request display about """
2360+ _args = self.__extract_signal_args(args)
2361+ # compute version
2362+ __version = "\n %s (%s)" % (INFO.VERSION, INFO.REVNO)
2363+ # display window
2364+ self.__gui.windows.about(__version)
2365+
2366+
2367+ def on_display_luciole_preferences(self, *args) :
2368+ """ Request display of application preferences """
2369+ _args = self.__extract_signal_args(args)
2370+ _cbs = {'done-preferences' : self.on_done_preferences}
2371+ self.__gui.windows.preferences( self.__configurer.conf_options,
2372+ _cbs)
2373+
2374+
2375+
2376+
2377+
2378+ def on_display_project_properties(self, *args) :
2379+ """ Request display project properties """
2380+ _args = self.__extract_signal_args(args)
2381+ # test if project exists
2382+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
2383+ # go in default mode before dispalying window
2384+ # due to webcam detection and avoid confilcts with acquisition
2385+ self._viewer.mode = self._viewer.DEFAULT
2386+
2387+ # change control widget in no acquisition mode
2388+ self.__gui.control_widget.mode = GUI_CONST.ACQUISTION_INACTIVE
2389+
2390+ # display message
2391+ self.__gui.status_bar.display_message(_('No Acquistion'))
2392+
2393+ # launch project poperties window
2394+ _cbs = \
2395+ {'change-project-properties' : self.on_change_project_properties}
2396+ self.__gui.windows.project_properties(self._project.data, _cbs)
2397+
2398+
2399+ def on_export_to_tool(self, *args) :
2400+ """ Request export to tool """
2401+ _args = self.__extract_signal_args(args)
2402+ # test if project exists
2403+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
2404+ self.__gui.windows.export_tool(self._project.data)
2405+
2406+
2407+ def on_export_to_video(self, *args) :
2408+ """ Request export to video """
2409+ _args = self.__extract_signal_args(args)
2410+ # test if project exists
2411+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
2412+ self.__gui.windows.export_video(self._project.data)
2413+
2414+
2415+ def on_import_image(self, *args) :
2416+ """ Request image import """
2417+ _args = self.__extract_signal_args(args)
2418+ # check if project exists
2419+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
2420+ # open filename chooser dialog
2421+ _filenames = self.__gui.windows.import_dialog()
2422+
2423+ if _filenames != [] :
2424+ _cbs = { 'done-import' : self.on_done_import}
2425+ # start import controller
2426+ CTRL_IMPORT.ImportController(_filenames,
2427+ self._project.data,
2428+ self.__rusher,
2429+ self.__gui.status_bar,
2430+ _cbs)
2431+ else :
2432+ _msg = _("No files or valid files choosen for image import.")
2433+ self.__gui.windows.error_message( _msg)
2434+ else :
2435+ _msg = _("Impossible to import images when no project are loaded.")
2436+ self.__gui.windows.error_message( _msg)
2437+
2438+ def on_move_down_chrono(self, *args) :
2439+ """ request to move down an image """
2440+ _args = self.__extract_signal_args(args)
2441+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
2442+ self.__gui.treeviews[LCONST.CHRONO].move_down()
2443+
2444+
2445+ def on_move_to_chrono(self, *args) :
2446+ """ request for move imge from capture to chrono"""
2447+ _args = self.__extract_signal_args(args)
2448+ # get images to move from capture widget
2449+ _images_name = self.__gui.treeviews[LCONST.CAPTURE].images_to_move()
2450+ # append this images in chrono widget
2451+ _images_obj = \
2452+ [ self.__rusher.get_image(_image) for _image in _images_name ]
2453+ for _image in _images_obj :
2454+ self.__gui.treeviews[LCONST.CHRONO].append_image(_image)
2455+
2456+
2457+ def on_move_up_chrono(self, *args) :
2458+ """ request to move up an image """
2459+ _args = self.__extract_signal_args(args)
2460+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
2461+ self.__gui.treeviews[LCONST.CHRONO].move_up()
2462+
2463+
2464+
2465+
2466+
2467+ def on_play_query_position(self, *args) :
2468+ """ Query for player position """
2469+ _args = self.__extract_signal_args(args)
2470+ (_position, _duration) = self._viewer.play_query_position()
2471+ if _args['cb'] :
2472+ # callback call to gui
2473+ _args['cb'](_position, _duration)
2474+
2475+
2476+ def on_play_video(self, *args) :
2477+ """ Play video request """
2478+ _args = self.__extract_signal_args(args)
2479+ # test if project exists
2480+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
2481+ try :
2482+ self._viewer.init_video_player(self._project.data)
2483+ except LEXCEP.LucioException, _err_msg :
2484+ raise LEXCEP.LucioException, _err_msg
2485+ else :
2486+ self._viewer.mode = self._viewer.PLAYER
2487+ try :
2488+ self._viewer.play_video(self._project.data)
2489+ except LEXCEP.LucioException, _err_msg :
2490+ raise LEXCEP.LucioException, _err_msg
2491+ else :
2492+ # send error message
2493+ msg = _("Can not play animation : No project loaded")
2494+ self.__gui.windows.error_message( msg)
2495+
2496+
2497+ def on_quit_app(self, *args) :
2498+ """ Request quit application """
2499+ _args = self.__extract_signal_args(args)
2500+ if self._project.mode == CTRL_CONST.PROJECT_LOADED and \
2501+ self._project.data['is_modified'] :
2502+ _msg = _('Project modified. Save project before exit ?')
2503+ _res = self.__gui.windows.question_message(_msg)
2504+ # if response is not a bool : cancel clicked
2505+ if isinstance(_res, bool) :
2506+ if _res :
2507+ # if True save project before exit
2508+ self._project.save()
2509+ GUI_CTRL.GuiController.quit()
2510+ else :
2511+ GUI_CTRL.GuiController.quit()
2512+
2513+ def on_save_as_project(self, *args) :
2514+ """ save as project request """
2515+ _args = self.__extract_signal_args(args)
2516+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
2517+ _dir = self.__gui.windows.dir_chooser()
2518+ if _dir != None :
2519+ # set viewer in default mode due to project reload
2520+ self._viewer.mode = self._viewer.DEFAULT
2521+ try :
2522+ self._project.save_as(_dir)
2523+ except LEXCEP.LucioException, _err_msg :
2524+ _msg = _('Impossible to save as project : %s' % _err_msg)
2525+ self.__gui.windows.error_message(_msg)
2526+ else :
2527+ _msg = _('Project saved as %s' % \
2528+ self._project.data['project_name'])
2529+ self.__gui.status_bar.display_message(_msg)
2530+
2531+
2532+ def on_save_project(self, *args) :
2533+ """ save project request """
2534+ _args = self.__extract_signal_args(args)
2535+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
2536+ try :
2537+ self._project.save()
2538+ except LEXCEP.LucioException, _err_msg :
2539+ _msg = _('Impossible to save project : %s' % _err_msg)
2540+ self.__gui.windows.error_message(_msg)
2541+ else :
2542+ _msg = _('Project %s saved' % \
2543+ self._project.data['project_name'])
2544+ self.__gui.status_bar.display_message(_msg)
2545+ # update project name on Main bar
2546+ self.__gui.set_program_bar(self._project.data['project_name'],
2547+ self._project.data['is_modified'])
2548+
2549+
2550+ def on_start_acquisition(self, *args ) :
2551+ """Request for starting acqusisition """
2552+ _args = self.__extract_signal_args(args)
2553+ # check if a project is loaded
2554+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
2555+ self._viewer.mode = self._viewer.ACQUIRER
2556+
2557+ # change control widget in acquisition mode
2558+ self.__gui.control_widget.mode = GUI_CONST.ACQUISITION_ACTIVE
2559+
2560+ else :
2561+ _msg = \
2562+ _(' Can not start acquisition when no project are loaded.')
2563+ self.__gui.windows.error_message(_msg)
2564+
2565+
2566+ def on_start_mixer(self, *args) :
2567+ """ Request mixer start """
2568+ _args = self.__extract_signal_args(args)
2569+ # check if a project is loaded
2570+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
2571+ # switch to viewer with acqusisition and mixer
2572+ self._viewer.mode = self._viewer.ACQUIRER_WITH_MIXER
2573+ # change control widget in no acquisition mode
2574+ self.__gui.control_widget.mode = \
2575+ GUI_CONST.ACQUISITION_ACTIVE_WITH_MIXER
2576+
2577+ # display message
2578+ self.__gui.status_bar.display_message(_('Acquiring with mixer'))
2579+ else :
2580+ # robustness
2581+ _msg = \
2582+ _('No project are loaded.')
2583+ self.__gui.windows.error_message(_msg)
2584+
2585+
2586+
2587+ def on_stop_acquisition(self, *args) :
2588+ """ Request to stop acquistion """
2589+ _args = self.__extract_signal_args(args)
2590+ # check if a project is loaded
2591+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
2592+ # switch to viewer default
2593+ self._viewer.mode = self._viewer.DEFAULT
2594+ # change control widget in no acquisition mode
2595+ self.__gui.control_widget.mode = GUI_CONST.ACQUISTION_INACTIVE
2596+
2597+ else :
2598+ # robustness
2599+ _msg = \
2600+ _('No project are loaded.')
2601+ self.__gui.windows.error_message(_msg)
2602+
2603+ def on_stop_mixer(self, *args) :
2604+ """ Request mixer stop """
2605+ _args = self.__extract_signal_args(args)
2606+ # check if a project is loaded
2607+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
2608+ # switch to viewer with acqusisition and mixer
2609+ self._viewer.mode = self._viewer.ACQUIRER
2610+ # change control widget in no acquisition mode
2611+ self.__gui.control_widget.mode = \
2612+ GUI_CONST.ACQUISITION_ACTIVE
2613+
2614+ # display message
2615+ self.__gui.status_bar.display_message(_('Acquiring'))
2616+ else :
2617+ # robustness
2618+ _msg = \
2619+ _('No project are loaded.')
2620+ self.__gui.windows.error_message(_msg)
2621+
2622+ def on_stop_video(self, *args) :
2623+ """ Stop video request """
2624+ _args = self.__extract_signal_args(args)
2625+ try :
2626+ self._viewer.stop_video()
2627+ except LEXCEP.LucioException, _err_msg :
2628+ raise LEXCEP.LucioException, _err_msg
2629+ else :
2630+ self._viewer.mode = self._viewer.DEFAULT
2631+
2632+
2633+ def on_take_snapshot(self, *args) :
2634+ """ take image snapshot request """
2635+ _args = self.__extract_signal_args(args)
2636+ if self._project.mode == CTRL_CONST.PROJECT_LOADED :
2637+ self._viewer.take_snapshot()
2638+ else :
2639+ # robustness
2640+ _msg = \
2641+ _('No project are loaded.')
2642+ self.__gui.windows.error_message(_msg)
2643+
2644+ _msg = \
2645+ _('No project are loaded.')
2646+ self.__gui.windows.error_message(_msg)
2647+
2648+
2649+ def on_view_image(self, *args) :
2650+ """
2651+ request image view/display :
2652+ Mix image with stream if viewer mode is acquirer with mixer
2653+ """
2654+ _args = self.__extract_signal_args(args)
2655+ #if mode is player does not allow image view
2656+ if self._viewer.mode == self._viewer.PLAYER :
2657+ pass
2658+
2659+ # if mode is acquirer with mixer and image is from capture treeview :
2660+ # mix image with acqusisition stream
2661+ elif self._viewer.mode == self._viewer.ACQUIRER_WITH_MIXER \
2662+ and _args['tv_type'] == LCONST.CAPTURE :
2663+ self._viewer.mix_image_in_stream(_args['image'])
2664+
2665+ # on other cases view image
2666+ else :
2667+ self._viewer.mode = self._viewer.IMAGER
2668+ self._viewer.view_image(_args['image'])
2669+
2670+ #change control widget in no acquisition mode
2671+ if self.__gui.control_widget.mode in \
2672+ ( GUI_CONST.ACQUISITION_ACTIVE,
2673+ GUI_CONST.ACQUISITION_ACTIVE_WITH_MIXER) :
2674+ self.__gui.control_widget.mode = GUI_CONST.ACQUISTION_INACTIVE
2675+
2676+
2677+ def run(self) :
2678+ """ main program ininite loop """
2679+ # TODO :
2680+ GUI_CTRL.GuiController.threads_enter()
2681+ # before starting main loop check for
2682+ # options given to application
2683+
2684+ # load a project ?
2685+ if self.__options.filename \
2686+ and os.path.exists(self.__options.filename):
2687+ # emit open project signal
2688+ self.__gui.emit(
2689+ "open-project", self.__options.filename)
2690+
2691+ GUI_CTRL.GuiController.main_loop()
2692+ GUI_CTRL.GuiController.threads_leave()
2693+ self.logger.info("Leaving luciole")
2694+ #
2695+ # Callbacks from controller
2696+ #
2697+ def on_done_rush(self, rusher) :
2698+ """ callback : when tush images are loaded """
2699+ if rusher != None \
2700+ and self._project.mode == CTRL_CONST.PROJECT_LOADED :
2701+ # load treeviews
2702+ # prepare data
2703+ _app_data = {}
2704+ _app_data['capture_images'] = self._project.data['capture_images']
2705+ _app_data['chrono_images'] = self._project.data['chrono_images']
2706+ _app_data['rush'] = rusher
2707+
2708+ self.__gui.load_treeviews(_app_data)
2709+
2710+ # init acquisition
2711+ self._viewer.init_acquisition(self._project.data)
2712+
2713+
2714+ # update fpi on gui and show it
2715+ self.__gui.update_framerate(int(self._project.data['fpi']))
2716+
2717+ # show project acquisition widgets
2718+ self.__gui.control_widget.mode = GUI_CONST.ACQUISTION_INACTIVE
2719+
2720+ # update project name on Main bar
2721+ self.__gui.set_program_bar(self._project.data['project_name'],
2722+ self._project.data['is_modified'])
2723+
2724+ # finish project load (project aspect)
2725+ self._project.finish_project_load()
2726+
2727+ # store rusher
2728+ self.__rusher = rusher
2729+
2730+
2731+
2732+ def on_done_snapshot(self) :
2733+ """
2734+ callback : image snapshot is done.
2735+ Now image can be processed into project
2736+ """
2737+ self._project.append_snapshot(self.__rusher, self._viewer.acquirer)
2738+
2739+ def on_done_preferences(self , modified_options) :
2740+ """ callback : preferences modified fome diaog """
2741+ # update prefernces
2742+ (_capture_trash, _ask_restart) = \
2743+ self.__configurer.update_preferences(modified_options)
2744+
2745+ # check if gui update is needed
2746+ if isinstance(_capture_trash, bool) :
2747+ self.__gui.capture_trash = _capture_trash
2748+
2749+ if isinstance(_ask_restart, bool) and _ask_restart == True :
2750+ _msg = \
2751+ _('Please restart Luciole to take into account the new theme ')
2752+ self.__gui.status_bar.display_message(_msg)
2753+
2754+ def on_change_project_properties(self, key, key_webcam= None, data =None) :
2755+ """ callback project change from properties window """
2756+ # webcam_data field of project dictionaty is a dictionaty
2757+ # Test if webcam data change key
2758+ if key == 'webcam_data':
2759+ # make a local copy of webcam_data
2760+ webcam_dict = self._project.data['webcam_data']
2761+ #test if webcam dict key exists
2762+ if webcam_dict.has_key(key_webcam) :
2763+ # update webcam key and call project change
2764+ webcam_dict[key_webcam] = data
2765+ self._project.change_project('webcam_data', webcam_dict)
2766+
2767+ def on_done_import(self, image_objs = [] ) :
2768+ """ callback : image import (rush generation) is done """
2769+ if image_objs != [] :
2770+ self._project.change_project( 'rush_images',
2771+ self.__rusher.dump_image_name())
2772+ # Not to be done in thread : interacts with gui
2773+ for _image_obj in image_objs :
2774+ self.__gui.append_capture(_image_obj)
2775+
2776+
2777+
2778+ #
2779+ # Error calbacks
2780+ #
2781+ def on_error(self, err_type, message) :
2782+ """ Main error callback """
2783+ _msg = _("error type : %s. %s" % (err_type, message))
2784+ # Display error on log window
2785+ self.logger.info(_msg)
2786+ #
2787+ # private methods
2788+ #
2789+ def __init_i18n(self) :
2790+ """ intialize locales """
2791+ _locale_path = LCONST.LOCALE_DIR
2792+
2793+ # Init the list of languages to support
2794+ _langs = []
2795+ #Check the default locale
2796+ _lc, _encoding = locale.getdefaultlocale()
2797+ if (_lc):
2798+ #If we have a default, it's the first in the list
2799+ _langs = [_lc]
2800+ # Now lets get all of the supported languages on the system
2801+ _language = os.environ.get('LANGUAGE', None)
2802+ _msg = "language : " , _language
2803+ self.logger.debug(_msg)
2804+ if (_language):
2805+ # langage comes back something like en_CA:en_US:en_GB:en
2806+ # on linuxy systems, on Win32 it's nothing, so we need to
2807+ # split it up into a list
2808+ _langs += _language.split(":")
2809+ # Now add on to the back of the list the translations that we
2810+ # know that we have, our defaults"""
2811+ _langs += ["en_US"]
2812+
2813+ # Now langs is a list of all of the languages that we are going
2814+ # to try to use. First we check the default, then what the system
2815+ # told us, and finally the 'known' list
2816+ _msg = "locale path", _locale_path
2817+ self.logger.debug(_msg)
2818+
2819+ GUI_CTRL.GuiController.init_18n(_locale_path)
2820+ #import gtk.glade
2821+ #gtk.glade.bindtextdomain(LCONST.APP_NAME, _locale_path)
2822+ #gtk.glade.textdomain(LCONST.APP_NAME)
2823+
2824+ gettext.bindtextdomain(LCONST.APP_NAME, _locale_path)
2825+ gettext.textdomain(LCONST.APP_NAME)
2826+
2827+ # Get the language to use
2828+ _lang = gettext.translation(LCONST.APP_NAME, _locale_path
2829+ , languages=_langs, fallback = True)
2830+ # Install the language, map _() (which we marked our
2831+ # strings to translate with) to self.lang.gettext() which will
2832+ # translate them."""
2833+ global _
2834+ _ = _lang.gettext
2835+
2836+
2837+
2838+ #
2839+ # static_method
2840+ #
2841+ @staticmethod
2842+ def __extract_signal_args(args) :
2843+ """
2844+ extract the signals arguments :
2845+ arfs is a tuple
2846+ The first argument is the emitter object ( gobject)
2847+ the second argument is a dict with all the parameter given
2848+ to signal.
2849+
2850+ The function returns the paramter dictionary
2851+ """
2852+ return args[1]
2853+
2854+
2855+if __name__ == '__main__' :
2856+ X = LucioleController(sys.argv)
2857+ X.run()
2858+
2859+
2860+
2861
2862=== added file 'luciole/ctrl/ctrl_img_vw.py'
2863--- luciole/ctrl/ctrl_img_vw.py 1970-01-01 00:00:00 +0000
2864+++ luciole/ctrl/ctrl_img_vw.py 2011-02-28 13:21:06 +0000
2865@@ -0,0 +1,82 @@
2866+# -*- Mode: Python -*-
2867+# vim:si:ai:et:sw=4:sts=4:ts=4
2868+#
2869+#
2870+# Copyright Nicolas Bertrand (nico@inattendu.org), 2009-2010
2871+#
2872+# This file is part of Luciole.
2873+#
2874+# Luciole is free software: you can redistribute it and/or modify
2875+# it under the terms of the GNU General Public License as published by
2876+# the Free Software Foundation, either version 3 of the License, or
2877+# (at your option) any later version.
2878+#
2879+# Luciole is distributed in the hope that it will be useful,
2880+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2881+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2882+# GNU General Public License for more details.
2883+#
2884+# You should have received a copy of the GNU General Public License
2885+# along with Luciole. If not, see <http://www.gnu.org/licenses/>.
2886+#
2887+#
2888+"""
2889+ctrl_img_vw.py :
2890+ manage image viewer
2891+"""
2892+# standard library imports
2893+
2894+# related third party imports
2895+
2896+from pitivi.action import ViewAction
2897+from pitivi.pipeline import Pipeline
2898+
2899+# local application/library specific imports
2900+from luciole.ctrl.base import ProjectMode
2901+
2902+
2903+class ProjectModeImgVw(ProjectMode) :
2904+ """ Base class """
2905+ __signals__ = {}
2906+
2907+ def __init__(self, ctrl_project, mode_type = None) :
2908+ ProjectMode.__init__(self, ctrl_project, "IMAGE_VIEW")
2909+
2910+
2911+
2912+
2913+
2914+
2915+
2916+ def prepare(self, project) :
2917+ """ prepare image viewer """
2918+ ProjectMode.prepare(self, project)
2919+
2920+
2921+ def release(self) :
2922+ """ Meta function
2923+ release """
2924+ ProjectMode.release(self)
2925+
2926+
2927+ def active_viewer(self, factory) :
2928+ self._pipeline = Pipeline()
2929+ self._view_action = ViewAction()
2930+
2931+ self.debug(" factory to play :%s", factory)
2932+ self._view_action.addProducers(factory)
2933+ if self._viewer :
2934+ self._viewer.setPipeline(None, 'IMAGE_VIEW')
2935+ self._viewer.hideSlider()
2936+
2937+ self._viewer.setAction(self._view_action)
2938+ self._viewer.setPipeline(self._pipeline, 'IMAGE_VIEW')
2939+ self._pipeline.play()
2940+
2941+ def stop(self) :
2942+
2943+ self._pipeline.stop()
2944+
2945+
2946+
2947+
2948
2949=== added file 'luciole/ctrl/ctrl_no_project.py'
2950--- luciole/ctrl/ctrl_no_project.py 1970-01-01 00:00:00 +0000
2951+++ luciole/ctrl/ctrl_no_project.py 2011-02-28 13:21:06 +0000
2952@@ -0,0 +1,60 @@
2953+#!/usr/bin/env python
2954+# -*- coding: utf-8 -*-
2955+# -*- Mode: Python -*-
2956+# vim:si:ai:et:sw=4:sts=4:ts=4
2957+#
2958+#
2959+# Copyright Nicolas Bertrand (nico@inattendu.org), 2009-2010
2960+#
2961+# This file is part of Luciole.
2962+#
2963+# Luciole is free software: you can redistribute it and/or modify
2964+# it under the terms of the GNU General Public License as published by
2965+# the Free Software Foundation, either version 3 of the License, or
2966+# (at your option) any later version.
2967+#
2968+# Luciole is distributed in the hope that it will be useful,
2969+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2970+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2971+# GNU General Public License for more details.
2972+#
2973+# You should have received a copy of the GNU General Public License
2974+# along with Luciole. If not, see <http://www.gnu.org/licenses/>.
2975+#
2976+#
2977+"""
2978+ctrl_no_project.py :
2979+ manages no project loaded mode
2980+"""
2981+# standard library imports
2982+# N/A
2983+
2984+# related third party imports
2985+from pitivi.signalgroup import SignalGroup
2986+
2987+# local application/library specific imports
2988+from luciole.ctrl.base import ProjectMode
2989+
2990+class ProjectModeNoProject(ProjectMode) :
2991+ __signals__ = {
2992+ }
2993+
2994+ def __init__(self, ctrl_project) :
2995+ ProjectMode.__init__(self, ctrl_project, "NO_PROJECT")
2996+ self.gui_signals = SignalGroup()
2997+
2998+ def prepare(self, project) :
2999+ """ Nothing to prepare """
3000+ pass
3001+
3002+ def release(self) :
3003+ ProjectMode.release(self)
3004+
3005+ def active_viewer(self) :
3006+ if self._viewer :
3007+ self._viewer.setPipeline(None, "NO_PROJECT")
3008+ else :
3009+ self.error("No viewer set")
3010+
3011+
3012+
3013
3014=== renamed file 'luciole/ctrl/project_ctrl.py' => 'luciole/ctrl/ctrl_project.py'
3015--- luciole/ctrl/project_ctrl.py 2010-09-07 06:27:09 +0000
3016+++ luciole/ctrl/ctrl_project.py 2011-02-28 13:21:06 +0000
3017@@ -22,7 +22,7 @@
3018 #
3019 #
3020 """
3021-project_ctrl.py :
3022+ctrl_project.py :
3023 Manage interactions with a luciole Project
3024 """
3025
3026@@ -31,325 +31,537 @@
3027 from gettext import gettext as _
3028
3029 # related third party imports
3030-# N/A
3031+from pitivi.signalinterface import Signallable
3032+from pitivi.log.loggable import Loggable
3033+
3034
3035 # local application/library specific imports
3036 import luciole.base.exceptions as LEXCEP
3037 import luciole.base.tools as LT
3038 import luciole.constants as LCONST
3039 import luciole.ctrl.constants as CTRL_CONST
3040+import luciole.ctrl.ctrl_acq as CTRL_ACQ
3041 import luciole.ctrl.ctrl_base as CTRL_BASE
3042+import luciole.ctrl.ctrl_timeline as CTRL_TMLN
3043+import luciole.ctrl.ctrl_no_project as CTRL_NO_PROJECT
3044+import luciole.ctrl.ctrl_img_vw as CTRL_IMG_VW
3045 import luciole.ctrl.load_rush as LRUSH
3046 import luciole.gui.constants as GUI_CONST
3047 import luciole.media.image as LI
3048-import luciole.project.project as LPF
3049+import luciole.project.project_etree as LPF
3050+
3051+from luciole.gui.windows.assistant_new_project import AssistantNewProject
3052+from luciole.gui.windows.dialog_project_properties import ProjectProperties
3053+from luciole.ctrl.import_image import ImportController
3054
3055 # NULL tuple constant defined for compat with gobject signals on ctrl.py file
3056 _NULL_TUPLE = None, None
3057
3058-class ProjectController(CTRL_BASE.CtrlBase) :
3059- """
3060- Class to manage luciole project
3061- SubClass of controller
3062- """
3063- #
3064- # CONSTANTS
3065- #
3066- __MODES = (CTRL_CONST.NO_PROJECT, CTRL_CONST.PROJECT_LOADED)
3067-
3068- def __get_data(self):
3069- """ getter for is_project"""
3070- return self.__data
3071- data = property( __get_data,
3072- None,
3073- None,
3074- """Retrieve project data """
3075- )
3076-
3077- def __get_mode(self):
3078- """ getter for project controller mode """
3079- return self.__mode
3080- def __set_mode(self, mode) :
3081- """ setter for project controller mode """
3082- # change mode only on transition
3083- if mode in self.__MODES and mode != self.__mode :
3084- self.__mode_call[mode]()
3085- mode = property( __get_mode, __set_mode, None,
3086- """ Project controller mode """)
3087-
3088- def __init__(self, gui, recent_mnger, cbs):
3089- """
3090- ctrl_cbs: dictionary of main main controller callbacks
3091- """
3092- CTRL_BASE.CtrlBase.__init__(self)
3093- self.__data = None
3094- self.__mode = CTRL_CONST.NO_PROJECT
3095- # init mode
3096- self.__mode_call = {
3097- CTRL_CONST.NO_PROJECT : \
3098- self.__set_mode_no_project,
3099- CTRL_CONST.PROJECT_LOADED : \
3100- self.__set_mode_project_loaded,
3101- }
3102-
3103- self.__gui = gui
3104- self.__gui_windows = gui.windows
3105- self.__cbs = cbs
3106- self.__recent_mngr = recent_mnger
3107-
3108- self.__pjt_file = LPF.ProjectToFile()
3109-
3110- def new(self) :
3111- """ create a new project """
3112- # if project exist : close it
3113- if self.__data != None :
3114- # (None, None) params for compat with gobject signal
3115- self.__cbs['close-project'](None, None)
3116- # launch New project assitant window
3117- self.__gui_windows.new_project_assistant(self._cb_new_project)
3118-
3119+class Project(object) :
3120+
3121+
3122+ def __init__(self) :
3123+ self.parser = LPF.ProjectToFile()
3124+ self.props = LPF.ProjectDico()
3125+ self.timeline = None
3126+ self.rush = None
3127+ self.acquirer = None
3128+
3129+
3130+ def save(self, path) :
3131+ _project_path = None
3132+ if path is not None :
3133+ try :
3134+ _project_path = self.parser.save_as(path , self.props)
3135+ except LEXCEP.LucioException, _err_msg :
3136+ raise LEXCEP.LucioException, _err_msg
3137+ else :
3138+ try :
3139+ _project_path = self.parser.save(self.props)
3140+ except LEXCEP.LucioException, _err_msg :
3141+ raise LEXCEP.LucioException, _err_msg
3142+ return _project_path
3143+
3144+ def set_non_pjt_dpdt_props(self) :
3145+ # init props not managed by porject in file
3146+ self.props['is_modified'] = False
3147+ self.props['is-mixer-active'] = False
3148+ self.props['image2mix'] = None
3149+ self.props['alpha'] = LCONST.DEFAULT_ALPHA_IMAGE
3150+
3151+class CtrlProject(Signallable, Loggable) :
3152+
3153+ # CONSTANTS
3154+ MODES = ("NO_PROJECT", "ACQUISITION", "TIMELINE", "IMAGE_VIEW", "EXPORT")
3155+ PROJECT_LOADED_MODES = ("ACQUISITION", "TIMELINE", "IMAGE_VIEW", "EXPORT")
3156+
3157+
3158+
3159+ __signals__ = {
3160+ "project-loaded": ["project"],
3161+ "project-closing" : ["project"],
3162+ "project-closed" : [],
3163+ 'project-modified' : ["is_modified"],
3164+ 'project-mode-changed' : ["mode"],
3165+ 'quit' : [],
3166+ 'import-image-done' : ['images'],
3167+ }
3168+
3169+ def _get_mode(self) :
3170+ """ mode getter """
3171+ return self._mode
3172+ mode = property(_get_mode,
3173+ None,
3174+ None,
3175+ """ project mode """)
3176+
3177+
3178+ def _get_is_modified(self):
3179+ """ is_modified getter """
3180+ return self.project.props['is_modified']
3181+
3182+ def _set_is_modified(self, is_modified = True) :
3183+ """ is_modified getter """
3184+ if isinstance(is_modified, bool)\
3185+ and\
3186+ is_modified != self.project.props['is_modified'] :
3187+ self.project.props['is_modified'] = is_modified
3188+ self.debug('emit signal project-modified : is_modified = %s',
3189+ is_modified)
3190+ self.emit('project-modified', is_modified)
3191+
3192+ is_modified = property(_get_is_modified,
3193+ _set_is_modified,
3194+ None,
3195+ """ is_modified project property """)
3196+
3197+
3198+ def __init__(self) :
3199+ Signallable.__init__(self)
3200+ Loggable.__init__(self)
3201+
3202+ self._mode = None
3203+ self.gui = None
3204+ self.project = Project()
3205+
3206+ self.ctrl_acq = CTRL_ACQ.ProjectModeAcquisition(self)
3207+ self.ctrl_tmln = CTRL_TMLN.ProjectModeTimeline(self)
3208+ self.ctrl_no_project = CTRL_NO_PROJECT.ProjectModeNoProject(self)
3209+ self.ctrl_img_vw = CTRL_IMG_VW.ProjectModeImgVw(self)
3210+
3211+ self._connect_to_acq(self.ctrl_acq)
3212+ self._connect_to_timeline(self.ctrl_tmln)
3213+
3214+
3215+ def connect_to_gui(self, gui) :
3216+ self.gui = gui
3217+
3218+ self.ctrl_acq.set_viewer(gui.drawarea)
3219+ self.ctrl_tmln.set_viewer(gui.drawarea)
3220+ self.ctrl_no_project.set_viewer(gui.drawarea)
3221+ self.ctrl_img_vw.set_viewer(gui.drawarea)
3222+
3223+ def no_project_mode(self) :
3224+ self.debug('No project mode')
3225+
3226+ if self._mode == 'ACQUISITION' :
3227+ self.ctrl_acq.stop()
3228+
3229+ if self._mode != 'NO_PROJECT' :
3230+ self._mode = 'NO_PROJECT'
3231+ self.ctrl_no_project.active_viewer()
3232+ else :
3233+ self.warning("impossible to switch to No Project mode (%s)",
3234+ self._mode )
3235+
3236+
3237+
3238+
3239+ def acquisition_mode(self, force = False) :
3240+ """ activate acquisition mode """
3241+ self.debug('Acquisition mode')
3242+ if self._mode in self.PROJECT_LOADED_MODES \
3243+ and self._mode != 'ACQUISITION' :
3244+ self._mode = 'ACQUISITION'
3245+ #force = True
3246+ if force == True :
3247+ self.debug('switch to acquisition mode forced')
3248+ # force prepare for pipelline configuration
3249+ # the _acquirer_ready_cb will call acquisition_mode
3250+ # when aquirer(pipeline) is ready
3251+ self.ctrl_acq.release()
3252+ self.ctrl_acq.prepare(self.project)
3253+ return
3254+
3255+ self.ctrl_acq.active_viewer()
3256+ self.ctrl_acq.start()
3257+ self.debug('Emit project-mode-changed : %s'%self._mode)
3258+ self.emit('project-mode-changed',self._mode)
3259+
3260+ else :
3261+ self.warning("impossible to switch to acquisition mode (%s)",
3262+ self._mode )
3263+
3264+ def timleline_mode(self) :
3265+ """ activate timeline mode """
3266+ if self._mode == 'ACQUISITION' :
3267+ self.ctrl_acq.stop()
3268+
3269+ if self._mode in self.PROJECT_LOADED_MODES \
3270+ and self._mode != 'TIMELINE' :
3271+
3272+ self._mode = 'TIMELINE'
3273+ self.ctrl_tmln.active_viewer()
3274+ self.debug('Emit project-mode-changed : %s'%self._mode)
3275+ self.emit('project-mode-changed',self._mode)
3276+ else :
3277+ self.warning("impossible to switch to timeline mode")
3278+
3279+
3280+ def img_vw_mode(self, origin, image_name, position = 0) :
3281+ if self._mode in self.PROJECT_LOADED_MODES :
3282+ if self._mode == 'ACQUISITION' :
3283+ self.ctrl_acq.stop()
3284+
3285+ if self._mode == 'TIMELINE' and origin == LCONST.CHRONO :
3286+ self.ctrl_tmln.go_to_position(position)
3287+ # TODO : implemnt better then return here
3288+ return
3289+
3290+ if self._mode == 'IMAGE_VIEW' :
3291+ self.ctrl_img_vw.stop()
3292+
3293+
3294+ _factory = self.ctrl_tmln._get_factory(image_name)
3295+ self.ctrl_img_vw.active_viewer(_factory)
3296+ self._mode = 'IMAGE_VIEW'
3297+
3298 def open(self, project_path = None) :
3299 """ open an existing project """
3300- # no project path open a Dialog
3301+ # no project path : leave
3302 if project_path == None :
3303- _path = self.__gui_windows.open_project()
3304- else :
3305- _path = project_path
3306+ return
3307+ self.debug("Starting project load of %s", project_path)
3308
3309 # if a project is loaded close it
3310- if self.__data != None :
3311+ if self._mode != 'NO_PROJECT' :
3312 # (None, None) params for compat with gobject signal
3313- self.__cbs['close-project'](None, None)
3314-
3315- # check if a path entered ( from gui)
3316- if _path != None :
3317- try :
3318- (_is_valid, self.__data) =self.__pjt_file.open(_path)
3319- except LEXCEP.LucioException, _err_msg :
3320- # Transmit error to controller: not stop application
3321- raise LEXCEP.LucioException, _err_msg
3322- else :
3323- if not _is_valid :
3324- _msg = _(" Webcam data not valid in project.\
3325- Please restart webcam detection")
3326- self.__gui_windows.error_message( _msg)
3327-
3328- # load project in application
3329- self.__load_project_in_app()
3330-
3331-
3332-
3333- def save_as(self, dir_path) :
3334- """ save as current project """
3335-
3336- # project saved and self.__data updated
3337- try :
3338- self.__pjt_file.save_as(dir_path , self.__data)
3339- except LEXCEP.LucioException, _err_msg :
3340- raise LEXCEP.LucioException, _err_msg
3341- else :
3342- self.__data['is_modified'] = False
3343- # saved project needs now to be reloaded
3344- # close it
3345- self.__set_mode_no_project()
3346- # reload project
3347-
3348- self.__set_mode_project_loaded()
3349- self.__load_project_in_app()
3350-
3351-
3352- def save(self) :
3353- """ save current project """
3354- try :
3355- self.__pjt_file.save(self.__data)
3356- except LEXCEP.LucioException, _err_msg :
3357- raise LEXCEP.LucioException, _err_msg
3358- else :
3359- self.__data['is_modified'] = False
3360-
3361-
3362-
3363-
3364-
3365- def append_snapshot(self, rusher, acquirer) :
3366- """ move acquired image to rush dir :
3367- 1. move image to tmp dir.
3368- 2. resize it.
3369- 3. move it to rush image dir """
3370- # get acquired image name
3371- l_acq_image = acquirer.image2save
3372-
3373- # build temp impage path
3374- l_temp_dir = os.path.join(self.__data['project_dir'], 'tmp')
3375- # copy name
3376- l_ac_image_temp = os.path.join(l_temp_dir, LCONST.ACQUIRED_IMAGE_NAME)
3377- # resized copy name
3378- l_ac_image_temp_rz = \
3379- os.path.join(l_temp_dir,LCONST.ACQUIRED_IMAGE_NAME_RZ)
3380-
3381- # build rush image name
3382- l_basename = LCONST.RUSH_FILENAME_TPL % rusher.rush_index
3383- l_rush_image = \
3384- os.path.join(self.__data['project_dir'], self.__data['rush_dir'])
3385- l_rush_image = os.path.join(l_rush_image, l_basename)
3386-
3387- try :
3388- # 1. move image acquired image to tmp dir
3389- LT.movef(l_acq_image, l_ac_image_temp)
3390-
3391- # 2. resize image result is in l_ac_image_temp_rz
3392- l_rz_obj = LI.ImageResize(l_ac_image_temp, l_ac_image_temp_rz )
3393- l_rz_obj.convert()
3394-
3395- # 3. move resized image to rush dire
3396- LT.movef(l_ac_image_temp_rz, l_rush_image)
3397-
3398- except LEXCEP.LucioException, _err_msg :
3399- raise LEXCEP.LucioException, _err_msg
3400- else :
3401- # 4. append image to rush list
3402- rusher.append(l_basename)
3403- # indicate project change (rush list)
3404- self.__change_project('rush_images', rusher.dump_image_name())
3405-
3406-
3407- # 5. append image object to capture list
3408- l_rush_image = rusher.get_image(l_basename)
3409- # 6. always update the image 2 mix, even if mixer is not active
3410- # used to memorize the last capture
3411- acquirer.Image2Mix = l_rush_image.path
3412-
3413- #7 append capture to treeview
3414- self.__gui.append_capture(l_rush_image)
3415-
3416- def change_framerate(self, fpi) :
3417- """ change framerate """
3418- # update framerate only if project exist
3419- if self.__data and isinstance(fpi, int):
3420- self.__change_project('fpi', str(fpi))
3421-
3422- def change_project(self, project_key, data) :
3423- """ Change project : External mathod """
3424- self.__change_project(project_key, data)
3425-
3426- def finish_project_load(self) :
3427+ # TODO : manage close project
3428+ #self.__cbs['close-project'](None, None)
3429+ pass
3430+
3431+ self.project = Project()
3432+ try :
3433+ (_is_valid, self.project.props) = \
3434+ self.project.parser.open(project_path)
3435+ except LEXCEP.LucioException, _err_msg :
3436+ # Transmit error to controller: not stop application
3437+ self.error(_err_msg)
3438+ # TODO : manage exceptions
3439+ raise LEXCEP.LucioException, _err_msg
3440+ else :
3441+ if not _is_valid :
3442+ _msg = _(" Webcam data not valid in project.\
3443+ Please restart webcam detection")
3444+ self.gui.windows.error_message( _msg)
3445+
3446+ # initiate project load
3447+ self._start_project_load()
3448+
3449+ def new_project(self ) :
3450+ """ new project creation """
3451+ if self._mode != 'NO_PROJECT' :
3452+ # close current project
3453+ self.close()
3454+
3455+ _ass = AssistantNewProject(self._new_project_cb)
3456+
3457+ # TODO : fot test purpose only
3458+ import os.path
3459+ import shutil
3460+ PROJECT_NAME = 'test_luciole'
3461+ PROJECT_PATH = '/home/nico/temp/testLuciole'
3462+
3463+ _p_path = os.path.join(PROJECT_PATH, PROJECT_NAME)
3464+ if os.path.exists(_p_path) :
3465+ # delete folder
3466+ self.debug('Deleting folder %s', _p_path)
3467+ shutil.rmtree(_p_path)
3468+
3469+ # set on assistant project name
3470+ _ass._pg1.entry_project_name.set_text(PROJECT_NAME)
3471+ # set a folder
3472+ _ass._pg1._file_chooser.set_current_folder(PROJECT_PATH)
3473+
3474+
3475+
3476+ def _new_project_cb(self, project_data) :
3477 """
3478- Method called to finsih project load.
3479+ new project callback :
3480+ signal generated by new project assistant
3481 """
3482- # add project loaded to recent manager
3483- self.__recent_mngr.add_project(os.path.join(
3484- self.__data['project_dir'],
3485- self.__data['xml_filename'] )
3486- )
3487-
3488- # Set project as not modified
3489- self.__data['is_modified'] = False
3490- #
3491- # def callbacks
3492- #
3493- def _cb_new_project(self, project_data) :
3494- """ callback when new project assistant is done """
3495- # 1. copy data in a project_dico object
3496- project_dico_data = LPF.ProjectDico()
3497- for _key in project_data.keys() :
3498- project_dico_data[_key] = project_data[_key]
3499-
3500- # 2. create project ( folder structure ,etc ...)
3501- # mising raise exception in case of create failure
3502+ self.debug(" create project with %s", project_data)
3503+ if project_data is None :
3504+ self.error('No data to load')
3505+ return
3506+ # create project on disk
3507+ self.project = Project()
3508 try :
3509- self.__pjt_file.create(project_dico_data)
3510+ _path = self.project.parser.create(project_data)
3511 except LEXCEP.LucioException, _err :
3512 raise LEXCEP.LucioException, _err
3513- else :
3514- self.__data = project_dico_data
3515-
3516- # 3. load created project in application
3517- self.__load_project_in_app()
3518-
3519-
3520-
3521- #
3522- # Private methods
3523- #
3524- def __set_mode_no_project(self):
3525- """ No project mode """
3526-
3527- #
3528- # clear gui
3529- #
3530- # clear treeviews
3531- self.__gui.clear_treeviews()
3532-
3533- # set control widget in open_Load mode
3534- self.__gui.control_widget.mode = GUI_CONST.LOAD_OR_CREATE
3535-
3536- # clear program bar
3537- self.__gui.set_program_bar('', False)
3538-
3539-
3540- self.__mode = CTRL_CONST.NO_PROJECT
3541-
3542- def __set_mode_project_loaded(self):
3543- """project loaded mode """
3544- self.__mode = CTRL_CONST.PROJECT_LOADED
3545-
3546- def __save(self) :
3547- """ private method for save """
3548+
3549+ self.debug(" created project on disk : %s", _path)
3550+ # load project on luciole
3551+ self.open(_path)
3552+
3553+
3554+ def close(self) :
3555+ """ close current project """
3556+ if self._mode in self.PROJECT_LOADED_MODES :
3557+ if self.project.props['is_modified'] == True :
3558+ if (self.emit('project-closing', self.project) == True) :
3559+ self.save()
3560+
3561+ self.debug(" Closing current project ")
3562+ self.release()
3563+ self.no_project_mode()
3564+ self.debug('emit signal project-closed')
3565+ self.emit('project-closed')
3566+ self.project = None
3567+
3568+ def save(self, dir_path = None ) :
3569+ if self._mode not in self.PROJECT_LOADED_MODES :
3570+ self.error('invalid mode for save : %s'%self._mode)
3571+ return
3572+ if dir_path and not os.path.exists(dir_path) :
3573+ self.error('Path %s exists. Impossible to save project', dir_path)
3574+ return
3575+ self.debug("%s",dir_path)
3576+ try :
3577+ _project_path = self.project.save(dir_path)
3578+ except LEXCEP.LucioException, _err_msg :
3579+ self.error('%s', _err_msg)
3580+ else :
3581+ self.debug('Project saved in %s', _project_path)
3582+ self.is_modified = False
3583+ if dir_path is not None and _project_path is not None :
3584+ # reload project is corretcly saved
3585+ self.close()
3586+ self.debug('Opening project with path %s',_project_path)
3587+ self.open(_project_path)
3588+
3589+ def set_framerate(self, fpi, force = False) :
3590+ if self._mode != "TIMELINE" and force == False :
3591+ self.info('update of fpi only availabe in TIMELINE mode')
3592+ return
3593+ if int(self.project.props['fpi']) != fpi :
3594+ self.project.props['fpi'] = str(fpi)
3595+ self.is_modified = True
3596+ # update timeline
3597+ self.ctrl_tmln.update_image_duration(fpi)
3598+
3599+
3600+
3601+ def new(self, project_data = None) :
3602+
3603+ # create new project
3604+
3605+ if project_data :
3606+ # TODO case with assistant
3607+ pass
3608+
3609+
3610+
3611+ def release(self) :
3612+
3613+ # release signals between aquirer and project
3614+ if self.project.props['hardtype'] in \
3615+ (LCONST.FAKE, LCONST.DVCAM, LCONST.WEBCAM ) :
3616+ #self._disconnect_from_acq(self.ctrl_acq)
3617+ pass
3618+
3619+ # release signals between timeline and project
3620+ # self._disconnect_from_timeline(self.ctrl_tmln)
3621+
3622+ # disconnect from gui
3623+ # TODO : Analyse when to be done on close project or quit ?
3624+ #self.disconnect_from_gui(self.gui)
3625+
3626+
3627+ def import_images(self, paths) :
3628+ if self._mode not in self.PROJECT_LOADED_MODES :
3629+ self.error('invalid mode for import: %s'%self._mode)
3630+ return
3631+
3632+ if paths != [] :
3633+ self.debug('Starting image importer for %s',paths)
3634+ importer = ImportController( paths,
3635+ self.project.props,
3636+ self.project.rush,
3637+ self.gui.status_bar
3638+ )
3639+ importer.connect('import-image-done', self._import_img_done_cb)
3640+ else :
3641+ self.info('No image to import')
3642+
3643+
3644+ def _import_img_done_cb(self, imager, images) :
3645+ """ import-image-done callback """
3646+ self.debug("RX import-image-done : %s", images)
3647+ if images != [] :
3648+ self.emit('import-image-done', images)
3649+
3650+ # update project
3651+ # get image names
3652+ _names = [ _img.name for _img in images]
3653+ self.project.props['rush_images'].extend(_names)
3654+ self.project.props['capture_images'].extend(_names)
3655+ self.is_modified = True
3656+ else :
3657+ self.warning('No image imported')
3658+
3659+ def show_project_properties(self) :
3660+ """ show project properties
3661+ webcam setup can be modified. So before displaying window all
3662+ pipelines and associated stuff should be stopped
3663+ """
3664+ if self._mode not in self.PROJECT_LOADED_MODES :
3665+ self.error('invalid mode for import: %s'%self._mode)
3666+ return
3667+ self.debug('%s',self.project.props)
3668+ self._old_mode = self._mode
3669+ if self._mode == 'ACQUISITION' :
3670+ # switch to TIMELINE to free the webcam
3671+ self.timleline_mode()
3672+
3673+
3674+ _properties = ProjectProperties(self.gui.main_window,
3675+ self.project.props)
3676+ _properties.connect('project-changed', self._project_modified_cb)
3677+ _properties.run()
3678+
3679+ def _project_modified_cb(self, dialog, project_data) :
3680+ """ callback for modified data from project properties dialog """
3681+ self.is_modified = True
3682+ if self._old_mode == 'ACQUISITION' :
3683+ # go back to acqusition mode
3684+ self.acquisition_mode(True)
3685+ self._old_mode = None
3686+
3687+
3688+
3689+ def _start_project_load(self) :
3690+
3691+
3692+ # start rusher controller
3693+ _rush_ctrl = LRUSH.ControllerLoadRush( self.project,
3694+ self.gui.status_bar )
3695+ self._connect_to_rush(_rush_ctrl)
3696+
3697+ # start acquirer controller
3698+ if self.project.props['hardtype'] in \
3699+ (LCONST.FAKE, LCONST.DVCAM, LCONST.WEBCAM ) :
3700+ # start acquisition controller
3701+ #self.acq_ctrl = CTRL_ACQ.CtrlAcq(self)
3702+ self.debug('Acquirer Object loaded %s', self.ctrl_acq)
3703+ else :
3704+ # No acquisition (only import and timeline)
3705+ self._mode = "TIMELINE"
3706+
3707+ # start timeline controller
3708+ #self.ctrl_tmln = CTRL_TMLN.CtrlTimeline(self)
3709+
3710+
3711+ def _connect_to_rush(self, rush_ctrl) :
3712+ rush_ctrl.connect('rush-loaded', self._rush_load_finish_cb)
3713+
3714+ def _disconnect_from_rush(self, rush_ctrl) :
3715+ rush_ctrl.disconnect_by_function( self._rush_load_finish_cb)
3716+
3717+ def _rush_load_finish_cb(self, rush_ctrl, rush_obj) :
3718+ self.debug(" Rush Load Finish :%s ", rush_obj.dump_image_name() )
3719+ if rush_obj != None :
3720+ self._terminate_project_load(rush_obj)
3721+ # Load rush is done only one time at load so disconnect it
3722+ self._disconnect_from_rush(rush_ctrl)
3723+
3724+ def _terminate_project_load(self, rush_obj) :
3725+ self.project.rush = rush_obj
3726+
3727+ # set project as not modified as it is just loaded
3728+ self.project.set_non_pjt_dpdt_props()
3729+
3730+ # set path for image to mix
3731+ self.project.props['image2mix'] =\
3732+ os.path.join(
3733+ self.project.props['project_dir'],
3734+ LCONST.IMAGE2MIX_NAME
3735+ )
3736+
3737+ # if a capture image exist ,
3738+ # mix with last one by copying it in 'image2mix'
3739+
3740+ if self.project.props['capture_images'] != []:
3741+ self.debug(' image 2 copy %s',
3742+ self.project.props['capture_images'][-1])
3743+ self._copy_to_image2mix(
3744+ self.project.props['capture_images'][-1])
3745+
3746+ self.debug("emit project-loaded : %s " , self.project)
3747+ self.emit('project-loaded', self.project )
3748+
3749+ def _connect_to_acq(self , ctrl_acq) :
3750+ ctrl_acq.connect('acquirer-ready', self._acquirer_ready_cb)
3751+ ctrl_acq.connect('acquirer-error', self._acquirer_error_cb)
3752+
3753+ def _disconnect_from_acq(self , ctrl_acq) :
3754+ ctrl_acq.disconnect_by_function( self._acquirer_ready_cb)
3755+ ctrl_acq.disconnect_by_function( self._acquirer_error_cb)
3756+
3757+
3758+ def _acquirer_ready_cb(self, ctrl_acq ) :
3759+ self.debug('RX acquirer-ready signal')
3760+ # TODO : work around on mode
3761+ # FIXME : set to image to force transtion to acquisition mode
3762+ # default mode when project is started
3763+ self._mode = 'IMAGE_VIEW'
3764+ self.acquisition_mode()
3765+
3766+
3767+
3768+ def _acquirer_error_cb(self, ctrl_acq, msg) :
3769+ self.error(msg)
3770+
3771+ def _connect_to_timeline(self, ctrl_tmln) :
3772+ ctrl_tmln.connect('timeline-loaded', self._timeline_loaded_cb)
3773+
3774+ def _disconnect_from_timeline(self, ctrl_tmln) :
3775+ ctrl_tmln.disconnect_from_project(self)
3776+ ctrl_tmln.disconnect_by_function(self._timeline_loaded_cb)
3777+
3778+ def _timeline_loaded_cb(self, ctrl_tmln):
3779 pass
3780-
3781- def __load_project_in_app(self) :
3782- """ load project data in luciole :
3783- 1. Load rusher
3784- 2. when rusher done a callback is generated.
3785- handled in main controller. Main controller
3786- load the acquistion object, and display
3787- project in gui
3788- """
3789- if self.__data != None :
3790- # set modified attibute
3791- self.__data['is_modified'] = False
3792-
3793- # set set mode as PROJECT LOADED
3794- self.__mode = CTRL_CONST.PROJECT_LOADED
3795-
3796- self.logger.\
3797- debug("-----------------------------------------------------")
3798- self.logger.debug("Luciole_controller project info: ")
3799- self.logger.\
3800- debug("-----------------------------------------------------")
3801- for _key, _value in self.__data.iteritems() :
3802- self.logger.debug("**%s** : %s "%(_key, _value))
3803- self.logger.\
3804- debug("-----------------------------------------------------")
3805-
3806- # Initilaisation of rush obj is threaded because its take a while
3807- # (generation of images pixbufs)
3808- # When rush load is finish : __on_rush_finish is called
3809- LRUSH.ControllerLoadRush( self.__data,
3810- self.__gui.status_bar,
3811- self.__cbs['done-rush'])
3812-
3813-
3814- def __change_project(self, project_key, data) :
3815- """ function called to update the current project """
3816- if self.__data != None :
3817- # is key exist ?
3818- if self.__data.has_key(project_key) :
3819- # update only on change
3820- if self.__data[project_key] != data :
3821- self.__data[project_key] = data
3822- self.__data['is_modified'] = True
3823- self.__gui.set_program_bar( self.__data['project_name'],
3824- self.__data['is_modified'])
3825- else :
3826- #raise Error/Exception
3827- _err_msg = "key %s not in project " % project_key
3828- raise LEXCEP.LucioException, _err_msg
3829+ #self.project.timeline = timeline
3830+
3831+
3832+ def _copy_to_image2mix(self, image_name) :
3833+
3834+ _source = os.path.join( self.project.props['project_dir'],
3835+ self.project.props['rush_dir'],
3836+ image_name)
3837+ try :
3838+ LT.copyf(_source, self.project.props['image2mix'])
3839+ except LEXCEP.LucioException, err :
3840+ self.error('impossible to copy %s in %s -- %s ',
3841+ _source,
3842+ self.project.props['image2mix'],
3843+ err)
3844 else :
3845- # raise Error/Exception
3846- _err_msg = " Project not loaded "
3847- raise LEXCEP.LucioException, _err_msg
3848-
3849+ self.debug(' copied %s in %s',
3850+ _source,
3851+ self.project.props['image2mix'])
3852+
3853
3854
3855
3856
3857=== added file 'luciole/ctrl/ctrl_timeline.py'
3858--- luciole/ctrl/ctrl_timeline.py 1970-01-01 00:00:00 +0000
3859+++ luciole/ctrl/ctrl_timeline.py 2011-02-28 13:21:06 +0000
3860@@ -0,0 +1,953 @@
3861+#!/usr/bin/env python
3862+# -*- coding: utf-8 -*-
3863+# -*- Mode: Python -*-
3864+# vim:si:ai:et:sw=4:sts=4:ts=4
3865+#
3866+#
3867+# Copyright Nicolas Bertrand (nico@inattendu.org), 2009-2010
3868+#
3869+# This file is part of Luciole.
3870+#
3871+# Luciole is free software: you can redistribute it and/or modify
3872+# it under the terms of the GNU General Public License as published by
3873+# the Free Software Foundation, either version 3 of the License, or
3874+# (at your option) any later version.
3875+#
3876+# Luciole is distributed in the hope that it will be useful,
3877+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3878+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3879+# GNU General Public License for more details.
3880+#
3881+# You should have received a copy of the GNU General Public License
3882+# along with Luciole. If not, see <http://www.gnu.org/licenses/>.
3883+#
3884+#
3885+"""
3886+ctrl_acq.py :
3887+ manages acquisition
3888+"""
3889+# standard library imports
3890+import os.path
3891+import urllib
3892+import gettext
3893+_ = gettext.gettext
3894+
3895+# related third party imports
3896+import gst
3897+import copy
3898+import gobject
3899+
3900+from pitivi.signalgroup import SignalGroup
3901+from pitivi.pipeline import Pipeline
3902+from pitivi.action import ViewAction
3903+from pitivi.timeline.timeline import Timeline, TimelineObject, TimelineError
3904+
3905+from pitivi.factories.timeline import TimelineSourceFactory
3906+
3907+from pitivi.stream import AudioStream, VideoStream
3908+from pitivi.settings import GlobalSettings
3909+from pitivi.settings import ExportSettings
3910+from pitivi.sourcelist import SourceList
3911+from pitivi.timeline.track import Track, SourceTrackObject
3912+
3913+from pitivi.utils import Seeker, getNextObject
3914+# local application/library specific imports
3915+from luciole.ctrl.base import ProjectMode
3916+import luciole.base.tools as LT
3917+import luciole.constants as LCONST
3918+import luciole.base.exceptions as LEXCEP
3919+
3920+class LucioleTimeline(Timeline) :
3921+ DEFAULT_DURATION = 1 * gst.SECOND
3922+
3923+ def __init__(self) :
3924+ Timeline.__init__(self)
3925+ # TODO : Temporary usage of GlobalSettings
3926+ self.settings = GlobalSettings()
3927+ self.default_duration = self.DEFAULT_DURATION
3928+
3929+ def change_order(self, images_list, position_dest) :
3930+ self.info("order changed move images %s at position %s",
3931+ images_list,
3932+ position_dest)
3933+
3934+ _tl_objects_to_move = [self.video_objects[_elem[0]] for _elem in images_list]
3935+ self.debug( " Objs to move :%s", [ obj.factory.name for obj in _tl_objects_to_move])
3936+
3937+ if position_dest[0] < len(self.video_objects ) :
3938+ # Move images inside sequence
3939+ _tl_obj_dest = self.video_objects[position_dest[0]]
3940+
3941+ for tl_obj in _tl_objects_to_move :
3942+
3943+ self.info(" Moving image %s (position %s) before %s (position : %s)",\
3944+ tl_obj.factory.name,
3945+ self.video_objects.index(tl_obj),
3946+ _tl_obj_dest.factory.name,
3947+ self.video_objects.index(_tl_obj_dest))
3948+
3949+ _tl_obj_tmp = self._move_timeline_object(tl_obj,_tl_obj_dest)
3950+ _tl_obj_dest = \
3951+ self.video_objects\
3952+ [self.video_objects.index(_tl_obj_tmp) + 1]
3953+
3954+ else :
3955+ # move images at the end of sequnce
3956+ for tl_obj in _tl_objects_to_move :
3957+ self.info(" Moving image %s (position %s) at end of sequence",\
3958+ tl_obj.factory.name,
3959+ self.video_objects.index(tl_obj) )
3960+ self._move_at_end(tl_obj)
3961+
3962+
3963+ def insert_factories(self, factories, position_dest) :
3964+ self.info("images to insert %s at position %s",
3965+ factories, position_dest)
3966+
3967+ if position_dest[0] < len(self.video_objects ) :
3968+ self._insert_factories_at_position( position_dest[0], factories)
3969+ else :
3970+ for _factory in factories :
3971+ # TODO : verify if append merhod usage is correct. regarding
3972+ # _insert_factories_at_position method (i.e. tl_obj creation and
3973+ # disable/enable timeleine methods)
3974+ self._append_obj(_factory)
3975+
3976+
3977+
3978+
3979+ def remove_images(self, images_list) :
3980+ self.info("images to remove on timeline %s", images_list)
3981+ _tl_objs_to_remove = \
3982+ [self.video_objects[_elem[0]] for _elem in images_list]
3983+
3984+ for tl_obj in _tl_objs_to_remove :
3985+ self._remove_object(tl_obj)
3986+
3987+
3988+
3989+
3990+
3991+ def create_timeline_object(self, factory) :
3992+
3993+ _stream_map = self._getSourceFactoryStreamMap(factory)
3994+ _timeline_object = TimelineObject(factory)
3995+ for stream, track in _stream_map.iteritems():
3996+ track_object = SourceTrackObject(factory, stream)
3997+ track.addTrackObject(track_object)
3998+ self.info("Stream: %s, Track: %s, Track duration: %d", str(stream),
3999+ str(track), track.duration)
4000+
4001+ _timeline_object.addTrackObject(track_object)
4002+ _timeline_object.start = 0
4003+ return _timeline_object
4004+
4005+
4006+
4007+
4008+
4009+ def add_sound_track(self, audio_factory, duration, is_video_duration = True, audio_offset = 0,) :
4010+ # TODO : only a offset of 0 is vlaid. with ofsset !=0 --> pieleine error
4011+ audio_offset = 0
4012+ # TODO : undestand better disable/ Enable updates.
4013+ # the usage od disable/enable avoid a fade transition at start ?
4014+ _vtrack = self._get_video_track()
4015+ self.debug('Video track duration %s, ', _vtrack.duration)
4016+ if is_video_duration :
4017+ _duration = _vtrack.duration
4018+ else :
4019+ _duration = duration
4020+
4021+ self.debug('Sound track %s, duration %s, audio offest %s',
4022+ audio_factory.name, _duration, audio_offset)
4023+
4024+ #self.disableUpdates()
4025+ _tl_obj = self.create_timeline_object(audio_factory)
4026+ _tl_obj.start = audio_offset
4027+ _tl_obj.duration = _duration
4028+ self.debug('timeline object duration = %s', _tl_obj.duration)
4029+ self.addTimelineObject( _tl_obj)
4030+ #self.enableUpdates()
4031+ return _tl_obj
4032+
4033+ def remove_sound_track(self, factory) :
4034+ # remove factory from timeline
4035+ self.removeFactory(factory)
4036+ # update duration
4037+ _vtrack = self._get_video_track()
4038+ if _vtrack.duration != self.duration :
4039+ self.duration = _vtrack.duration
4040+
4041+
4042+
4043+
4044+
4045+
4046+
4047+
4048+
4049+ def update_timeline_duration(self, duration, sound_stop_with_video = True ) :
4050+
4051+
4052+ # copy objects for vaoiding update problems
4053+ _video_objs = copy.copy(self.video_objects)
4054+ self.debug('New duration %s image to update %s', duration, [ _obj.factory.name for _obj in _video_objs ] )
4055+
4056+ _old_duration = _video_objs[0].duration
4057+ # check type of image duration update
4058+ # if duration update increase, start update by the last image
4059+ # in timeline (right to left order)
4060+ # if duration update decrease, start update fu the first image
4061+ # in timeline (left to riht order )
4062+
4063+ _reverse = False
4064+ if duration > _old_duration :
4065+ # new duration > old duration
4066+ # start update of images in reverse order
4067+ _start = (len(_video_objs) -1) * duration
4068+ _video_objs.reverse()
4069+ _reverse = True
4070+ else :
4071+ # new duration < old duration
4072+ _start = 0
4073+
4074+ for _obj in _video_objs :
4075+
4076+ self.debug('Before Update %s : start %s duration %s, factory duration %s',
4077+ _obj.factory.name,
4078+ _obj.start,
4079+ _obj.duration,
4080+ _obj.factory.duration)
4081+
4082+ _obj.setDuration(duration, True)
4083+ _obj.setStart(_start, True)
4084+ if _reverse == True :
4085+ _start = _start - duration
4086+ else :
4087+ _start = _start + duration
4088+
4089+ self.debug('Updated %s : start %s duration %s ',
4090+ _obj.factory.name,
4091+ _obj._getStart(),
4092+ _obj._getDuration())
4093+
4094+ _total_duration = len(_video_objs) * duration
4095+ self.debug(' Total duration %s', _total_duration)
4096+ if sound_stop_with_video == True :
4097+ _obj = self._get_audio_object()
4098+ #TODO ; check case of audio track duration < images duration
4099+ if _obj is not None :
4100+ _obj.setDuration(_total_duration, True, True)
4101+ self.debug('NEW audio %s track duration %s factory duration %s',_obj, _obj.duration, _obj.factory.duration)
4102+ self.debug('NEW timeline duration %s', self.duration )
4103+
4104+
4105+ def _move_timeline_object(self, tl_src, tl_dest) :
4106+ _idx_src = self.video_objects.index(tl_src)
4107+ _idx_dest = self.video_objects.index(tl_dest)
4108+ # select objects to shift on timeline
4109+ if _idx_src < _idx_dest :
4110+ _objs_to_move = self.video_objects[_idx_src+1:_idx_dest]
4111+ _duration = -1 * tl_src.duration
4112+ #set start duration of src before dest
4113+ tl_src.start = self.video_objects[_idx_dest -1].start
4114+ else :
4115+ _objs_to_move = self.video_objects[_idx_dest:_idx_src]
4116+ _duration = 1 * tl_src.duration
4117+ #set start duration at dest
4118+ tl_src.start = self.video_objects[_idx_dest].start
4119+
4120+ # shift objects to move
4121+ self._shift_object(_objs_to_move, _duration)
4122+
4123+ return tl_src
4124+
4125+ def _move_at_end(self, tl_src):
4126+
4127+ # get objecs to move
4128+ _objs_to_move = self._getVideoObjsAfter(tl_src)
4129+
4130+ # set last postion fo tl_src
4131+ tl_src.start = self.video_objects[-1].start
4132+
4133+ # shift back the objects to move
4134+ _duration = -1*tl_src.duration
4135+ self._shift_object(_objs_to_move, _duration)
4136+
4137+ def _move_object(self, pos_src, pos_dest) :
4138+
4139+ if pos_src == pos_dest :
4140+ # nothing to move
4141+ return
4142+ _pos_dest = self.video_objects[pos_dest].start
4143+ _obj_src = self.video_objects[pos_src]
4144+ # select objects to move
4145+ if pos_src > pos_dest :
4146+ _objs_to_move = self.video_objects[pos_dest:pos_src]
4147+ _duration = 1 * _obj_src.duration
4148+ else :
4149+ _objs_to_move = self.video_objects[pos_src+1:pos_dest+1]
4150+ _duration = -1 * _obj_src.duration
4151+ # shift objects to move
4152+ self._shift_object(_objs_to_move, _duration)
4153+
4154+ # move object at despoistion
4155+ _obj_src.start = _pos_dest
4156+
4157+ def _get_track_objects(self, objs, track) :
4158+ track_objs = []
4159+ for _obj in objs :
4160+ if track == _obj.track_objects[0].track :
4161+ track_objs.append(_obj)
4162+
4163+ return track_objs
4164+
4165+
4166+
4167+
4168+
4169+ def _insert_factories_at_position(self, position, factories_to_insert) :
4170+ # TODO : undestand better disable/ Enable updates.
4171+ # the usage od disable/enable avoid a fade transition between images: Why ?
4172+ self.disableUpdates()
4173+ _timeline_objects = []
4174+ _position_obj = self.video_objects[position]
4175+ self.debug( "_position_obj :%s", _position_obj.factory.name)
4176+ _objs_after = self._getVideoObjsAfter(_position_obj)
4177+ _objs_after.insert(0, _position_obj)
4178+
4179+ _dur = _position_obj.start
4180+
4181+ for _factory in factories_to_insert :
4182+ _timeline_obect = self.create_timeline_object(_factory)
4183+ _timeline_objects.append(_timeline_obect)
4184+
4185+ _insert_duration = self._calc_objs_duration(_timeline_objects)
4186+ self._shift_object(_objs_after, _insert_duration)
4187+
4188+ for _tl_obj in _timeline_objects :
4189+ _tl_obj.start = _dur
4190+ _dur += _tl_obj.duration
4191+ self.addTimelineObject( _tl_obj)
4192+
4193+ self.enableUpdates()
4194+
4195+ def _calc_objs_duration(self, objs_to_insert) :
4196+ _duration = 0
4197+ for _obj in objs_to_insert :
4198+ _duration += _obj.duration
4199+ self.info("shift duration = %s", _duration)
4200+ return _duration
4201+
4202+ def _shift_object(self, timeline_objects, duration) :
4203+ for _obj in timeline_objects :
4204+ self.debug("%s", _obj.factory.name)
4205+ _obj.start += duration
4206+ self.info ( "%s timeline objects shifted from %s"%(len(timeline_objects), duration))
4207+
4208+ def _append_obj(self, factory):
4209+
4210+ timeline_object = self.addSourceFactory(factory)
4211+ self.info("appended %s"%timeline_object)
4212+ return timeline_object
4213+
4214+ def _remove_object(self, tl_obj) :
4215+ _objs_after = self._getVideoObjsAfter(tl_obj)
4216+ _duration = -1* tl_obj.duration
4217+ self.debug("obj to remove %s ", tl_obj)
4218+ _vtrack = self._get_video_track()
4219+ _vtrack.removeTrackObject(tl_obj)
4220+
4221+ # update start point of objects after deleted one
4222+ self._shift_object(_objs_after, _duration)
4223+
4224+
4225+ def _get_video_track(self) :
4226+ for track in self.tracks:
4227+ if isinstance(track.stream, VideoStream) :
4228+ return track
4229+
4230+ def _get_audio_track(self):
4231+ for track in self.tracks:
4232+ if isinstance(track.stream, AudioStream) :
4233+ return track
4234+
4235+
4236+ def _get_video_track_objects(self) :
4237+ _vtrack = self._get_video_track()
4238+
4239+ _res = _vtrack.track_objects
4240+ #self.debug("Video Track objects : %s",
4241+ # [ obj.factory.name for obj in _res])
4242+
4243+ return _res
4244+ video_objects = property(_get_video_track_objects)
4245+
4246+ def _get_audio_object(self) :
4247+ _audio_obj = []
4248+ _res = None
4249+ for _obj in self.timeline_objects :
4250+ if isinstance(_obj.track_objects[0].stream, AudioStream) :
4251+ _audio_obj.append(_obj)
4252+
4253+ if len(_audio_obj) > 1 :
4254+ raise TimelineError(" More then one audio stream in timeline")
4255+ elif len(_audio_obj) == 1 :
4256+ _res = _audio_obj[0]
4257+
4258+
4259+ return _res
4260+ audio_object = property(_get_audio_object)
4261+
4262+
4263+ def _getVideoObjsAfter(self, timeline_object) :
4264+ """
4265+ get video objects after timeline objects
4266+ This function retrieves objects only from video track
4267+ """
4268+ objects = []
4269+ priority= None
4270+ if timeline_object in self.video_objects :
4271+ _res = getNextObject(timeline_object, self.video_objects, priority)
4272+ while _res is not None :
4273+ objects.append(_res)
4274+ _res = getNextObject(_res, self.video_objects, priority)
4275+ else :
4276+ self.error('timeline_object %s not in video_track',
4277+ timeline_object.factory.name)
4278+
4279+ self.debug(" video objs after %s : %s",
4280+ timeline_object.factory.name,
4281+ [ obj.factory.name for obj in objects])
4282+ return objects
4283+
4284+ def _get_image_sequence(self) :
4285+ _list = [ _obj.factory.name for _obj in self.video_objects]
4286+ return _list
4287+
4288+
4289+
4290+
4291+class ProjectModeTimeline(ProjectMode):
4292+ __signals__ = {
4293+ 'timeline-loaded' : ['timeline'] ,
4294+ 'sound-props-changed' : ['active', 'stop_with_video'],
4295+ }
4296+
4297+ _25_FPS = (1 * gst.SECOND)/25
4298+
4299+ def __init__(self, ctrl_project) :
4300+ ProjectMode.__init__(self, ctrl_project, "TIMELINE")
4301+
4302+ self.acq_signals = SignalGroup()
4303+ self.timeline_signals = SignalGroup()
4304+ self.debug('ProjectModeTimeline initialized ')
4305+ self._is_stop_sound_with_video = False
4306+
4307+ # prepare connection to acquisiton mode
4308+ self._connect_to_ctrl_acq(self.ctrl_project.ctrl_acq)
4309+
4310+ def prepare(self, project) :
4311+ ProjectMode.prepare(self, project)
4312+
4313+ # prepare sources
4314+ self.sources = SourceList()
4315+ self.sources.connect("source-added", self._sourceAddedCb)
4316+ self.sources.connect("source-removed", self._sourceRemovedCb)
4317+ self.uris = []
4318+
4319+
4320+
4321+ # prepare timeline
4322+ self.timeline = LucioleTimeline()
4323+ self._timeline_factory = TimelineSourceFactory(self.timeline)
4324+
4325+ # prepare pipeline and resource
4326+ self._pipeline = Pipeline()
4327+ # TODO : connect to pipeline signals ?
4328+ #self._pipeline.connect('eos', self._eos_cb)
4329+
4330+ self._view_action = ViewAction()
4331+ self._view_action.addProducers(self._timeline_factory)
4332+ self._pipeline.addAction(self._view_action)
4333+ # why seeker ?
4334+ self.seeker = Seeker(80)
4335+
4336+ #
4337+ # initialize video settings
4338+ #
4339+ settings = self.getAutoSettings()
4340+ self._videocaps = settings.getVideoCaps()
4341+
4342+ # fill timeline with audio and video track
4343+ video = VideoStream(gst.Caps(settings.getVideoCaps()))
4344+ track = Track(video)
4345+ self.timeline.addTrack(track)
4346+ audio = AudioStream(gst.Caps(settings.getAudioCaps()))
4347+ track = Track(audio)
4348+ self.timeline.addTrack(track)
4349+
4350+
4351+ self._load_timeline(self.project)
4352+
4353+ self.debug("Prepare Timeline : %s", self.timeline )
4354+
4355+
4356+ def release(self) :
4357+ self._mode = "INVALID"
4358+ self._pipeline.release()
4359+ self._pipeline = None
4360+ del self._timeline_factory
4361+ del self.timeline
4362+ self._view_action = None
4363+
4364+
4365+
4366+ def active_viewer(self) :
4367+ if self._viewer :
4368+ self.debug(" Timeline :%s, timeline duration :%s ", self.timeline, self.timeline.duration)
4369+
4370+ self._viewer.setPipeline(None)
4371+ self._viewer.showSlider()
4372+ self._viewer.setAction(self._view_action)
4373+ self._viewer.setPipeline(self._pipeline, "TIMELINE")
4374+
4375+ # chnage pipeline status to activate it ( seek, duration)
4376+ self._pipeline.pause()
4377+ self.debug("Pipeline state : %s", self._pipeline.getState())
4378+ self._viewer.prepare_viewer(self.timeline.duration)
4379+ # FIXME : hack to make the gtk.HScale seek slider UI behave properly
4380+ #self._viewer._durationChangedCb(None, self.timeline.duration)
4381+ self.debug("actual sequence %s", [_obj.factory.name for _obj in self.timeline.video_objects])
4382+
4383+ else :
4384+ self.error("No viewer defined")
4385+
4386+ def change_order(self, images, target) :
4387+ self.debug('images : %s , target :%s', images, target)
4388+ self.timeline.change_order(images, target)
4389+ # update project
4390+ self._update_project()
4391+
4392+ def insert(self, images, target) :
4393+ self.debug('images : %s , target :%s', images, target)
4394+ # get factories from source
4395+ _factories = [ self._get_factory(_elem) for _elem in images ]
4396+ # insert factories on timeline
4397+ self.timeline.insert_factories(_factories, target)
4398+ # update project
4399+ self._update_project()
4400+
4401+ def remove(self, images) :
4402+ self.debug('images : %s' , images)
4403+ self.timeline.remove_images(images)
4404+
4405+ # update project
4406+ self._update_project()
4407+
4408+ def _update_project(self) :
4409+ """ update project properties according image sequence """
4410+ _img_seq = self.timeline._get_image_sequence()
4411+ self.info('Image sequence : %s', _img_seq)
4412+ # update project according image sequence
4413+ self.project.props['chrono_images'] =_img_seq
4414+ self.ctrl_project.is_modified = True
4415+
4416+
4417+ def add_sound_track(self, path) :
4418+ """ add a sound track to timeline """
4419+ if os.path.splitext(path)[1] != '.wav' :
4420+ _msg = _('Invalid sound file : %s. Only wav files allowed.' % path)
4421+ self.error(_msg)
4422+ self.ctrl_project.gui.status_bar.display_message(_msg)
4423+ return
4424+
4425+ self.debug('Add sound track : %s', path )
4426+ # check if a sound track is avlbl, if yes remove it
4427+ _dest_path = os.path.join( self.project.props['project_dir'],
4428+ LCONST.SOUND_TRACK_NAME)
4429+
4430+ # first remove current sound track from timeline and sources
4431+ self._remove_sound_track(True)
4432+
4433+
4434+
4435+
4436+ try :
4437+ LT.copyf(path, _dest_path)
4438+ except LEXCEP.LucioException, err:
4439+ self.error(err)
4440+ else :
4441+ self.AddSources([_dest_path])
4442+ self.ctrl_project.is_modified = True
4443+ self.project.props['sound']['name'] = LCONST.SOUND_TRACK_NAME
4444+ self._update_sound_props(self.project.props, active='no')
4445+ _msg = _('Sound track %s loaded in project'% path )
4446+ self.info(_msg)
4447+ self.ctrl_project.gui.status_bar.display_message(_msg)
4448+
4449+
4450+
4451+ def go_to_position(self, position) :
4452+
4453+ self._viewer.seek(position*self.default_duration)
4454+
4455+ def update_image_duration(self , fpi ) :
4456+ self.default_duration = self._25_FPS * fpi
4457+ # upadte image factory duration
4458+ for _factory in self.sources.getSources() :
4459+ if _factory.name == LCONST.SOUND_TRACK_NAME :
4460+ # dont update sound track duration
4461+ continue
4462+ _factory.duration = self.default_duration
4463+ self.debug('Updated duration (%s) of factory %s',
4464+ _factory.duration,
4465+ _factory.name)
4466+
4467+
4468+ self.timeline.update_timeline_duration(self.default_duration, self._is_stop_sound_with_video)
4469+
4470+
4471+
4472+ def _remove_sound_track(self, force = True) :
4473+ """
4474+ if force = True --> rm from timeline and source list
4475+ if force = False --> rm from timeline only
4476+ """
4477+ _factory = self._get_factory(LCONST.SOUND_TRACK_NAME)
4478+ if _factory is None :
4479+ # nothing to suppress
4480+ self.debug('No factory to suppress')
4481+ return
4482+
4483+ if self.project.props['sound']['active'] == 'yes' :
4484+ self.timeline.remove_sound_track(_factory)
4485+ # disconnect callback who updates audio duration regarding
4486+ # video duration
4487+ try :
4488+ self.timeline.disconnect_by_function(self._video_track_added)
4489+ except :
4490+ #TODO : what we do ?
4491+ pass
4492+ if force == True :
4493+ self.sources.removeUri(_factory.uri)
4494+
4495+ self._is_stop_sound_with_video = False
4496+
4497+
4498+
4499+
4500+ def add_sound_track_to_timeline(self) :
4501+ _factory = self._get_factory(LCONST.SOUND_TRACK_NAME)
4502+ if _factory :
4503+ self.debug('Sound Factory %s, %r , duration %s',
4504+ _factory.name,
4505+ _factory,
4506+ _factory.duration)
4507+ self._is_stop_sound_with_video = False
4508+
4509+ _tl_obj = self.timeline.add_sound_track(
4510+ _factory,
4511+ _factory.duration,
4512+ self._is_stop_sound_with_video,
4513+ 0)
4514+ self.timeline.connect('timeline-object-added', self._video_track_added, _tl_obj)
4515+ self.debug('Timeline duration : %s', self.timeline.duration)
4516+ self.debug('Pipeline duration : %s', self._pipeline.getDuration())
4517+ else :
4518+ self.warning ("Sound factory does not exists")
4519+
4520+ self._update_sound_props(self.project.props,
4521+ active ='yes',
4522+ stop_with_video = 'no'
4523+ )
4524+
4525+
4526+
4527+
4528+ def remove_sound_track_from_timeline(self) :
4529+ _factory = self._get_factory(LCONST.SOUND_TRACK_NAME)
4530+ if _factory :
4531+ self.debug('remove Sound Factory %s from timeline', _factory.name)
4532+ self.timeline.removeFactory(_factory)
4533+ self.timeline.disconnect_by_function(self._video_track_added)
4534+ else :
4535+ self.info('No sound track to remove')
4536+
4537+ self._update_sound_props(self.project.props, active ='no')
4538+
4539+
4540+
4541+ def _video_track_added(self, timeline , timeline_object , audio_object) :
4542+ if timeline_object.track_objects[0] in timeline.video_objects :
4543+ # a video object is added
4544+ _vtrack = timeline._get_video_track()
4545+ self.debug('video track duration %s , object added %s , audio_object duration :%s ',
4546+ _vtrack.duration, timeline_object.factory.name, audio_object.duration)
4547+
4548+ if _vtrack.duration > audio_object.duration :
4549+ audio_object.duration = _vtrack.duration
4550+ self.debug ('Audio object duration updated')
4551+ else :
4552+ self.info('timeline _object %s not a video track', timeline_object.factory.name)
4553+
4554+ def stop_sound_with_video(self, is_stop = True) :
4555+ self._is_stop_sound_with_video = is_stop
4556+ # check if audio_track yet in timeline
4557+ try :
4558+ _obj = self.timeline.audio_object
4559+ except TimelineError, err :
4560+ self.error('Timeline error : %s',err)
4561+ else :
4562+ self.debug("Update duration")
4563+ if _obj :
4564+ if self._is_stop_sound_with_video :
4565+ _vtrack = self.timeline._get_video_track()
4566+ _duration = _vtrack.duration
4567+ _stop_with_video = 'yes'
4568+
4569+ else :
4570+ self.debug('Set audio track duration')
4571+ _factory = self._get_factory(LCONST.SOUND_TRACK_NAME)
4572+ _duration = _factory.duration
4573+ _stop_with_video = 'no'
4574+
4575+ self._update_sound_props(self.project.props,
4576+ stop_with_video = _stop_with_video)
4577+ _obj.duration = _duration
4578+ self.debug("timeline duration %s", self.timeline.duration)
4579+
4580+
4581+
4582+ def _connect_to_ctrl_acq(self, ctrl_acq) :
4583+ self.debug(" Connecting to ProjectModeAcquisition ")
4584+ self.acq_signals.connect(
4585+ ctrl_acq,'snapshot-taken', None, self._snapshot_taken_cb)
4586+
4587+
4588+ def _snapshot_taken_cb(self, ctrl_acq, rush_image) :
4589+ self.debug('RX signal snapshot-taken : %s', rush_image.name)
4590+ #TODO : set the correct duration
4591+ self.AddSources([rush_image.path] )
4592+
4593+ def _load_timeline(self, project) :
4594+ _project = project.props
4595+ self.debug('rush images to load : %s', _project['capture_images'] )
4596+ _sources = \
4597+ [ os.path.join( _project['project_dir'],
4598+ _project['rush_dir'],
4599+ _image) \
4600+ for _image in _project['capture_images'] ]
4601+ _image_duration = self._25_FPS * int(_project['fpi'])
4602+ uris = ["file://" + urllib.quote(os.path.abspath(path))\
4603+ for path in _sources]
4604+
4605+ # check if sound track active
4606+ if _project['sound']['active'] == 'yes' :
4607+ # check if sound track really present
4608+ _sound_track = os.path.join(
4609+ _project['project_dir'],
4610+ LCONST.SOUND_TRACK_NAME)
4611+ if os.path.exists(_sound_track) :
4612+ _uri = "file://" + urllib.quote(os.path.abspath(_sound_track))
4613+ uris.append(_uri)
4614+ else :
4615+ self.error(
4616+ 'Sound track active but no sound file present (%s)' %
4617+ _sound_track)
4618+
4619+ # discover the factories
4620+ closure = {"rediscovered": 0}
4621+ discoverer = self.sources.discoverer
4622+ discoverer.connect("discovery-done", self._discovererDiscoveryDoneCb,
4623+ _project, _image_duration, uris, closure)
4624+ discoverer.connect("discovery-error", self._discovererDiscoveryErrorCb,
4625+ _project, _image_duration, uris, closure)
4626+
4627+ # start discoverer
4628+
4629+ self.sources.addUris(uris)
4630+ self.default_duration = _image_duration
4631+
4632+
4633+ def _finishLoadingProject(self, project, duration) :
4634+ _start = 0
4635+ self.debug ('project %s', project)
4636+ for _image in project['chrono_images'] :
4637+ _factory = self._get_factory(_image)
4638+ _factory.default_duration = duration
4639+ _tl_obj = self.timeline.create_timeline_object(_factory)
4640+ _tl_obj.start = _start
4641+ _tl_obj.duration = duration
4642+ _start += duration
4643+ self.timeline.addTimelineObject( _tl_obj)
4644+ if project['sound']['active'] == 'yes' :
4645+ _factory = self._get_factory(LCONST.SOUND_TRACK_NAME)
4646+ if _factory :
4647+
4648+ self.debug('Sound Factory %s, %r , duration %s',
4649+ _factory.name,
4650+ _factory,
4651+ _factory.duration)
4652+ self._is_stop_sound_with_video = False
4653+ if project['sound']['stop_with_video'] == 'yes' :
4654+ self._is_stop_sound_with_video = True
4655+ _tl_obj = self.timeline.add_sound_track(
4656+ _factory,
4657+ _factory.duration,
4658+ self._is_stop_sound_with_video,
4659+ 0)
4660+ self.timeline.connect('timeline-object-added', self._video_track_added, _tl_obj)
4661+ # force emit of project sound props
4662+ self._update_sound_props(
4663+ project = project,
4664+ active = project['sound']['active'],
4665+ stop_with_video = project['sound']['stop_with_video'])
4666+
4667+
4668+ self.debug('Emit timeline-loaded')
4669+ self.emit('timeline-loaded')
4670+
4671+
4672+
4673+ def _update_sound_props(self, project, active = None, stop_with_video = None) :
4674+ _is_modified = False
4675+ _is_active = None
4676+ _is_stop_with_video = None
4677+ if project is None :
4678+ self.error("No project defined")
4679+ return
4680+
4681+ if active is not None :
4682+ if active not in ('yes','no') :
4683+ self.error('invalid value for prop active (%s)',
4684+ active)
4685+ return
4686+ if active != project['sound']['active'] :
4687+ project['sound']['active'] = active
4688+ _is_modified = True
4689+
4690+ if active == 'yes' :
4691+ _is_active = True
4692+ else :
4693+ _is_active = False
4694+ if stop_with_video :
4695+ if stop_with_video not in ('yes','no') :
4696+ self.error('invalid value for prop active (%s)',
4697+ stop_with_video)
4698+ return
4699+
4700+ if stop_with_video != project['sound']['stop_with_video'] :
4701+ project['sound']['stop_with_video'] = stop_with_video
4702+ _is_modified = True
4703+ if stop_with_video == 'yes' :
4704+ _is_stop_with_video = True
4705+ else :
4706+ _is_stop_with_video = False
4707+
4708+ self.debug('sound is_active :%s is_stop_with_video : %s, project %s',
4709+ active,
4710+ stop_with_video,
4711+ project['sound'] )
4712+
4713+ if _is_modified == True :
4714+ self.ctrl_project.is_modified = True
4715+
4716+ if _is_stop_with_video is not None \
4717+ or \
4718+ _is_active is not None :
4719+ self.debug('Emit sound-props-changed : active %s stop_with_video %s',
4720+ _is_active, _is_stop_with_video )
4721+ self.emit('sound-props-changed', _is_active, _is_stop_with_video)
4722+
4723+
4724+
4725+ # Sources associated functions
4726+ #
4727+ def AddSources(self, file_list) :
4728+
4729+ uris = ["file://" + urllib.quote(os.path.abspath(path))\
4730+ for path in file_list]
4731+ self.uris = uris
4732+ self.debug("Sources to add %s", uris)
4733+ # add uris
4734+ self.sources.addUris(uris)
4735+
4736+ def _discovererDiscoveryDoneCb(self, discoverer, uri, factory,
4737+ project , duration, uris, closure):
4738+ self.debug("discovery done for %s",factory.name)
4739+ if factory.uri not in uris:
4740+ # someone else is using discoverer, this signal isn't for us
4741+ return
4742+
4743+ # update duration of image factory
4744+ if factory.name != LCONST.SOUND_TRACK_NAME :
4745+ factory.duration = self.default_duration
4746+
4747+
4748+ if factory not in self.sources.getSources() :
4749+ self.debug("Factory duration :%s", factory.duration)
4750+ self.sources.addFactory(factory)
4751+
4752+ closure["rediscovered"] += 1
4753+ if closure["rediscovered"] == len(uris):
4754+ self._finishLoadingProject(project, duration)
4755+
4756+ def _discovererDiscoveryErrorCb(self, discoverer, uri, factory,
4757+ project, duration, uris, closure):
4758+ self.warning('Impossible to load %s'%uri)
4759+
4760+ def _get_factory(self, image_name) :
4761+ _factory = None
4762+ for _source in self.sources.getSources() :
4763+ if _source.name == image_name :
4764+ _factory = _source
4765+ break
4766+ if _factory == None :
4767+ self.error("No factory found for %s", image_name)
4768+ return _factory
4769+
4770+ def _sourceAddedCb(self, sourcelist, factory):
4771+ #self.info("source added to timeline : %s %s"%(sourcelist, factory))
4772+ factory.setFilterCaps(self._videocaps)
4773+ factory.default_duration = self.default_duration
4774+ # #timeline_object = self.timeline.addSourceFactory(factory)
4775+ #self.info("Object %s starts at %s", factory.name, timeline_object.start)
4776+ self.debug("RX source-added Source %s added -- %s", factory.name, factory)
4777+
4778+ def _sourceRemovedCb(self, sourcelist, uri, factory):
4779+ self.timeline.removeFactory(factory)
4780+ self.info('Source %s removed call back'%uri)
4781+ # TODO : emit source-removed ?
4782+ #self.emit('source-removed', uri, factory)
4783+
4784+ def getAutoSettings(self):
4785+ """
4786+ Computes and returns smart settings for the project.
4787+ If the project only has one source, it will be that source's settings.
4788+ If it has more than one, it will return the largest setting that suits
4789+ all contained sources.
4790+ """
4791+ settings = ExportSettings()
4792+ if not self.timeline:
4793+ self.warning\
4794+ ("project doesn't have a timeline, returning default settings")
4795+ return settings
4796+
4797+ # FIXME: this is ugly, but rendering for now assumes at most one audio
4798+ # and one video tracks
4799+ have_audio = have_video = False
4800+ for track in self.timeline.tracks:
4801+ if isinstance(track.stream, VideoStream) and track.duration != 0:
4802+ have_video = True
4803+ elif isinstance(track.stream, AudioStream) and track.duration != 0:
4804+ have_audio = True
4805+
4806+ if not have_audio:
4807+ settings.aencoder = None
4808+
4809+ if not have_video:
4810+ settings.vencoder = None
4811+
4812+ return settings
4813+
4814
4815=== modified file 'luciole/ctrl/import_image.py'
4816--- luciole/ctrl/import_image.py 2010-09-04 15:17:48 +0000
4817+++ luciole/ctrl/import_image.py 2011-02-28 13:21:06 +0000
4818@@ -36,6 +36,9 @@
4819 # N/A
4820 import gobject
4821
4822+from pitivi.signalinterface import Signallable
4823+from pitivi.log.loggable import Loggable
4824+
4825 # local application/library specific imports
4826 import luciole.base.exceptions as LEXCEP
4827 import luciole.base.tools as LT
4828@@ -62,6 +65,7 @@
4829 """ run thread """
4830 # create rush list object
4831 _image_objs = []
4832+
4833 for (_index, _filename) in enumerate(self.__filenames) :
4834 # copy image to rush folder and resize it
4835 try :
4836@@ -116,8 +120,11 @@
4837 LT.copyf(p_image_path, _ac_image_temp)
4838
4839 # 2. resize image result is in _ac_image_temp_rz
4840- _rz_obj = LI.ImageResize(_ac_image_temp, _ac_image_temp_rz )
4841- _rz_obj.convert()
4842+ if self.__pjt_data['resize'] == 'yes' :
4843+ _rz_obj = LI.ImageResize(_ac_image_temp, _ac_image_temp_rz )
4844+ _rz_obj.convert()
4845+ else :
4846+ _ac_image_temp_rz = _ac_image_temp
4847
4848 # 3. move resized image to rush dire
4849 LT.movef(_ac_image_temp_rz, _rush_image)
4850@@ -127,7 +134,7 @@
4851 return _basename
4852
4853
4854-class ImportController(object):
4855+class ImportController(Signallable, Loggable):
4856 """ This class is used to manage the import of image in project .
4857 The class use a Thread for importing images (it can takes) long.
4858 The thread and this class communicate via a queue.
4859@@ -135,17 +142,21 @@
4860 The queue is checked cyclicly via a gobject timer
4861 """
4862 _TIMEOUT = 50 # 50 ms timer
4863-
4864- def __init__(self, filenames, project_data, rusher, status_bar, cbs ) :
4865+ __signals__ = {
4866+ 'import-image-done' : ['image_name'],
4867+ }
4868+ def __init__(self, filenames, project_data, rusher, status_bar) :
4869 """ Init Thread, init gobject timer and clear progressbar """
4870+ Signallable.__init__(self)
4871+ Loggable.__init__(self)
4872 self.__filenames = filenames
4873 self.__pjt_data = project_data
4874 self.__rusher = rusher
4875 self.__status_bar = status_bar
4876- self.__cbs = cbs
4877
4878
4879 # initialize thread
4880+ self.debug('Starting thread')
4881 self.queue = Queue.Queue()
4882 self.t_rush = ImportThread(self.__filenames,
4883 self.__pjt_data,
4884@@ -176,11 +187,15 @@
4885 pass
4886 else :
4887 if _qmsg.has_key('progression') :
4888+
4889+ self.debug(' on progress')
4890 self._progress_bar_on_progress(_qmsg['progression'])
4891 if _qmsg.has_key('finish') :
4892 # finisg project load ( Non threaded section )
4893- self.__cbs['done-import'](_qmsg['finish'])
4894-
4895+ self.debug('emit import-image-done : %s',_qmsg['finish'])
4896+ self.emit('import-image-done', _qmsg['finish'])
4897+
4898+
4899 #update as finsih staus/progress bard
4900 self._progress_bar_complete()
4901 # stop the timer
4902@@ -198,6 +213,7 @@
4903 _msg = _('All images imported')
4904 # use of idle_add because gui update not in the same thread
4905 self.__status_bar.stop(_msg)
4906+
4907
4908 def _progress_bar_on_progress(self, ratio = None):
4909 """ indicate that import is going on """
4910
4911=== modified file 'luciole/ctrl/load_rush.py'
4912--- luciole/ctrl/load_rush.py 2010-09-04 15:17:48 +0000
4913+++ luciole/ctrl/load_rush.py 2011-02-28 13:21:06 +0000
4914@@ -35,6 +35,7 @@
4915
4916 # related third party imports
4917 import gobject
4918+from pitivi.signalinterface import Signallable
4919
4920 # local application/library specific imports
4921 import luciole.media.image as LI
4922@@ -80,24 +81,27 @@
4923 self.queue.put(_qmsg, block=False)
4924
4925
4926-class ControllerLoadRush(object):
4927+class ControllerLoadRush(Signallable):
4928 """ This class is used to manage the load of a project in application.
4929 The class use a Thread for launch rush object init. (it can takes) long.
4930 The thread and this class communicate vai a queue. Information on queue are progression and finish.
4931 The queue is checked cyclicly via a gobject timer
4932 """
4933 _TIMEOUT = 10 # 10 ms timer
4934+
4935+ __signals__ = {
4936+ "rush-loaded" : ["rush_obj"],
4937+ }
4938
4939- def __init__(self, project, status_bar, cb_on_finish) :
4940+ def __init__(self, project, status_bar) :
4941 """ Init Thread, init gobject timer and clear progressbar """
4942- self.project = project
4943- self._cb_on_finish = cb_on_finish
4944-
4945+ Signallable.__init__(self)
4946+ self.project = project.props
4947 self.__status_bar = status_bar
4948
4949 # initialize thread
4950 self.queue = Queue.Queue()
4951- self.t_rush = RushLoaderThread(project, self.queue)
4952+ self.t_rush = RushLoaderThread(self.project, self.queue)
4953
4954 #clear progressbar
4955 self._progress_bar_clear()
4956@@ -144,6 +148,7 @@
4957
4958 def _progress_bar_complete(self):
4959 """ Progress bar full : Project loaded """
4960+ # TODO : This message should not displayed here
4961 _msg = _( 'Project %s is loaded')%self.project['project_name']
4962 # use of idle_add because gui update not in the same thread
4963 self.__status_bar.stop(_msg)
4964@@ -157,7 +162,7 @@
4965 def _on_rush_finish(self, rush_obj):
4966 """ Finish the load after rush generation """
4967 # call back to indicate finish at controller
4968- self._cb_on_finish(rush_obj)
4969+ self.emit("rush-loaded",rush_obj )
4970
4971
4972
4973
4974=== modified file 'luciole/ctrl/viewer_ctrl.py'
4975--- luciole/ctrl/viewer_ctrl.py 2010-09-06 09:00:55 +0000
4976+++ luciole/ctrl/viewer_ctrl.py 2011-02-28 13:21:06 +0000
4977@@ -225,7 +225,14 @@
4978 # send error message
4979 _msg = _("Can not play animation : No image on montage view ")
4980 self.__gui.window.error_message(_msg)
4981-
4982+
4983+ def play_query_position(self) :
4984+ """ query position """
4985+ _position = LCONST.CLOCK_TIME_NONE
4986+ _duration = LCONST.CLOCK_TIME_NONE
4987+ if self.__mode == self.PLAYER :
4988+ (_position, _duration) = self.__lucio_player.query_postion()
4989+ return (_position, _duration)
4990
4991 def play_video(self, app_data) :
4992 """ play a video from chrono images """
4993
4994=== added file 'luciole/data/images/white.jpg'
4995Binary files luciole/data/images/white.jpg 1970-01-01 00:00:00 +0000 and luciole/data/images/white.jpg 2011-02-28 13:21:06 +0000 differ
4996=== modified file 'luciole/data/templates/project_template.xml'
4997--- luciole/data/templates/project_template.xml 2010-03-30 07:18:34 +0000
4998+++ luciole/data/templates/project_template.xml 2011-02-28 13:21:06 +0000
4999@@ -3,7 +3,8 @@
5000 <metas>
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes:
to status/vote changes: