七月
28
Pyside2教程:CSV编辑器
import sys
import csv
from PySide2 import QtWidgets as qtw
from PySide2 import QtGui as qtg
from PySide2 import QtCore as qtc
class CsvTableModel(qtc.QAbstractTableModel):
"""CSV表格的模型"""
def __init__(self, csv_file):
super(CsvTableModel, self).__init__()
self.filename = csv_file
with open(self.filename) as fh:
csvreader = csv.reader(fh)
self._headers = next(csvreader)
self._data = list(csvreader)
def rowCount(self, parent):
return len(self._data)
def columnCount(self, parent):
return len(self._headers)
def data(self, index, role):
"""
用于获取模型中的数据
:param index: QModelIndex类的实例,我们用到了row()和column()两个方法
:param role: View获取数据时,会告诉model自己用设个数据干什么
:return: 对应的数据
"""
if role in (qtc.Qt.DisplayRole, qtc.Qt.EditRole):
return self._data[index.row()][index.column()]
def headerData(self, section, orientation, role=None):
"""
获取头文件
:param section: 表头的序号
:param orientation: qtc.Qt.Horizontal或者qtc.Qt.Vertical,行表头或者列表头
:param role: View获取数据时,会告诉model自己用设个数据干什么
:return:
"""
if orientation == qtc.Qt.Horizontal and role == qtc.Qt.DisplayRole:
return self._headers[section]
else:
return super().headerData(section, orientation, role)
def sort(self, column, order=None):
# 在排序之前需要发射layoutAboutToBeChanged信号
self.layoutAboutToBeChanged.emit()
self._data.sort(key=lambda x: x[column])
if order == qtc.Qt.DescendingOrder:
self._data.reverse()
# 在排序之后需要发射layoutChanged信号
self.layoutChanged.emit()
def flags(self, index):
return super(CsvTableModel, self).flags(index) | qtc.Qt.ItemIsEditable
def setData(self, index, value, role=None):
if index.isValid() and role == qtc.Qt.EditRole:
self._data[index.row()][index.column()] = value
self.dataChanged.emit(index, index, [role])
return True
else:
return False
def insertRows(self, row, count, parent=None, *args, **kwargs):
self.beginInsertRows(parent or qtc.QModelIndex(), row, row + count -1)
for i in range(count):
default_row = [''] * len(self._headers)
self._data.insert(row, default_row)
self.endInsertRows()
def removeRows(self, row, count, parent=None, *args, **kwargs):
self.beginRemoveRows(parent or qtc.QModelIndex(), row, row + count -1)
for i in range(count):
del self._data[row]
self.endRemoveRows()
def save_data(self):
# 这里必须有newline='',否则每行数据后面会多一个空行
with open(self.filename, 'w', encoding='utf8', newline='') as fh:
writer = csv.writer(fh)
writer.writerow(self._headers)
writer.writerows(self._data)
# 将主程序封装成类
class MainWindow(qtw.QMainWindow):
def __init__(self):
# 引入父类的__init__方法
super().__init__(parent=None)
# Main UI code goes here
self.tableview = qtw.QTableView()
self.tableview.setSortingEnabled(True)
self.setCentralWidget(self.tableview)
# menu部分
menu = self.menuBar()
file_menu = menu.addMenu('文件')
file_menu.addAction('打开', self.select_file)
file_menu.addAction('保存', self.save_file)
edit_menu = menu.addMenu('编辑')
edit_menu.addAction('在上方插入', self.insert_above)
edit_menu.addAction('在下方插入', self.insert_below)
edit_menu.addAction('删除行', self.remove_rows)
# End main UI code
# 显示主窗口
self.show()
def select_file(self):
filename, _ = qtw.QFileDialog.getOpenFileName(
self,
'请选择要打开的csv文件',
qtc.QDir.currentPath(),
'CSV Files (*.csv) ;; All Files (*)'
)
if filename:
self.model = CsvTableModel(filename)
self.tableview.setModel(self.model)
def save_file(self):
if self.model:
self.model.save_data()
def insert_above(self):
selected = self.tableview.selectedIndexes()
row = selected[0].row() if selected else 0
self.model.insertRows(row, 1)
def insert_below(self):
selected = self.tableview.selectedIndexes()
row = selected[-1].row() if selected else self.model.rowCount(None)
self.model.insertRows(row+1, 1)
def remove_rows(self):
selected = self.tableview.selectedIndexes()
if selected:
# 注意删除行的时候,如果选中同一行中的多个元素,应该按照删除一行来处理。这里用set方法去除相同的行。
self.model.removeRows(selected[0].row(), len(set(cell.row() for cell in selected)))
if __name__ == '__main__':
app = qtw.QApplication()
main_window = MainWindow()
sys.exit(app.exec_())