1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
|
# Copyright 2016, Chris PeBenito <pebenito@ieee.org>
#
# SPDX-License-Identifier: LGPL-2.1-only
#
#
import csv
from PyQt6 import QtCore, QtGui, QtWidgets
from .. import models
__all__ = ("SEToolsTableView",)
class SEToolsTableView(QtWidgets.QTableView):
"""
QTableView class extended for saving CSV files and context menu actions
provided by the model.
"""
def contextMenuEvent(self, event: QtGui.QContextMenuEvent) -> None: # type: ignore[override]
"""Handle the context menu event."""
menu = QtWidgets.QMenu(self)
menu.setAttribute(QtCore.Qt.WidgetAttribute.WA_DeleteOnClose)
# Add any actions provided by the model.
index = self.indexAt(event.pos())
if index.isValid():
for action in self.model().data(index, models.ModelRoles.ContextMenuRole):
action.setParent(menu)
menu.addAction(action)
menu.addSeparator()
# Add the save to CSV action
save_csv_action = QtGui.QAction("Save table to CSV...", self)
save_csv_action.triggered.connect(self.choose_csv_save_location)
menu.addAction(save_csv_action)
menu.exec(event.globalPos())
return
def copy(self) -> None:
datamodel = self.model()
selected_text = []
current_row: int = -1
current_col: int = -1
prev_row: int = -1
prev_col: int = -1
for index in sorted(self.selectionModel().selectedIndexes()):
current_row = index.row()
current_col = index.column()
if prev_row is not None and current_row != prev_row:
selected_text.append('\n')
elif prev_col is not None and current_col != prev_col:
selected_text.append('\t')
selected_text.append(datamodel.data(index, models.ModelRoles.DisplayRole))
prev_row = current_row
prev_col = current_col
cb = QtWidgets.QApplication.clipboard()
assert cb, "No clipboard available, this is an SETools bug" # type narrowing
cb.setText("".join(selected_text))
def cut(self) -> None:
self.copy()
def choose_csv_save_location(self) -> None:
filename = QtWidgets.QFileDialog.getSaveFileName(
self,
"Save to CSV",
"table.csv",
"Comma Separated Values Spreadsheet (*.csv);;"
"All Files (*)")[0]
if filename:
self.save_csv(filename)
def save_csv(self, filename: str) -> None:
"""Save the current table data to the specified CSV file."""
datamodel = self.model()
row_count = datamodel.rowCount()
col_count = datamodel.columnCount()
with open(filename, 'w') as fd:
writer = csv.writer(fd, quoting=csv.QUOTE_MINIMAL)
# write headers
csv_row = []
for col in range(col_count):
csv_row.append(datamodel.headerData(col,
QtCore.Qt.Orientation.Horizontal,
models.ModelRoles.DisplayRole))
writer.writerow(csv_row)
# write data
for row in range(row_count):
csv_row = []
for col in range(col_count):
index = datamodel.index(row, col)
csv_row.append(datamodel.data(index, models.ModelRoles.DisplayRole))
writer.writerow(csv_row)
#
# Overridden methods for typing purposes
#
# @typing.override
def model(self) -> QtCore.QAbstractItemModel:
"""Type-narrowed model() method. See QTableView.model() for more info."""
model = super().model()
assert model, "No model set, this is an SETools bug"
return model
# @typing.override
def selectionModel(self) -> QtCore.QItemSelectionModel:
"""
Type-narrowed selectionModel() method. See QTableView.selectionModel() for more info.
"""
selection_model = super().selectionModel()
assert selection_model, "No selection model set, this is an SETools bug"
return selection_model
|