Search code examples
pythonpyqt5qtablewidget

How can i align a CellWidget of a TableWidget to the center of the Item in pyqt5


i have a comboBox inside of a tableWidget and the verticalHeader DefaultSectionSize is 60.

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        QtWidgets.QMainWindow.__init__(self,parent)
        self.table = QTableWidget()
        self.setCentralWidget(self.table)
       
        self.table.verticalHeader().setDefaultSectionSize(60)
        self.table.setColumnCount(2)
        self.table.setRowCount(2)
        
        data = ["1","2"]
    
        for i in range(2):
            item = QTableWidgetItem(data[i])
            self.table.setItem(i,0,item)
            self.combo_sell = QComboBox()
            self.combo_sell.setMaximumHeight(30)
            self.table.setCellWidget(i,1,self.combo_sell)

But since i set the maximum size of the comboBox to 30, it stays in the top of the item.

image

I want to know if there's a way to align it to the center.


Solution

  • When setting an index widget, the view tries to set the widget geometry based on the visualRect() of the index. Setting a fixed dimension forces the widget to align itself to the default origin, which is usually the top left corner.

    The only way to vertically center a widget with a fixed height is to use a container with a vertical box layout and add the combo to it:

            for i in range(2):
                item = QTableWidgetItem(data[i])
                item.setTextAlignment(Qt.AlignCenter)
                self.table.setItem(i,0,item)
                container = QWidget()
                layout = QVBoxLayout(container)
                combo_sell = QComboBox()
                layout.addWidget(combo_sell)
                combo_sell.setMaximumHeight(30)
                self.table.setCellWidget(i, 1, container)
    

    Note: setting instance attributes in a for loop is pointless, as the reference is lost every time the cycle loops.

    If you need a simple reference to the combo, you can set it as an attribute of the widget:

        container.combo_sell = QComboBox()
    

    In this way you can easily access it when required:

            widget = self.table.cellWidget(row, column)
            if widget and hasattr(widget, 'combo'):
                combo = widget.combo
                print(combo.currentIndex())
    

    Note that the reference is created for the python wrapper of the widget, and that behavior might change in future versions of Qt. A better and safer way to achieve this would be to use a subclass, which would also allow easier access to the combo:

    class TableCombo(QWidget):
        def __init__(self):
            super().__init__()
            layout = QVBoxLayout(self)
            self.combo = QComboBox()
            layout.addWidget(self.combo)
            self.combo.setMaximumHeight(30)
            self.currentIndex = self.combo.currentIndex
            self.setCurrentIndex = self.combo.setCurrentIndex
            self.addItems = self.combo.addItems
    
    # ...
    
                combo_sell = TableCombo()
                self.table.setCellWidget(i, 1, combo_sell)
    
    # ...
    
            combo = self.table.cellWidget(row, column)
            print(combo.currentIndex())