1

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 !

JPFrancoia
  • 4,866
  • 10
  • 43
  • 73
  • Does it help to call `.refresh()` on the splitter? The docs mention that you should not need to do this, but it may help. – mfitzp Mar 01 '15 at 17:06
  • Nope, it changes nothing, sorry. – JPFrancoia Mar 01 '15 at 17:09
  • Thanks for the clarification. This is odd, at a loss to explain it. – mfitzp Mar 01 '15 at 17:23
  • I notice that `321+818+769=1908` and `224+572+537=1333`. If you do `321/1908=0.168` and `224/1333=0.168`. That is the first region is still taking up 16.8% of the window, the window is just smaller. Is that true? – mfitzp Mar 01 '15 at 17:31
  • Yeah I'm starting to smell something too. I added an edit to my question. But no the window is not smaller. Everything is exactly the same when I close and open my program. – JPFrancoia Mar 01 '15 at 17:33
  • Have you tried using this in conjunction with `saveGeometry` and `restoreGeometry`? – mfitzp Mar 01 '15 at 17:34
  • Yes, you can see my EDIT 2. – JPFrancoia Mar 01 '15 at 17:36
  • Do you do `restoreGeometry` before or after `restoreState`? I'm wondering if the sizes are getting set relative to a window at a larger size (perhaps not displayed), then being resized down when the window is rendered. – mfitzp Mar 01 '15 at 17:39
  • I tried both, but it didn't make any difference. – JPFrancoia Mar 01 '15 at 17:45
  • What happens if you call `saveState` again? Does it get smaller and smaller? – mfitzp Mar 01 '15 at 17:54

2 Answers2

0

The documentation for .restoreState() indicates that you have to recast the returned value to a ByteArray (QSettings can return Variant type, not the same type as you originally set).

self.splitter.restoreState(self.options.value("final_splitter").toByteArray())

It is worth noting that the behaviour of QSettings differs somewhat between different versions of PyQt/PySide and on different platforms but the above should work in all cases.

mfitzp
  • 15,275
  • 7
  • 50
  • 70
  • I tried: self.splitter2.restoreState(self.options.value("Window/final_splitter").toByteArray()), but I have an exception telling me it is already a byteArray. The splitter dimensions ARE restored, visually, in the window, but it seems its size attribute is not set properly. – JPFrancoia Mar 01 '15 at 17:14
  • Yeh, I was wondering if when settings them the UI was being updated but it not being a `ByteArray` was preventing it storing it internally. Oh well, back to the drawing board. – mfitzp Mar 01 '15 at 17:16
0

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_())
ekhumoro
  • 115,249
  • 20
  • 229
  • 336
  • Not really, I'm trying to get the sizes of my splitter to do some resizing. But actually, the sizes of my splitter are smaller than they should be, but they are displayed normally. the sizes() method returns a wrong result. Also, MainWindow.geometry() returns a smaller width than the window actually has. – JPFrancoia Mar 01 '15 at 21:00
  • @Rififi. My example works as expected. If you have a counter-example, you will need to show it in your question. – ekhumoro Mar 01 '15 at 21:08
  • QtCore.QTimer.singleShot(0, lambda: print('Timer:', self.splitter.sizes())) should print the real size of the splitter ? – JPFrancoia Mar 01 '15 at 21:10
  • @Rififi. Yes, it does for me on Linux. using python 3.4.2, qt 4.8.6, pyqt 4.11.3. What's your setup? – ekhumoro Mar 01 '15 at 21:18
  • Yes ok, it does for me too now, wrong copy/paste. I tried the QTimer thing in my own code, but it doesn't solve the issue. So I tried to uncomment the lines which restore the window (see EDIT 2). Then, sizes() returns the right sizes. The problem comes from restoring the window. – JPFrancoia Mar 01 '15 at 21:28
  • 1
    @Rififi. I expanded my example to use a `QMainWindow` that saves/restores state/geometry and it all works as expected for me. You are going to have to produce a working example that reproduces the problem if you want more help. – ekhumoro Mar 01 '15 at 23:24
  • Ok, let's work on your code. I think I found where the problem comes from, you have the same "bug" as me. But I don't know if it's a bug, or if it's the expected behavior. Could you try something very simple ? Start your code. Maximize the window. Close the window. Note the dimensions. For me: Close: window geometry: (1920, 1002, 0, 25). Now starts the program again. The window is displayed maximized. Note the dimensions. For me: Timer: window geometry: (786, 242, 0, 45). These are not the dimensions of the window. The problem comes from the maximizing. – JPFrancoia Mar 02 '15 at 14:54
  • @Rififi. I've edited the code slightly to add a small (20 ms) delay to the timer, which fixes the issue for me (only tested on Linux, though). – ekhumoro Mar 02 '15 at 19:21
  • Thanks, it finally worked. I have to add something however: you can't perform other actions, such as looking in a database, while restoring the window. I was doing that, and it caused the bug, even with the timer. – JPFrancoia Mar 02 '15 at 21:03