PyQt5. Растягивание таблицы QTableWidget() на всю область

Есть таблица QTableWidget() со свойством table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) для растягивания столбцов на всю областью. Однако при использовании этого свойства теряется возможность изменения ширины столбцов в окне. Можно ли как-то изменить это поведение? Методы setStretchLastSection(True) и setSectionResizeMode(index, QHeaderView.Stretch) не подходят, так как количество столбцов определятся динамически при заполнении таблицы, в этом случае таблица может уйти за границы поля либо будет большой перекос по ширине в каком-то одном выбранном столбце, этого хотелось бы избежать.

import sys
from PyQt5.QtWidgets import *


class MainInterface(QMainWindow):

    def __init__(self, tuple_of_dict: tuple = None):
        super().__init__()

        self.__tuple_of_dict = tuple_of_dict

        self.centralWidget = QWidget()
        self.setCentralWidget(self.centralWidget)

        self.setMinimumHeight(400)
        self.setMinimumWidth(650)

        table = QTableWidget()

        if self.__tuple_of_dict:
            table_headers = tuple(self.__tuple_of_dict[0].keys())

            table.setColumnCount(len(table_headers))
            table.setRowCount(len(self.__tuple_of_dict))
            table.setHorizontalHeaderLabels(table_headers)
            table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
            # table.horizontalHeader().setStretchLastSection(True)
            # table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
            for num, row in enumerate(self.__tuple_of_dict):
                for column in table_headers:
                    if isinstance(row[column], bool):
                        row[column] = "True" if row[column] else "False"
                    elif isinstance(row[column], type(None)):
                        row[column] = "None"
                    row_item = QTableWidgetItem(row[column])
                    table.setItem(num, table_headers.index(column), row_item)
                    row_item.setToolTip(row[column])

        box = QGroupBox("Table")
        h_layout = QHBoxLayout(box)
        h_layout.addWidget(table)

        g_layout = QGridLayout(self.centralWidget)
        g_layout.addWidget(box, 1, 1)


if __name__ == '__main__':

    result = ({"id": "673543", "devicename": "bla_bla_bla", "description": "My Fancy Device"}, )

    app = QApplication(sys.argv)
    # app.setStyleSheet(MAIN_QSS)
    app.setStyle("Fusion")
    ex = MainInterface(tuple_of_dict=result)
    ex.show()
    sys.exit(app.exec_())

Ответы (2 шт):

Автор решения: Sergey Tatarincev

Вы можете указать какой столбец будет растягиваться (не все сразу, как в вашем примере!), при этом все размеры будут меняться

import sys
from PyQt5.QtWidgets import *


class MainInterface(QMainWindow):

    def __init__(self, tuple_of_dict: tuple = None):
        super().__init__()

        self.__tuple_of_dict = tuple_of_dict

        self.centralWidget = QWidget()
        self.setCentralWidget(self.centralWidget)

        self.setMinimumHeight(400)
        self.setMinimumWidth(650)

        table = QTableWidget()

        if self.__tuple_of_dict:
            table_headers = tuple(self.__tuple_of_dict[0].keys())

            table.setColumnCount(len(table_headers))
            table.setRowCount(len(self.__tuple_of_dict))
            table.setHorizontalHeaderLabels(table_headers)
            for num, row in enumerate(self.__tuple_of_dict):
                for column in table_headers:
                    if isinstance(row[column], bool):
                        row[column] = "True" if row[column] else "False"
                    elif isinstance(row[column], type(None)):
                        row[column] = "None"
                    row_item = QTableWidgetItem(row[column])
                    table.setItem(num, table_headers.index(column), row_item)
                    row_item.setToolTip(row[column])
        table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch) # вот так
        box = QGroupBox("Table")
        h_layout = QHBoxLayout(box)
        h_layout.addWidget(table)

        g_layout = QGridLayout(self.centralWidget)
        g_layout.addWidget(box, 1, 1)


if __name__ == '__main__':
    result = ({"id": "673543", "devicename": "bla_bla_bla", "description": "My Fancy Device", "another": "value"}, )
    app = QApplication(sys.argv)
    app.setStyle("Fusion")
    ex = MainInterface(tuple_of_dict=result)
    ex.show()
    sys.exit(app.exec_())
