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