Advertisement
Guest User

Untitled

a guest
Feb 21st, 2020
96
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.83 KB | None | 0 0
  1. class StartDurationProperties(Gtk.Expander, Loggable):
  2. """Widget for configuring the placement and size of the clip."""
  3.  
  4. __signals__ = {
  5. 'selection-changed': []}
  6.  
  7. def __init__(self, app):
  8. Gtk.Expander.__init__(self)
  9. Loggable.__init__(self)
  10. self.settings = {}
  11. self.app = app
  12. self._project = None
  13. self._selection = None
  14. self.source = None
  15. self._selected_clip = None
  16. self.spin_buttons = {}
  17. self.spin_buttons_handler_ids = {}
  18. self.set_label(_("Start/Duration"))
  19.  
  20. self.builder = Gtk.Builder()
  21. self.builder.add_from_file(os.path.join(get_ui_dir(),
  22. "cliplength.ui"))
  23. self.__control_bindings = {}
  24. # Used to make sure self.__control_bindings_changed doesn't get called
  25. # when bindings are changed from this class
  26. self.__own_bindings_change = False
  27. self.add(self.builder.get_object("start_duration_box"))
  28. self._init_buttons()
  29. self._init_box_spinners()
  30. self.show_all()
  31. self.hide()
  32.  
  33. self.app.project_manager.connect_after(
  34. "new-project-loaded", self._new_project_loaded_cb)
  35. self.app.project_manager.connect_after(
  36. "project-closed", self.__project_closed_cb)
  37.  
  38. def _new_project_loaded_cb(self, unused_app, project):
  39. if self._selection is not None:
  40. self._selection.disconnect_by_func(self._selection_changed_cb)
  41. self._selection = None
  42. if self._project:
  43. self._project.pipeline.disconnect_by_func(self._position_cb)
  44.  
  45. self._project = project
  46. if project:
  47. self._selection = project.ges_timeline.ui.selection
  48. self._selection.connect('selection-changed', self._selection_changed_cb)
  49. self._project.pipeline.connect("position", self._position_cb)
  50.  
  51. def __project_closed_cb(self, unused_project_manager, unused_project):
  52. self._project = None
  53.  
  54. def _init_buttons(self):
  55. spinbtn_start = self.builder.get_object("start_point_spinbtn")
  56. spinbtn_start.connect("value-changed", self._on_start_changed_cb)
  57. disable_scroll(spinbtn_start)
  58. spinbtn_duration = self.builder.get_object("duration_spinbtn")
  59. spinbtn_duration.connect("value-changed", self._on_duration_changed_cb)
  60. disable_scroll(spinbtn_duration)
  61.  
  62. def _init_box_spinners(self):
  63. self.__setup_box_spinner("duration_measurement")
  64. self.__setup_box_spinner("start_point_measurement")
  65.  
  66. def __update_control_bindings(self):
  67. self.__control_bindings = {}
  68. if self.__source_uses_keyframes():
  69. self.__set_control_bindings()
  70.  
  71. def __source_uses_keyframes(self):
  72. if self.source is None:
  73. return False
  74.  
  75. for prop in ["posy"]:
  76. binding = self.source.get_control_binding(prop)
  77. if binding is None:
  78. return False
  79.  
  80. return True
  81.  
  82. def __remove_control_bindings(self):
  83. for propname, binding in self.__control_bindings.items():
  84. control_source = binding.props.control_source
  85. # control_source.unset_all() can't be used here as it doesn't emit
  86. # the 'value-removed' signal, so the undo system wouldn't notice
  87. # the removed keyframes
  88. keyframes_ts = [keyframe.timestamp for keyframe in control_source.get_all()]
  89. for ts in keyframes_ts:
  90. control_source.unset(ts)
  91. self.__own_bindings_change = True
  92. self.source.remove_control_binding(propname)
  93. self.__own_bindings_change = False
  94. self.__control_bindings = {}
  95.  
  96. def __set_control_bindings(self):
  97. adding_kfs = not self.__source_uses_keyframes()
  98.  
  99. if adding_kfs:
  100. self.app.action_log.begin("Transformation properties keyframes activate",
  101. toplevel=True)
  102.  
  103. for prop in ["posy"]:
  104. binding = self.source.get_control_binding(prop)
  105.  
  106. if not binding:
  107. control_source = GstController.InterpolationControlSource()
  108. control_source.props.mode = GstController.InterpolationMode.LINEAR
  109. self.__own_bindings_change = True
  110. self.source.set_control_source(control_source, prop, "direct-absolute")
  111. self.__own_bindings_change = False
  112. self.__set_default_keyframes_values(control_source, prop)
  113.  
  114. binding = self.source.get_control_binding(prop)
  115. self.__control_bindings[prop] = binding
  116.  
  117. if adding_kfs:
  118. self.app.action_log.commit("Transformation properties keyframes activate")
  119.  
  120. def __set_default_keyframes_values(self, control_source, prop):
  121. res, val = self.source.get_child_property(prop)
  122. assert res
  123. control_source.set(self.source.props.in_point, val)
  124. control_source.set(self.source.props.in_point + self.source.props.duration, val)
  125.  
  126. def _default_values_cb(self, unused_widget):
  127. with self.app.action_log.started("Transformation properties reset default",
  128. finalizing_action=CommitTimelineFinalizingAction(self._project.pipeline),
  129. toplevel=True):
  130. if self.__source_uses_keyframes():
  131. self.__remove_control_bindings()
  132.  
  133. for prop in ["posy"]:
  134. self.source.set_child_property(prop, self.source.ui.default_position[prop])
  135.  
  136. def __get_source_property(self, prop):
  137. if self.__source_uses_keyframes():
  138. try:
  139. position = self._project.pipeline.get_position()
  140. start = self.source.props.start
  141. in_point = self.source.props.in_point
  142. duration = self.source.props.duration
  143.  
  144. # If the position is outside of the clip, take the property
  145. # value at the start/end (whichever is closer) of the clip.
  146. source_position = max(0, min(position - start, duration - 1)) + in_point
  147. value = self.__control_bindings[prop].get_value(source_position)
  148. res = value is not None
  149. return res, value
  150. except PipelineError:
  151. pass
  152.  
  153. return self.source.get_child_property(prop)
  154.  
  155. def _position_cb(self, unused_pipeline, unused_position):
  156. if not self.__source_uses_keyframes():
  157. return
  158. for prop in ["posx", "posy", "width"]:
  159. self.__update_spin_btn(prop)
  160. # Keep the overlay stack in sync with the spin buttons values
  161. self.app.gui.editor.viewer.overlay_stack.update(self.source)
  162.  
  163. def __source_property_changed_cb(self, unused_source, unused_element, param):
  164. self.__update_spin_btn(param.name)
  165.  
  166. def __update_spin_btn(self, prop):
  167. assert self.source
  168.  
  169. try:
  170. spin = self.spin_buttons[prop]
  171. spin_handler_id = self.spin_buttons_handler_ids[prop]
  172. except KeyError:
  173. return
  174.  
  175. res, value = self.__get_source_property(prop)
  176. assert res
  177. if spin.get_value() != value:
  178. # Make sure self._on_value_changed_cb doesn't get called here. If that
  179. # happens, we might have unintended keyframes added.
  180. with spin.handler_block(spin_handler_id):
  181. spin.set_value(value)
  182.  
  183. def _control_bindings_changed(self, unused_track_element, unused_binding):
  184. if self.__own_bindings_change:
  185. # Do nothing if the change occurred from this class
  186. return
  187.  
  188. self.__update_control_bindings()
  189.  
  190. def __set_prop(self, prop, value):
  191. assert self.source
  192.  
  193. if self.__source_uses_keyframes():
  194. try:
  195. position = self._project.pipeline.get_position()
  196. start = self.source.props.start
  197. in_point = self.source.props.in_point
  198. duration = self.source.props.duration
  199. if position < start or position > start + duration:
  200. return
  201. source_position = position - start + in_point
  202.  
  203. with self.app.action_log.started(
  204. "Transformation property change",
  205. finalizing_action=CommitTimelineFinalizingAction(self._project.pipeline),
  206. toplevel=True):
  207. self.__control_bindings[prop].props.control_source.set(source_position, value)
  208. except PipelineError:
  209. self.warning("Could not get pipeline position")
  210. return
  211. else:
  212. with self.app.action_log.started("Transformation property change",
  213. finalizing_action=CommitTimelineFinalizingAction(self._project.pipeline),
  214. toplevel=True):
  215. self.source.set_child_property(prop, value)
  216.  
  217. def __setup_spin_button(self, widget_name, property_name):
  218. """Creates a SpinButton for editing a property value."""
  219. spinbtn = self.builder.get_object(widget_name)
  220. handler_id = spinbtn.connect("value-changed", self._on_value_changed_cb, property_name)
  221. disable_scroll(spinbtn)
  222. self.spin_buttons[property_name] = spinbtn
  223. self.spin_buttons_handler_ids[property_name] = handler_id
  224.  
  225. def __setup_box_spinner(self, spinner_id):
  226. seconds_name = spinner_id + "_seconds"
  227. frames_name = spinner_id + "_frames"
  228. self.settings[spinner_id] = self.builder.get_object(spinner_id)
  229. for value_id, text in ((seconds_name, _("Seconds")),
  230. (frames_name, _("Frames"))):
  231. self.settings[spinner_id].append(value_id, text)
  232.  
  233. def _on_duration_changed_cb(self, spinbtn):
  234. ges_clip = self._project.ges_timeline.ui.selection.get_single_clip(GES.Clip)
  235.  
  236. editing_context = EditingContext(ges_clip,
  237. self._project.ges_timeline,
  238. GES.EditMode.EDIT_TRIM,
  239. GES.Edge.EDGE_END,
  240. self.app,
  241. True)
  242.  
  243. editing_context.edit_to(spinbtn.get_value() * Gst.SECOND + ges_clip.get_start(), ges_clip.ui.layer.ges_layer)
  244. editing_context.finish()
  245.  
  246. def _on_start_changed_cb(self, spinbtn):
  247. ges_clip = self._project.ges_timeline.ui.selection.get_single_clip(GES.Clip)
  248.  
  249. editing_context = EditingContext(ges_clip,
  250. self._project.ges_timeline,
  251. GES.EditMode.EDIT_NORMAL,
  252. GES.Edge.EDGE_NONE,
  253. self.app,
  254. True)
  255.  
  256. editing_context.edit_to(spinbtn.get_value() * Gst.SECOND, ges_clip.ui.layer.ges_layer)
  257. editing_context.finish()
  258.  
  259. def _on_value_changed_cb(self, spinbtn, prop):
  260. if not self.source:
  261. return
  262.  
  263. value = spinbtn.get_value()
  264.  
  265. res, cvalue = self.__get_source_property(prop)
  266. if not res:
  267. return
  268.  
  269. if value != cvalue:
  270. self.__set_prop(prop, value)
  271. self.app.gui.editor.viewer.overlay_stack.update(self.source)
  272.  
  273. def __set_source(self, source):
  274. if self.source:
  275. try:
  276. self.source.disconnect_by_func(self.__source_property_changed_cb)
  277. disconnect_all_by_func(self.source, self._control_bindings_changed)
  278. except TypeError:
  279. pass
  280. self.source = source
  281. if self.source:
  282. self.__update_control_bindings()
  283. for prop in self.spin_buttons:
  284. self.__update_spin_btn(prop)
  285. self.source.connect("deep-notify", self.__source_property_changed_cb)
  286. self.source.connect("control-binding-added", self._control_bindings_changed)
  287. self.source.connect("control-binding-removed", self._control_bindings_changed)
  288.  
  289. def _selection_changed_cb(self, unused_timeline):
  290. if len(self._selection) == 1:
  291. clip = list(self._selection)[0]
  292. source = clip.find_track_element(None, GES.VideoSource)
  293. if source:
  294. self._selected_clip = clip
  295. self.__set_source(source)
  296. self.app.gui.editor.viewer.overlay_stack.select(source)
  297. self.show()
  298. return
  299.  
  300. # Deselect
  301. if self._selected_clip:
  302. self._selected_clip = None
  303. self._project.pipeline.commit_timeline()
  304. self.__set_source(None)
  305. self.hide()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement