[pitivi] clipproperties: Enable specifing a blending mode
- From: Alexandru Băluț <alexbalut src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pitivi] clipproperties: Enable specifing a blending mode
- Date: Fri, 6 May 2022 00:41:31 +0000 (UTC)
commit a748da28a6d2f12d0dce5b683c4e8d305706f8f8
Author: hulettdalton <hulettdalton gmail com>
Date: Tue Apr 27 10:17:51 2021 -0500
clipproperties: Enable specifing a blending mode
Changes were made in GES to expose the compositor's "operator"
property to clips through their video source.
This change implements a selector for blending modes that changes
an individual clip's operator property, which changes how it is
blended.
Fixes #2313
data/ui/clipblending.ui | 41 ++++++++++++++++++++
data/ui/clipcompositing.ui | 55 ++++++++++++++++++++-------
pitivi/clip_properties/compositing.py | 71 ++++++++++++++++++++++++++---------
pitivi/clipproperties.py | 1 +
tests/test_clipproperties.py | 36 ++++++++++++++++++
5 files changed, 174 insertions(+), 30 deletions(-)
---
diff --git a/data/ui/clipblending.ui b/data/ui/clipblending.ui
new file mode 100644
index 000000000..dc4c2978a
--- /dev/null
+++ b/data/ui/clipblending.ui
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.2 -->
+<interface>
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkGrid" id="blending_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Blending Mode:</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="blending_mode">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="active">1</property>
+ <signal name="changed" handler="_blending_property_changed_cb" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+</interface>
diff --git a/data/ui/clipcompositing.ui b/data/ui/clipcompositing.ui
index 64062c41e..e08850e49 100644
--- a/data/ui/clipcompositing.ui
+++ b/data/ui/clipcompositing.ui
@@ -1,17 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
- <requires lib="gtk+" version="3.10"/>
- <object class="GtkActionGroup" id="actiongroup1"/>
+ <requires lib="gtk+" version="3.20"/>
<object class="GtkAdjustment" id="fade_in_adjustment">
<property name="upper">1</property>
- <property name="step-increment">0.01</property>
- <property name="page-increment">10</property>
+ <property name="step-increment">1</property>
+ <property name="page-increment">5</property>
+ <signal name="value-changed" handler="_fade_in_adjustment_value_changed_cb" swapped="no"/>
</object>
<object class="GtkAdjustment" id="fade_out_adjustment">
<property name="upper">1</property>
- <property name="step-increment">0.01</property>
- <property name="page-increment">10</property>
+ <property name="step-increment">1</property>
+ <property name="page-increment">5</property>
+ <signal name="value-changed" handler="_fade_out_adjustment_value_changed_cb" swapped="no"/>
</object>
<object class="GtkImage" id="icon_reset1">
<property name="visible">True</property>
@@ -23,22 +24,20 @@
<property name="can-focus">False</property>
<property name="icon-name">edit-clear-all-symbolic</property>
</object>
- <!-- n-columns=5 n-rows=2 -->
+ <!-- n-columns=5 n-rows=3 -->
<object class="GtkGrid" id="compositing_box">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="valign">start</property>
- <property name="margin-left">12</property>
- <property name="margin-top">6</property>
- <property name="margin-bottom">6</property>
- <property name="row-spacing">6</property>
+ <property name="border-width">10</property>
+ <property name="row-spacing">10</property>
<property name="column-spacing">6</property>
<child>
<object class="GtkLabel" id="label10">
<property name="visible">True</property>
<property name="can-focus">False</property>
- <property name="halign">start</property>
+ <property name="halign">end</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">Fade-in:</property>
<property name="xalign">0</property>
@@ -93,7 +92,7 @@
<object class="GtkLabel" id="label11">
<property name="visible">True</property>
<property name="can-focus">False</property>
- <property name="halign">start</property>
+ <property name="halign">end</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">Fade-out:</property>
<property name="xalign">0</property>
@@ -151,6 +150,7 @@
<property name="tooltip-text" translatable="yes">Reset fade-in</property>
<property name="image">icon_reset1</property>
<property name="relief">none</property>
+ <signal name="clicked" handler="_reset_fade_in_clicked_cb" swapped="no"/>
</object>
<packing>
<property name="left-attach">4</property>
@@ -166,11 +166,40 @@
<property name="tooltip-text" translatable="yes">Reset fade-out</property>
<property name="image">icon_reset2</property>
<property name="relief">none</property>
+ <signal name="clicked" handler="_reset_fade_out_clicked_cb" swapped="no"/>
</object>
<packing>
<property name="left-attach">4</property>
<property name="top-attach">1</property>
</packing>
</child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="halign">end</property>
+ <property name="label" translatable="yes">Blending:</property>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="blending_mode">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="halign">start</property>
+ <signal name="changed" handler="_blending_property_changed_cb" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">2</property>
+ <property name="width">3</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
</object>
</interface>
diff --git a/pitivi/clip_properties/compositing.py b/pitivi/clip_properties/compositing.py
index 77e15eb93..814f90493 100644
--- a/pitivi/clip_properties/compositing.py
+++ b/pitivi/clip_properties/compositing.py
@@ -3,6 +3,9 @@
# Copyright (c) 2021, Tyler Senne <tsenne2 huskers unl edu>
# Copyright (c) 2021, Michael Ervin <michael ervin huskers unl edu>
# Copyright (c) 2021, Aaron Friesen <afriesen4 huskers unl edu>
+# Copyright (c) 2021, Andres Ruiz <andres ruiz3210 gmail com>
+# Copyright (c) 2021, Dalton Hulett <hulettdalton gmail com>
+# Copyright (c) 2021, Reed Lawrence <reed lawrence zenofchem com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -22,6 +25,7 @@ from typing import Iterable
from typing import Optional
from gi.repository import GES
+from gi.repository import GObject
from gi.repository import Gst
from gi.repository import GstController
from gi.repository import Gtk
@@ -37,7 +41,7 @@ FADE_OPACITY_THRESHOLD = 0.9
class CompositingProperties(Gtk.Expander, Loggable):
- """Widget for setting the opacity-related properties of a clip.
+ """Widget for setting the opacity and compositing properties of a clip.
Attributes:
app (Pitivi): The app.
@@ -54,12 +58,11 @@ class CompositingProperties(Gtk.Expander, Loggable):
builder = Gtk.Builder()
builder.add_from_file(os.path.join(get_ui_dir(), "clipcompositing.ui"))
+ builder.connect_signals(self)
- self.add(builder.get_object("compositing_box"))
+ compositing_box = builder.get_object("compositing_box")
self._fade_in_adjustment = builder.get_object("fade_in_adjustment")
self._fade_out_adjustment = builder.get_object("fade_out_adjustment")
- reset_fade_in_button = builder.get_object("reset_fade_in_button")
- reset_fade_out_button = builder.get_object("reset_fade_out_button")
self._video_source: Optional[GES.VideoSource] = None
self._control_source: Optional[Gst.ControlSource] = None
@@ -70,14 +73,25 @@ class CompositingProperties(Gtk.Expander, Loggable):
self._applying_fade: bool = False
self._updating_adjustments: bool = False
- self._fade_in_adjustment.connect("value-changed", self.__fade_in_adjustment_changed_cb)
- self._fade_out_adjustment.connect("value-changed", self.__fade_out_adjustment_changed_cb)
- reset_fade_in_button.connect("pressed", self.__reset_fade_in_cb)
- reset_fade_out_button.connect("pressed", self.__reset_fade_out_cb)
+ self.blending_combo = builder.get_object("blending_mode")
+ # Translators: These are compositing operators.
+ # See https://www.cairographics.org/operators/ for explanation and
+ # visualizations.
+ for value_id, text in (("source", _("Source")),
+ ("over", _("Over")),
+ # TODO: Add back when
https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1199 is fixed
+ # ("add", _("Add")),
+ ):
+ self.blending_combo.append(value_id, text)
+
+ self.add(compositing_box)
+ compositing_box.show_all()
def set_source(self, video_source: GES.VideoSource) -> None:
+ self.debug("Source set to %s", video_source)
if self._control_source:
disconnect_all_by_func(self._control_source, self.__keyframe_changed_cb)
+ self._video_source.disconnect_by_func(self._source_deep_notify_cb)
self._video_source.disconnect_by_func(self.__keyframe_changed_cb)
self._control_source = None
@@ -89,16 +103,16 @@ class CompositingProperties(Gtk.Expander, Loggable):
control_binding = self._video_source.get_control_binding("alpha")
assert control_binding
self._control_source = control_binding.props.control_source
-
self._control_source.connect("value-added", self.__keyframe_changed_cb)
self._control_source.connect("value-changed", self.__keyframe_changed_cb)
self._control_source.connect("value-removed", self.__keyframe_changed_cb)
self._video_source.connect("notify::duration", self.__keyframe_changed_cb)
-
self._update_adjustments()
- self.show_all()
- else:
- self.hide()
+
+ self._video_source.connect("deep-notify", self._source_deep_notify_cb)
+ self._update_blending()
+
+ self.props.visible = bool(self._video_source)
@property
def _duration(self) -> int:
@@ -138,22 +152,22 @@ class CompositingProperties(Gtk.Expander, Loggable):
self._fade_in_adjustment.props.upper = (self._duration - self._fade_out) / Gst.SECOND
self._fade_out_adjustment.props.upper = (self._duration - self._fade_in) / Gst.SECOND
- def __fade_in_adjustment_changed_cb(self, adjustment: Gtk.Adjustment) -> None:
+ def _fade_in_adjustment_value_changed_cb(self, adjustment: Gtk.Adjustment) -> None:
if not self._updating_adjustments:
fade_timestamp: int = int(self._fade_in_adjustment.props.value * Gst.SECOND)
self._move_keyframe(self._fade_in, fade_timestamp, 0, self._duration - self._fade_out)
self._update_adjustments()
- def __fade_out_adjustment_changed_cb(self, adjustment: Gtk.Adjustment) -> None:
+ def _fade_out_adjustment_value_changed_cb(self, adjustment: Gtk.Adjustment) -> None:
if not self._updating_adjustments:
fade_timestamp: int = self._duration - int(self._fade_out_adjustment.props.value * Gst.SECOND)
self._move_keyframe(self._duration - self._fade_out, fade_timestamp, self._duration,
self._fade_in)
self._update_adjustments()
- def __reset_fade_in_cb(self, button: Gtk.Button) -> None:
+ def _reset_fade_in_clicked_cb(self, button: Gtk.Button) -> None:
self._fade_in_adjustment.props.value = 0
- def __reset_fade_out_cb(self, button: Gtk.Button) -> None:
+ def _reset_fade_out_clicked_cb(self, button: Gtk.Button) -> None:
self._fade_out_adjustment.props.value = 0
def __keyframe_changed_cb(self, control_source: GstController.TimedValueControlSource, timed_value:
GstController.ControlPoint) -> None:
@@ -219,3 +233,26 @@ class CompositingProperties(Gtk.Expander, Loggable):
self._control_source.set(edge_timestamp, 0)
finally:
self._applying_fade = False
+
+ def _update_blending(self) -> None:
+ res, value = self._video_source.get_child_property("operator")
+ assert res
+ self.blending_combo.handler_block_by_func(self._blending_property_changed_cb)
+ try:
+ self.blending_combo.set_active_id(value.value_nick)
+ finally:
+ self.blending_combo.handler_unblock_by_func(self._blending_property_changed_cb)
+
+ def _source_deep_notify_cb(self, element: GES.TimelineElement, obj: GObject.Object, prop:
GObject.ParamSpec) -> None:
+ self._update_blending()
+
+ def _blending_property_changed_cb(self, combo: Gtk.ComboBox) -> None:
+ pipeline = self.app.project_manager.current_project.pipeline
+ with self.app.action_log.started("set operator",
+ finalizing_action=CommitTimelineFinalizingAction(pipeline),
+ toplevel=True):
+ self._video_source.handler_block_by_func(self._source_deep_notify_cb)
+ try:
+ self._video_source.set_child_property("operator", self.blending_combo.get_active_id())
+ finally:
+ self._video_source.handler_unblock_by_func(self._source_deep_notify_cb)
diff --git a/pitivi/clipproperties.py b/pitivi/clipproperties.py
index cb906bc3d..a685602f5 100644
--- a/pitivi/clipproperties.py
+++ b/pitivi/clipproperties.py
@@ -74,6 +74,7 @@ DEFAULT_FONT_DESCRIPTION = "Sans 36"
DEFAULT_VALIGNMENT = "absolute"
DEFAULT_HALIGNMENT = "absolute"
DEFAULT_DROP_SHADOW = True
+DEFAULT_BLENDING = "over"
# Max speed rate we allow to be applied to clips.
# The minimum is 1 / MAX_RATE.
diff --git a/tests/test_clipproperties.py b/tests/test_clipproperties.py
index 3f9a0b632..03755d265 100644
--- a/tests/test_clipproperties.py
+++ b/tests/test_clipproperties.py
@@ -321,6 +321,42 @@ class TransformationPropertiesTest(common.TestCase):
self.assertTrue(ret)
self.assertEqual(value, source.ui.default_position[prop])
+ @common.setup_timeline
+ @common.setup_clipproperties
+ def test_operator(self):
+ timeline = self.app.gui.editor.timeline_ui.timeline
+
+ clip, = self.add_clips_simple(timeline, 1)
+ timeline.selection.select([clip])
+ source = self.compositing_box._video_source
+ self.assertIsNotNone(source)
+ ret, value = source.get_child_property("operator")
+ self.assertEqual((ret, value.value_nick), (True, "over"))
+
+ self.compositing_box.blending_combo.set_active_id("source")
+ ret, value = source.get_child_property("operator")
+ self.assertEqual((ret, value.value_nick), (True, "source"))
+
+ self.compositing_box.blending_combo.set_active_id("over")
+ ret, value = source.get_child_property("operator")
+ self.assertEqual((ret, value.value_nick), (True, "over"))
+
+ self.app.action_log.undo()
+ ret, value = source.get_child_property("operator")
+ self.assertEqual((ret, value.value_nick), (True, "source"))
+
+ self.app.action_log.undo()
+ ret, value = source.get_child_property("operator")
+ self.assertEqual((ret, value.value_nick), (True, "over"))
+
+ self.app.action_log.redo()
+ ret, value = source.get_child_property("operator")
+ self.assertEqual((ret, value.value_nick), (True, "source"))
+
+ self.app.action_log.redo()
+ ret, value = source.get_child_property("operator")
+ self.assertEqual((ret, value.value_nick), (True, "over"))
+
class TitlePropertiesTest(common.TestCase):
"""Tests for the TitleProperties class."""
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]