PyQt5使用记录之二 —— QTableView实现数据的显示、编辑、删除与添加

     数据的显示、编辑、删除与添加也是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 add_group(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)



猜你喜欢

转载自blog.csdn.net/cloveses/article/details/80943496
今日推荐