首页 > 解决方案 > 如何在没有可见空白的情况下扩展/减少 QTable 视图

问题描述

我有一个子类QAbstractTableModel
,可以在不滚动的情况下以全尺寸显示表格视图中的数据我已经关闭滚动条
以消除表格视图周围的空白我已将垂直/水平表格长度设置为特定值。

在此处输入图像描述
问题是我已经向模型添加了一个添加/删除行方法,因此表视图现在扩展/缩小
以调整表视图行为以显示完整大小的数据并且没有空白我已经设置了水平标题, table_view.horizontalHeader().setStretchLastSection(True)
它会被截断水平方向上的空白区域正确地
对空白区域的垂直标题切割进行相同的操作,但会过度拉伸最后一行

过度拉伸

我尝试将每一行设置为默认大小

table_view.verticalHeader().setSectionResizeMode(qtw.QHeaderView.Fixed)
        table_view.verticalHeader().setDefaultSectionSize(40)

但这会再次打开空白

简而言之:我正在寻找一种方法来在表格视图中以全尺寸显示模型数据而没有空白,同时能够删除/插入一行


代码示例

#!/usr/bin/env python

"""

"""

import sys
import re


from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc
from PyQt5.QtCore import Qt

from PyQt5 import QtGui as qtg


class ViewModel(qtc.QAbstractTableModel):

    def __init__(self, input_data=None):
        super().__init__()

        self.input_data = input_data or [["data","data","data","data"],["data","data","data","data"]]

    #

    def data(self, index, role):  # parameter index, role are needed !
        """

        """
        if role == qtc.Qt.DisplayRole:
            try:
                text = self.input_data[index.row()][index.column()]
            except IndexError:
                text = None

            return text

    def rowCount(self, index=qtc.QModelIndex()):
        return 0 if index.isValid() else len(self.input_data)


    def columnCount(self, index):
        return len(self.input_data[0])


    def insertRows(self, position, rows, parent=qtc.QModelIndex()):

        print(position) # -1
        position = (position + self.rowCount()) if position < 0 else position
        start = position

        end = position + rows - 1

        if end <= 8:
            self.beginInsertRows(parent, start, end)
            self.input_data.append([])
            self.endInsertRows()
            return True
        else:
            return False


    def removeRows(self, position, rows, parent=qtc.QModelIndex()):
        position = (position + self.rowCount()) if position < 0 else position

        start = position

        end = position + rows - 1

        if end >= 1:
            self.beginRemoveRows(parent, start, end)
            del self.input_data[start:end + 1]
            self.endRemoveRows()
            return True
        else:
            return False



    def headerData(self, section, orientation, role):

        if role == qtc.Qt.DisplayRole:

            if orientation == qtc.Qt.Horizontal:
                return "hight " + str(section+1) + " /mm"
            if orientation == qtc.Qt.Vertical:
                return "width " + str(section+1)


    def flags(self, index):
        return qtc.Qt.ItemIsEditable | qtc.Qt.ItemIsSelectable | qtc.Qt.ItemIsEnabled

    def setData(self, index, value, role=qtc.Qt.EditRole):
        if role == qtc.Qt.EditRole:
            try:
                row = index.row()
                column = index.column()

                pattern = '^[\d]+(?:,[\d]+)?$'


                if re.fullmatch(pattern, value, flags=0):
                    print("true")
                    self.input_data[row][column] = value  # float

                else:
                    print("nope")
                    pass

                return True

            except ValueError:
                print("not a number")
                return False


    def display_model_data(self):
        print(self.input_data)


class MainWindow(qtw.QWidget):
    def __init__(self):
        super().__init__()

        # geometry
        self.setGeometry(900, 360, 700, 800)


        # View
        table_view = qtw.QTableView()



        # done # turn scroll bars off
        table_view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        table_view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)





        self.model = ViewModel()
        table_view.setModel(self.model)




        table_view.horizontalHeader().setStretchLastSection(True)
        # table_view.verticalHeader().setStretchLastSection(True)

        table_view.verticalHeader().setSectionResizeMode(qtw.QHeaderView.Fixed)
        table_view.verticalHeader().setDefaultSectionSize(24)

        table_view.verticalHeader().setStretchLastSection(True)
        #     verticalHeader->setSectionResizeMode(QHeaderView::Fixed);
        # verticalHeader->setDefaultSectionSize(24);



        # widgets
        self.insert_row_button = qtw.QPushButton("insert row")
        self.deleate_row_button = qtw.QPushButton("deleate row")

        # layout
        layout = qtw.QVBoxLayout()
        layout.addWidget(table_view)
        layout.addWidget(self.insert_row_button)
        layout.addWidget(self.deleate_row_button)


        self.setLayout(layout)
        self.show()

        # function
        self.insert_row_button.clicked.connect(lambda: self.model.insertRows(-1, 1))
        self.deleate_row_button.clicked.connect(lambda: self.model.removeRows(-1, 1))


