Python 如何在没有可见空白的情况下展开/缩小QTable视图

Python 如何在没有可见空白的情况下展开/缩小QTable视图,python,pyqt,pyqt5,qtableview,qlayout,Python,Pyqt,Pyqt5,Qtableview,Qlayout,我有一个子类QAbstractTableModel 为了在不滚动的情况下完整显示表格视图中的数据,我打开了滚动条 为了消除表格视图周围的空白,我将垂直/水平表格长度设置为特定值。 问题是 我在模型中添加了一个add/delate行方法,因此表视图现在可以展开/收缩 为了调整表视图的行为,以显示完整大小的数据,而不显示空白,我将水平标题设置为 table_view.horizontalHeader().setStretchLastSection(True) 它正确地切断了水平方向上的

我有一个子类
QAbstractTableModel

为了在不滚动的情况下完整显示表格视图中的数据,我打开了滚动条
为了消除表格视图周围的空白,我将垂直/水平表格长度设置为特定值。


问题是 我在模型中添加了一个add/delate行方法,因此表视图现在可以展开/收缩
为了调整表视图的行为,以显示完整大小的数据,而不显示空白,我将水平标题设置为
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_())

#/usr/bin/env python
"""
"""
导入系统
进口稀土
从PyQt5将QtWidgets作为qtw导入
从PyQt5导入QtCore作为qtc
从PyQt5.QtCore导入Qt
从PyQt5将QtGui作为qtg导入
类视图模型(qtc.QAbstractTableModel):
定义初始化(自,输入数据=无):
super()。\uuuu init\uuuuu()
self.input_data=输入_数据或[[“数据”、“数据”、“数据”、“数据”]、[“数据”、“数据”、“数据”]]
#
def数据(自身、索引、角色):#需要参数索引、角色!
"""
"""
如果角色==qtc.Qt.DisplayRole:
尝试:
text=self.input_data[index.row()][index.column()]
除索引器外:
文本=无
返回文本
def行数(self,index=qtc.QModelIndex()):
如果index.isValid()else len(self.input\u数据),则返回0
def列数(自身,索引):
返回len(自输入_数据[0])
def insertRows(self、position、rows、parent=qtc.QModelIndex()):
打印(位置)#-1
如果位置<0,则位置=(位置+self.rowCount())否则位置
开始=位置
结束=位置+行-1
如果end=1:
self.beginRemoveRows(父、开始、结束)
del self.输入_数据[开始:结束+1]
self.endRemoveTows()
返回真值
其他:
返回错误
def headerData(自身、部门、方向、角色):
如果角色==qtc.Qt.DisplayRole:
如果方向==qtc.Qt.Horizontal:
返回“高度”+str(截面+1)+“/mm”
如果方向==qtc.Qt.Vertical:
返回“宽度”+str(截面+1)
def标志(自、索引):
返回qtc.Qt.ItemIsEditable | qtc.Qt.ItemIsSelectable | qtc.Qt.ItemIsEnabled
def setData(self、index、value、role=qtc.Qt.EditRole):
如果角色==qtc.Qt.EditRole:
尝试:
行=索引。行()
column=index.column()
模式='^[\d]+(?:,[\d]+)?$'
如果re.fullmatch(模式、值、标志=0):
打印(“真实”)
self.input_data[行][列]=值#浮点
其他:
打印(“否”)
通过
返回真值
除值错误外:
打印(“不是数字”)
返回错误
def显示模型数据(自身):
打印(自输入数据)
类主窗口(qtw.QWidget):
定义初始化(自):
super()。\uuuu init\uuuuu()
#几何学
self.setGeometry(900360700800)
#看法
table_view=qtw.QTableView()
#完成#关闭滚动条
表_view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
表_view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.model=ViewModel()
表_view.setModel(self.model)
table_view.horizontalHeader().setStretchLastSection(真)
#table_view.VerticHeader().setStretchLastSection(真)
table_view.verticalHeader().setSectionResizeMode(qtw.QHeaderView.Fixed)
table_view.verticalHeader().setDefaultSectionSize(24)
table_view.VerticHeader().setStretchLastSection(真)
#垂直标头->设置区段ResizeMode(QHeaderView::Fixed);
#垂直收割台->设置DefaultSectionSize(24);
#小部件
self.insert_row_button=qtw.QPushButton(“插入行”)
self.deleate_row_button=qtw.QPushButton(“deleate row”)
#布局
layout=qtw.QVBoxLayout()
layout.addWidget(表视图)
layout.addWidget(self.insert\u row\u按钮)
layout.addWidget(self.deleate_row_按钮)
self.setLayout(布局)
self.show()
#作用
self.insert_row_按钮。单击。连接(lambda:self.model.insertRows(-1,1))
self.delate_行_按钮。单击。连接(lambda:self.model.removows(-1,1))
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
app=qtw.QApplication(sys.argv)
w=主窗口()
sys.exit(app.exec_())

空间不能神奇地消失。假设桌子的总高度是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_())
“”“
"""
导入系统
进口稀土
从PyQt5将QtWidgets作为qtw导入
从PyQt5导入QtCore作为qtc
从PyQt5.QtCore导入Qt
从PyQt5导入Q
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_())
class ExpandingTableView(qtw.QTableView):
    shown = False
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
        self.verticalHeader().sectionResized.connect(self.updateGeometry)
        self.verticalHeader().sectionCountChanged.connect(self.updateGeometry)

    def setVerticalHeader(self, header):
        self.verticalHeader().sectionResized.disconnect(self.updateGeometry)
        self.verticalHeader().sectionCountChanged.disconnect(self.updateGeometry)
        super().setVerticalHeader(header)
        header.sectionResized.connect(self.updateGeometry)
        header.sectionCountChanged.connect(self.updateGeometry)

    def setModel(self, model):
        if self.model():
            self.model().rowsInserted.disconnect(self.updateGeometry)
            self.model().rowsRemoved.disconnect(self.updateGeometry)
        super().setModel(model)
        if model:
            model.rowsInserted.connect(self.updateGeometry)
            model.rowsRemoved.connect(self.updateGeometry)
        self.updateGeometry()

    # optional, if you want to ensure that a minimum height is always respected
    def updateGeometry(self):
        self.setMinimumHeight(min(self.sizeHint().height(), 
            self.verticalHeader().defaultSectionSize() * 8))
        super().updateGeometry()

    def sizeHint(self):
        height = 0
        if self.horizontalHeader().isVisible():
            height += self.horizontalHeader().height()
        height += self.verticalHeader().length() + self.frameWidth() * 2
        return QSize(super().sizeHint().width(), height)

    def showEvent(self, event):
        super().showEvent(event)
        # when the view is shown the first time it might not have computed the
        # correct size hint, let's ensure that we notify the underlying
        # layout manager(s)
        if not self.shown:
            self.shown = True
            self.updateGeometry()