0
votes

I have a bunch of widgets in a layout, and the layout is the child of a QFrame. This allows me to create a border around this layout. Now when any of the children receive focus, I would like to change the border color of the QFrame to indicate to the user that is where the focus currently is. How best to do this without subclassing the focuInEvent/focusOutEvent of every child with callbacks to the stylesheet of their parent widget (the QFrame)? When testing to focusInEvent of the QFrame I can never get it to trigger. Is there some sort of child focus event or something?

1
You can connect each of the children to some handler that will run something like sender()->parent()->doSomeThingWithBorder();JLev
Thanks @JLev, that could probably work. I actually got this working at the end of the night using event filters (which I was only dimly aware of before). I'll post an answer soon, I'm just finding the best way of subclassing everything to keep it clean and all.Spencer

1 Answers

1
votes

I think I came up with a pretty good solution for this after trying a few things out and learning a ton more about eventFilter's. Basically I found that you need to install an event filter in the parent and catch all focus events of the children. It's easier to show an example, this is a bit more complicated then it perhaps needs to be but it illustrates some important points:

import os
import sys
from PyQt4 import QtGui, QtCore


class BasePanel(QtGui.QWidget):
    """This is more or less abstract, subclass it for 'panels' in the main UI"""
    def __init__(self, parent=None):
        super(BasePanel, self).__init__(parent)
        self.frame_layout = QtGui.QVBoxLayout()
        self.frame_layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(self.frame_layout)

        self.frame = QtGui.QFrame()
        self.frame.setObjectName("base_frame")
        self.frame.setFrameStyle(QtGui.QFrame.Box | QtGui.QFrame.Plain)
        self.frame.setLineWidth(1)
        self.frame_layout.addWidget(self.frame)

        self.base_layout = QtGui.QVBoxLayout()
        self.frame.setLayout(self.base_layout)

        self.focus_in_color = "rgb(50, 255, 150)"
        self.focus_out_color = "rgb(100, 100, 100)"
        self.frame.setStyleSheet("#base_frame {border: 1px solid %s}" % self.focus_out_color)
        self.installEventFilter(self) # this will catch focus events
        self.install_filters()

    def eventFilter(self, object, event):
        if event.type() == QtCore.QEvent.FocusIn:
            self.frame.setStyleSheet("#base_frame {border: 1px solid %s}" % self.focus_in_color)
        elif event.type() == QtCore.QEvent.FocusOut:
            self.frame.setStyleSheet("#base_frame {border: 1px solid %s}" % self.focus_out_color)
        return False # passes this event to the child, i.e. does not block it from the child widgets

    def install_filters(self):
        # this will install the focus in/out event filter in all children of the panel
        for widget in self.findChildren(QtGui.QWidget):
            widget.installEventFilter(self)


class LeftPanel(BasePanel):
    def __init__(self, parent=None):
        super(LeftPanel, self).__init__(parent)

        title = QtGui.QLabel("Left Panel")
        title.setAlignment(QtCore.Qt.AlignCenter)
        self.base_layout.addWidget(title)

        edit = QtGui.QLineEdit()
        self.base_layout.addWidget(edit)



class RightPanel(BasePanel):
    def __init__(self, parent=None):
        super(RightPanel, self).__init__(parent)

        title = QtGui.QLabel("Right Panel")
        title.setAlignment(QtCore.Qt.AlignCenter)
        self.base_layout.addWidget(title)

        edit = QtGui.QLineEdit()
        self.base_layout.addWidget(edit)

class MainApp(QtGui.QMainWindow):
    def __init__(self):
        super(MainApp, self).__init__()

        main_layout = QtGui.QHBoxLayout()
        central_widget = QtGui.QWidget()
        central_widget.setLayout(main_layout)
        self.setCentralWidget(central_widget)

        left_panel = LeftPanel()
        main_layout.addWidget(left_panel)

        right_panel = RightPanel()
        main_layout.addWidget(right_panel)


if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    ex = MainApp()
    ex.show()

    sys.exit(app.exec_())