1、主要类:
QStandardItemModel类
(1)功能:以项数据(item data)为基础的标准数据模型类,通常与QTableView组成Model/View结构,实现通用二维数据的管理。每一个项是一个QStandardItem类的对象,用于存储项的数据、字体格式、对齐方式等各种角色的数据。
QTableView类
(1)功能:二维数据表视图组件,有多个行和多个列,每个基本显示单元是一个单元格,通过setModel()函数设置一个QStandardItemModel类的数据模型之后,一个单元格显示是QStandardItemModel数据模型中的一个项。
QItemSelectionModel
(1)功能:用于跟踪视图组件的单元格选择状态的类,当在QTableView组件上选择某个单元格或多个单元格时,通过QItemSelectionModel可以获得选中单元格的模型索引,为单元格的选择操作提供方便。
三种类的关系:
QStandardItemModel是数据模型类;QItemSelectionModel需要设置一个QStandardItemModel对象作为数据模型;QTableView是界面视图组件,它需要设置一个QStandardItemModel类对象作为数据模型,若要实现方便的项选择操作,还需要设置一个QItemSelectionModel类对象作为项选择模型。
测试用例:
以下面的软件界面为例;
测试以下功能:
1、打开一个纯文本文件,该文件是规则的二维数据文件,通过字符串处理获取表头和各行各列的数据,导入一个QStandardItemModel数据模型。
2、编辑修改数据模型的数据,可以插入行、添加行、删除行,可以在QTableView视图组件中直接修改单元格的数据内容。
3、可以设置数据模型中某个项的不同角色的数据,设置文字对齐方式、是否粗体等。
4、通过QItemSelectionModel获取视图组件上的当前单元格,以及选择单元格的范围,对选择的单元格进行操作。
5、将数据模型的数据内容显示到QPlainTextEdit组件里,显示数据模型的内容,检验在视图组件上做的修改是否与数据模型同步。
6、将修改后的模型数据另存为一个文本文件。
QStandardItemModel的使用
1、界面初始化
项目模板mainWindowApp复制创建,窗体文件是MainWindow.ui。
关键在于数据模型和选择模型的创建,以及与视图组件的关联、信号与槽的关联等。
import sys,os
from PyQt5.QtWidgets import (QApplication, QMainWindow,
QLabel, QAbstractItemView, QFileDialog)
from PyQt5.QtCore import Qt, pyqtSlot,QItemSelectionModel, QModelIndex
from PyQt5.QtGui import QStandardItemModel, QStandardItem
from ui_MainWindow import Ui_MainWindow
class QmyMainWindow(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent) #调用父类构造函数,创建窗体
self.ui = Ui_MainWindow() #创建UI对象
self.ui.setupUi(self) #构造UI界面
self.setCentralWidget(self.ui.splitter)
self.__ColCount = 6 #列数=6
self.itemModel = QStandardItemModel(5,self.__ColCount,self)# 数据模型,10行6列
self.selectionModel = QItemSelectionModel(self.itemModel) #Item选择模型
self.selectionModel.currentChanged.connect(self.do_curChanged)
self.__lastColumnTitle="测井取样"
self.__lastColumnFlags=(Qt.ItemIsSelectable
| Qt.ItemIsUserCheckable | Qt.ItemIsEnabled)
##tableView设置
self.ui.tableView.setModel(self.itemModel) #设置数据模型
self.ui.tableView.setSelectionModel(self.selectionModel) #设置选择模型
oneOrMore=QAbstractItemView.ExtendedSelection
self.ui.tableView.setSelectionMode(oneOrMore) #可多选
itemOrRow=QAbstractItemView.SelectItems
self.ui.tableView.setSelectionBehavior(itemOrRow) #单元格选择
self.ui.tableView.verticalHeader().setDefaultSectionSize(22)#缺省行高
self.ui.tableView.setAlternatingRowColors(True) #交替行颜色
self.ui.tableView.setEnabled(False) #先禁用tableView
self.__buildStatusBar()
在构造函数中创建数据模型(QStandardItemModel)和选择模型(QItemSelectionModel)
1、创建QStandardItemModel对象时指定数据模型的行数和列数;
2、创建QItemSelectionModel对象以QStandardItemModel对象为参数,这样数据模型与选择模型建立了联系;
3、构造函数还将QItemSelectionModel对象的currentChanged()信号与自定义槽函数do_curChanged()关联;
4、为界面组件设置数据模型与选择模型,调用setModel(QStandardItemModel)与setSelectionModel(QItemSelectionModel)建立联系。
5、设置选择模式,调用函数setSelectionMode(模式枚举)
6、创建状态栏,调用私有函数__buildStatusBar()
2、从文本文件导入数据
有些数据是以纯文本格式保存的,它们有固定的列数,每一列是一项数据,实际构成一个二维数据表。
下面是“打开文件”的槽函数代码:
## ==========由connectSlotsByName() 自动连接的槽函数==================
@pyqtSlot() ##“打开文件”
def on_actOpen_triggered(self):
## curPath=QDir.currentPath() #获取当前路径
curPath=os.getcwd() #获取当前路径
filename,flt=QFileDialog.getOpenFileName(self,"打开一个文件",curPath,
"井斜数据文件(*.txt);;所有文件(*.*)")
if (filename==""):
return
self.LabCurFile.setText("当前文件:"+filename)
self.ui.plainTextEdit.clear()
aFile=open(filename,'r')
allLines=aFile.readlines() #读取所有行,list类型,每行末尾带有 \n
aFile.close()
for strLine in allLines:
self.ui.plainTextEdit.appendPlainText(strLine.strip())
self.__iniModelFromStringList(allLines)
self.ui.tableView.setEnabled(True) #tableView可用
self.ui.actAppend.setEnabled(True) #更新Actions的enable属性
self.ui.actInsert.setEnabled(True)
self.ui.actDelete.setEnabled(True)
self.ui.actSave.setEnabled(True)
self.ui.actModelData.setEnabled(True)
1、os模块使用getcwd()函数获取当前工作路径;
2、QFileDialog类通过getOpenFileName()打开文件对话框选择一个文件,返回两个变量,分别是选择的文件名(filename)和文件过滤器(flt)。
3、打开文件使用Python内建的打开文件函数open(),以只读方式打开文件,使用readlines()函数一次性将所有文本读入一个字符串列表里。列表每一行具有换行符\n。
4、遍历列表,提取每一行进行显示appendPlainText()
5、__iniModelFromStringList()函数为私有函数,根据读取的文本内容进行数据模型初始化。
__iniModelFromStringList()函数的代码如下:
def __iniModelFromStringList(self,allLines): ##从字符串列表构建模型
rowCnt=len(allLines) #文本行数,第1行是标题
self.itemModel.setRowCount(rowCnt-1) #实际数据行数
headerText=allLines[0].strip() #第1行是表头,去掉末尾的换行符 "\n"
headerList=headerText.split("\t") #转换为字符串列表
self.itemModel.setHorizontalHeaderLabels(headerList) #设置表头标题
self.__lastColumnTitle=headerList[len(headerList)-1] # 最后一列表头的标题,即“测井取样”
lastColNo=self.__ColCount-1 #最后一列的列号
for i in range(rowCnt-1):
lineText=allLines[i+1].strip() #一行的文字,\t分隔
strList=lineText.split("\t") #分割为字符串列表
for j in range(self.__ColCount-1): #不含最后一列
item=QStandardItem(strList[j])
self.itemModel.setItem(i,j,item) #设置模型的item
item=QStandardItem(self.__lastColumnTitle) #最后一列
item.setFlags(self.__lastColumnFlags)
item.setCheckable(True)
if (strList[lastColNo]=="0"):
item.setCheckState(Qt.Unchecked)
else:
item.setCheckState(Qt.Checked)
self.itemModel.setItem(i,lastColNo,item) #设置最后一列的item
1、参数allLines是文本文件所有行构成的字符串列表,每一行是allLines列表的一个字符串。第1行是表头文字,从第2行开始是数据。
2、len(列表)函数:是获取列表的元素个数,即原文本每行的个数
3、setRowCount(行数)函数:设置列表的函数
4、从allLines获取表头字符串headerText,然后用 str.split()函数将一个字符串分割成一个字符串列表headerList
5、QStandardItemModel类的setHorizontalHeaderLabels(列表)函数:并设置为数据模型的表头标题
6、QStandardItemModel以二维表格的形式保存项数据,每个项是一个QStandardItem对象,每个项对应着QTableView的一个单元格。项数据不仅可以存储显示的文字,还可以存储其他角色的数据
7、数据文件的最后一列是一个逻辑型数据,在界面组件tableView上显示时为其提供一个复选框,此功能通过调用QStandardItem的setCheckable(True)函数实现。
8、QStandardItem的setFlags(ItemFlags)函数可以设置项的一些标志,ItemFlags是枚举类型Qt.ItemFlag的值的组合。在QmyMainWindow的构造函数中定义了一个私有变量self.__lastColumnFlags用于设置最后一列的标志。
3、数据修改
界面组件tableView设置为可编辑时,双击一个单元格可以修改其内容,对于使用复选框的列,改变复选框的勾选状态,就可以修改单元格关联项的数据。
数据修改是针对数据模型的,数据模型被修改后,会在tableview显示出来。
比如添加行、插入行、删除行。
@pyqtSlot() ##在最后添加一行
def on_actAppend_triggered(self):
itemlist=[] # QStandardItem 对象列表
for i in range(self.__ColCount-1): #不包括最后一列
item=QStandardItem("0")
itemlist.append(item)
item=QStandardItem(self.__lastColumnTitle) #最后一列
item.setCheckable(True)
item.setFlags(self.__lastColumnFlags)
itemlist.append(item)
self.itemModel.appendRow(itemlist) #添加一行
curIndex=self.itemModel.index(self.itemModel.rowCount()-1,0)
self.selectionModel.clearSelection()
self.selectionModel.setCurrentIndex(curIndex,QItemSelectionModel.Select)
@pyqtSlot() ##插入一行
def on_actInsert_triggered(self):
itemlist=[] # QStandardItem 对象列表
for i in range(self.__ColCount-1): #不包括最后一列
item=QStandardItem("0")
itemlist.append(item)
item=QStandardItem(self.__lastColumnTitle) #最后一列
item.setFlags(self.__lastColumnFlags)
item.setCheckable(True)
item.setCheckState(Qt.Checked)
itemlist.append(item)
curIndex=self.selectionModel.currentIndex(); #获取当前选中项的模型索引
self.itemModel.insertRow(curIndex.row(),itemlist); #在当前行的前面插入一行
self.selectionModel.clearSelection()
self.selectionModel.setCurrentIndex(curIndex,QItemSelectionModel.Select)
@pyqtSlot() ##删除当前行
def on_actDelete_triggered(self):
curIndex=self.selectionModel.currentIndex() #获取当前选择单元格的模型索引
self.itemModel.removeRow(curIndex.row()) #删除当前行
(1)添加行
相当于在列表最后一行添加初始化的item,可以是0或者勾选状态。最后一行的每个item组成一个itemlist,使用QStandardItemModel的appendRow()函数在数据模型的最后添加一行
函数原型appendRow(self, items)
参数items是一个QStandardItem类型的列表,需要为添加的行的每个项创建一个QStandardItem类型的对象,然后传递给appendRow()函数。
要获得QStandardItemModel中的某个单元格的模型索引,就要使用其index()函数。在程序中,通过传递单元格的行号和列号获取其模型索引。
要通过程序选择某个项,使用选择模型QItemSelectionModel的相应方法,选择操作也会及时反映到关联的界面组件tableView上。这里用到了QItemSelectionModel类的**clearSelection()和setCurrentIndex()**函数分别完成清除选择和设置刚插入的行的第一列单元格被选中的功能。
(2)插入行
“插入行”按钮的功能是在当前行的前面插入一行,其实现代码与“添加行”类似。
使用QStandardItemModel的insertRow()函数插入一行
函数原型insertRow(self, row, items)
参数row是一个行号,表示在此行号的行之前插入一行,若row等于或大于总行数,则在最后添加一行。items是一个QStandardItem类型的列表。
(3)删除行
“删除行”按钮的功能是删除当前行,首先从选择模型中获取当前单元格的模型索引,然后从模型索引中获取行号,调用removeRow(row)函数删除指定的行。
4、单元格格式设置
项数据模型的每个项是一个QStandardItem对象,QStandardItem提供了一些接口函数可以设置每个项的字体、背景颜色、前景颜色、文字对齐方式、图标等。
本示例用文字对齐方式、粗体设置来说明项数据模型的格式设置。
def __setCellAlignment(self, align=Qt.AlignHCenter):
if (not self.selectionModel.hasSelection()): #没有选择的项
return
selectedIndex=self.selectionModel.selectedIndexes() #模型索引列表
count=len(selectedIndex)
for i in range(count):
index=selectedIndex[i] #获取其中的一个模型索引
item=self.itemModel.itemFromIndex(index) #获取一个单元格的项数据对象
item.setTextAlignment(align) #设置文字对齐方式
@pyqtSlot() ##左对齐
def on_actAlignLeft_triggered(self):
self.__setCellAlignment(Qt.AlignLeft | Qt.AlignVCenter)
@pyqtSlot() ##中间对齐
def on_actAlignCenter_triggered(self):
self.__setCellAlignment(Qt.AlignHCenter| Qt.AlignVCenter)
@pyqtSlot() ##右对齐
def on_actAlignRight_triggered(self):
self.__setCellAlignment(Qt.AlignRight | Qt.AlignVCenter)
@pyqtSlot(bool) ##字体Bold
def on_actFontBold_triggered(self,checked):
if (not self.selectionModel.hasSelection()): #没有选择的项
return
selectedIndex=self.selectionModel.selectedIndexes() #模型索引列表
count=len(selectedIndex)
for i in range(count):
index=selectedIndex[i] #获取其中的一个模型索引
item=self.itemModel.itemFromIndex(index) #获取一个单元格的项数据对象
font=item.font()
font.setBold(checked)
item.setFont(font)
3个设置对齐方式的Action的代码都调用了自定义**私有函数__setCellAlignment()**在此函数中用到了QItemSelectionModel的以下两个函数。
hasSelection()函数:返回一个bool值,表示是否有选中的单元格。
selectedIndexes()函数:返回一个元素为QModelIndex类型的列表,包括所有被选中的单元格的模型索引,当界面组件tableView设置为多选时可以选择多个单元格。
使用QStandardItemModel类的itemFromIndex(index)函数,返回的是模型索引为index的QStandardItem对象。
5、数据另存为文件
在视图组件上对数据的修改都会自动更新到数据模型里,单击工具栏上的“模型数据”按钮可以将数据模型的数据内容显示到窗体右侧的PlainTextEdit组件里
@pyqtSlot() ##模型数据显示到plainTextEdit里
def on_actModelData_triggered(self):
self.ui.plainTextEdit.clear()
lineStr=""
for i in range(self.itemModel.columnCount()-1): #表头,不含最后一列
item=self.itemModel.horizontalHeaderItem(i)
lineStr=lineStr+item.text()+"\t"
item=self.itemModel.horizontalHeaderItem(self.__ColCount-1) #最后一列
lineStr=lineStr+item.text() #表头文字字符串
self.ui.plainTextEdit.appendPlainText(lineStr)
for i in range(self.itemModel.rowCount()):
lineStr=""
for j in range(self.itemModel.columnCount()-1): #不包括最后一列
item=self.itemModel.item(i,j)
lineStr=lineStr+item.text()+"\t"
item=self.itemModel.item(i,self.__ColCount-1) #最后一列
if (item.checkState()==Qt.Checked):
lineStr=lineStr+"1"
else:
lineStr=lineStr+"0"
self.ui.plainTextEdit.appendPlainText(lineStr)
这里主要涉及项数据模型的表头标题的读取、项数据内容逐行逐列的读取。
QStandardItemModel的horizontalHeaderItem()函数返回行标题的一个数据项对象,其函数原型为:
horizontalHeaderItem(self, colNo) -> QStandardItem
参数colNo是列号,返回值是一个QStandardItem对象。
读取的各表头标题用**Tab键(/t)**分隔组成一个字符串,添加到plainTextEdit里显示。
QStandardItemModel的item()函数根据行号和列号返回工作区的数据项,其函数原型是:
item(self, rowNo, colNo: int = 0) -> QStandardItem
rowNo是行号,colNo是列号,返回值是一个QStandardItem对象。
工具栏上的“另存文件”按钮可以将数据模型的数据另存为一个文本文件,其实现代码如下:
@pyqtSlot() ##保存文件
def on_actSave_triggered(self):
## curPath=QDir.currentPath() #获取当前路径
curPath=os.getcwd() #获取当前路径
filename,flt=QFileDialog.getSaveFileName(self,"保存文件",curPath,
"井斜数据文件(*.txt);;所有文件(*.*)")
if (filename==""):
return
self.on_actModelData_triggered() #更新数据到plainTextEdit
aFile=open(filename,'w') #以写方式打开
aFile.write(self.ui.plainTextEdit.toPlainText())
aFile.close()
这里先调用了槽函数on_actModelData_triggered()将数据模型的内容显示到组件plainTextEdit里,然后利用了QPlainTextEdit的**toPlainText()**将其内容全部导出为文本,并写入文件。