pyqt QTableView


原文链接: pyqt QTableView

PyQt5高级界面控件之QTableWidget(四) - jia666666的博客 - CSDN博客

QTableView 实现Excel表格编辑功能

PyQt5使用记录之二 —— QTableView实现数据的显示、编辑、删除与添加 - cloveses的专栏 - CSDN博客
PyQt5高级界面控件之QTableView(一) - jia666666的博客 - CSDN博客

QTableWidget与QTableView的区别?

QTableWidget继承自QTableView。QSqlTableModel能与QTableView绑定,但不能于QTableWidget绑定

前言

在通常情况下,一个应用需要和一批数据进行交互,然后以表格的形式输出这些信息,这时就需要用到QTableView类了,在QTableView中可以使用自定义的数据模型来显示内容,通过setModel来绑定数据源
QTableWidget继承自QTableView,主要区别是QTableView可以使用自定义的数据模型来显示内容(先通setModel来绑定数据源),而QTableWidget自能使用标准的数据模型,并且其单元格数据是通过QTableWidgetItem对象实现的,通常QTableWidget就能够满足我们的要求

QTableView可用的模式

QTableView控件可以绑定一个模型数据用来更新控件上的内容

名称含义
QStringListModel储存一组字符串
QstandardItemModel存储任意层次结构的数据
QDirModel对文件系统进行封装
QSqlQueryModel对SQL的查询结果集进行封装
QSqlTableModel对SQL中的表格进行封装
QSqlRelationalTableModel对带有foreign key的SQL表格进行封装
QSortFilterProxyModel对模型中的数据进行排序或过滤

实例:QTableView的使用

import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *

class Table(QWidget):
    def __init__(self,parent=None):
        super(Table, self).__init__(parent)
        #设置标题与初始大小
        self.setWindowTitle('QTableView表格视图的例子')
        self.resize(500,300)

        #设置数据层次结构,4行4列
        self.model=QStandardItemModel(4,4)
        #设置水平方向四个头标签文本内容
        self.model.setHorizontalHeaderLabels(['标题1','标题2','标题3','标题4'])

        # #Todo 优化2 添加数据
        # self.model.appendRow([
        #     QStandardItem('row %s,column %s' % (11,11)),
        #     QStandardItem('row %s,column %s' % (11,11)),
        #     QStandardItem('row %s,column %s' % (11,11)),
        #     QStandardItem('row %s,column %s' % (11,11)),
        # ])

        for row in range(4):
            for column in range(4):
                item=QStandardItem('row %s,column %s'%(row,column))
                #设置每个位置的文本值
                self.model.setItem(row,column,item)

        #实例化表格视图,设置模型为自定义的模型
        self.tableView=QTableView()
        self.tableView.setModel(self.model)

        # #todo 优化1 表格填满窗口
        # #水平方向标签拓展剩下的窗口部分,填满表格
        # self.tableView.horizontalHeader().setStretchLastSection(True)
        # #水平方向,表格大小拓展到适当的尺寸
        # self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        #
        # #TODO 优化3 删除当前选中的数据
        # indexs=self.tableView.selectionModel().selection().indexes()
        # print(indexs)
        # if len(indexs)>0:
        #     index=indexs[0]
        #     self.model.removeRows(index.row(),1)

        #设置布局
        layout=QVBoxLayout()
        layout.addWidget(self.tableView)
        self.setLayout(layout)
if __name__ == '__main__':
    app=QApplication(sys.argv)
    table=Table()
    table.show()
    sys.exit(app.exec_())

运行程序,显示效果如图
这里写图片描述
从图中可以看出,表格并没有填满窗口,每列都可以自由拉伸,但是可能会出现滚动条

优化1:需要表格填充满窗口,可以添加一下代码

#水平方向标签拓展剩下的窗口部分,填满表格
self.tableView.horizontalHeader().setStretchLastSection(True)
#水平方向,表格大小拓展到适当的尺寸      
self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)

效果如下
这里写图片描述
优化2:添加数据

#Todo 优化2 添加数据
        self.model.appendRow([
            QStandardItem('row %s,column %s' % (11,11)),
            QStandardItem('row %s,column %s' % (11,11)),
            QStandardItem('row %s,column %s' % (11,11)),
            QStandardItem('row %s,column %s' % (11,11)),
        ])

效果如图
这里写图片描述
优化3:删除当前选中的数据

indexs=self.tableView.selectionModel().selection().indexes()
        print(indexs)
        if len(indexs)>0:
            index=indexs[0]
            self.model.removeRows(index.row(),1)

这里写图片描述

相关文件及下载地址

https://download.csdn.net/download/jia666666/10609488

数据的显示、编辑、删除与添加也是GUI编程的常见功能,作为初用者,使用笨拙的方式基本实现的功能。运用QTableView和QStandardItemModel相结合的方式实现数据的显示与增、删、改。基本代码如下,详见注释:

.....
    self.player_tabview = QTableView() # 建立QTableView类实例
    self.player_model = QStandardItemModel() # 建立数据模型实例
    self.player_model.setHorizontalHeaderLabels(head_lst) # 设置列标题
    if datas: # 向模型添加数据
     r,c = len(datas),len(datas[0])
     for r,rdata in enumerate(datas):
     for c,cell in enumerate(rdata):

            it = QStandardItem(str(cell))
            # it.setEditable(False)                      # 设置单元不可编辑
            self.player_model.setItem(r,c,it)

    self.player_tabview.setModel(self.player_model) # 添加模型到QTableView实例中

    self.player_model.itemChanged.connect(functools.partial(self.edit_cell,obj,keys))  # 当某单元格被编辑后,会触发该信号,并调用edit_cell方法

    del_btn = QPushButton('删除')
    del_btn.clicked.connect(self.del_row) # 删除行的按钮信号与槽的连接

    add_btn = QPushButton('添加分组')
    add_btn.clicked.connect(self.add_group) # 添加行的按钮信号与槽的连接

