Search code examples
pythonpyqtqsplitter

QSplitter.sizes indicates wrong sizes


I save the state of one of my QSplitter like that:

self.options.setValue("Window/final_splitter", self.splitter2.saveState())

And I restore it like that:

self.splitter2.restoreState(self.options.value("Window/final_splitter"))

When I save the splitter, splitter2.sizes() indicates: [321, 818, 769]

When I restore the splitter, it has visually the same dimensions as when I closed it, but splitter2.sizes() indicates: [224, 572, 537]

And I can't perform more actions on the splitter because I can't get its size right.

Do you have any idea about how to solve this bug ?

EDIT: even more odd:

saving:

self.options.setValue("Window/final_splitter", self.splitter2.sizes())

-> self.splitter2.sizes(): [321, 844, 743]

Restoring:

sizes_splitter2 = [int(nbr) for nbr in self.options.value("Window/final_splitter", [])]
self.splitter2.setSizes(sizes_splitter2)
  • sizes_splitter2: [321, 844, 743]
  • self.splitter2.sizes(): [224, 590, 519]

EDIT 2: When I save the splitter, I also do (self is a QMainWIndow):

self.options.setValue("window_geometry", self.saveGeometry())
self.options.setValue("window_state", self.saveState())

If I comment those lines, the splitter.sizes() returns the good values. But those 2 lines do their job, the window is restored to the right size with them !


Solution

  • I'm guessing that you're trying to query the splitter sizes before the window is fully shown.

    One way to work around this is to use a single shot timer to perform further actions after the event loop has started.

    Hopefully this demo script should show what's going on:

    from PyQt4 import QtCore, QtGui
    
    class Window(QtGui.QMainWindow):
        def __init__(self):
            super(Window, self).__init__()
            widget = QtGui.QWidget(self)
            layout = QtGui.QVBoxLayout(widget)
            self.setCentralWidget(widget)
            self.splitter = QtGui.QSplitter(self)
            self.splitter.addWidget(QtGui.QTextEdit(self))
            self.splitter.addWidget(QtGui.QTextEdit(self))
            self.splitter.addWidget(QtGui.QTextEdit(self))
            self.button = QtGui.QPushButton('Test', self)
            self.button.clicked.connect(lambda: self.printSizes('Test'))
            layout.addWidget(self.splitter)
            layout.addWidget(self.button)
            self.printSizes('Show Before')
            settings = self.settings()
            value = settings.value('splitter')
            if value is not None:
                self.splitter.restoreState(value)
            value = settings.value('layout')
            if value is not None:
                self.restoreState(value)
            value = settings.value('geometry')
            if value is not None:
                self.restoreGeometry(value)
            self.printSizes('Show After')
            QtCore.QTimer.singleShot(
                20, lambda: self.printSizes('Timer'))
    
        def closeEvent(self, event):
            self.printSizes('Close')
            settings = self.settings()
            settings.setValue("splitter", self.splitter.saveState())
            settings.setValue("geometry", self.saveGeometry())
            settings.setValue("layout", self.saveState())
    
        def settings(self):
            return QtCore.QSettings(
                QtCore.QSettings.IniFormat,
                QtCore.QSettings.UserScope, 'test', 'test')
    
        def printSizes(self, message):
            print('%s:' % message)
            print('  window geometry:', (
                self.width(), self.height(), self.x(), self.y()))
            print('  splitter sizes:', self.splitter.sizes())
            print()
    
    if __name__ == '__main__':
    
        import sys
        app = QtGui.QApplication(sys.argv)
        window = Window()
        window.show()
        sys.exit(app.exec_())