Skip to content

Commit

Permalink
feat!: allow binding multiple properties (IgnisGObject.bind_many()) (
Browse files Browse the repository at this point in the history
…#103)

Co-authored-by: Link <[email protected]>
  • Loading branch information
PacificViking and linkfrg authored Jan 7, 2025
1 parent 40a9a46 commit a0a4167
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 13 deletions.
14 changes: 14 additions & 0 deletions docs/user/dynamic_content.rst
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,20 @@ Try changing the speaker volume using a tool like ``pavucontrol``.

When the property changes, the transform function (if provided) will be called with the new value, and it should return the processed result.

Multiple Binding
-----------------

You can bind multiple properties at the same time with ``bind_many()``.

.. code-block:: python
Widget.Scale(
value=audio.speaker.bind_many(
["volume", "is_muted"],
lambda volume, is_muted: 0 if is_muted else volume,
),
)
``notify`` signal
------------------
The ``notify`` is a special signal that emits when a property changes.
Expand Down
49 changes: 36 additions & 13 deletions ignis/gobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ class Binding(GObject.Object):
def __init__(
self,
target: GObject.Object,
target_property: str,
target_properties: list[str],
transform: Callable | None = None,
):
self._target = target
self._target_property = target_property
self._target_properties = target_properties
self._transform = transform
super().__init__()

Expand All @@ -28,13 +28,13 @@ def target(self) -> GObject.Object:
return self._target

@GObject.Property
def target_property(self) -> str:
def target_properties(self) -> list[str]:
"""
- required, read-only
The property on the target GObject.
The properties on the target GObject to bind.
"""
return self._target_property
return self._target_properties

@GObject.Property
def transform(self) -> Callable | None:
Expand Down Expand Up @@ -106,7 +106,7 @@ def set_property(self, property_name: str, value: Any) -> None:
self.bind_property2(
source_property=property_name,
target=value.target,
target_property=value.target_property,
target_properties=value.target_properties,
transform=value.transform,
)
else:
Expand All @@ -116,26 +116,37 @@ def bind_property2(
self,
source_property: str,
target: GObject.Object,
target_property: str,
target_properties: list[str],
transform: Callable | None = None,
) -> None:
"""
Bind ``source_property`` on ``self`` with ``target_property`` on ``target``.
Bind ``source_property`` on ``self`` with ``target_properties`` on ``target``.
Args:
source_property: The property on ``self`` to bind.
target: the target ``GObject.Object``.
target_property: the property on ``target`` to bind.
target_properties: the properties on ``target`` to bind.
transform: The function that accepts a new property value and returns the processed value.
"""

def callback(*args):
value = target.get_property(target_property.replace("-", "_"))
values = [
target.get_property(target_property.replace("-", "_"))
for target_property in target_properties
]

if transform:
value = transform(value)
value = transform(*values)
else:
if len(values) != 1:
raise IndexError("No transform function on multiple binding")
value = values[0]

self.set_property(source_property, value)

target.connect(f"notify::{target_property.replace('_', '-')}", callback)
for target_property in target_properties:
target.connect(f"notify::{target_property.replace('_', '-')}", callback)

callback()

def bind(self, property_name: str, transform: Callable | None = None) -> Binding:
Expand All @@ -148,7 +159,19 @@ def bind(self, property_name: str, transform: Callable | None = None) -> Binding
Returns:
:class:`~ignis.gobject.Binding`
"""
return Binding(self, property_name, transform)
return Binding(self, [property_name], transform)

def bind_many(self, property_names: list[str], transform: Callable) -> Binding:
"""
Creates ``Binding`` from property names on ``self``.
Args:
property_names: List of property names of ``self``.
transform: The function that accepts a new property values and returns the processed value. The values will be passed according to the order in ``property_names``.
Returns:
:class:`~ignis.gobject.Binding`
"""
return Binding(self, property_names, transform)

def __getattribute__(self, name: str) -> Any:
# This modified __getattribute__ method redirect all "set_" methods to set_property method to provive bindings support.
Expand Down

0 comments on commit a0a4167

Please sign in to comment.