PySide - PyQt : How to make set QTableWidget column width as proportion of the available space?
Asked Answered
N

11

44

I'm developing a computer application with PySide and I'm using the QTableWidget. Let's say my table has 3 columns, but the data they contain is very different, like (for each row) a long sentence in the first column, then 3-digit numbers in the two last columns. I'd like to have my table resize in order to adjust its size to the data, or at least to be able to set the column sizes as (say) 70/15/15 % of the available space.

What is the best way to do this ?

I've tried table.horizontalHeader().setResizeMode(QHeaderView.Stretch) after reading this question but it makes 3 columns of the same size.

I've also tried table.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents) thanks to Fabio's comment but it doesn't fill all the available space as needed.

Neither Interactive, Fixed, Stretch, ResizeToContents from the QHeaderView documentation seem to give me what I need (see second edit).

Any help would be appreciated, even if it is for Qt/C++ ! Thank you very much.


EDIT : I found kind of a workaround but it's still not what I'm looking for :

header = table.horizontalHeader()
header.setResizeMode(QHeaderView.ResizeToContents)
header.setStretchLastSection(True)

It would be better if there existed a setStretchFirstSection method, but unfortunately there does not seem to be one.


EDIT 2 :

The only thing that can be modified in the table is the last column, the user can enter a number in it. Red arrows indicates what I'd like to have.

Here's what happens with StretchStretch

Here's what happens with ResizeToContents ResizeToContents

Nephrectomy answered 29/6, 2016 at 11:53 Comment(3)
Try QHeaderView.ResizeToContents instead of QHeaderView.StretchEquate
This works, but it's not quite what I'm looking for, because with ResizeToContents, as my columns are not large enough, they do not fill the space available. I will edit so it becomes more clear.Nephrectomy
For your 2nd edit, I believe the correct thing is to set the column with a default width from your rough estimation, then since it can be stretched users can modify them later as they wish.Glary
S
104

This can be solved by setting the resize-mode for each column. The first section must stretch to take up the available space, whilst the last two sections just resize to their contents:

PyQt5:

header = self.table.horizontalHeader()       
header.setSectionResizeMode(0, QHeaderView.Stretch)
header.setSectionResizeMode(1, QHeaderView.ResizeToContents)
header.setSectionResizeMode(2, QHeaderView.ResizeToContents)

PyQt6/PyQt5:

header = self.table.horizontalHeader()       
header.setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch)
header.setSectionResizeMode(1, QHeaderView.ResizeMode.ResizeToContents)
header.setSectionResizeMode(2, QHeaderView.ResizeMode.ResizeToContents)

PyQt4:

header = self.table.horizontalHeader()
header.setResizeMode(0, QHeaderView.Stretch)
header.setResizeMode(1, QHeaderView.ResizeToContents)
header.setResizeMode(2, QHeaderView.ResizeToContents)
Spacing answered 30/6, 2016 at 17:33 Comment(2)
I didn't know that I could choose a different QHeaderView for each column. This works fine, though I changed to Fixed for the last two columns and then added a minimum width with self.table.setColumnWidth(index, width) (suits better my case)Nephrectomy
In qt5 this function is changed to setSectionResizeMode()Clavicytherium
P
14

As mentioned before, you can do this by setting the resize-mode of each column. However, if you have a lot of columns this can be a lot of code. The way I do it is setting the "general" resize-mode to "ResizeToContent" and than for one (or more) columns to "Stretch"!

Here is the code:

PyQt4:

header = self.table.horizontalHeader()
header.setResizeMode(QtGui.QHeaderView.ResizeToContents)
header.setResizeMode(0, QtGui.QHeaderView.Stretch)

PyQt5:

header = self.table.horizontalHeader()
header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)       
header.setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch)
Pending answered 5/10, 2018 at 11:48 Comment(0)
M
5

PyQt4

header = self.table.horizontalHeader()
header.setResizeMode(0, QtGui.QHeaderView.Stretch)
header.setResizeMode(1, QtGui.QHeaderView.ResizeToContents)
header.setResizeMode(2, QtGui.QHeaderView.ResizeToContents)
header.setResizeMode(3, QtGui.QHeaderView.Stretch)

PyQt5

header = self.table.horizontalHeader()       
header.setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch)
header.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents)
header.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents)
header.setSectionResizeMode(3, QtWidgets.QHeaderView.Stretch)
Microgroove answered 22/12, 2017 at 18:49 Comment(0)
N
4

I have used this and setMinumumsectionsize has solved my problem of Qtableview cell size

self.questionWidget.tableView.setModel(self.model)
self.questionWidget.tableView.verticalHeader().setVisible(False)
self.questionWidget.tableView.horizontalHeader().setMinimumSectionSize(200)

Nanette answered 20/11, 2020 at 16:59 Comment(0)
S
3

You can do this with QItemDelegates or QStyledItemDelegates. If you want to resize to contents and have automatic stretch, you'll need to choose which column is the "stretch" column.

