Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- class StartDurationProperties(Gtk.Expander, Loggable):
- """Widget for configuring the placement and size of the clip."""
- __signals__ = {
- 'selection-changed': []}
- def __init__(self, app):
- Gtk.Expander.__init__(self)
- Loggable.__init__(self)
- self.settings = {}
- self.app = app
- self._project = None
- self._selection = None
- self.source = None
- self._selected_clip = None
- self.spin_buttons = {}
- self.spin_buttons_handler_ids = {}
- self.set_label(_("Start/Duration"))
- self.builder = Gtk.Builder()
- self.builder.add_from_file(os.path.join(get_ui_dir(),
- "cliplength.ui"))
- self.__control_bindings = {}
- # Used to make sure self.__control_bindings_changed doesn't get called
- # when bindings are changed from this class
- self.__own_bindings_change = False
- self.add(self.builder.get_object("start_duration_box"))
- self._init_buttons()
- self._init_box_spinners()
- self.show_all()
- self.hide()
- self.app.project_manager.connect_after(
- "new-project-loaded", self._new_project_loaded_cb)
- self.app.project_manager.connect_after(
- "project-closed", self.__project_closed_cb)
- def _new_project_loaded_cb(self, unused_app, project):
- if self._selection is not None:
- self._selection.disconnect_by_func(self._selection_changed_cb)
- self._selection = None
- if self._project:
- self._project.pipeline.disconnect_by_func(self._position_cb)
- self._project = project
- if project:
- self._selection = project.ges_timeline.ui.selection
- self._selection.connect('selection-changed', self._selection_changed_cb)
- self._project.pipeline.connect("position", self._position_cb)
- def __project_closed_cb(self, unused_project_manager, unused_project):
- self._project = None
- def _init_buttons(self):
- spinbtn_start = self.builder.get_object("start_point_spinbtn")
- spinbtn_start.connect("value-changed", self._on_start_changed_cb)
- disable_scroll(spinbtn_start)
- spinbtn_duration = self.builder.get_object("duration_spinbtn")
- spinbtn_duration.connect("value-changed", self._on_duration_changed_cb)
- disable_scroll(spinbtn_duration)
- def _init_box_spinners(self):
- self.__setup_box_spinner("duration_measurement")
- self.__setup_box_spinner("start_point_measurement")
- def __update_control_bindings(self):
- self.__control_bindings = {}
- if self.__source_uses_keyframes():
- self.__set_control_bindings()
- def __source_uses_keyframes(self):
- if self.source is None:
- return False
- for prop in ["posy"]:
- binding = self.source.get_control_binding(prop)
- if binding is None:
- return False
- return True
- def __remove_control_bindings(self):
- for propname, binding in self.__control_bindings.items():
- control_source = binding.props.control_source
- # control_source.unset_all() can't be used here as it doesn't emit
- # the 'value-removed' signal, so the undo system wouldn't notice
- # the removed keyframes
- keyframes_ts = [keyframe.timestamp for keyframe in control_source.get_all()]
- for ts in keyframes_ts:
- control_source.unset(ts)
- self.__own_bindings_change = True
- self.source.remove_control_binding(propname)
- self.__own_bindings_change = False
- self.__control_bindings = {}
- def __set_control_bindings(self):
- adding_kfs = not self.__source_uses_keyframes()
- if adding_kfs:
- self.app.action_log.begin("Transformation properties keyframes activate",
- toplevel=True)
- for prop in ["posy"]:
- binding = self.source.get_control_binding(prop)
- if not binding:
- control_source = GstController.InterpolationControlSource()
- control_source.props.mode = GstController.InterpolationMode.LINEAR
- self.__own_bindings_change = True
- self.source.set_control_source(control_source, prop, "direct-absolute")
- self.__own_bindings_change = False
- self.__set_default_keyframes_values(control_source, prop)
- binding = self.source.get_control_binding(prop)
- self.__control_bindings[prop] = binding
- if adding_kfs:
- self.app.action_log.commit("Transformation properties keyframes activate")
- def __set_default_keyframes_values(self, control_source, prop):
- res, val = self.source.get_child_property(prop)
- assert res
- control_source.set(self.source.props.in_point, val)
- control_source.set(self.source.props.in_point + self.source.props.duration, val)
- def _default_values_cb(self, unused_widget):
- with self.app.action_log.started("Transformation properties reset default",
- finalizing_action=CommitTimelineFinalizingAction(self._project.pipeline),
- toplevel=True):
- if self.__source_uses_keyframes():
- self.__remove_control_bindings()
- for prop in ["posy"]:
- self.source.set_child_property(prop, self.source.ui.default_position[prop])
- def __get_source_property(self, prop):
- if self.__source_uses_keyframes():
- try:
- position = self._project.pipeline.get_position()
- start = self.source.props.start
- in_point = self.source.props.in_point
- duration = self.source.props.duration
- # If the position is outside of the clip, take the property
- # value at the start/end (whichever is closer) of the clip.
- source_position = max(0, min(position - start, duration - 1)) + in_point
- value = self.__control_bindings[prop].get_value(source_position)
- res = value is not None
- return res, value
- except PipelineError:
- pass
- return self.source.get_child_property(prop)
- def _position_cb(self, unused_pipeline, unused_position):
- if not self.__source_uses_keyframes():
- return
- for prop in ["posx", "posy", "width"]:
- self.__update_spin_btn(prop)
- # Keep the overlay stack in sync with the spin buttons values
- self.app.gui.editor.viewer.overlay_stack.update(self.source)
- def __source_property_changed_cb(self, unused_source, unused_element, param):
- self.__update_spin_btn(param.name)
- def __update_spin_btn(self, prop):
- assert self.source
- try:
- spin = self.spin_buttons[prop]
- spin_handler_id = self.spin_buttons_handler_ids[prop]
- except KeyError:
- return
- res, value = self.__get_source_property(prop)
- assert res
- if spin.get_value() != value:
- # Make sure self._on_value_changed_cb doesn't get called here. If that
- # happens, we might have unintended keyframes added.
- with spin.handler_block(spin_handler_id):
- spin.set_value(value)
- def _control_bindings_changed(self, unused_track_element, unused_binding):
- if self.__own_bindings_change:
- # Do nothing if the change occurred from this class
- return
- self.__update_control_bindings()
- def __set_prop(self, prop, value):
- assert self.source
- if self.__source_uses_keyframes():
- try:
- position = self._project.pipeline.get_position()
- start = self.source.props.start
- in_point = self.source.props.in_point
- duration = self.source.props.duration
- if position < start or position > start + duration:
- return
- source_position = position - start + in_point
- with self.app.action_log.started(
- "Transformation property change",
- finalizing_action=CommitTimelineFinalizingAction(self._project.pipeline),
- toplevel=True):
- self.__control_bindings[prop].props.control_source.set(source_position, value)
- except PipelineError:
- self.warning("Could not get pipeline position")
- return
- else:
- with self.app.action_log.started("Transformation property change",
- finalizing_action=CommitTimelineFinalizingAction(self._project.pipeline),
- toplevel=True):
- self.source.set_child_property(prop, value)
- def __setup_spin_button(self, widget_name, property_name):
- """Creates a SpinButton for editing a property value."""
- spinbtn = self.builder.get_object(widget_name)
- handler_id = spinbtn.connect("value-changed", self._on_value_changed_cb, property_name)
- disable_scroll(spinbtn)
- self.spin_buttons[property_name] = spinbtn
- self.spin_buttons_handler_ids[property_name] = handler_id
- def __setup_box_spinner(self, spinner_id):
- seconds_name = spinner_id + "_seconds"
- frames_name = spinner_id + "_frames"
- self.settings[spinner_id] = self.builder.get_object(spinner_id)
- for value_id, text in ((seconds_name, _("Seconds")),
- (frames_name, _("Frames"))):
- self.settings[spinner_id].append(value_id, text)
- def _on_duration_changed_cb(self, spinbtn):
- ges_clip = self._project.ges_timeline.ui.selection.get_single_clip(GES.Clip)
- editing_context = EditingContext(ges_clip,
- self._project.ges_timeline,
- GES.EditMode.EDIT_TRIM,
- GES.Edge.EDGE_END,
- self.app,
- True)
- editing_context.edit_to(spinbtn.get_value() * Gst.SECOND + ges_clip.get_start(), ges_clip.ui.layer.ges_layer)
- editing_context.finish()
- def _on_start_changed_cb(self, spinbtn):
- ges_clip = self._project.ges_timeline.ui.selection.get_single_clip(GES.Clip)
- editing_context = EditingContext(ges_clip,
- self._project.ges_timeline,
- GES.EditMode.EDIT_NORMAL,
- GES.Edge.EDGE_NONE,
- self.app,
- True)
- editing_context.edit_to(spinbtn.get_value() * Gst.SECOND, ges_clip.ui.layer.ges_layer)
- editing_context.finish()
- def _on_value_changed_cb(self, spinbtn, prop):
- if not self.source:
- return
- value = spinbtn.get_value()
- res, cvalue = self.__get_source_property(prop)
- if not res:
- return
- if value != cvalue:
- self.__set_prop(prop, value)
- self.app.gui.editor.viewer.overlay_stack.update(self.source)
- def __set_source(self, source):
- if self.source:
- try:
- self.source.disconnect_by_func(self.__source_property_changed_cb)
- disconnect_all_by_func(self.source, self._control_bindings_changed)
- except TypeError:
- pass
- self.source = source
- if self.source:
- self.__update_control_bindings()
- for prop in self.spin_buttons:
- self.__update_spin_btn(prop)
- self.source.connect("deep-notify", self.__source_property_changed_cb)
- self.source.connect("control-binding-added", self._control_bindings_changed)
- self.source.connect("control-binding-removed", self._control_bindings_changed)
- def _selection_changed_cb(self, unused_timeline):
- if len(self._selection) == 1:
- clip = list(self._selection)[0]
- source = clip.find_track_element(None, GES.VideoSource)
- if source:
- self._selected_clip = clip
- self.__set_source(source)
- self.app.gui.editor.viewer.overlay_stack.select(source)
- self.show()
- return
- # Deselect
- if self._selected_clip:
- self._selected_clip = None
- self._project.pipeline.commit_timeline()
- self.__set_source(None)
- self.hide()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement