Search code examples
pythonpyqtfocuspyqt5qdockwidget

Reset focus in tabified dock widgets


Currently I'm having trouble with the focus of tabified QDockWidgets. Working with this small example using two tabified QDockWidgets:

from PyQt5.QtWidgets import QApplication, QMainWindow, QDockWidget, QTextEdit
from PyQt5.Qt import Qt

app = QApplication( [] )
main = QMainWindow(None, Qt.Window)
main.show()

dock1 = QDockWidget( "D1", main )
dock1.setWidget( QTextEdit( dock1 ) )
main.addDockWidget( Qt.TopDockWidgetArea, dock1 )

dock2 = QDockWidget( "D2", main )
dock2.setWidget( QTextEdit( dock2 ) )
main.tabifyDockWidget( dock1, dock2 )

exit( app.exec() )

I experienced that the QTextEdits are not losing focus when switching to an other tab of the tabified widgets.

  1. write Text in one of the fields
  2. click on the other Tab on the QTabBar
  3. continue with your keyboard-input

The second text you were writen was still in focus of the first Widget.


So, in general:
Is there a way to lose the focus of the tabified widgets, regardless if it is a QTextEdit or some other Widget(s) (maybe even with a complex child-layout structure)?

I tried clearFocus() on the QDockWidget, but this just seems to work if the QDockWidget itselfe is containing the focus (not it's child).
Using setFocus() before also doesn't seems like a good option, because the widget (or it's childs) might doesn't contain the focus in every case. So it would be unappropriate to might steal it from some other widget.


I think the best anchors to react on would be:

and if you would somehow locate the dynamicly create QTabBar:


Solution

  • One quite simple way to do this is to check to see if the current focus widget is a descendant of a QDockWidget whenever the tab changes, and then reset the focus if necessary:

    from PyQt5.QtWidgets import QApplication, QMainWindow, QDockWidget, QTextEdit
    from PyQt5.QtCore import Qt
    
    class DockWidget(QDockWidget):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.setWidget(QTextEdit(self))
            self.visibilityChanged.connect(self.updateFocus)
    
        def updateFocus(self, visible):
            if visible:
                widget = QApplication.instance().focusWidget()
                while widget is not None:
                    if isinstance(widget, QDockWidget):
                        widget = None
                    elif widget.parentWidget() is not None:
                        widget = widget.parentWidget()
                    else:
                        break
                if widget is None:
                    self.setFocus()
                    self.focusNextChild()
    
    app = QApplication([''])
    main = QMainWindow()
    main.setCentralWidget(QTextEdit(main))
    
    dock1 = DockWidget("D1", main)
    main.addDockWidget(Qt.TopDockWidgetArea, dock1)
    
    dock2 = DockWidget("D2", main)
    main.tabifyDockWidget(dock1, dock2)
    
    main.show()
    app.exec()