class ResizeDelegate(QStyledItemDelegate):

    def __init__(self, table, stretch_column, *args, **kwargs):
        super(ResizeDelegate, self).__init__(*args, **kwargs)        
        self.table = table
        self.stretch_column = stretch_column

    def sizeHint(self, option, index):
        size = super(ResizeDelegate, self).sizeHint(option, index)
        if index.column() == self.stretch_column:
            total_width = self.table.viewport().size().width()
            calc_width = size.width()
            for i in range(self.table.columnCount()):
                if i != index.column():
                    option_ = QtGui.QStyleOptionViewItem()
                    index_ = self.table.model().index(index.row(), i)
                    self.initStyleOption(option_, index_)
                    size_ = self.sizeHint(option_, index_)
                    calc_width += size_.width()
            if calc_width < total_width:
                size.setWidth(size.width() + total_width - calc_width)
        return size

...

table = QTableWidget()
delegate = ResizeDelegate(table, 0)
table.setItemDelegate(delegate)
... # Add items to table
table.resizeColumnsToContents()

You can set the resize mode to ResizeToContents, or if you want the user to be able to adjust the column width as needed, just call resizeColumnsToContents manually after making changes to the table items.

You also may need to fudge around with the width calculations a bit because of margins and padding between columns (like add a pixel or two to the calculated_width for each column to account for the cell border).

Scatter answered 29/6, 2016 at 18:25 Comment(1)
This is good because now my columns don't resize at anytime like they did before, but they still don't take all the available space, and changing the "stretch" column does not seem to have any effect... Am I doing something wrong ? I will edit my question with the results I getNephrectomy
C
2

I had to remove the QtWidget to get mine to work.

        header = self.tag_table.horizontalHeader()
        header.setSectionResizeMode(QHeaderView.ResizeToContents)
Checkers answered 11/2, 2021 at 2:56 Comment(1)
Whether QtWidget. is needed or not depends solely on the way you import the modules ;)Nephrectomy
C
1

PyQt6

header = self.table.horizontalHeader()       
header.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeMode.Stretch)
header.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeMode.ResizeToContents)
header.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeMode.ResizeToContents)

Resize options are enums of type ResizeMode
Other available options are

 Interactive = ... # type: QHeaderView.ResizeMode
 Fixed = ... # type: QHeaderView.ResizeMode
 Stretch = ... # type: QHeaderView.ResizeMode
 ResizeToContents = ... # type: QHeaderView.ResizeMode
 Custom = ... # type: QHeaderView.ResizeMode
Caress answered 19/9, 2022 at 3:56 Comment(0)
P
0

This worked for me in PyQt5:

table_name.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)

It will auto ajust the size of all columns.

Here's all my horizontal header settings:

    table_name.horizontalHeader().setVisible(True)
    table_name.horizontalHeader().setCascadingSectionResizes(True)
    table_name.horizontalHeader().setDefaultSectionSize(140)
    table_name.horizontalHeader().setHighlightSections(False)
    table_name.horizontalHeader().setMinimumSectionSize(100)
    table_name.horizontalHeader().setSortIndicatorShown(False)
    table_name.horizontalHeader().setStretchLastSection(False)
   table_name.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
Prig answered 3/5, 2022 at 12:41 Comment(0)
F
0

In a class which inherits from QTableWidget, I implemented this function in PyQt5:

def set_header_stretch_first_section(self):
    header = self.horizontalHeader()
    header.setSectionResizeMode(0, QHeaderView.Stretch)
    for i in range(1, self.columnCount()):
        header.setSectionResizeMode(i, QHeaderView.ResizeToContents)
Frail answered 10/1, 2023 at 8:30 Comment(0)
F
0

I was not able to find an "out of the box" solution but I did the below:

Create a thread which checks whether the size of the table has changed every 50 milliseconds and if it has, trigger a signal to a slot which updates the column widths based on the current table size.

In the below example, the ratio of column widths are 1:1:2.

Example:

import time
from PySide6.QtCore import Signal

# Class definition here

def __init__(self):
    # Class initialization here
    self.signal_table_size_changed = Signal()
    self.signal_table_size_changed.connect(self.adjust_table_column_width)
    self.table_width = None
    threading.Thread(target=self.table_size_watcher).start()


def adjust_table_column_width(self):
    self.table_width = self.table.size().width()
    self.table.setColumnWidth(0, 0.25 * self.table_width)
    self.table.setColumnWidth(1, 0.25 * self.table_width)
    self.table.setColumnWidth(2, 0.50 * self.table_width)

def table_size_watcher(self):
    while True:
        if self.table_width != self.table.size().width():
            self.signal_table_size_changed.emit()
        time.sleep(0.05)
Fungicide answered 30/1, 2023 at 19:30 Comment(0)
J
0

before this fix in PySide6 the 5 columns in the QTableView would stretch beyond the visible table

after this fix, they are aligned perfectly (btw the table view has fixed width of 371px) (using QtDesigner too)

self.table_view = QTableView()
self.model.setHeaderData(index,Qt.Horizontal,value)
self.table_view.setColumnWidth(0,65)  #(index,width-px)
self.table_view.setColumnWidth(1,75)
self.table_view.setColumnWidth(2,75)
self.table_view.setColumnWidth(3,75)
self.table_view.setColumnWidth(4,71)

screenshot (lower right on green screen): enter image description here

Joust answered 20/3, 2023 at 23:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.