Search code examples
pyqt6class-attributesfocusoutfocusin

Showing/hiding of a widget on focus at another widget


I would like "Button" object to disappear when "Target" object is not in focus (for example, when object "Secondary" is focused) and to re-appear when "Target" is in focus again. So, "Target" focused = "Button" visible. In other words, in the code below there are two lines, "Line A" and "Line B", that I would like to implement in the code.

`

import sys
from PyQt6.QtWidgets import QApplication, QWidget, QPushButton, QLineEdit


class Wn(QWidget):

    def __init__(self):
        super().__init__()

        self.target = Target("Target", self)
        self.target.setFixedSize(400, 60)
        self.target.move(50, 50)
        self.secondary = QLineEdit("Secondary", self)
        self.secondary.setFixedSize(400, 60)
        self.secondary.move(50, 150)
        self.button = QPushButton("Appears @ Target focused. Disappears @ target not focused", self)
        self.button.setFixedSize(400, 60)
        self.button.move(50, 250)


class Target(QLineEdit):

    def focusInEvent(self, k):
        print("The target is in focus: Button should be shown")
        self.setStyleSheet("background-color: red;")
        # Wn.button.setHidden(False)                             # Line A

    def focusOutEvent(self, p):
        print("The target is out of focus: Button should be hidden")
        self.setStyleSheet("background-color: white;")
        # Wn.button.setHidden(True)                              # Line B


app = QApplication(sys.argv)
wn = Wn()
wn.show()
sys.exit(app.exec())

`


Solution

  • You can create a signal and emit it whenever the focus changes, then connect it with the button's setVisible().

    class Wn(QWidget):
        def __init__(self):
            # ...
            self.target.focusChanged.connect(self.button.setVisible)
    
    
    class Target(QLineEdit):
        focusChanged = pyqtSignal(bool)
    
        def focusInEvent(self, k):
            super().focusInEvent(k)
            print("The target is in focus: Button should be shown")
            self.setStyleSheet("background-color: red;")
            self.focusChanged.emit(True)
    
        def focusOutEvent(self, p):
            super().focusOutEvent(p)
            print("The target is out of focus: Button should be hidden")
            self.setStyleSheet("background-color: white;")
            self.focusChanged.emit(False)
    

    Alternatively, you can just install an event filter on the line edit and look for FocusIn and FocusOut events.

    Note that you should always call the base implementation of event handler overrides, unless you really know what you're doing, otherwise you might prevent proper default behavior of the object.

    Also, layout managers should always be used instead of fixed geometries. Since the visibility of a widget also nullifies its size in the layout and adapts the other widgets managed by it (similarly to display: none in CSS), you should probably consider using setRetainSizeWhenHidden() for the widget's size policy:

    class Wn(QWidget):
        def __init__(self):
            # ...
            # create a proper layout and add widgets
            # ...
            policy = self.button.sizePolicy()
            policy.setRetainSizeWhenHidden(True)
            self.button.setSizePolicy(policy)