Bidirectional Bindings using PyGObject - cannot get it to work, help or working example needed

Hi,

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 https://stackoverflow.com/q/67763050/2068912. 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.

best regards, Johannes

Heh, Philip Withnall maintains GLib/GObject

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)

Instead, flags must be passed as follows:

self.model.bind_property("txt", self.widget, "txt", 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

1 Like

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.