if __name__ == '__main__':
    app = qtw.QApplication(sys.argv)
    w = MainWindow()
    sys.exit(app.exec_())

标签: pythonpyqtpyqt5qtableviewqlayout

解决方案


空间不能神奇地消失。假设表格总高度为 600。如果表格中有两行,则第一行是 40。然后,如果您不想在表格底部留空,则第二行是 600 - 40 = 560 . 如果将每行的高度设置为 40,则空白空间的高度将为 600 - 2 * 40 = 520。您不能要求(总高度 600)+(两行,每行 40)+(无空白底部的空间)。

所以,让我猜猜,你想要(a.底部没有空格)+(b,空间被均匀地分成一行,这样最后一行就不会看起来很奇怪。)。如果是这种情况,我已将您的代码编辑到下面,它解释了所有内容:

"""

"""

import sys
import re


from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc
from PyQt5.QtCore import Qt

from PyQt5 import QtGui as qtg


class ViewModel(qtc.QAbstractTableModel):

    def __init__(self, input_data=None):
        super().__init__()

        self.input_data = input_data or [["data","data","data","data"],["data","data","data","data"]]

    #

    def data(self, index, role):  # parameter index, role are needed !
        """

        """
        if role == qtc.Qt.DisplayRole:
            try:
                text = self.input_data[index.row()][index.column()]
            except IndexError:
                text = None

            return text

    def rowCount(self, index=qtc.QModelIndex()):
        return 0 if index.isValid() else len(self.input_data)


    def columnCount(self, index):
        return len(self.input_data[0])


    def insertRows(self, position, rows, parent=qtc.QModelIndex()):

        print(position) # -1
        position = (position + self.rowCount()) if position < 0 else position
        start = position

        end = position + rows - 1

        if end <= 8:
            self.beginInsertRows(parent, start, end)
            self.input_data.append([])
            self.endInsertRows()
            return True
        else:
            return False


    def removeRows(self, position, rows, parent=qtc.QModelIndex()):
        position = (position + self.rowCount()) if position < 0 else position

        start = position

        end = position + rows - 1

        if end >= 1:
            self.beginRemoveRows(parent, start, end)
            del self.input_data[start:end + 1]
            self.endRemoveRows()
            return True
        else:
            return False



    def headerData(self, section, orientation, role):

        if role == qtc.Qt.DisplayRole:

            if orientation == qtc.Qt.Horizontal:
                return "hight " + str(section+1) + " /mm"
            if orientation == qtc.Qt.Vertical:
                return "width " + str(section+1)


    def flags(self, index):
        return qtc.Qt.ItemIsEditable | qtc.Qt.ItemIsSelectable | qtc.Qt.ItemIsEnabled

    def setData(self, index, value, role=qtc.Qt.EditRole):
        if role == qtc.Qt.EditRole:
            try:
                row = index.row()
                column = index.column()

                pattern = '^[\d]+(?:,[\d]+)?$'


                if re.fullmatch(pattern, value, flags=0):
                    print("true")
                    self.input_data[row][column] = value  # float

                else:
                    print("nope")
                    pass

                return True

            except ValueError:
                print("not a number")
                return False


    def display_model_data(self):
        print(self.input_data)


class NoBlankSpaceAtBottomEnvenlySplitTableView(qtw.QTableView):
    def sizeHintForRow(self, row):
        row_count = self.model().rowCount()
        height = self.viewport().height()
        row_height = int(height/row_count)
        if row < row_count - 1:
            return row_height
        else:
            return super().sizeHintForRow(row)


class MainWindow(qtw.QWidget):
    def __init__(self):
        super().__init__()

        # geometry
        self.setGeometry(900, 360, 700, 800)


        # View
        # table_view = qtw.QTableView()
        table_view = NoBlankSpaceAtBottomEnvenlySplitTableView()



        # done # turn scroll bars off
        table_view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        table_view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)





        self.model = ViewModel()
        table_view.setModel(self.model)




        table_view.horizontalHeader().setStretchLastSection(True)
        table_view.verticalHeader().setStretchLastSection(True)

        # table_view.verticalHeader().setSectionResizeMode(qtw.QHeaderView.Fixed)
        #table_view.verticalHeader().setDefaultSectionSize(24)
        table_view.verticalHeader().setSectionResizeMode(
            qtw.QHeaderView.ResizeToContents)  # Add this line

        table_view.verticalHeader().setStretchLastSection(True)
        #     verticalHeader->setSectionResizeMode(QHeaderView::Fixed);
        # verticalHeader->setDefaultSectionSize(24);



        # widgets
        self.insert_row_button = qtw.QPushButton("insert row")
        self.deleate_row_button = qtw.QPushButton("deleate row")

        # layout
        layout = qtw.QVBoxLayout()
        layout.addWidget(table_view)
        layout.addWidget(self.insert_row_button)
        layout.addWidget(self.deleate_row_button)


        self.setLayout(layout)
        self.show()

        # function
        self.insert_row_button.clicked.connect(lambda: self.model.insertRows(-1, 1))
        self.deleate_row_button.clicked.connect(lambda: self.model.removeRows(-1, 1))


