七月 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_())


文章由安静原创,发表于www.ajnote.com。转载请注明出处

发表 2020年7月28日 自 anjing 类别 "Pyside2学习笔记

发表评论

电子邮件地址不会被公开。 必填项已用*标注