I am trying to get bidirectional property bindings to work in python (using pygobject and gtk3). It appears that the binding syncs one-way only even though I set the bidirectional flag.
I posted an according question on stackoverflow gtk - How to do 2-way data binding using Python+PyGObject's GObject.bind_property function? - Stack Overflow. The stackoverflow question includes a minimal code example with a failing test case to make it easy to reproduce and hopefully fix the problem. I would like to bring it to this forum’s attention as well because the stackoverflow question/answer seems to be stuck without solution.
Can someone help me figure out how to use the bidirectional bindings in python? I would be most grateful for hints / links / an answer over on stackoverlow / other working examples.
Anyway the problem is your not setting the binding to be bidirectional, try
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject
import unittest
class Obj(GObject.Object):
"""A very simple GObject with a `txt` property."""
name = "default"
txt = GObject.Property(type=str, default="default")
def __init__(self, name, *args, **kwargs):
super().__init__(*args, **kwargs)
self.name = name
self.connect("notify", self.log)
def log(self, source, parameter_name):
print(
f"The '{self.name}' object received a notify event, "
f"its txt now is '{self.txt}'."
)
class TestBindings(unittest.TestCase):
def setUp(self):
"""Sets up a bidirectional binding between a model and a widget"""
print(f"\n\n{self.id()}")
self.model = Obj("model")
self.widget = Obj("widget")
self.model.bind_property(
"txt", self.widget, "txt", GObject.BindingFlags.BIDIRECTIONAL
)
@unittest.skip("suceeds")
def test_properties_are_found(self):
"""Verifies that the `txt` properties are correctly set up."""
for obj in [self.model, self.widget]:
self.assertIsNotNone(obj.find_property("txt"))
@unittest.skip("suceeds")
def test_model_syncs_to_widget(self, data="hello"):
"""Verifies that model changes propagate to the widget"""
self.model.txt = data
self.assertEqual(self.widget.txt, data)
def test_widget_syncs_to_model(self, data="world"):
"""Verifies that widget changes propagate back into the model"""
self.widget.txt = data
self.assertEqual(self.widget.txt, data) # SUCCEEDS
self.assertEqual(self.model.txt, data) # FAILS
if __name__ == "__main__":
unittest.main()
Hi! Thank you very much for solving the error, this was driving me crazy and I am very glad to have it working now. And thank you for pointing out Philip Whitnall’s role in maintaining Glib, I was not aware of that.
Note for future reference, to make it even more clear in case anybody else is having a similar problem :-). The following code does not work because flags are not passed correctly:
# broken, flags need to be set as positional argument:
self.model.bind_property("txt", self.widget, "txt", flags=GObject.BindingFlags.BIDIRECTIONAL)
EDIT, additional note: Proper use of bidirectional bindings is, e.g., demonstrated in pygobject’s source code in the test_bidirectional_binding test case.
Thank you so much for your help!
best regards, Johannes
hi Zander, do you want to provide your answer over on stackoverflow as well? I am asking because you may value the stackoverflow points and badges associated with posting an answer. thanks, Johannes.