Search code examples
python-3.xqt5pyside2qsplitter

Pyside2: QSplitter replaceWidget not working as expected


I have a QSplitter with two widgets in it and want to replace one of them:

import sys
from PySide2.QtWidgets import (QMainWindow, QApplication, 
    QVBoxLayout, QWidget, QSplitter, QTextEdit, QLabel)


class Example(QWidget):  
    def __init__(self):
        super().__init__()

        splitter = QSplitter()
        splitter.insertWidget(0, QLabel('test'))
        splitter.insertWidget(1, QTextEdit())

        print(splitter.count())  # prints 2
        splitter.replaceWidget(1, QLabel('something else'))
        print(splitter.count())  # prints 1

        self.layout = QVBoxLayout(self)
        self.layout.addWidget(splitter)


if __name__ == "__main__":
    app = QApplication()

    main = Example()
    main.show()

    sys.exit(app.exec_())

My understanding of replaceWidget is that it will replace an existing widget on index X in the splitter by the provided widget. In the above example on index 0 there's a QLabel('test') and on index 1 QTextEdit().

After trying to replace the widget on index 1, I would expect it to be QLabel('something else'). But it seems that the widget on index 1 just gets deleted from looking at the output of the count() before and after the replace operation.

Can anyone see the issue?


Solution

  • This seems to be caused by a bug in PySide2. The count should be 2 after replaceWidget() is called, but it's 1.

    According to the docs for replaceWidget, the splitter should take ownership of the new widget and reparent it. But PySide seems to lose track of it somehow - so presumably it just gets garbage-collected. The same code run in PyQt5 works as expected, although the inserted widget still won't show up (because reparenting will always make a widget invisble). The way to work around these issues is to make sure the widget has a parent before inserting it in the splitter, and then call show() on it afterwards:

        label = QLabel('something else', self)
    
        print(splitter.count())
        splitter.replaceWidget(1, label)
        print(splitter.count())
    
        label.show()
    

    This works correctly for me in both PySide2 (5.15.2) and PyQt5 (5.15.2).