def del_row(self):
   reply = QMessageBox.question(self, '确认', '确定删除数据?',QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
   if reply == QMessageBox.Yes:
   r = self.player_tabview.currentIndex().row()
   item = self.player_model.index(r,0)
   del_rowdb(Team,int(item.data())) # 调用删除数据的方法
   self.player_model.removeRow(r)

def edit_cell(self,obj,keys,abc): # 编辑数据调用的方法
    # abc QStandardItem对象
    print(obj,keys,abc,abc.whatsThis())
    r = self.player_tabview.currentIndex().row() # 获取行号
    c = self.player_tabview.currentIndex().column() # 获取列序
   curr_data = self.player_tabview.currentIndex().data()
    item = self.player_model.index(r,0)
    param = dict()
    param[keys[c]] = curr_data
    save_cell(obj,int(item.data()),param)
   
def addgroup(self): # 添加数据方法
    v = MyDialog()
    if v.exec
():
       name,game = v.get_data() # 通过自定义对话框获取要建立行的数据
       if name and game:
           info = add_groupdb(name,int(game))
           if info:
               QMessageBox.warning(self,'错误',info,QMessageBox.Ok)
           else:
              QMessageBox.information(self,'完成','成功建立!',QMessageBox.Ok)
              self.edit_group()
后来,通过查阅资料,了解了PyQt5的Model/View/Delegate的设计模式,即Model持有数据,下与数据源交互(数据的查询、修改与添加),上与View交互,主要为View提供要显示的数据。View提供数据的显示和与用户交互。Delegate可以实现定制数据显示的方式和编辑方式,在实际使用时,Delegate可以不用自定义,而使用默认的实现即可。实现简单的功能,只通过继承PyQt5.QtCore.QAbstractTableModel能更方便的实现数据的CRUD,但是需要了解必须实现的接口方法及其功能。以下是通过继承PyQt5.QtCore.QAbstractTableModel自定义自定义一个Model的实例代码。

from PyQt5.QtCore import QAbstractTableModel,QModelIndex,QVariant,Qt
from models.mydb import TObj,db_session,select

ID,NAME,AGE,TEL = range(4)
HEADERS = ('id','姓名','年龄','电话')
CONVERTS_FUNS = (None,None,int,None)

class TObjModel(QAbstractTableModel):

def __init__(self,headers=HEADERS):
    super().__init__()
    self.datas = []                   # 用来持有为View提供的数据,此类中用列表中嵌套列表来实现
    self.headers = headers
    self.load()                       # 初始化时,自动载入数据

def load(self):
    # 载入数据函数,可以从任何数据源载入
    self.beginResetModel()
    with db_session:                  # 这里使用Pony这个ORM来完成数据库的操作
        tobjs = select(t for t in TObj)
        for tobj in tobjs:
            self.datas.append([tobj.id,tobj.name,tobj.age,tobj.tel])
    print(self.datas)
    self.endResetModel()

def data(self,index,role=Qt.DisplayRole):
    # 供视图调用,以获取用以显示的数据
    if (not index.isValid() or not (0 <= index.row() < len(self.datas))):  # 无效的数据请求
        return None

    row,col = index.row(),index.column()
    data = self.datas[row]
    if role == Qt.DisplayRole:
        item = data[col]
        if col == AGE:                             # 还可以实现数据的转换显示或显示处理后的数据
            item = int(item)
        return item
    return None

def rowCount(self,index=QModelIndex()):           # 必须实现的接口方法(返回数据行数)
    return len(self.datas)

def columnCount(self,index=QModelIndex()):        # 必须实现的接口方法(返回数据列数)
    return len(self.headers)

def headerData(self,section,orientation,role=Qt.DisplayRole):
    # 实现标题行的定义
    if role != Qt.DisplayRole:
        return None

    if orientation == Qt.Horizontal:
        return self.headers[section]
    return int(section + 1)

# 以下为编辑功能所必须实现的方法
def setData(self,index,value,role=Qt.EditRole):
    # 编辑后更新模型中的数据 View中编辑后,View会调用这个方法修改Model中的数据
    if index.isValid() and 0 <= index.row() < len(self.datas) and value:
        col = index.column()
        print(col)
        if 0 < col < len(self.headers):
            self.beginResetModel()
            if CONVERTS_FUNS[col]:                                         # 必要的时候执行数据类型的转换
                self.datas[index.row()][col] = CONVERTS_FUNS[col](value)
            else:
                self.datas[index.row()][col] = value
            self.dirty = True
            self.endResetModel()
            return True
    return False

def flags(self, index):                            # 必须实现的接口方法,不实现,则View中数据不可编辑
    if not index.isValid():
        return Qt.ItemIsEnabled
    return Qt.ItemFlags(
            QAbstractTableModel.flags(self, index)|
            Qt.ItemIsEditable | Qt.ItemIsSelectable)

def insertRows(self,position,rows=1,index=QModelIndex()):
    # position 插入位置;rows 插入行数
    self.beginInsertRows(QModelIndex(),position,position + rows -1)
    pass #  对self.datas进行操作
    self.endInsertRows()
    self.dirty = True
    return True

def removeRows(self,position,rows=1,index=QModelIndex):
    # position 删除位置;rows 删除行数
    self.beginRemoveRows(QModelIndex(),position,position + rows -1)
    pass  #  对self.datas进行操作
    self.endRemoveRows()
    self.dirty = True
    return True

# 可单独定义保存数据的方法(遍历datas进行保存),供用户退出时或选择保存时,保存数据
# 也可以在用户编辑时立即保存,即在setData方法中保存

    使用这个类的方法很简单,只要实例化后,并将其设置为QTableView的Model即可,也可在适当的时候,调用其load()方法,重新载入数据。代码就更简单了:

    self.player_tabview = QTableView()
    self.player_model = TObjModel()
    self.player_tabview.setModel(self.player_model)

————————————————
版权声明:本文为CSDN博主「cloveses」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/cloveses/article/details/80943496

`