Modal windows have event handling issues when QApplication.focusChanged() is used to set QSS of widget



  • What I'm trying to do is effortlessly change QDockWidget style on focusIn/focusOut events of children, as described over here (https://stackoverflow.com/a/50001844/714907). Problem is that when I open any kind of Qt-derived modal window and try to set QSS, buttons of that window behave extremely weird: once pressed, they get stuck in pressed state. Though this doesn't happen for not standard buttons -- the ones not included in QDialogButtonBox, the ones you add as a normal widgets.

    Everything works when I don't set QSS in else-case from the example -- for the widget that lost focus. Same is true for empty QSS, thus it's not the calls to setStyleSheet() that cause this. With zero-wait single shot timer buttons get stuck in unpressed state, though I think you can notice extremely fast change to pressed state. Changing window title works flawlessly, so changing of properties of QDockWidget in such case isn't the issue. But here's what's interesting: native Qt modal windows show red background along with QDockWidget, meaning that both widgets have focus, whereas native OS modal dialogs show parent QDockWidget in blue. I.e., could it be some glitch with improper setting of focus, and Qt doesn't remove focus from QDockWidget before opening modal window?

    BTW, here's somewhat related issue: https://forum.qt.io/topic/32850/qapplication-focuschanged-does-not-actually-report-focus-events-qt-4-8/3 Fix described in last post worked for me -- use of modeless windows. That's how I was able to figure out that this is modality-based issue.

    Here's the example that reproduces this issue. Also, it does the same thing in PyQt5, which most likely means it's not a bug from one of those wrappers. I have painted entire widget rather than QDockWidget, so that it was easier to spot changes of focus.

    from PySide2.QtWidgets import *
    from PySide2.QtCore import *
    from PySide2.QtGui import *
    import sys
    
    
    class Dock(QDockWidget):
        def __init__(self, *arg, **kwarg):
            QDockWidget.__init__(self, *arg, **kwarg)
            QApplication.instance().focusChanged.connect(self.focusInAppChanged)
    
        def focusInAppChanged(self, old, new):
            w = new
            while w and w != self:
                w = w.parentWidget()
    
            if w == self:
                print('x'*3, self.windowTitle())
                qss = 'QWidget { background-color: red; }'
                self.setStyleSheet(qss)
                # self.setStyleSheet('')
                # QTimer.singleShot(0, lambda: self.setStyleSheet(qss))
                # self.setWindowTitle('focus')
            # in this case it's OK to set QSS on any kind of widget, not just QDockWidget --
            # this way we can see which widget has focus.  as explained above, said issue
            # occurs only in if-case.
            else:
                print('x', self.windowTitle())
                qss = 'QWidget { background-color: blue; }'
                self.setStyleSheet(qss)
                # self.setStyleSheet('')
                # QTimer.singleShot(0, lambda: self.setStyleSheet(qss))
                # self.setWindowTitle('no focus')
    
    
    if __name__ == '__main__':
        app = QApplication([])
        app.setStyle('Fusion')
        w = QMainWindow()
    
    
        d1 = Dock('d1')
        b1 = QPushButton('Qt modal window')
        b1.pressed.connect(lambda: QInputDialog.getText(d1, 'getText()', 'enter text'))
        d1.setWidget(b1)
        w.addDockWidget(Qt.RightDockWidgetArea, d1, Qt.Horizontal)
    
        d2 = Dock('d2')
        b2 = QPushButton('OS modal window')
        b2.pressed.connect(lambda: QFileDialog.getOpenFileName(d2, 'getOpenFileName()', '.'))
        d2.setWidget(b2)
        w.addDockWidget(Qt.RightDockWidgetArea, d2, Qt.Horizontal)
    
        d3 = Dock('d3')
        d3.setWidget(QTextEdit('d3'))
        w.addDockWidget(Qt.RightDockWidgetArea, d3, Qt.Horizontal)
    
    
        w.show()
        sys.exit(app.exec_())
    


  • OK, so I just tried to pass QMainWindow instance as a parent to QFileDialog, and it seems that this focus issue is gone.

    So now my question is this: does anyone else think this is a bug, so that I could file bug report? I wonder if it's something related to modality type as described over here (http://doc.qt.io/qt-5/qdialog.html#modal-dialogs), like QDockWidget being able to turn into Qt.Tool. But then again, by default QDialog-based classes use Qt.ApplicationModal... Anyway, what you think: is this a bug?


  • Lifetime Qt Champion

    Hi,

    Just a wild guess but the dialog that you invoke triggers a local event loop that blocks the application flow until you’re done with it. That event loop now processes all the events, so there might be something regarding that.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.