Python Forum
Two QlineEdit box, which interrelated - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: Python Coding (https://python-forum.io/forum-7.html)
+--- Forum: GUI (https://python-forum.io/forum-10.html)
+--- Thread: Two QlineEdit box, which interrelated (/thread-28974.html)



Two QlineEdit box, which interrelated - GMCobraz - Aug-12-2020

Hi guys,

I have a general question here.

How can I make sure two QLineEdit box are interrelated?
I haven't code, just to get an idea here.

For example, box A and B are interrelated.
When I change box A, B will update too.

But how I change box B and update box A?
My understanding is the code is flow from box A to box B, the value from box A will overwrite box B changed by user earlier.

Should I implement something like textChanged or valueChanged?

Thank you very much.


RE: Two QlineEdit box, which interrelated - deanhystad - Aug-14-2020

This question sounds familiar.

First I would ask myself why do I have two line edit boxes that are interrelated.

Assuming this is a good idea in your case, when do you want to "sync" the entries? While typing? When the control loses focus? When enter is pressed?

I found QLineEdit lacking and added the concept of "accept". I wanted to ignore typing and only sent a notification when the enter key was pressed or if the QLineEdit lost focus. When this happens my modified QLineEdit emits a valuechanged signal.
class LineEdit(QLineEdit):
    """QLineEdit with revert on focus out and valuechanged signal.
    valuechanged differs from textChanged in that the value does
    not change until the enter/return key is pressed.  If I lose
    focus before enter/return is pressed I perform the selected
    revert action
    """
    # Instance variables
    revert: bool    # Action taken when control loses focus
    _value: str     # Accepted text

    # This acts like an ivar but must be declared here.  Why?
    valuechanged = Signal(str)

    def __init__(self, *args, revert=True, **kv_args):
        super().__init__(*args, **kv_args)
        self._value = ''
        self.revert = revert

    def event(self, ev):
        """Override QLineEdit event method.
        Intercept tab key press. When tab pressed accept the text and
        navigate to the next control.  Also capture return key pressed
        since processing is the same as tab key.  Capture focus out
        event and revert to last accepted text
        """
        if ev.type() == QEvent.KeyPress:
            if ev.key() in (int(Qt.Key_Tab), int(Qt.Key_Return)):
                self._accepttext()
        elif ev.type() == QEvent.FocusOut:
            self._focusout()
        return super().event(ev)

    def _focusout(self):
        """Called when control loses focus.  Based on revert mode
        either revert the last accepted text, or treat as if return
        was pressed"""
        if self._value != self.text():
            if self.revert:
                self.setText(self._value)
            else:
                self._accepttext()

    def _accepttext(self):
        """Called when changed text is accepted.  Emit valuechanged signal"""
        self._value = self.text()
        self.valuechanged.emit(self._value)

    def clear(self):
        """Set to no display"""
        self.value = ''

    @property
    def value(self):
        """Accepted text"""
        return self._value

    @value.setter
    def value(self, value):
        self._value = value
        self.setText(value)
Once you how notification should work, it comes down to either using one of the available signals, or subclassing QLineEdit to act the way you want. Eventually you will call a function that updates the controls. Depending on how they are related this may be the same function for both, or separate functions for each. You will have to implement some kind of lock to prevent the A callback changing B from calling the B callback. For this I use a common variable in the callback functions.
def a_changed()
    if not lockedout
        lockedout = True
        # set value of B
        lockedout = False

def b_changed()
    if not lockedout
        lockedout = True
        # set value of A
        lockedout = False