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