if __name__ == '__main__':
    app = qtw.QApplication(sys.argv)
    w = MainWindow()
    sys.exit(app.exec_())

编辑:表格自动根据行调整其高度

import sys
import re


from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc
from PyQt5.QtCore import Qt, QSize
from PyQt5.QtWidgets import QSizePolicy

from PyQt5 import QtGui as qtg


class ViewModel(qtc.QAbstractTableModel):

    def __init__(self, input_data=None):
        super().__init__()

        self.input_data = input_data or [["data","data","data","data"],["data","data","data","data"]]

    #

    def data(self, index, role):  # parameter index, role are needed !
        """

        """
        if role == qtc.Qt.DisplayRole:
            try:
                text = self.input_data[index.row()][index.column()]
            except IndexError:
                text = None

            return text

    def rowCount(self, index=qtc.QModelIndex()):
        return 0 if index.isValid() else len(self.input_data)


    def columnCount(self, index):
        return len(self.input_data[0])


    def insertRows(self, position, rows, parent=qtc.QModelIndex()):

        print(position) # -1
        position = (position + self.rowCount()) if position < 0 else position
        start = position

        end = position + rows - 1

        if end <= 8:
            self.beginInsertRows(parent, start, end)
            self.input_data.append([])
            self.endInsertRows()
            return True
        else:
            return False


    def removeRows(self, position, rows, parent=qtc.QModelIndex()):
        position = (position + self.rowCount()) if position < 0 else position

        start = position

        end = position + rows - 1

        if end >= 1:
            self.beginRemoveRows(parent, start, end)
            del self.input_data[start:end + 1]
            self.endRemoveRows()
            return True
        else:
            return False



    def headerData(self, section, orientation, role):

        if role == qtc.Qt.DisplayRole:

            if orientation == qtc.Qt.Horizontal:
                return "hight " + str(section+1) + " /mm"
            if orientation == qtc.Qt.Vertical:
                return "width " + str(section+1)


    def flags(self, index):
        return qtc.Qt.ItemIsEditable | qtc.Qt.ItemIsSelectable | qtc.Qt.ItemIsEnabled

    def setData(self, index, value, role=qtc.Qt.EditRole):
        if role == qtc.Qt.EditRole:
            try:
                row = index.row()
                column = index.column()

                pattern = '^[\d]+(?:,[\d]+)?$'


                if re.fullmatch(pattern, value, flags=0):
                    print("true")
                    self.input_data[row][column] = value  # float

                else:
                    print("nope")
                    pass

                return True

            except ValueError:
                print("not a number")
                return False


    def display_model_data(self):
        print(self.input_data)


class AutoExpandingTableView(qtw.QTableView):
    # def sizeHintForRow(self, row):
    #     row_count = self.model().rowCount()
    #     height = self.viewport().height()
    #     row_height = int(height/row_count)
    #     if row < row_count - 1:
    #         return row_height
    #     else:
    #         return super().sizeHintForRow(row)

    def sizeHint(self):
        viewport_size_hint = self.viewportSizeHint()
        return QSize(
            self.width(),
            viewport_size_hint.height()
        )


class MainWindow(qtw.QWidget):
    def __init__(self):
        super().__init__()

        # geometry
        self.setGeometry(900, 360, 700, 800)


        # View
        # table_view = qtw.QTableView()
        table_view = AutoExpandingTableView()
        table_view.setSizePolicy(
            QSizePolicy.Expanding,
            QSizePolicy.Preferred
        )

        # done # turn scroll bars off
        table_view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        table_view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        self.model = ViewModel()
        table_view.setModel(self.model)
        table_view.model().rowsInserted.connect(table_view.adjustSize)
        table_view.model().rowsRemoved.connect(table_view.adjustSize)

        table_view.horizontalHeader().setStretchLastSection(True)
        # table_view.verticalHeader().setStretchLastSection(True)

        # table_view.verticalHeader().setSectionResizeMode(qtw.QHeaderView.Fixed)
        #table_view.verticalHeader().setDefaultSectionSize(24)
        table_view.verticalHeader().setSectionResizeMode(
            qtw.QHeaderView.ResizeToContents)  # Add this line

        # widgets
        self.insert_row_button = qtw.QPushButton("insert row")
        self.deleate_row_button = qtw.QPushButton("deleate row")

        # layout
        layout = qtw.QVBoxLayout()
        layout.addWidget(table_view)
        layout.addStretch()
        layout.addWidget(self.insert_row_button)
        layout.addWidget(self.deleate_row_button)


        self.setLayout(layout)
        self.show()

        # function
        self.insert_row_button.clicked.connect(lambda: self.model.insertRows(-1, 1))
        self.deleate_row_button.clicked.connect(lambda: self.model.removeRows(-1, 1))


if __name__ == '__main__':
    app = qtw.QApplication(sys.argv)
    w = MainWindow()
    sys.exit(app.exec_())

推荐阅读