Farzad
Farzad

Reputation: 141

Add custom widget to a QTreeWidget column in PyQt5

I want to have a QTreeWidget with hierarchical items where one column contains a custom widget. I created a custom widget that contains a QLabel and a QSpinBox in a horizontal layout.

MySlider.py looks like this:

from PyQt5 import QtWidgets 
from PyQt5 import QtCore 
from PyQt5.Qt import Qt 
import sys

class MySlider(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(self.__class__, self).__init__(parent)
        
        self.horizontalLayoutWidget = QtWidgets.QWidget(parent)
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)

        self.label = QtWidgets.QLabel(self.horizontalLayoutWidget)
        self.label.setText('Spinbox:')
        self.spinBox1 = QtWidgets.QSpinBox(self.horizontalLayoutWidget)

        self.horizontalLayout.addWidget(self.label)
        self.horizontalLayout.addWidget(self.spinBox1)

Then I want to create a QTreeWidget where the third column is filled with a MySlider widget. The application looks like this:

import sys
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QAbstractItemView
from MySlider import MySlider

def main():
    _translate = QtCore.QCoreApplication.translate
    app = QtWidgets.QApplication(sys.argv)
    tree = QtWidgets.QTreeWidget()
    tree.setColumnCount(3)
    headerItem = QtWidgets.QTreeWidgetItem()
    headerItem.setText(0, _translate("MainWindow", "md-name"))
    headerItem.setText(1, _translate("MainWindow", "md_value"))
    headerItem.setText(2, _translate("MainWindow", "others"))
    tree.setHeaderItem(headerItem)

    parent = QtWidgets.QTreeWidgetItem(tree)
    parent.setText(0, "Parent 1")
    parent.setText(1, "")
    parent.setFlags(parent.flags() | Qt.ItemIsTristate | Qt.ItemIsEditable)

    for x in range(3):
        child = QtWidgets.QTreeWidgetItem(parent)
        child.setFlags(child.flags() | Qt.ItemIsEditable)
        child.setText(0, "Child {}".format(x))
        line_edit = QtWidgets.QLineEdit(tree)

        rs = MySlider(tree)
        tree.setItemWidget(child, 1, line_edit)
        tree.setItemWidget(child, 2, rs)

    tree.setEditTriggers(QAbstractItemView.AllEditTriggers)
    tree.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

The problem is: the custom widget is not placed in the third column as wished, but is shown in the top-left corner of the window. Has anyone any idea why it goes wrong?

Upvotes: 1

Views: 2018

Answers (1)

ekhumoro
ekhumoro

Reputation: 120578

There are several problems with the definition of the MySlider class.

Firstly, you should never use self.__class__ with super. In certain scenarios it can cause an infifnite recursion, and in any case it's completely unnecessary to pass arguments to super in Python 3.

Secondly, there's no need to create an inner widget for the layout - and it should not be parented to the object that's passed in as the parent argument (this is what actually causes the widget to be displayed in the wrong place).

Finally, you probably need to make some adjustments to the layout to ensure that the custom widget does not take up too much space, and that its child widgets expand to the appropriate sizes.

Here is how the class should look:

class MySlider(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        layout = QtWidgets.QHBoxLayout(self)
        # keep only the default margin on the left
        layout.setContentsMargins(-1, 0, 0, 0)
        self.label = QtWidgets.QLabel()
        self.label.setText('Spinbox:')
        self.spinBox1 = QtWidgets.QSpinBox()
        # make sure the spin-box doesn't get too small
        self.spinBox1.setMinimumWidth(80)
        layout.addWidget(self.label)
        layout.addWidget(self.spinBox1)
        # don't allow the spin-box to exapnd too much
        layout.addStretch()

If you'd prefer to have the spin-box expand to fill the whole column, you can omit the last line and do this instead:

        # make sure the spin-box fills the whole column
        layout.addWidget(self.spinBox1, 1)
        

Note that in the code above, the child widgets aren't given an explicit parent. Whenever widgets are added to a layout, Qt will automatically reparent them to whichever widget eventually contains the layout.

Upvotes: 1

Related Questions