→ Ссылка
Автор решения: Sergey Tatarincev

Нашел для вас пример. Из минусов - "руками" можно оставить пустое место после последней секции

import sys
from PyQt5 import QtWidgets
from PyQt5.QtCore import QAbstractTableModel, Qt, QVariant

class TableModel(QAbstractTableModel):
    def __init__(self, parent, datain, headerdata):
        QAbstractTableModel.__init__(self, parent)

        self.arraydata=datain
        self.headerdata=headerdata

    def rowCount(self,p):
        return len(self.arraydata)

    def columnCount(self,p):
        if len(self.arraydata)>0:
            return len(self.arraydata[0])
        return 0

    def data(self, index, role):
        if not index.isValid():
            return QVariant()
        elif role != Qt.DisplayRole:
            return QVariant()
        return QVariant(self.arraydata[index.row()][index.column()])

    def headerData(self, col, orientation, role):
        if orientation==Qt.Horizontal and role==Qt.DisplayRole:
            return self.headerdata[col]
        return None

class MyHeaderView(QtWidgets.QHeaderView):
    def __init__(self,parent):
        QtWidgets.QHeaderView.__init__(self,Qt.Horizontal,parent)
        self.sectionResized.connect(self.myresize)

    def myresize(self, *args):
        '''Resize while keep total width constant'''

        # keep a copy of column widths
        ws=[]
        for c in range(self.count()):
            wii=self.sectionSize(c)
            ws.append(wii)

        if args[0]>0 or args[0]<self.count():
            for ii in range(args[0],self.count()):
                if ii==args[0]:
                    # resize present column
                    self.resizeSection(ii,args[2])
                elif ii==args[0]+1:
                    # if present column expands, shrink the one to the right
                    self.resizeSection(ii,ws[ii]-(args[2]-args[1]))
                else:
                    # keep all others as they were
                    self.resizeSection(ii,ws[ii])

    def resizeEvent(self, event):
        """Resize table as a whole, need this to enable resizing"""

        super(QtWidgets.QHeaderView, self).resizeEvent(event)
        self.setSectionResizeMode(1,QtWidgets.QHeaderView.Stretch)
        for column in range(self.count()):
            self.setSectionResizeMode(column, QtWidgets.QHeaderView.Stretch)
            width = self.sectionSize(column)
            self.setSectionResizeMode(column, QtWidgets.QHeaderView.Interactive)
            self.resizeSection(column, width)

        return

class MainFrame(QtWidgets.QMainWindow):

    def __init__(self):
        super(MainFrame,self).__init__()
        self.centralWidget = QtWidgets.QWidget()
        self.setCentralWidget(self.centralWidget)
        self.setMinimumHeight(400)
        self.setMinimumWidth(650)

        self.doc_table = self.createTable()
        box = QtWidgets.QGroupBox("Table")
        h_layout = QtWidgets.QHBoxLayout(box)
        h_layout.addWidget(self.doc_table)

        g_layout = QtWidgets.QGridLayout(self.centralWidget)
        g_layout.addWidget(box, 1, 1)

        self.show()

    def createTable(self):
        # create some dummy data
        self.tabledata=[['aaa' ,' title1', True, 1999],
                    ['bbb' ,' title2', True, 2000],
                    ['ccc' ,' title3', False, 2001]
                    ]
        header=['author', 'title', 'read', 'year']

        tablemodel=TableModel(self,self.tabledata,header)
        tv=QtWidgets.QTableView(self)
        hh=MyHeaderView(self)
        tv.setHorizontalHeader(hh)
        tv.setModel(tablemodel)
        tv.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
        tv.setShowGrid(True)

        hh.setSectionsMovable(True)
        hh.setStretchLastSection(False)

        return tv


class MainWindow(QtWidgets.QMainWindow):

    def __init__(self):
        super(MainWindow,self).__init__()

        self.main_frame=MainFrame()
        self.setCentralWidget(self.main_frame)
        self.setGeometry(100,100,800,600)
        self.show()


if __name__=='__main__':

    app=QtWidgets.QApplication(sys.argv)
    mainwindow=MainWindow()
    sys.exit(app.exec_())

P.S.: Мопед не мой я просто загуглил объяву(ц)

→ Ссылка