pythonpyqt5resizeqtablewidgetqpushbutton

How to disable the size limit of the QPushButton in the QTableWidget in PyQt5?


I am trying to find solution by some time but nothing works so far..

I made a simple QTableWidget in PyQt5 and while casual QTableWidgetItems and QLabel resizes well using the option .resizeColumnsToContents(), the QPushButton somehow keeps its minimum width which makes the column too wide.

I tried using setMinimumWidth(1) on the button but it didn't work.

Here is the code I made:

import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QTableWidget, QApplication, QTableWidgetItem, QPushButton, QLabel


class Table(QTableWidget):
    def __init__(self, *args):
        super().__init__(*args)
        button = QPushButton("X")
        button.setMinimumWidth(1)
        self.setHorizontalHeaderLabels(["1", "2", "3", "4"])
        self.setItem(0, 0, Cell("Val1"))
        self.setCellWidget(0, 1, button)
        self.setCellWidget(0, 2, QLabel("Y"))
        self.setItem(0, 3, Cell("Val3"))
        self.resizeColumnsToContents()
        self.resizeRowsToContents()


class Cell(QTableWidgetItem):
    def __init__(self, *args):
        super().__init__(*args)
        self.setTextAlignment(Qt.AlignCenter)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    widget = Table(1, 4)
    widget.show()
    sys.exit(app.exec_())

Solution

  • When using widgets in item views, the header auto resizing uses the widget's size hint, not the minimum size.

    A QPushButton normally has a wider size hint (even if it doesn't have text), so you have two options:

    1. use a QToolButton instead (which is normally smaller by default);
    2. create a subclass of QPushButton and override its sizeHint();

    In the second case, you could just return a minimum arbitrary size:

    class PushButton(QPushButton):
        def sizeHint(self):
            return QSize(10, 10)
    
    # ...
    button = PushButton("X")
    self.setCellWidget(0, 1, button)
    

    But that wouldn't be appropriate, because you'd need to try to show the full text of the button.

    A proper solution should use the style, which has a function that can compute the proper sizes of widgets and other elements: sizeFromContents().

    Note that in this case you should use a QStyleOptionToolButton and the CT_ToolButton type (instead of QStyleOptionButton and CT_PushButton), otherwise you'll end up with a larger button anyway:

    class PushButton(QPushButton):
        def sizeHint(self):
            self.ensurePolished()
            opt = QStyleOptionToolButton()
            opt.initFrom(self)
            fm = self.fontMetrics()
            textSize = fm.size(Qt.TextShowMnemonic, self.text())
            textSize.setWidth(textSize.width() + 
                fm.horizontalAdvance(' ') * 2)
            opt.rect.setSize(textSize)
            return self.style().sizeFromContents(
                QStyle.CT_ToolButton, opt, textSize)