Solved Simplest way for two-way binding a text field
-
Hi, I'm having trouble to have updates sent to a bound property without explicitly defining an event handler.
class Model(QtCore.QObject): """simplest model with a text property""" def __init__(self): super().__init__() self._member_a = 'YES' def a(self): return self._member_a def set_a(self, v): print(f'changing to {v}') self._member_a = v @QtCore.Signal def a_changed(self): pass a = QtCore.Property(str, a, set_a, notify=a_changed) model = Model() app = QtGui.QGuiApplication(sys.argv) view = QtQuick.QQuickView() view.rootContext().setContextProperty('model', model) view.setResizeMode(QtQuick.QQuickView.SizeRootObjectToView) with Path('Main.qml').open('w') as f: f.write(''' import QtQuick 2.0 import QtQuick.Controls 2.0 TextField { text: model.a // I don't want this: // onEditingFinished: model.a = text }''') view.setSource('Main.qml') view.resize(100, 100) view.show() result = app.exec_()
My setup is with PySide6 6..0.1:
$ poetry show astroid 2.5 An abstract syntax tree for Python with inference support. isort 5.7.0 A Python utility / library to sort Python imports. lazy-object-proxy 1.5.2 A fast and thorough lazy object proxy. mccabe 0.6.1 McCabe checker, plugin for flake8 pylint 2.7.0 python code static checker pyside6 6.0.1 Python bindings for the Qt cross-platform application and UI framework shiboken6 6.0.1 Python / C++ bindings helper module toml 0.10.2 Python Library for Toms Obvious, Minimal Language wrapt 1.12.1 Module for decorators, wrappers and monkey patching.
I tried to find proper guidance in the documentation, but failed.
Is there a better way than to define the
onEditingFinished
handler?Thanks!
-
@xtofl In QML the binding is unidirectional: from right to left:
Foo { id: foo a: bar.b }
This binding indicates that every time the property "b" (which must be a signal) of the object "bar" then the property "a" of the object "foo" will be updated with that value. In python it would be something like:
bar.b_Signal.connect(lambda: setattr(foo, "a", foo.b))
If you want to implement a 2-way binding then create a class that listens when one of those properties change ("a" or "b") and then updates the other.
-
Hi,
@xtofl said in Simplest way for two-way binding a text field:
def set_a(self, v):
print(f'changing to {v}')
self._member_a = vYou are not emitting the change signal.
The classic implementation is:
def set_a(self, value): if self._member_a == value: return self._member_a = value self.a_changed.emit(value)
-
Thank you. Only... this makes things even less simple. Are you saying that this is the simplest way?
-
In what way is it less simple ?
The only simpler would be:
def set_a(self, value): if self._member_a != value: self._member_a = value self.a_changed.emit(value)
-
Thanks - the
emit
is a necessary addition, I agree. I meant that adding a line by definition makes code less simple.But what I really don't understand is why I would need the
onEditingFinished
handler in the qml itself. I was hoping to get the 2-way binding working without explicit event handling, by just bindingTextfield.text
property tomodel.a
property. -
@xtofl In QML the binding is unidirectional: from right to left:
Foo { id: foo a: bar.b }
This binding indicates that every time the property "b" (which must be a signal) of the object "bar" then the property "a" of the object "foo" will be updated with that value. In python it would be something like:
bar.b_Signal.connect(lambda: setattr(foo, "a", foo.b))
If you want to implement a 2-way binding then create a class that listens when one of those properties change ("a" or "b") and then updates the other.