Bindable properties for Python
Project description
Binding
This package brings property binding to Python. It allows you to bind attributes of one object to other attributes of itself or other objects like so:
label.text.bind(model.value)
That means, whenever model.value
is changed, label.text
changes as well.
Installation
This package can be installed using the Python package installer pip:
python3 -m pip install binding
Alternatively you can find the source code on GitHub.
Usage
You can apply binding in two different ways: automatic updates using bindable properties or by calling an update functional explicitely. Furthermore, you can specify the binding direction as well as converter functions.
The following examples give a more detailed explanation. The code snippets build upon each other and are meant to be called in succession.
Bindable properties
If you have control over the class implementation, you can introduce a BindableProperty
for the respective attributes. It will intercept each write access the attribute and propagate the changed value to bound properties:
from binding import BindableProperty
class Person:
name = BindableProperty()
def __init__(self, name=None):
self.name = name
class Car:
driver = BindableProperty()
def __init__(self, driver=None):
self.driver = driver
person = Person('Robert')
car = Car()
car.driver.bind(person.name)
assert car.driver == person.name == 'Robert'
person.name = 'Bob'
assert car.driver == person.name == 'Bob'
Binding with non-bindable attributes
Suppose you have a class which you cannot or don't want to change.
That means it has no BindableProperty
to observe value changes.
You can bind its attributes nevertheless:
class License:
def __init__(self, name=None):
self.name = name
license = License()
license.name.bind(person.name)
person.name = 'Todd'
assert license.name == person.name == 'Todd'
But if the license name is changed, there is no BindableProperty
to notice write access to its value.
We have to manually trigger the propagation to bound objects.
from binding import update
license.name = 'Ben'
assert person.name != license.name == 'Ben'
update()
assert person.name == license.name == 'Ben'
One-way binding
The .bind()
method registers two-way binding.
But you can also specify one-way binding using .bind_from()
or .bind_to()
, respectively.
In the following example car
receives updates person
, but not the other way around.
person = Person('Ken')
car = Car()
car.driver.bind_from(person.name)
assert car.driver == person.name == 'Ken'
person.name = 'Sam'
assert car.driver == person.name == 'Sam'
car.driver = 'Seth'
assert car.driver != person.name == 'Sam'
Likewise you can specify forward binding to let person
be updated when car
changes:
person = Person('Keith')
car = Car()
car.driver.bind_to(person.name)
assert car.driver == person.name == None
car.driver = 'Kent'
assert car.driver == person.name == 'Kent'
person.name = 'Grant'
assert car.driver != person.name == 'Grant'
Converters
For all types of binding - forward, backward, two-way, via bindable properties or non-bindable attributes - you can define converter functions that translate values from one side to another. The following example demonstrates the conversion between Celsius and Fahrenheit.
class Temperature:
c = BindableProperty()
f = BindableProperty()
def __init__(self):
self.c = 0.0
self.f = 0.0
t = Temperature()
t.f.bind(t.c, forward=lambda f: (f - 32) / 1.8, backward=lambda c: c * 1.8 + 32)
assert t.c == 0.0 and t.f == 32.0
t.f = 68.0
assert t.c == 20.0 and t.f == 68.0
t.c = 100.0
assert t.c == 100.0 and t.f == 212.0
Note that bind_to()
only needs a forward
converter.
Similarly bind_from
has only a backward
converter.
Implementation and dependencies
To achieve such a lean API we utilize three main techniques:
-
For extending basic types with
bind()
,bind_to()
andbind_from()
methods we usecurse
from the forbiddenfruit package. -
For intercepting write access to attributes we implement
BindableProperties
as descriptors. -
For finding the object and attribute name of the caller and the argument of our
bind()
methods we use inspection tools from the inspect and executing packages.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file binding-0.2.tar.gz
.
File metadata
- Download URL: binding-0.2.tar.gz
- Upload date:
- Size: 5.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.1.6 CPython/3.9.5 Linux/5.4.0-1047-azure
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 329b7ab019d06f86269e1e80dac3eeb3e8b7b1f52c790c73197c31c82e1b4e74 |
|
MD5 | f45e2de8ee2572cafb3b6975a39ff61b |
|
BLAKE2b-256 | 98660133e78a8485a6ac7284badcaebe93a37f239ff19260c8d45e6ee2c0b5af |
File details
Details for the file binding-0.2-py3-none-any.whl
.
File metadata
- Download URL: binding-0.2-py3-none-any.whl
- Upload date:
- Size: 4.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.1.6 CPython/3.9.5 Linux/5.4.0-1047-azure
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 8f90492df742a8398a9e9cf727d562da3f4ff840a3bf4016f75896782fe87f02 |
|
MD5 | fe13db41a780da15bbfee70d75cf0b1b |
|
BLAKE2b-256 | b0fde90a6a0cab5b459a005687a0f27c69438f858680e8541ca83bf9ce28d8af |