Search code examples
pythonpython-3.xpyside6pyqt6

QMdiArea, stop subwindows from leaving area


Is it possible to avoid the effect of the subwindow being partially obscured?

sub window is not totally visible after being dragged.


Solution

  • QMdiArea automatically installs its event filter on any new QMdiSubWindow, so you can override eventFilter(), check for geometry changes, and ensure that the geometry is always within the viewport rectangle.

    In the following example I created a helper function to do so, which can also be called whenever the mdi area is resized, in order to ensure that windows are always visible even when the area is resized to a size that would potentially hide windows.

    class MdiFixBoundaries(QtWidgets.QMdiArea):
        def fixGeometry(self, window, viewGeo):
            winGeo = window.geometry()
            if not viewGeo.contains(winGeo):
                if winGeo.right() > viewGeo.right():
                    winGeo.moveRight(viewGeo.right())
                if winGeo.x() < 0:
                    winGeo.moveLeft(0)
    
                if winGeo.bottom() > viewGeo.bottom():
                    winGeo.moveBottom(viewGeo.bottom())
                if winGeo.y() < 0:
                    winGeo.moveTop(0)
                if winGeo != window.geometry():
                    window.setGeometry(winGeo)
                    return True
            return False
    
        def eventFilter(self, obj, event):
            if (event.type() == event.Move and 
                isinstance(obj, QtWidgets.QMdiSubWindow) and
                self.fixGeometry(obj, self.viewport().geometry())):
                    return True
            return super().eventFilter(obj, event)
    
        def resizeEvent(self, event):
            super().resizeEvent(event)
            viewGeo = self.viewport().geometry()
            for win in self.subWindowList():
                self.fixGeometry(win, viewGeo)
    
    
    app = QtWidgets.QApplication([])
    mdi = MdiFixBoundaries()
    for i in range(3):
        test = mdi.addSubWindow(QtWidgets.QWidget())
        test.resize(320, 240)
    mdi.show()
    app.exec()
    

    Note: the ifs in fixGeometry() must be kept, without any elif and in that order, otherwise you'll risk recursion in case the size of a window is bigger than the viewport.