254 lines
8.0 KiB
Python
254 lines
8.0 KiB
Python
# ***************************************************************************
|
|
# * Copyright (c) 2025 Stefan Tröger <stefantroeger@gmx.net> *
|
|
# * *
|
|
# * This file is part of the FreeCAD CAx development system. *
|
|
# * *
|
|
# * This program is free software; you can redistribute it and/or modify *
|
|
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
|
# * as published by the Free Software Foundation; either version 2 of *
|
|
# * the License, or (at your option) any later version. *
|
|
# * for detail see the LICENCE text file. *
|
|
# * *
|
|
# * This program is distributed in the hope that it will be useful, *
|
|
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
# * GNU Library General Public License for more details. *
|
|
# * *
|
|
# * You should have received a copy of the GNU Library General Public *
|
|
# * License along with this program; if not, write to the Free Software *
|
|
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
|
# * USA *
|
|
# * *
|
|
# ***************************************************************************
|
|
|
|
__title__ = "FreeCAD table view widget to visualize vtkTable"
|
|
__author__ = "Stefan Tröger"
|
|
__url__ = "https://www.freecad.org"
|
|
|
|
## @package vtk_table_view
|
|
# \ingroup FEM
|
|
# \brief A Qt widget to show a vtkTable
|
|
|
|
from PySide import QtGui
|
|
from PySide import QtCore
|
|
|
|
import FreeCAD
|
|
import FreeCADGui
|
|
|
|
from vtkmodules.vtkIOCore import vtkDelimitedTextWriter
|
|
|
|
translate = FreeCAD.Qt.translate
|
|
|
|
|
|
class VtkTableModel(QtCore.QAbstractTableModel):
|
|
# Simple table model. Only supports single component columns
|
|
# One can supply a header_names dict to replace the table column names
|
|
# in the header. It is a dict "column_idx (int)" to "new name"" or
|
|
# "orig_name (str)" to "new name"
|
|
|
|
def __init__(self, header_names=None):
|
|
super().__init__()
|
|
self._table = None
|
|
if header_names:
|
|
self._header = header_names
|
|
else:
|
|
self._header = {}
|
|
|
|
def setTable(self, table, header_names=None):
|
|
self.beginResetModel()
|
|
self._table = table
|
|
if header_names:
|
|
self._header = header_names
|
|
self.endResetModel()
|
|
|
|
def rowCount(self, index):
|
|
|
|
if not self._table:
|
|
return 0
|
|
|
|
return self._table.GetNumberOfRows()
|
|
|
|
def columnCount(self, index):
|
|
|
|
if not self._table:
|
|
return 0
|
|
|
|
return self._table.GetNumberOfColumns()
|
|
|
|
def data(self, index, role):
|
|
|
|
if not self._table:
|
|
return None
|
|
|
|
if role == QtCore.Qt.DisplayRole:
|
|
col = self._table.GetColumn(index.column())
|
|
return col.GetTuple(index.row())[0]
|
|
|
|
return None
|
|
|
|
def headerData(self, section, orientation, role):
|
|
|
|
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
|
|
if section in self._header:
|
|
return self._header[section]
|
|
|
|
name = self._table.GetColumnName(section)
|
|
if name in self._header:
|
|
return self._header[name]
|
|
|
|
return name
|
|
|
|
if orientation == QtCore.Qt.Vertical and role == QtCore.Qt.DisplayRole:
|
|
return section
|
|
|
|
return None
|
|
|
|
def getTable(self):
|
|
return self._table
|
|
|
|
|
|
class VtkTableSummaryModel(QtCore.QAbstractTableModel):
|
|
# Simple model showing a summary of the table.
|
|
# Only supports single component columns
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._table = None
|
|
|
|
def setTable(self, table):
|
|
self.beginResetModel()
|
|
self._table = table
|
|
self.endResetModel()
|
|
|
|
def rowCount(self, index):
|
|
|
|
if not self._table:
|
|
return 0
|
|
|
|
return self._table.GetNumberOfColumns()
|
|
|
|
def columnCount(self, index):
|
|
return 2 # min, max
|
|
|
|
def data(self, index, role):
|
|
|
|
if not self._table:
|
|
return None
|
|
|
|
if role == QtCore.Qt.DisplayRole:
|
|
col = self._table.GetColumn(index.row())
|
|
range = col.GetRange()
|
|
return range[index.column()]
|
|
|
|
return None
|
|
|
|
def headerData(self, section, orientation, role):
|
|
|
|
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
|
|
return ["Min", "Max"][section]
|
|
|
|
if orientation == QtCore.Qt.Vertical and role == QtCore.Qt.DisplayRole:
|
|
return self._table.GetColumnName(section)
|
|
|
|
return None
|
|
|
|
def getTable(self):
|
|
return self._table
|
|
|
|
|
|
class VtkTableView(QtGui.QWidget):
|
|
|
|
def __init__(self, model):
|
|
super().__init__()
|
|
|
|
self.model = model
|
|
|
|
layout = QtGui.QVBoxLayout()
|
|
layout.setContentsMargins(0, 0, 0, 0)
|
|
layout.setSpacing(0)
|
|
|
|
# start with the toolbar
|
|
self.toolbar = QtGui.QToolBar()
|
|
csv_action = QtGui.QAction(self)
|
|
csv_action.triggered.connect(self.exportCsv)
|
|
csv_action.setIcon(FreeCADGui.getIcon("Std_Export"))
|
|
csv_action.setToolTip(translate("FEM", "Export to CSV"))
|
|
self.toolbar.addAction(csv_action)
|
|
|
|
copy_action = QtGui.QAction(self)
|
|
copy_action.triggered.connect(self.copyToClipboard)
|
|
copy_action.setIcon(FreeCADGui.getIcon("edit-copy"))
|
|
shortcut = QtGui.QKeySequence(QtGui.QKeySequence.Copy)
|
|
copy_action.setToolTip(
|
|
translate("FEM", "Copy selection to clipboard ({})".format(shortcut.toString()))
|
|
)
|
|
copy_action.setShortcut(shortcut)
|
|
self.toolbar.addAction(copy_action)
|
|
|
|
layout.addWidget(self.toolbar)
|
|
|
|
# now the table view
|
|
self.table_view = QtGui.QTableView()
|
|
self.table_view.setModel(model)
|
|
self.model.modelReset.connect(self.modelReset)
|
|
|
|
# fast initial resize and manual resizing still allowed!
|
|
header = self.table_view.horizontalHeader()
|
|
header.setResizeContentsPrecision(10)
|
|
self.table_view.resizeColumnsToContents()
|
|
|
|
layout.addWidget(self.table_view)
|
|
self.setLayout(layout)
|
|
|
|
@QtCore.Slot()
|
|
def modelReset(self):
|
|
# The model is reset, make sure the header visibility is working
|
|
# This is needed in case new data was added
|
|
self.table_view.resizeColumnsToContents()
|
|
|
|
@QtCore.Slot(bool)
|
|
def exportCsv(self, state):
|
|
|
|
file_path, filter = QtGui.QFileDialog.getSaveFileName(
|
|
None, translate("FEM", "Save as csv file"), "", "CSV (*.csv)"
|
|
)
|
|
if not file_path:
|
|
FreeCAD.Console.PrintMessage(
|
|
translate("FEM", "CSV file export aborted: no filename selected")
|
|
)
|
|
return
|
|
|
|
writer = vtkDelimitedTextWriter()
|
|
writer.SetFileName(file_path)
|
|
writer.SetInputData(self.model.getTable())
|
|
writer.Write()
|
|
|
|
@QtCore.Slot()
|
|
def copyToClipboard(self):
|
|
|
|
sel_model = self.table_view.selectionModel()
|
|
selection = sel_model.selectedIndexes()
|
|
|
|
if len(selection) < 1:
|
|
return
|
|
|
|
copy_table = ""
|
|
previous = selection.pop(0)
|
|
for current in selection:
|
|
|
|
data = self.model.data(previous, QtCore.Qt.DisplayRole)
|
|
copy_table += str(data)
|
|
|
|
if current.row() != previous.row():
|
|
copy_table += "\n"
|
|
else:
|
|
copy_table += "\t"
|
|
|
|
previous = current
|
|
|
|
copy_table += str(self.model.data(selection[-1], QtCore.Qt.DisplayRole))
|
|
copy_table += "\n"
|
|
|
|
clipboard = QtGui.QApplication.instance().clipboard()
|
|
clipboard.setText(copy_table)
|