diff --git a/src/Mod/Fem/App/FemPostFilterPyImp.cpp b/src/Mod/Fem/App/FemPostFilterPyImp.cpp index 2e09c9e3f0..69a5d8f23e 100644 --- a/src/Mod/Fem/App/FemPostFilterPyImp.cpp +++ b/src/Mod/Fem/App/FemPostFilterPyImp.cpp @@ -199,7 +199,7 @@ PyObject* FemPostFilterPy::getOutputAlgorithm(PyObject* args) auto algorithm = getFemPostFilterPtr()->getFilterOutput(); PyObject* py_algorithm = vtkPythonUtil::GetObjectFromPointer(algorithm); - return Py::new_reference_to(py_algorithm); + return Py::new_reference_to(py_algorithm); #else PyErr_SetString(PyExc_NotImplementedError, "VTK python wrapper not available"); Py_Return; diff --git a/src/Mod/Fem/App/FemPostObjectPyImp.cpp b/src/Mod/Fem/App/FemPostObjectPyImp.cpp index a35e0b569a..8b242abcf7 100644 --- a/src/Mod/Fem/App/FemPostObjectPyImp.cpp +++ b/src/Mod/Fem/App/FemPostObjectPyImp.cpp @@ -30,9 +30,9 @@ #include "FemPostObjectPy.cpp" #ifdef FC_USE_VTK_PYTHON - #include - #include -#endif //BUILD_FEM_VTK +#include +#include +#endif // BUILD_FEM_VTK using namespace Fem; @@ -71,7 +71,7 @@ PyObject* FemPostObjectPy::getDataSet(PyObject* args) auto dataset = getFemPostObjectPtr()->getDataSet(); if (dataset) { PyObject* py_algorithm = vtkPythonUtil::GetObjectFromPointer(dataset); - return Py::new_reference_to(py_algorithm); + return Py::new_reference_to(py_algorithm); } return Py_None; #else diff --git a/src/Mod/Fem/App/FemPostPipelinePyImp.cpp b/src/Mod/Fem/App/FemPostPipelinePyImp.cpp index 388aed35de..2c493074e6 100644 --- a/src/Mod/Fem/App/FemPostPipelinePyImp.cpp +++ b/src/Mod/Fem/App/FemPostPipelinePyImp.cpp @@ -35,8 +35,8 @@ // clang-format on #ifdef FC_USE_VTK_PYTHON - #include -#endif //BUILD_FEM_VTK +#include +#endif // BUILD_FEM_VTK using namespace Fem; @@ -329,7 +329,7 @@ PyObject* FemPostPipelinePy::getOutputAlgorithm(PyObject* args) auto algorithm = getFemPostPipelinePtr()->getOutputAlgorithm(); PyObject* py_algorithm = vtkPythonUtil::GetObjectFromPointer(algorithm); - return Py::new_reference_to(py_algorithm); + return Py::new_reference_to(py_algorithm); #else PyErr_SetString(PyExc_NotImplementedError, "VTK python wrapper not available"); Py_Return; diff --git a/src/Mod/Fem/Gui/TaskPostBoxes.cpp b/src/Mod/Fem/Gui/TaskPostBoxes.cpp index 88cf26f8f9..e50bb0bf98 100644 --- a/src/Mod/Fem/Gui/TaskPostBoxes.cpp +++ b/src/Mod/Fem/Gui/TaskPostBoxes.cpp @@ -213,7 +213,11 @@ TaskPostWidget::TaskPostWidget(Gui::ViewProviderDocumentObject* view, setWindowIcon(icon); m_icon = icon; - m_connection = m_object->signalChanged.connect(boost::bind(&TaskPostWidget::handlePropertyChange, this, boost::placeholders::_1, boost::placeholders::_2)); + m_connection = + m_object->signalChanged.connect(boost::bind(&TaskPostWidget::handlePropertyChange, + this, + boost::placeholders::_1, + boost::placeholders::_2)); } TaskPostWidget::~TaskPostWidget() @@ -404,7 +408,8 @@ void TaskDlgPost::modifyStandardButtons(QDialogButtonBox* box) } } -void TaskDlgPost::processCollapsedWidgets() { +void TaskDlgPost::processCollapsedWidgets() +{ for (auto& widget : Content) { auto* task_box = dynamic_cast(widget); @@ -417,7 +422,7 @@ void TaskDlgPost::processCollapsedWidgets() { if (!post_widget || !post_widget->initiallyCollapsed()) { continue; } - post_widget->setGeometry(QRect(QPoint(0,0), post_widget->sizeHint())); + post_widget->setGeometry(QRect(QPoint(0, 0), post_widget->sizeHint())); task_box->hideGroupBox(); } } @@ -584,7 +589,8 @@ void TaskPostFrames::applyPythonCode() // we apply the views widgets python code } -bool TaskPostFrames::initiallyCollapsed() { +bool TaskPostFrames::initiallyCollapsed() +{ return (ui->FrameTable->rowCount() == 0); } diff --git a/src/Mod/Fem/Gui/TaskPostBoxes.h b/src/Mod/Fem/Gui/TaskPostBoxes.h index 3c60fe8ddf..816dafb080 100644 --- a/src/Mod/Fem/Gui/TaskPostBoxes.h +++ b/src/Mod/Fem/Gui/TaskPostBoxes.h @@ -157,7 +157,8 @@ public: virtual void apply() {}; // returns if the widget shall be collapsed when opening the task dialog - virtual bool initiallyCollapsed() { + virtual bool initiallyCollapsed() + { return false; }; diff --git a/src/Mod/Fem/Gui/TaskPostExtraction.cpp b/src/Mod/Fem/Gui/TaskPostExtraction.cpp index 4de2401c7c..e61033957c 100644 --- a/src/Mod/Fem/Gui/TaskPostExtraction.cpp +++ b/src/Mod/Fem/Gui/TaskPostExtraction.cpp @@ -48,9 +48,7 @@ using namespace Gui; // box to handle data extractions TaskPostExtraction::TaskPostExtraction(ViewProviderFemPostObject* view, QWidget* parent) - : TaskPostWidget(view, - Gui::BitmapFactory().pixmap("FEM_PostHistogram"), QString(), - parent) + : TaskPostWidget(view, Gui::BitmapFactory().pixmap("FEM_PostHistogram"), QString(), parent) { // we load the python implementation, and try to get the widget from it, to add // directly our widget @@ -73,7 +71,7 @@ TaskPostExtraction::TaskPostExtraction(ViewProviderFemPostObject* view, QWidget* m_panel = Py::Object(method.apply(args)); } catch (Py::Exception&) { - Base::PyException e; // extract the Python error text + Base::PyException e; // extract the Python error text e.reportException(); } @@ -97,7 +95,8 @@ TaskPostExtraction::TaskPostExtraction(ViewProviderFemPostObject* view, QWidget* Base::Console().error("Unable to import data extraction widget\n"); }; -TaskPostExtraction::~TaskPostExtraction() { +TaskPostExtraction::~TaskPostExtraction() +{ Base::PyGILStateLocker lock; try { @@ -123,7 +122,7 @@ void TaskPostExtraction::onPostDataChanged(Fem::FemPostObject* obj) } } catch (Py::Exception&) { - Base::PyException e; // extract the Python error text + Base::PyException e; // extract the Python error text e.reportException(); } }; @@ -139,7 +138,7 @@ bool TaskPostExtraction::isGuiTaskOnly() } } catch (Py::Exception&) { - Base::PyException e; // extract the Python error text + Base::PyException e; // extract the Python error text e.reportException(); } @@ -156,7 +155,7 @@ void TaskPostExtraction::apply() } } catch (Py::Exception&) { - Base::PyException e; // extract the Python error text + Base::PyException e; // extract the Python error text e.reportException(); } } @@ -172,7 +171,7 @@ bool TaskPostExtraction::initiallyCollapsed() } } catch (Py::Exception&) { - Base::PyException e; // extract the Python error text + Base::PyException e; // extract the Python error text e.reportException(); } diff --git a/src/Mod/Fem/Gui/Workbench.cpp b/src/Mod/Fem/Gui/Workbench.cpp index 44c1186c6f..31f396fbe9 100644 --- a/src/Mod/Fem/Gui/Workbench.cpp +++ b/src/Mod/Fem/Gui/Workbench.cpp @@ -218,7 +218,7 @@ Gui::ToolBarItem* Workbench::setupToolBars() const #ifdef FC_USE_VTK_PYTHON << "FEM_PostVisualization" #endif - ; + ; #endif Gui::ToolBarItem* utils = new Gui::ToolBarItem(root); @@ -374,7 +374,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const #ifdef FC_USE_VTK_PYTHON << "FEM_PostVisualization" #endif - ; + ; #endif Gui::MenuItem* utils = new Gui::MenuItem; diff --git a/src/Mod/Fem/InitGui.py b/src/Mod/Fem/InitGui.py index 35c835f81e..e7d0a2ada7 100644 --- a/src/Mod/Fem/InitGui.py +++ b/src/Mod/Fem/InitGui.py @@ -83,6 +83,7 @@ class FemWorkbench(Workbench): # check vtk version to potentially find missmatchs if "BUILD_FEM_VTK_PYTHON" in FreeCAD.__cmake__: from femguiutils.vtk_module_handling import vtk_module_handling + vtk_module_handling() def GetClassName(self): diff --git a/src/Mod/Fem/ObjectsFem.py b/src/Mod/Fem/ObjectsFem.py index 1ca11e2566..2bd6e74056 100644 --- a/src/Mod/Fem/ObjectsFem.py +++ b/src/Mod/Fem/ObjectsFem.py @@ -696,6 +696,7 @@ def makePostLineplot(doc, name="Lineplot"): post_lineplot.PostLineplot(obj) if FreeCAD.GuiUp: from femviewprovider import view_post_lineplot + view_post_lineplot.VPPostLineplot(obj.ViewObject) return obj @@ -710,6 +711,7 @@ def makePostLineplotFieldData(doc, name="FieldData2D"): post_lineplot.PostLineplotFieldData(obj) if FreeCAD.GuiUp: from femviewprovider import view_post_lineplot + view_post_lineplot.VPPostLineplotFieldData(obj.ViewObject) return obj @@ -724,6 +726,7 @@ def makePostLineplotIndexOverFrames(doc, name="IndexOverFrames2D"): post_lineplot.PostLineplotIndexOverFrames(obj) if FreeCAD.GuiUp: from femviewprovider import view_post_lineplot + view_post_lineplot.VPPostLineplotIndexOverFrames(obj.ViewObject) return obj @@ -738,6 +741,7 @@ def makePostHistogram(doc, name="Histogram"): post_histogram.PostHistogram(obj) if FreeCAD.GuiUp: from femviewprovider import view_post_histogram + view_post_histogram.VPPostHistogram(obj.ViewObject) return obj @@ -752,6 +756,7 @@ def makePostHistogramFieldData(doc, name="FieldData1D"): post_histogram.PostHistogramFieldData(obj) if FreeCAD.GuiUp: from femviewprovider import view_post_histogram + view_post_histogram.VPPostHistogramFieldData(obj.ViewObject) return obj @@ -766,6 +771,7 @@ def makePostHistogramIndexOverFrames(doc, name="IndexOverFrames1D"): post_histogram.PostHistogramIndexOverFrames(obj) if FreeCAD.GuiUp: from femviewprovider import view_post_histogram + view_post_histogram.VPPostHistogramIndexOverFrames(obj.ViewObject) return obj @@ -780,6 +786,7 @@ def makePostTable(doc, name="Table"): post_table.PostTable(obj) if FreeCAD.GuiUp: from femviewprovider import view_post_table + view_post_table.VPPostTable(obj.ViewObject) return obj @@ -794,6 +801,7 @@ def makePostTableFieldData(doc, name="FieldData1D"): post_table.PostTableFieldData(obj) if FreeCAD.GuiUp: from femviewprovider import view_post_table + view_post_table.VPPostTableFieldData(obj.ViewObject) return obj @@ -808,6 +816,7 @@ def makePostTableIndexOverFrames(doc, name="IndexOverFrames1D"): post_table.PostTableIndexOverFrames(obj) if FreeCAD.GuiUp: from femviewprovider import view_post_table + view_post_table.VPPostTableIndexOverFrames(obj.ViewObject) return obj diff --git a/src/Mod/Fem/femcommands/commands.py b/src/Mod/Fem/femcommands/commands.py index 8c85fc7270..5d662074be 100644 --- a/src/Mod/Fem/femcommands/commands.py +++ b/src/Mod/Fem/femcommands/commands.py @@ -1231,6 +1231,7 @@ class _PostFilterGlyph(CommandManager): self.is_active = "with_vtk_selresult" self.do_activated = "add_filter_set_edit" + # the string in add command will be the page name on FreeCAD wiki FreeCADGui.addCommand("FEM_Analysis", _Analysis()) FreeCADGui.addCommand("FEM_ClippingPlaneAdd", _ClippingPlaneAdd()) @@ -1295,4 +1296,5 @@ if "BUILD_FEM_VTK_PYTHON" in FreeCAD.__cmake__: import femobjects.post_table from femguiutils import post_visualization + post_visualization.setup_commands("FEM_PostVisualization") diff --git a/src/Mod/Fem/femcommands/manager.py b/src/Mod/Fem/femcommands/manager.py index f653d053a0..bb2edc3e05 100644 --- a/src/Mod/Fem/femcommands/manager.py +++ b/src/Mod/Fem/femcommands/manager.py @@ -381,6 +381,7 @@ class CommandManager: # check if we should use python filter from femguiutils.vtk_module_handling import vtk_compatibility_abort + if vtk_compatibility_abort(True): return diff --git a/src/Mod/Fem/femguiutils/data_extraction.py b/src/Mod/Fem/femguiutils/data_extraction.py index cb55295703..dfe0cea7f8 100644 --- a/src/Mod/Fem/femguiutils/data_extraction.py +++ b/src/Mod/Fem/femguiutils/data_extraction.py @@ -37,8 +37,7 @@ from vtkmodules.vtkCommonCore import vtkVersion from vtkmodules.vtkCommonDataModel import vtkTable from vtkmodules.vtkFiltersGeneral import vtkSplitColumnComponents -if vtkVersion.GetVTKMajorVersion() > 9 and \ - vtkVersion.GetVTKMinorVersion() > 3: +if vtkVersion.GetVTKMajorVersion() > 9 and vtkVersion.GetVTKMinorVersion() > 3: from vtkmodules.vtkFiltersCore import vtkAttributeDataToTableFilter else: from vtkmodules.vtkInfovisCore import vtkDataObjectToTable @@ -51,6 +50,7 @@ import femobjects.base_fempostextractors as extr from femtaskpanels.base_fempostpanel import _BasePostTaskPanel from . import extract_link_view + ExtractLinkView = extract_link_view.ExtractLinkView @@ -83,11 +83,10 @@ class DataExtraction(_BasePostTaskPanel): # setup the extraction widget self._extraction_view = ExtractLinkView(self.Object, True, self) - self.widget.layout().addSpacing(self.widget.Data.size().height()/3) + self.widget.layout().addSpacing(self.widget.Data.size().height() / 3) self.widget.layout().addWidget(self._extraction_view) self._extraction_view.repopulate() - @QtCore.Slot() def showData(self): @@ -96,7 +95,7 @@ class DataExtraction(_BasePostTaskPanel): widget = vtk_table_view.VtkTableView(self.data_model) layout = QtGui.QVBoxLayout() layout.addWidget(widget) - layout.setContentsMargins(0,0,0,0) + layout.setContentsMargins(0, 0, 0, 0) dialog.setLayout(layout) dialog.resize(1500, 900) @@ -110,7 +109,7 @@ class DataExtraction(_BasePostTaskPanel): widget = vtk_table_view.VtkTableView(self.summary_model) layout = QtGui.QVBoxLayout() layout.addWidget(widget) - layout.setContentsMargins(0,0,0,0) + layout.setContentsMargins(0, 0, 0, 0) dialog.setLayout(layout) dialog.resize(600, 900) @@ -126,8 +125,7 @@ class DataExtraction(_BasePostTaskPanel): if not algo: self.data_model.setTable(vtkTable()) - if vtkVersion.GetVTKMajorVersion() > 9 and \ - vtkVersion.GetVTKMinorVersion() > 3: + if vtkVersion.GetVTKMajorVersion() > 9 and vtkVersion.GetVTKMinorVersion() > 3: filter = vtkAttributeDataToTableFilter() else: filter = vtkDataObjectToTable() diff --git a/src/Mod/Fem/femguiutils/extract_link_view.py b/src/Mod/Fem/femguiutils/extract_link_view.py index a6f6067080..eec8ba6927 100644 --- a/src/Mod/Fem/femguiutils/extract_link_view.py +++ b/src/Mod/Fem/femguiutils/extract_link_view.py @@ -44,6 +44,7 @@ translate = FreeCAD.Qt.translate # a model showing available visualizations and possible extractions # ################################################################# + def build_new_visualization_tree_model(): # model that shows all options to create new visualizations @@ -67,6 +68,7 @@ def build_new_visualization_tree_model(): return model + def build_add_to_visualization_tree_model(): # model that shows all possible visualization objects to add data to @@ -92,7 +94,9 @@ def build_add_to_visualization_tree_model(): for ext in visualizations[vis_type].extractions: icon = FreeCADGui.getIcon(ext.icon) name = ext.name.removeprefix(vis_type) - ext_item = QtGui.QStandardItem(icon, translate("FEM", "Add {}").format(name)) + ext_item = QtGui.QStandardItem( + icon, translate("FEM", "Add {}").format(name) + ) ext_item.setFlags(QtGui.Qt.ItemIsEnabled) ext_item.setData(ext) vis_item.appendRow(ext_item) @@ -102,10 +106,13 @@ def build_add_to_visualization_tree_model(): return model + def build_post_object_item(post_object, extractions, vis_type): # definitely build a item and add the extractions - post_item = QtGui.QStandardItem(post_object.ViewObject.Icon, translate("FEM", "From {}").format(post_object.Label)) + post_item = QtGui.QStandardItem( + post_object.ViewObject.Icon, translate("FEM", "From {}").format(post_object.Label) + ) post_item.setFlags(QtGui.Qt.ItemIsEnabled) post_item.setData(post_object) @@ -150,9 +157,11 @@ def build_add_from_data_tree_model(vis_type): return model + # implementation of GUI and its functionality # ########################################### + class _ElideToolButton(QtGui.QToolButton): # tool button that elides its text, and left align icon and text @@ -174,7 +183,7 @@ class _ElideToolButton(QtGui.QToolButton): button_size = super().sizeHint() icn_size = self.iconSize() min_margin = max((button_size - icn_size).height(), 6) - return QtCore.QSize(self.iconSize().width()+10, icn_size.height() + min_margin) + return QtCore.QSize(self.iconSize().width() + 10, icn_size.height() + min_margin) def paintEvent(self, event): @@ -190,11 +199,10 @@ class _ElideToolButton(QtGui.QToolButton): margin = (self.height() - self.iconSize().height()) / 2 icn_width = self.iconSize().width() if self._icon.isNull(): - icn_width = 0; - + icn_width = 0 fm = self.fontMetrics() - txt_size = self.width() - icn_width - 2*margin + txt_size = self.width() - icn_width - 2 * margin if not self._icon.isNull(): # we add the margin between icon and text txt_size -= margin @@ -205,7 +213,7 @@ class _ElideToolButton(QtGui.QToolButton): xpos = margin if not self._icon.isNull() and txt_size < txt_min: # center icon - xpos = self.width()/2 - self.iconSize().width()/2 + xpos = self.width() / 2 - self.iconSize().width() / 2 if not self._icon.isNull(): match type(self._icon): @@ -213,10 +221,12 @@ class _ElideToolButton(QtGui.QToolButton): painter.drawPixmap(xpos, margin, self._icon.scaled(self.iconSize())) xpos += self.iconSize().width() case QtGui.QIcon: - self._icon.paint(painter, QtCore.QRect(QtCore.QPoint(margin, margin), self.iconSize())) + self._icon.paint( + painter, QtCore.QRect(QtCore.QPoint(margin, margin), self.iconSize()) + ) xpos += self.iconSize().width() - xpos += margin # the margin to the text + xpos += margin # the margin to the text if txt_size >= txt_min: text = fm.elidedText(self._text, QtGui.Qt.ElideMiddle, txt_size) @@ -227,7 +237,7 @@ class _ElideToolButton(QtGui.QToolButton): class _TreeChoiceButton(QtGui.QToolButton): - selection = QtCore.Signal(object,object) + selection = QtCore.Signal(object, object) def __init__(self, model): super().__init__() @@ -254,9 +264,10 @@ class _TreeChoiceButton(QtGui.QToolButton): self.popup = QtGui.QWidgetAction(self) self.popup.setDefaultWidget(self.tree_view) self.setPopupMode(QtGui.QToolButton.InstantPopup) - self.addAction(self.popup); + self.addAction(self.popup) QtCore.Slot(QtCore.QModelIndex) + def selectIndex(self, index): item = self.model.itemFromIndex(index) @@ -274,6 +285,7 @@ class _TreeChoiceButton(QtGui.QToolButton): # check if we should be disabled self.setEnabled(bool(model.rowCount())) + class _SettingsPopup(QtGui.QMenu): close = QtCore.Signal() @@ -297,7 +309,7 @@ class _SettingsPopup(QtGui.QMenu): widget.setLayout(vbox) vbox2 = QtGui.QVBoxLayout() - vbox2.setContentsMargins(0,0,0,0) + vbox2.setContentsMargins(0, 0, 0, 0) vbox2.addWidget(widget) self.setLayout(vbox2) @@ -321,7 +333,7 @@ class _SettingsPopup(QtGui.QMenu): class _SummaryWidget(QtGui.QWidget): - delete = QtCore.Signal(object, object) # to delete: document object, summary widget + delete = QtCore.Signal(object, object) # to delete: document object, summary widget def __init__(self, st_object, extractor, post_dialog): super().__init__() @@ -335,17 +347,16 @@ class _SummaryWidget(QtGui.QWidget): # build the UI hbox = QtGui.QHBoxLayout() - hbox.setContentsMargins(6,0,6,0) + hbox.setContentsMargins(6, 0, 6, 0) hbox.setSpacing(2) self.extrButton = self._button(extractor.ViewObject.Icon, extr_label) self.viewButton = self._button(extr_repr[0], extr_repr[1], 1) size = self.viewButton.iconSize() - size.setWidth(size.width()*2) + size.setWidth(size.width() * 2) self.viewButton.setIconSize(size) - if st_object: self.stButton = self._button(st_object.ViewObject.Icon, st_object.Label) hbox.addWidget(self.stButton) @@ -357,7 +368,9 @@ class _SummaryWidget(QtGui.QWidget): self.viewButton.hide() self.warning = QtGui.QLabel(self) - self.warning.full_text = translate("FEM", "{}: Data source not available").format(extractor.Label) + self.warning.full_text = translate("FEM", "{}: Data source not available").format( + extractor.Label + ) hbox.addWidget(self.warning) self.rmButton = QtGui.QToolButton(self) @@ -371,16 +384,16 @@ class _SummaryWidget(QtGui.QWidget): # add the separation line vbox = QtGui.QVBoxLayout() - vbox.setContentsMargins(0,0,0,0) + vbox.setContentsMargins(0, 0, 0, 0) vbox.setSpacing(5) vbox.addItem(hbox) self.frame = QtGui.QFrame(self) - self.frame.setFrameShape(QtGui.QFrame.HLine); + self.frame.setFrameShape(QtGui.QFrame.HLine) vbox.addWidget(self.frame) policy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred) self.setSizePolicy(policy) - #self.setMinimumSize(self.extrButton.sizeHint()+self.frame.sizeHint()*3) + # self.setMinimumSize(self.extrButton.sizeHint()+self.frame.sizeHint()*3) self.setLayout(vbox) # connect actions. We add functions to widget, as well as the data we need, @@ -393,8 +406,7 @@ class _SummaryWidget(QtGui.QWidget): self.rmButton.clicked.connect(self.deleteTriggered) # make sure initial drawing happened - #self._redraw() - + # self._redraw() def _button(self, icon, text, stretch=2): @@ -408,7 +420,6 @@ class _SummaryWidget(QtGui.QWidget): btn.setSizePolicy(policy) return btn - @QtCore.Slot() def showVisualization(self): if vis.is_visualization_object(self._st_object): @@ -426,14 +437,18 @@ class _SummaryWidget(QtGui.QWidget): # very weird values. Hence we build the coords of the widget # ourself - summary = dialog.parent() # == self + summary = dialog.parent() # == self base_widget = summary.parent() viewport = summary.parent() scroll = viewport.parent() - top_left = summary.geometry().topLeft() + base_widget.geometry().topLeft() + viewport.geometry().topLeft() - delta = (summary.width() - dialog.size().width())/2 - local_point = QtCore.QPoint(top_left.x()+delta, top_left.y()+summary.height()) + top_left = ( + summary.geometry().topLeft() + + base_widget.geometry().topLeft() + + viewport.geometry().topLeft() + ) + delta = (summary.width() - dialog.size().width()) / 2 + local_point = QtCore.QPoint(top_left.x() + delta, top_left.y() + summary.height()) global_point = scroll.mapToGlobal(local_point) dialog.setGeometry(QtCore.QRect(global_point, dialog.sizeHint())) @@ -485,7 +500,6 @@ class _SummaryWidget(QtGui.QWidget): self.extrButton.setToolTip(extr_label) - class ExtractLinkView(QtGui.QWidget): def __init__(self, obj, is_source, post_dialog): @@ -506,7 +520,7 @@ class ExtractLinkView(QtGui.QWidget): self._scroll_view.setWidgetResizable(True) self._scroll_widget = QtGui.QWidget(self._scroll_view) vbox = QtGui.QVBoxLayout() - vbox.setContentsMargins(0,6,0,0) + vbox.setContentsMargins(0, 6, 0, 0) vbox.addStretch() self._scroll_widget.setLayout(vbox) self._scroll_view.setWidget(self._scroll_widget) @@ -541,7 +555,7 @@ class ExtractLinkView(QtGui.QWidget): hbox.addWidget(self._add) vbox = QtGui.QVBoxLayout() - vbox.setContentsMargins(0,0,0,0) + vbox.setContentsMargins(0, 0, 0, 0) vbox.addItem(hbox) vbox.addWidget(self._scroll_view) @@ -616,7 +630,8 @@ class ExtractLinkView(QtGui.QWidget): return None - QtCore.Slot(object, object) # visualization data, extraction data + QtCore.Slot(object, object) # visualization data, extraction data + def newVisualization(self, vis_data, ext_data): FreeCADGui.addModule(vis_data.module) @@ -630,17 +645,13 @@ class ExtractLinkView(QtGui.QWidget): analysis = self._find_parent_analysis(self._object) if analysis: - FreeCADGui.doCommand( - f"FreeCAD.ActiveDocument.{analysis.Name}.addObject(visualization)" - ) + FreeCADGui.doCommand(f"FreeCAD.ActiveDocument.{analysis.Name}.addObject(visualization)") # create extraction and add it FreeCADGui.doCommand( f"extraction = {ext_data.module}.{ext_data.factory}(FreeCAD.ActiveDocument)" ) - FreeCADGui.doCommand( - f"extraction.Source = FreeCAD.ActiveDocument.{self._object.Name}" - ) + FreeCADGui.doCommand(f"extraction.Source = FreeCAD.ActiveDocument.{self._object.Name}") # default values: color color_prop = FreeCADGui.ActiveDocument.ActiveObject.Proxy.get_default_color_property() if color_prop: @@ -648,15 +659,13 @@ class ExtractLinkView(QtGui.QWidget): f"extraction.ViewObject.{color_prop} = visualization.ViewObject.Proxy.get_next_default_color()" ) - FreeCADGui.doCommand( - f"visualization.addObject(extraction)" - ) - + FreeCADGui.doCommand(f"visualization.addObject(extraction)") self._post_dialog._recompute() self.repopulate() - QtCore.Slot(object, object) # visualization object, extraction data + QtCore.Slot(object, object) # visualization object, extraction data + def addExtractionToVisualization(self, vis_obj, ext_data): FreeCADGui.addModule(ext_data.module) @@ -666,9 +675,7 @@ class ExtractLinkView(QtGui.QWidget): FreeCADGui.doCommand( f"extraction = {ext_data.module}.{ext_data.factory}(FreeCAD.ActiveDocument)" ) - FreeCADGui.doCommand( - f"extraction.Source = FreeCAD.ActiveDocument.{self._object.Name}" - ) + FreeCADGui.doCommand(f"extraction.Source = FreeCAD.ActiveDocument.{self._object.Name}") # default values: color color_prop = FreeCADGui.ActiveDocument.ActiveObject.Proxy.get_default_color_property() @@ -677,14 +684,13 @@ class ExtractLinkView(QtGui.QWidget): f"extraction.ViewObject.{color_prop} = (Gui.ActiveDocument.{vis_obj.Name}.Proxy.get_next_default_color())" ) - FreeCADGui.doCommand( - f"App.ActiveDocument.{vis_obj.Name}.addObject(extraction)" - ) + FreeCADGui.doCommand(f"App.ActiveDocument.{vis_obj.Name}.addObject(extraction)") self._post_dialog._recompute() self.repopulate() - QtCore.Slot(object, object) # post object, extraction data + QtCore.Slot(object, object) # post object, extraction data + def addExtractionToPostObject(self, post_obj, ext_data): FreeCADGui.addModule(ext_data.module) @@ -694,9 +700,7 @@ class ExtractLinkView(QtGui.QWidget): FreeCADGui.doCommand( f"extraction = {ext_data.module}.{ext_data.factory}(FreeCAD.ActiveDocument)" ) - FreeCADGui.doCommand( - f"extraction.Source = FreeCAD.ActiveDocument.{post_obj.Name}" - ) + FreeCADGui.doCommand(f"extraction.Source = FreeCAD.ActiveDocument.{post_obj.Name}") # default values for color color_prop = FreeCADGui.ActiveDocument.ActiveObject.Proxy.get_default_color_property() @@ -705,10 +709,7 @@ class ExtractLinkView(QtGui.QWidget): f"extraction.ViewObject.{color_prop} = Gui.ActiveDocument.{self._object.Name}.Proxy.get_next_default_color()" ) - FreeCADGui.doCommand( - f"App.ActiveDocument.{self._object.Name}.addObject(extraction)" - ) + FreeCADGui.doCommand(f"App.ActiveDocument.{self._object.Name}.addObject(extraction)") self._post_dialog._recompute() self.repopulate() - diff --git a/src/Mod/Fem/femguiutils/post_visualization.py b/src/Mod/Fem/femguiutils/post_visualization.py index 6724fda779..557c177cd3 100644 --- a/src/Mod/Fem/femguiutils/post_visualization.py +++ b/src/Mod/Fem/femguiutils/post_visualization.py @@ -45,6 +45,7 @@ import FreeCAD _registry = {} + @dataclass class _Extraction: @@ -55,6 +56,7 @@ class _Extraction: module: str factory: str + @dataclass class _Visualization: @@ -64,6 +66,7 @@ class _Visualization: factory: str extractions: list[_Extraction] + # Register a visualization by type, icon and factory function def register_visualization(visualization_type, icon, module, factory): if visualization_type in _registry: @@ -71,7 +74,10 @@ def register_visualization(visualization_type, icon, module, factory): _registry[visualization_type] = _Visualization(visualization_type, icon, module, factory, []) -def register_extractor(visualization_type, extraction_type, icon, dimension, etype, module, factory): + +def register_extractor( + visualization_type, extraction_type, icon, dimension, etype, module, factory +): if not visualization_type in _registry: raise ValueError("visualization not registered yet") @@ -79,6 +85,7 @@ def register_extractor(visualization_type, extraction_type, icon, dimension, ety extraction = _Extraction(extraction_type, icon, dimension, etype, module, factory) _registry[visualization_type].extractions.append(extraction) + def get_registered_visualizations(): return copy.deepcopy(_registry) @@ -86,6 +93,7 @@ def get_registered_visualizations(): def _to_command_name(name): return "FEM_PostVisualization" + name + class _VisualizationGroupCommand: def GetCommands(self): @@ -97,14 +105,19 @@ class _VisualizationGroupCommand: return 0 def GetResources(self): - return { 'MenuText': QtCore.QT_TRANSLATE_NOOP("FEM", 'Data Visualizations'), - 'ToolTip': QtCore.QT_TRANSLATE_NOOP("FEM", 'Different visualizations to show post processing data in')} + return { + "MenuText": QtCore.QT_TRANSLATE_NOOP("FEM", "Data Visualizations"), + "ToolTip": QtCore.QT_TRANSLATE_NOOP( + "FEM", "Different visualizations to show post processing data in" + ), + } def IsActive(self): if not FreeCAD.ActiveDocument: return False import FemGui + return bool(FemGui.getActiveAnalysis()) @@ -120,12 +133,12 @@ class _VisualizationCommand: tooltip = f"Create a {self._visualization_type} post processing data visualization" return { - "Pixmap": vis.icon, - "MenuText": QtCore.QT_TRANSLATE_NOOP(cmd, "Create {}".format(self._visualization_type)), - "Accel": "", - "ToolTip": QtCore.QT_TRANSLATE_NOOP(cmd, tooltip), - "CmdType": "AlterDoc" - } + "Pixmap": vis.icon, + "MenuText": QtCore.QT_TRANSLATE_NOOP(cmd, "Create {}".format(self._visualization_type)), + "Accel": "", + "ToolTip": QtCore.QT_TRANSLATE_NOOP(cmd, tooltip), + "CmdType": "AlterDoc", + } def IsActive(self): # active analysis available @@ -133,6 +146,7 @@ class _VisualizationCommand: return False import FemGui + return bool(FemGui.getActiveAnalysis()) def Activated(self): @@ -144,17 +158,12 @@ class _VisualizationCommand: FreeCADGui.addModule(vis.module) FreeCADGui.addModule("FemGui") - FreeCADGui.doCommand( - f"obj = {vis.module}.{vis.factory}(FreeCAD.ActiveDocument)" - ) - FreeCADGui.doCommand( - f"FemGui.getActiveAnalysis().addObject(obj)" - ) + FreeCADGui.doCommand(f"obj = {vis.module}.{vis.factory}(FreeCAD.ActiveDocument)") + FreeCADGui.doCommand(f"FemGui.getActiveAnalysis().addObject(obj)") FreeCADGui.Selection.clearSelection() - FreeCADGui.doCommand( - "FreeCADGui.ActiveDocument.setEdit(obj)" - ) + FreeCADGui.doCommand("FreeCADGui.ActiveDocument.setEdit(obj)") + def setup_commands(toplevel_name): # creates all visualization commands and registers them. The diff --git a/src/Mod/Fem/femguiutils/vtk_table_view.py b/src/Mod/Fem/femguiutils/vtk_table_view.py index f126640d24..b6e8c939b3 100644 --- a/src/Mod/Fem/femguiutils/vtk_table_view.py +++ b/src/Mod/Fem/femguiutils/vtk_table_view.py @@ -39,13 +39,14 @@ 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): + def __init__(self, header_names=None): super().__init__() self._table = None if header_names: @@ -53,7 +54,7 @@ class VtkTableModel(QtCore.QAbstractTableModel): else: self._header = {} - def setTable(self, table, header_names = None): + def setTable(self, table, header_names=None): self.beginResetModel() self._table = table if header_names: @@ -105,6 +106,7 @@ class VtkTableModel(QtCore.QAbstractTableModel): def getTable(self): return self._table + class VtkTableSummaryModel(QtCore.QAbstractTableModel): # Simple model showing a summary of the table. # Only supports single component columns @@ -126,7 +128,7 @@ class VtkTableSummaryModel(QtCore.QAbstractTableModel): return self._table.GetNumberOfColumns() def columnCount(self, index): - return 2 # min, max + return 2 # min, max def data(self, index, role): @@ -143,7 +145,7 @@ class VtkTableSummaryModel(QtCore.QAbstractTableModel): def headerData(self, section, orientation, role): if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole: - return ["Min","Max"][section] + return ["Min", "Max"][section] if orientation == QtCore.Qt.Vertical and role == QtCore.Qt.DisplayRole: return self._table.GetColumnName(section) @@ -162,7 +164,7 @@ class VtkTableView(QtGui.QWidget): self.model = model layout = QtGui.QVBoxLayout() - layout.setContentsMargins(0,0,0,0) + layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) # start with the toolbar @@ -177,7 +179,9 @@ class VtkTableView(QtGui.QWidget): 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.setToolTip( + translate("FEM", "Copy selection to clipboard ({})".format(shortcut.toString())) + ) copy_action.setShortcut(shortcut) self.toolbar.addAction(copy_action) @@ -205,15 +209,19 @@ class VtkTableView(QtGui.QWidget): @QtCore.Slot(bool) def exportCsv(self, state): - file_path, filter = QtGui.QFileDialog.getSaveFileName(None, translate("FEM", "Save as csv file"), "", "CSV (*.csv)") + 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")) + 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(); + writer.SetInputData(self.model.getTable()) + writer.Write() @QtCore.Slot() def copyToClipboard(self): @@ -228,19 +236,18 @@ class VtkTableView(QtGui.QWidget): previous = selection.pop(0) for current in selection: - data = self.model.data(previous, QtCore.Qt.DisplayRole); + data = self.model.data(previous, QtCore.Qt.DisplayRole) copy_table += str(data) if current.row() != previous.row(): - copy_table += '\n' + copy_table += "\n" else: - copy_table += '\t' + copy_table += "\t" previous = current copy_table += str(self.model.data(selection[-1], QtCore.Qt.DisplayRole)) - copy_table += '\n' + copy_table += "\n" clipboard = QtGui.QApplication.instance().clipboard() clipboard.setText(copy_table) - diff --git a/src/Mod/Fem/femobjects/base_fempostextractors.py b/src/Mod/Fem/femobjects/base_fempostextractors.py index 48bd9c4951..9e2ad7104d 100644 --- a/src/Mod/Fem/femobjects/base_fempostextractors.py +++ b/src/Mod/Fem/femobjects/base_fempostextractors.py @@ -36,22 +36,26 @@ from vtkmodules.vtkCommonDataModel import vtkTable from PySide.QtCore import QT_TRANSLATE_NOOP from . import base_fempythonobject + _PropHelper = base_fempythonobject._PropHelper # helper functions # ################ + def is_extractor_object(obj): if not hasattr(obj, "Proxy"): return False return hasattr(obj.Proxy, "ExtractionType") + def get_extraction_type(obj): # returns the extractor type string, or throws exception if # not a extractor return obj.Proxy.ExtractionType + def get_extraction_dimension(obj): # returns the extractor dimension string, or throws exception if # not a extractor @@ -104,7 +108,6 @@ class Extractor(base_fempythonobject.BaseFemPythonObject): FreeCAD.Console.PrintWarning("Invalid object: Line source must be FemPostObject") obj.Source = None - def get_vtk_table(self, obj): if not obj.DataTable: obj.DataTable = vtkTable() @@ -135,7 +138,6 @@ class Extractor1D(Extractor): def __init__(self, obj): super().__init__(obj) - def _get_properties(self): prop = [ _PropHelper( @@ -149,14 +151,15 @@ class Extractor1D(Extractor): type="App::PropertyEnumeration", name="XComponent", group="X Data", - doc=QT_TRANSLATE_NOOP("FEM", "Which part of the X field vector to use for the X axis"), + doc=QT_TRANSLATE_NOOP( + "FEM", "Which part of the X field vector to use for the X axis" + ), value=[], ), ] return super()._get_properties() + prop - def onChanged(self, obj, prop): super().onChanged(obj, prop) @@ -213,7 +216,7 @@ class Extractor1D(Extractor): if array.GetNumberOfComponents() == 1: table.AddColumn(array) else: - component_array = vtkDoubleArray(); + component_array = vtkDoubleArray() component_array.SetNumberOfComponents(1) component_array.SetNumberOfTuples(array.GetNumberOfTuples()) c_idx = obj.getEnumerationsOfProperty("XComponent").index(obj.XComponent) @@ -233,7 +236,7 @@ class Extractor1D(Extractor): array.SetNumberOfTuples(num) array.SetNumberOfComponents(1) for i in range(num): - array.SetValue(i,i) + array.SetValue(i, i) case "Position": @@ -266,6 +269,7 @@ class Extractor1D(Extractor): return label + class Extractor2D(Extractor1D): ExtractionDimension = "2D" @@ -273,7 +277,6 @@ class Extractor2D(Extractor1D): def __init__(self, obj): super().__init__(obj) - def _get_properties(self): prop = [ _PropHelper( @@ -287,14 +290,15 @@ class Extractor2D(Extractor1D): type="App::PropertyEnumeration", name="YComponent", group="Y Data", - doc=QT_TRANSLATE_NOOP("FEM", "Which part of the Y field vector to use for the Y axis"), + doc=QT_TRANSLATE_NOOP( + "FEM", "Which part of the Y field vector to use for the Y axis" + ), value=[], ), ] return super()._get_properties() + prop - def onChanged(self, obj, prop): super().onChanged(obj, prop) @@ -348,7 +352,7 @@ class Extractor2D(Extractor1D): if array.GetNumberOfComponents() == 1: table.AddColumn(array) else: - component_array = vtkDoubleArray(); + component_array = vtkDoubleArray() component_array.SetNumberOfComponents(1) component_array.SetNumberOfTuples(array.GetNumberOfTuples()) c_idx = obj.getEnumerationsOfProperty("YComponent").index(obj.YComponent) diff --git a/src/Mod/Fem/femobjects/base_fempostvisualizations.py b/src/Mod/Fem/femobjects/base_fempostvisualizations.py index ffaa94ee8f..5c7465d5bc 100644 --- a/src/Mod/Fem/femobjects/base_fempostvisualizations.py +++ b/src/Mod/Fem/femobjects/base_fempostvisualizations.py @@ -38,6 +38,7 @@ from . import base_fempostextractors # helper functions # ################ + def is_visualization_object(obj): if not obj: return False @@ -71,27 +72,23 @@ def is_visualization_extractor_type(obj, vistype): return True - # Base class for all visualizations # It collects all data from its extraction objects into a table. # Note: Never use directly, always subclass! This class does not create a # Visualization variable, hence will not work correctly. class PostVisualization(base_fempythonobject.BaseFemPythonObject): - def __init__(self, obj): super().__init__(obj) obj.addExtension("App::GroupExtensionPython") self._setup_properties(obj) - def _setup_properties(self, obj): pl = obj.PropertiesList for prop in self._get_properties(): if not prop.name in pl: prop.add_to_object(obj) - def _get_properties(self): # override if subclass wants to add additional properties @@ -106,14 +103,12 @@ class PostVisualization(base_fempythonobject.BaseFemPythonObject): ] return prop - def onDocumentRestored(self, obj): # if a new property was added we handle it by setup # Override if subclass needs to handle changed property type self._setup_properties(obj) - def onChanged(self, obj, prop): # Ensure only correct child object types are in the group @@ -123,13 +118,14 @@ class PostVisualization(base_fempythonobject.BaseFemPythonObject): children = obj.Group for child in obj.Group: if not is_visualization_extractor_type(child, self.VisualizationType): - FreeCAD.Console.PrintWarning(f"{child.Label} is not a {self.VisualizationType} extraction object, cannot be added") + FreeCAD.Console.PrintWarning( + f"{child.Label} is not a {self.VisualizationType} extraction object, cannot be added" + ) children.remove(child) if len(obj.Group) != len(children): obj.Group = children - def execute(self, obj): # Collect all extractor child data into our table # Note: Each childs table can have different number of rows. We need @@ -144,7 +140,9 @@ class PostVisualization(base_fempythonobject.BaseFemPythonObject): # to none without recompute, and the visualization was manually # recomputed afterwards if not child.Source and (child.Table.GetNumberOfColumns() > 0): - FreeCAD.Console.PrintWarning(f"{child.Label} has data, but no Source object. Will be ignored") + FreeCAD.Console.PrintWarning( + f"{child.Label} has data, but no Source object. Will be ignored" + ) continue c_table = child.Table @@ -159,18 +157,16 @@ class PostVisualization(base_fempythonobject.BaseFemPythonObject): else: array.SetNumberOfComponents(c_array.GetNumberOfComponents()) array.SetNumberOfTuples(rows) - array.Fill(0) # so that all non-used entries are set to 0 + array.Fill(0) # so that all non-used entries are set to 0 for j in range(c_array.GetNumberOfTuples()): array.SetTuple(j, c_array.GetTuple(j)) array.SetName(f"{child.Source.Name}: {c_array.GetName()}") table.AddColumn(array) - obj.Table = table return False - def getLongestColumnLength(self, obj): # iterate all extractor children and get the column lengths diff --git a/src/Mod/Fem/femobjects/post_extract1D.py b/src/Mod/Fem/femobjects/post_extract1D.py index 8fcec6e2da..f70c6c65c9 100644 --- a/src/Mod/Fem/femobjects/post_extract1D.py +++ b/src/Mod/Fem/femobjects/post_extract1D.py @@ -33,6 +33,7 @@ import FreeCAD from . import base_fempostextractors from . import base_fempythonobject + _PropHelper = base_fempythonobject._PropHelper from vtkmodules.vtkCommonCore import vtkDoubleArray @@ -53,11 +54,14 @@ class PostFieldData1D(base_fempostextractors.Extractor1D): super().__init__(obj) def _get_properties(self): - prop =[ _PropHelper( + prop = [ + _PropHelper( type="App::PropertyBool", name="ExtractFrames", group="Multiframe", - doc=QT_TRANSLATE_NOOP("FEM", "Specify if the field shall be extracted for every available frame"), + doc=QT_TRANSLATE_NOOP( + "FEM", "Specify if the field shall be extracted for every available frame" + ), value=False, ), ] @@ -77,14 +81,16 @@ class PostFieldData1D(base_fempostextractors.Extractor1D): obj.Table = table return - timesteps=[] + timesteps = [] if obj.ExtractFrames: # check if we have timesteps info = obj.Source.getOutputAlgorithm().GetOutputInformation(0) if info.Has(vtkStreamingDemandDrivenPipeline.TIME_STEPS()): timesteps = info.Get(vtkStreamingDemandDrivenPipeline.TIME_STEPS()) else: - FreeCAD.Console.PrintWarning("No frames available in data, ignoring \"ExtractFrames\" property") + FreeCAD.Console.PrintWarning( + 'No frames available in data, ignoring "ExtractFrames" property' + ) if not timesteps: # get the dataset and extract the correct array @@ -124,11 +130,14 @@ class PostIndexOverFrames1D(base_fempostextractors.Extractor1D): super().__init__(obj) def _get_properties(self): - prop =[_PropHelper( + prop = [ + _PropHelper( type="App::PropertyInteger", name="Index", group="X Data", - doc=QT_TRANSLATE_NOOP("FEM", "Specify for which index the data should be extracted"), + doc=QT_TRANSLATE_NOOP( + "FEM", "Specify for which index the data should be extracted" + ), value=0, ), ] @@ -154,7 +163,6 @@ class PostIndexOverFrames1D(base_fempostextractors.Extractor1D): if info.Has(vtkStreamingDemandDrivenPipeline.TIME_STEPS()): timesteps = info.Get(vtkStreamingDemandDrivenPipeline.TIME_STEPS()) - algo = obj.Source.getOutputAlgorithm() frame_array = vtkDoubleArray() idx = obj.Index @@ -168,8 +176,10 @@ class PostIndexOverFrames1D(base_fempostextractors.Extractor1D): array = self._x_array_from_dataset(obj, dataset, copy=False) # safeguard for invalid access - if idx < 0 or array.GetNumberOfTuples()-1 < idx: - raise Exception(f"Invalid index: {idx} is not in range 0 - {array.GetNumberOfTuples()-1}") + if idx < 0 or array.GetNumberOfTuples() - 1 < idx: + raise Exception( + f"Invalid index: {idx} is not in range 0 - {array.GetNumberOfTuples()-1}" + ) if not setup: frame_array.SetNumberOfComponents(array.GetNumberOfComponents()) @@ -183,14 +193,15 @@ class PostIndexOverFrames1D(base_fempostextractors.Extractor1D): array = self._x_array_from_dataset(obj, dataset, copy=False) # safeguard for invalid access - if idx < 0 or array.GetNumberOfTuples()-1 < idx: - raise Exception(f"Invalid index: {idx} is not in range 0 - {array.GetNumberOfTuples()-1}") + if idx < 0 or array.GetNumberOfTuples() - 1 < idx: + raise Exception( + f"Invalid index: {idx} is not in range 0 - {array.GetNumberOfTuples()-1}" + ) frame_array.SetNumberOfComponents(array.GetNumberOfComponents()) frame_array.SetNumberOfTuples(1) frame_array.SetTuple(0, idx, array) - if frame_array.GetNumberOfComponents() > 1: frame_array.SetName(f"{obj.XField} ({obj.XComponent}) @Idx {obj.Index}") else: diff --git a/src/Mod/Fem/femobjects/post_extract2D.py b/src/Mod/Fem/femobjects/post_extract2D.py index 86827d7a7e..64cba2d5c7 100644 --- a/src/Mod/Fem/femobjects/post_extract2D.py +++ b/src/Mod/Fem/femobjects/post_extract2D.py @@ -33,6 +33,7 @@ import FreeCAD from . import base_fempostextractors from . import base_fempythonobject + _PropHelper = base_fempythonobject._PropHelper from vtkmodules.vtkCommonCore import vtkDoubleArray @@ -53,17 +54,19 @@ class PostFieldData2D(base_fempostextractors.Extractor2D): super().__init__(obj) def _get_properties(self): - prop =[ _PropHelper( + prop = [ + _PropHelper( type="App::PropertyBool", name="ExtractFrames", group="Multiframe", - doc=QT_TRANSLATE_NOOP("FEM", "Specify if the field shall be extracted for every available frame"), + doc=QT_TRANSLATE_NOOP( + "FEM", "Specify if the field shall be extracted for every available frame" + ), value=False, ), ] return super()._get_properties() + prop - def execute(self, obj): # on execution we populate the vtk table @@ -85,7 +88,9 @@ class PostFieldData2D(base_fempostextractors.Extractor2D): if info.Has(vtkStreamingDemandDrivenPipeline.TIME_STEPS()): timesteps = info.Get(vtkStreamingDemandDrivenPipeline.TIME_STEPS()) else: - FreeCAD.Console.PrintWarning("No frames available in data, ignoring \"ExtractFrames\" property") + FreeCAD.Console.PrintWarning( + 'No frames available in data, ignoring "ExtractFrames" property' + ) if not timesteps: # get the dataset and extract the correct array @@ -140,11 +145,14 @@ class PostIndexOverFrames2D(base_fempostextractors.Extractor2D): super().__init__(obj) def _get_properties(self): - prop =[_PropHelper( + prop = [ + _PropHelper( type="App::PropertyInteger", name="Index", group="Data", - doc=QT_TRANSLATE_NOOP("FEM", "Specify for which point index the data should be extracted"), + doc=QT_TRANSLATE_NOOP( + "FEM", "Specify for which point index the data should be extracted" + ), value=0, ), ] @@ -178,7 +186,6 @@ class PostIndexOverFrames2D(base_fempostextractors.Extractor2D): if info.Has(vtkStreamingDemandDrivenPipeline.TIME_STEPS()): timesteps = info.Get(vtkStreamingDemandDrivenPipeline.TIME_STEPS()) - algo = obj.Source.getOutputAlgorithm() frame_x_array = vtkDoubleArray() @@ -198,8 +205,10 @@ class PostIndexOverFrames2D(base_fempostextractors.Extractor2D): array = self._y_array_from_dataset(obj, dataset, copy=False) # safeguard for invalid access - if idx < 0 or array.GetNumberOfTuples()-1 < idx: - raise Exception(f"Invalid index: {idx} is not in range 0 - {array.GetNumberOfTuples()-1}") + if idx < 0 or array.GetNumberOfTuples() - 1 < idx: + raise Exception( + f"Invalid index: {idx} is not in range 0 - {array.GetNumberOfTuples()-1}" + ) if not setup: frame_y_array.SetNumberOfComponents(array.GetNumberOfComponents()) @@ -211,21 +220,22 @@ class PostIndexOverFrames2D(base_fempostextractors.Extractor2D): else: frame_x_array.SetNumberOfTuples(1) frame_x_array.SetNumberOfComponents(1) - frame_x_array.SetTuple1(0,0) + frame_x_array.SetTuple1(0, 0) algo.Update() dataset = algo.GetOutputDataObject(0) array = self._y_array_from_dataset(obj, dataset, copy=False) # safeguard for invalid access - if idx < 0 or array.GetNumberOfTuples()-1 < idx: - raise Exception(f"Invalid index: {idx} is not in range 0 - {array.GetNumberOfTuples()-1}") + if idx < 0 or array.GetNumberOfTuples() - 1 < idx: + raise Exception( + f"Invalid index: {idx} is not in range 0 - {array.GetNumberOfTuples()-1}" + ) frame_y_array.SetNumberOfComponents(array.GetNumberOfComponents()) frame_y_array.SetNumberOfTuples(1) frame_y_array.SetTuple(0, idx, array) - frame_x_array.SetName("Frames") if frame_y_array.GetNumberOfComponents() > 1: frame_y_array.SetName(f"{obj.YField} ({obj.YComponent}) @Idx {obj.Index}") diff --git a/src/Mod/Fem/femobjects/post_glyphfilter.py b/src/Mod/Fem/femobjects/post_glyphfilter.py index 51c7d480c6..a783835656 100644 --- a/src/Mod/Fem/femobjects/post_glyphfilter.py +++ b/src/Mod/Fem/femobjects/post_glyphfilter.py @@ -33,6 +33,7 @@ import FreeCAD # check vtk version to potentially find missmatchs from femguiutils.vtk_module_handling import vtk_module_handling + vtk_module_handling() # IMPORTANT: Never import vtk directly. Often vtk is compiled with different QT diff --git a/src/Mod/Fem/femobjects/post_histogram.py b/src/Mod/Fem/femobjects/post_histogram.py index fcbb1ce2e7..fb0b1343cc 100644 --- a/src/Mod/Fem/femobjects/post_histogram.py +++ b/src/Mod/Fem/femobjects/post_histogram.py @@ -31,6 +31,7 @@ __url__ = "https://www.freecad.org" # check vtk version to potentially find missmatchs from femguiutils.vtk_module_handling import vtk_module_handling + vtk_module_handling() from . import base_fempostextractors @@ -40,31 +41,35 @@ from . import post_extract1D from femguiutils import post_visualization # register visualization and extractors -post_visualization.register_visualization("Histogram", - ":/icons/FEM_PostHistogram.svg", - "ObjectsFem", - "makePostHistogram") +post_visualization.register_visualization( + "Histogram", ":/icons/FEM_PostHistogram.svg", "ObjectsFem", "makePostHistogram" +) -post_visualization.register_extractor("Histogram", - "HistogramFieldData", - ":/icons/FEM_PostField.svg", - "1D", - "Field", - "ObjectsFem", - "makePostHistogramFieldData") +post_visualization.register_extractor( + "Histogram", + "HistogramFieldData", + ":/icons/FEM_PostField.svg", + "1D", + "Field", + "ObjectsFem", + "makePostHistogramFieldData", +) -post_visualization.register_extractor("Histogram", - "HistogramIndexOverFrames", - ":/icons/FEM_PostIndex.svg", - "1D", - "Index", - "ObjectsFem", - "makePostHistogramIndexOverFrames") +post_visualization.register_extractor( + "Histogram", + "HistogramIndexOverFrames", + ":/icons/FEM_PostIndex.svg", + "1D", + "Index", + "ObjectsFem", + "makePostHistogramIndexOverFrames", +) # Implementation # ############## + def is_histogram_extractor(obj): if not base_fempostextractors.is_extractor_object(obj): @@ -80,6 +85,7 @@ class PostHistogramFieldData(post_extract1D.PostFieldData1D): """ A 1D Field extraction for histograms. """ + VisualizationType = "Histogram" @@ -87,6 +93,7 @@ class PostHistogramIndexOverFrames(post_extract1D.PostIndexOverFrames1D): """ A 1D index extraction for histogram. """ + VisualizationType = "Histogram" @@ -94,13 +101,5 @@ class PostHistogram(base_fempostvisualizations.PostVisualization): """ A post processing plot for showing extracted data as histograms """ + VisualizationType = "Histogram" - - - - - - - - - diff --git a/src/Mod/Fem/femobjects/post_lineplot.py b/src/Mod/Fem/femobjects/post_lineplot.py index 8d4b725128..3216400415 100644 --- a/src/Mod/Fem/femobjects/post_lineplot.py +++ b/src/Mod/Fem/femobjects/post_lineplot.py @@ -31,6 +31,7 @@ __url__ = "https://www.freecad.org" # check vtk version to potentially find missmatchs from femguiutils.vtk_module_handling import vtk_module_handling + vtk_module_handling() from . import base_fempostextractors @@ -40,31 +41,35 @@ from . import post_extract2D from femguiutils import post_visualization # register visualization and extractors -post_visualization.register_visualization("Lineplot", - ":/icons/FEM_PostLineplot.svg", - "ObjectsFem", - "makePostLineplot") +post_visualization.register_visualization( + "Lineplot", ":/icons/FEM_PostLineplot.svg", "ObjectsFem", "makePostLineplot" +) -post_visualization.register_extractor("Lineplot", - "LineplotFieldData", - ":/icons/FEM_PostField.svg", - "2D", - "Field", - "ObjectsFem", - "makePostLineplotFieldData") +post_visualization.register_extractor( + "Lineplot", + "LineplotFieldData", + ":/icons/FEM_PostField.svg", + "2D", + "Field", + "ObjectsFem", + "makePostLineplotFieldData", +) -post_visualization.register_extractor("Lineplot", - "LineplotIndexOverFrames", - ":/icons/FEM_PostIndex.svg", - "2D", - "Index", - "ObjectsFem", - "makePostLineplotIndexOverFrames") +post_visualization.register_extractor( + "Lineplot", + "LineplotIndexOverFrames", + ":/icons/FEM_PostIndex.svg", + "2D", + "Index", + "ObjectsFem", + "makePostLineplotIndexOverFrames", +) # Implementation # ############## + def is_lineplot_extractor(obj): if not base_fempostextractors.is_extractor_object(obj): @@ -80,6 +85,7 @@ class PostLineplotFieldData(post_extract2D.PostFieldData2D): """ A 2D Field extraction for lineplot. """ + VisualizationType = "Lineplot" @@ -87,15 +93,13 @@ class PostLineplotIndexOverFrames(post_extract2D.PostIndexOverFrames2D): """ A 2D index extraction for lineplot. """ - VisualizationType = "Lineplot" + VisualizationType = "Lineplot" class PostLineplot(base_fempostvisualizations.PostVisualization): """ A post processing plot for showing extracted data as line plots """ + VisualizationType = "Lineplot" - - - diff --git a/src/Mod/Fem/femobjects/post_table.py b/src/Mod/Fem/femobjects/post_table.py index 0a64dc733d..a12398ab9e 100644 --- a/src/Mod/Fem/femobjects/post_table.py +++ b/src/Mod/Fem/femobjects/post_table.py @@ -31,6 +31,7 @@ __url__ = "https://www.freecad.org" # check vtk version to potentially find missmatchs from femguiutils.vtk_module_handling import vtk_module_handling + vtk_module_handling() from . import base_fempostextractors @@ -40,31 +41,35 @@ from . import post_extract1D from femguiutils import post_visualization # register visualization and extractors -post_visualization.register_visualization("Table", - ":/icons/FEM_PostSpreadsheet.svg", - "ObjectsFem", - "makePostTable") +post_visualization.register_visualization( + "Table", ":/icons/FEM_PostSpreadsheet.svg", "ObjectsFem", "makePostTable" +) -post_visualization.register_extractor("Table", - "TableFieldData", - ":/icons/FEM_PostField.svg", - "1D", - "Field", - "ObjectsFem", - "makePostTableFieldData") +post_visualization.register_extractor( + "Table", + "TableFieldData", + ":/icons/FEM_PostField.svg", + "1D", + "Field", + "ObjectsFem", + "makePostTableFieldData", +) -post_visualization.register_extractor("Table", - "TableIndexOverFrames", - ":/icons/FEM_PostIndex.svg", - "1D", - "Index", - "ObjectsFem", - "makePostTableIndexOverFrames") +post_visualization.register_extractor( + "Table", + "TableIndexOverFrames", + ":/icons/FEM_PostIndex.svg", + "1D", + "Index", + "ObjectsFem", + "makePostTableIndexOverFrames", +) # Implementation # ############## + def is_table_extractor(obj): if not base_fempostextractors.is_extractor_object(obj): @@ -80,6 +85,7 @@ class PostTableFieldData(post_extract1D.PostFieldData1D): """ A 1D Field extraction for tables. """ + VisualizationType = "Table" @@ -87,6 +93,7 @@ class PostTableIndexOverFrames(post_extract1D.PostIndexOverFrames1D): """ A 1D index extraction for table. """ + VisualizationType = "Table" @@ -94,6 +101,5 @@ class PostTable(base_fempostvisualizations.PostVisualization): """ A post processing plot for showing extracted data as tables """ + VisualizationType = "Table" - - diff --git a/src/Mod/Fem/femtaskpanels/base_fempostpanel.py b/src/Mod/Fem/femtaskpanels/base_fempostpanel.py index 5efa1aa12a..81fa9107eb 100644 --- a/src/Mod/Fem/femtaskpanels/base_fempostpanel.py +++ b/src/Mod/Fem/femtaskpanels/base_fempostpanel.py @@ -53,7 +53,9 @@ class _BasePostTaskPanel(base_femtaskpanel._BaseTaskPanel): # ########################## def getStandardButtons(self): - return QtGui.QDialogButtonBox.Apply | QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel + return ( + QtGui.QDialogButtonBox.Apply | QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel + ) def clicked(self, button): # apply button hit? @@ -63,8 +65,9 @@ class _BasePostTaskPanel(base_femtaskpanel._BaseTaskPanel): def open(self): # open a new transaction if non is open if not FreeCAD.getActiveTransaction(): - FreeCAD.ActiveDocument.openTransaction(translate("FEM", "Edit {}").format(self.obj.Label)) - + FreeCAD.ActiveDocument.openTransaction( + translate("FEM", "Edit {}").format(self.obj.Label) + ) # Helper functions # ################ @@ -83,6 +86,3 @@ class _BasePostTaskPanel(base_femtaskpanel._BaseTaskPanel): cbox.setCurrentText(getattr(obj, prop)) cbox.blockSignals(False) - - - diff --git a/src/Mod/Fem/femtaskpanels/task_post_extractor.py b/src/Mod/Fem/femtaskpanels/task_post_extractor.py index 56fdd998b5..9c54352f10 100644 --- a/src/Mod/Fem/femtaskpanels/task_post_extractor.py +++ b/src/Mod/Fem/femtaskpanels/task_post_extractor.py @@ -52,6 +52,3 @@ class _ExtractorTaskPanel(base_fempostpanel._BasePostTaskPanel): view.setWindowIcon(obj.ViewObject.Icon) self.form = [app, view] - - - diff --git a/src/Mod/Fem/femtaskpanels/task_post_glyphfilter.py b/src/Mod/Fem/femtaskpanels/task_post_glyphfilter.py index a9a13139e9..570ca63b66 100644 --- a/src/Mod/Fem/femtaskpanels/task_post_glyphfilter.py +++ b/src/Mod/Fem/femtaskpanels/task_post_glyphfilter.py @@ -56,7 +56,6 @@ class _TaskPanel(base_fempostpanel._BasePostTaskPanel): # form made from param and selection widget self.form = [self.widget, vobj.createDisplayTaskWidget()] - # Setup functions # ############### diff --git a/src/Mod/Fem/femtaskpanels/task_post_histogram.py b/src/Mod/Fem/femtaskpanels/task_post_histogram.py index 496fc1792a..df70e2f18d 100644 --- a/src/Mod/Fem/femtaskpanels/task_post_histogram.py +++ b/src/Mod/Fem/femtaskpanels/task_post_histogram.py @@ -40,6 +40,7 @@ from femguiutils import vtk_table_view translate = FreeCAD.Qt.translate + class _TaskPanel(base_fempostpanel._BasePostTaskPanel): """ The TaskPanel for editing properties of glyph filter @@ -68,7 +69,6 @@ class _TaskPanel(base_fempostpanel._BasePostTaskPanel): self.data_widget.setWindowTitle(translate("FEM", "Histogram data")) self.data_widget.setWindowIcon(FreeCADGui.getIcon(":/icons/FEM_PostHistogram.svg")) - # histogram parameter widget self.view_widget = FreeCADGui.PySideUic.loadUi( FreeCAD.getHomePath() + "Mod/Fem/Resources/ui/TaskPostHistogram.ui" @@ -81,7 +81,6 @@ class _TaskPanel(base_fempostpanel._BasePostTaskPanel): # form made from param and selection widget self.form = [self.data_widget, self.view_widget] - # Setup functions # ############### @@ -121,12 +120,13 @@ class _TaskPanel(base_fempostpanel._BasePostTaskPanel): self.view_widget.BarWidth.valueChanged.connect(self.barWidthChanged) self.view_widget.HatchWidth.valueChanged.connect(self.hatchWidthChanged) - QtCore.Slot() + def showPlot(self): self.obj.ViewObject.Proxy.show_visualization() QtCore.Slot() + def showTable(self): # TODO: make data model update when object is recomputed @@ -137,49 +137,58 @@ class _TaskPanel(base_fempostpanel._BasePostTaskPanel): widget = vtk_table_view.VtkTableView(data_model) layout = QtGui.QVBoxLayout() layout.addWidget(widget) - layout.setContentsMargins(0,0,0,0) + layout.setContentsMargins(0, 0, 0, 0) dialog.setLayout(layout) dialog.resize(1500, 900) dialog.show() - QtCore.Slot(int) + def binsChanged(self, bins): self.obj.ViewObject.Bins = bins QtCore.Slot(int) + def typeChanged(self, idx): self.obj.ViewObject.Type = idx QtCore.Slot(bool) + def comulativeChanged(self, state): self.obj.ViewObject.Cumulative = state QtCore.Slot() + def titleChanged(self): self.obj.ViewObject.Title = self.view_widget.Title.text() QtCore.Slot() + def xLabelChanged(self): self.obj.ViewObject.XLabel = self.view_widget.XLabel.text() QtCore.Slot() + def yLabelChanged(self): self.obj.ViewObject.YLabel = self.view_widget.YLabel.text() QtCore.Slot(int) + def legendPosChanged(self, idx): self.obj.ViewObject.LegendLocation = idx QtCore.Slot(bool) + def legendShowChanged(self, state): self.obj.ViewObject.Legend = state QtCore.Slot(float) + def barWidthChanged(self, value): self.obj.ViewObject.BarWidth = value QtCore.Slot(float) + def hatchWidthChanged(self, value): self.obj.ViewObject.HatchLineWidth = value diff --git a/src/Mod/Fem/femtaskpanels/task_post_lineplot.py b/src/Mod/Fem/femtaskpanels/task_post_lineplot.py index 474d84b80b..f5598e1874 100644 --- a/src/Mod/Fem/femtaskpanels/task_post_lineplot.py +++ b/src/Mod/Fem/femtaskpanels/task_post_lineplot.py @@ -40,6 +40,7 @@ from femguiutils import vtk_table_view translate = FreeCAD.Qt.translate + class _TaskPanel(base_fempostpanel._BasePostTaskPanel): """ The TaskPanel for editing properties of glyph filter @@ -68,7 +69,6 @@ class _TaskPanel(base_fempostpanel._BasePostTaskPanel): self.data_widget.setWindowTitle(translate("FEM", "Lineplot data")) self.data_widget.setWindowIcon(FreeCADGui.getIcon(":/icons/FEM_PostLineplot.svg")) - # lineplot parameter widget self.view_widget = FreeCADGui.PySideUic.loadUi( FreeCAD.getHomePath() + "Mod/Fem/Resources/ui/TaskPostLineplot.ui" @@ -81,7 +81,6 @@ class _TaskPanel(base_fempostpanel._BasePostTaskPanel): # form made from param and selection widget self.form = [self.data_widget, self.view_widget] - # Setup functions # ############### @@ -104,7 +103,6 @@ class _TaskPanel(base_fempostpanel._BasePostTaskPanel): self.view_widget.LegendShow.setChecked(viewObj.Legend) self._enumPropertyToCombobox(viewObj, "LegendLocation", self.view_widget.LegendPos) - # connect callbacks self.view_widget.Scale.activated.connect(self.scaleChanged) self.view_widget.Grid.toggled.connect(self.gridChanged) @@ -116,12 +114,13 @@ class _TaskPanel(base_fempostpanel._BasePostTaskPanel): self.view_widget.LegendShow.toggled.connect(self.legendShowChanged) self.view_widget.LegendPos.activated.connect(self.legendPosChanged) - QtCore.Slot() + def showPlot(self): self.obj.ViewObject.Proxy.show_visualization() QtCore.Slot() + def showTable(self): # TODO: make data model update when object is recomputed @@ -132,37 +131,43 @@ class _TaskPanel(base_fempostpanel._BasePostTaskPanel): widget = vtk_table_view.VtkTableView(data_model) layout = QtGui.QVBoxLayout() layout.addWidget(widget) - layout.setContentsMargins(0,0,0,0) + layout.setContentsMargins(0, 0, 0, 0) dialog.setLayout(layout) dialog.resize(1500, 900) dialog.show() - QtCore.Slot(int) + def scaleChanged(self, idx): self.obj.ViewObject.Scale = idx QtCore.Slot(bool) + def gridChanged(self, state): self.obj.ViewObject.Grid = state QtCore.Slot() + def titleChanged(self): self.obj.ViewObject.Title = self.view_widget.Title.text() QtCore.Slot() + def xLabelChanged(self): self.obj.ViewObject.XLabel = self.view_widget.XLabel.text() QtCore.Slot() + def yLabelChanged(self): self.obj.ViewObject.YLabel = self.view_widget.YLabel.text() QtCore.Slot(int) + def legendPosChanged(self, idx): self.obj.ViewObject.LegendLocation = idx QtCore.Slot(bool) + def legendShowChanged(self, state): self.obj.ViewObject.Legend = state diff --git a/src/Mod/Fem/femtaskpanels/task_post_table.py b/src/Mod/Fem/femtaskpanels/task_post_table.py index 1eda150016..98fd1686d6 100644 --- a/src/Mod/Fem/femtaskpanels/task_post_table.py +++ b/src/Mod/Fem/femtaskpanels/task_post_table.py @@ -39,6 +39,7 @@ from femguiutils import extract_link_view as elv translate = FreeCAD.Qt.translate + class _TaskPanel(base_fempostpanel._BasePostTaskPanel): """ The TaskPanel for editing properties of glyph filter @@ -68,7 +69,6 @@ class _TaskPanel(base_fempostpanel._BasePostTaskPanel): # form made from param and selection widget self.form = [self.data_widget] - # Setup functions # ############### @@ -77,8 +77,6 @@ class _TaskPanel(base_fempostpanel._BasePostTaskPanel): # connect data widget self.data_widget.show_table.clicked.connect(self.showTable) - @QtCore.Slot() def showTable(self): self.obj.ViewObject.Proxy.show_visualization() - diff --git a/src/Mod/Fem/femviewprovider/view_base_femobject.py b/src/Mod/Fem/femviewprovider/view_base_femobject.py index a86c1288a2..10ba8e2fd0 100644 --- a/src/Mod/Fem/femviewprovider/view_base_femobject.py +++ b/src/Mod/Fem/femviewprovider/view_base_femobject.py @@ -40,6 +40,7 @@ from femobjects.base_fempythonobject import _PropHelper False if FemGui.__name__ else True # flake8, dummy FemGui usage + class _GuiPropHelper(_PropHelper): """ Helper class to manage property data inside proxy objects. diff --git a/src/Mod/Fem/femviewprovider/view_base_fempostextractors.py b/src/Mod/Fem/femviewprovider/view_base_fempostextractors.py index bb4aca0934..b2df81ef0d 100644 --- a/src/Mod/Fem/femviewprovider/view_base_fempostextractors.py +++ b/src/Mod/Fem/femviewprovider/view_base_fempostextractors.py @@ -1,4 +1,3 @@ - # *************************************************************************** # * Copyright (c) 2025 Stefan Tröger * # * * @@ -37,6 +36,7 @@ from PySide import QtGui from femtaskpanels import task_post_extractor + class VPPostExtractor: """ A View Provider for extraction of data @@ -74,18 +74,18 @@ class VPPostExtractor: if not group: return - if (hasattr(group.ViewObject, "Proxy") and - hasattr(group.ViewObject.Proxy, "childViewPropertyChanged")): + if hasattr(group.ViewObject, "Proxy") and hasattr( + group.ViewObject.Proxy, "childViewPropertyChanged" + ): group.ViewObject.Proxy.childViewPropertyChanged(vobj, prop) - def setEdit(self, vobj, mode): # build up the task panel taskd = task_post_extractor._ExtractorTaskPanel(vobj.Object) - #show it + # show it FreeCADGui.Control.showDialog(taskd) return True @@ -112,7 +112,6 @@ class VPPostExtractor: def loads(self, state): return None - # To be implemented by subclasses: # ################################ diff --git a/src/Mod/Fem/femviewprovider/view_base_fempostvisualization.py b/src/Mod/Fem/femviewprovider/view_base_fempostvisualization.py index 9b357a4d0c..3abf56b29a 100644 --- a/src/Mod/Fem/femviewprovider/view_base_fempostvisualization.py +++ b/src/Mod/Fem/femviewprovider/view_base_fempostvisualization.py @@ -32,6 +32,7 @@ __url__ = "https://www.freecad.org" import FreeCAD import FreeCADGui + class VPPostVisualization: """ A View Provider for visualization objects @@ -42,23 +43,19 @@ class VPPostVisualization: self._setup_properties(vobj) vobj.addExtension("Gui::ViewProviderGroupExtensionPython") - def _setup_properties(self, vobj): pl = vobj.PropertiesList for prop in self._get_properties(): if not prop.name in pl: prop.add_to_object(vobj) - def _get_properties(self): return [] - def attach(self, vobj): self.Object = vobj.Object self.ViewObject = vobj - def isShow(self): # Mark ourself as visible in the tree return True @@ -66,7 +63,7 @@ class VPPostVisualization: def getDisplayModes(self, obj): return ["Dialog"] - def doubleClicked(self,vobj): + def doubleClicked(self, vobj): guidoc = FreeCADGui.getDocument(vobj.Object.Document) @@ -107,7 +104,6 @@ class VPPostVisualization: def loads(self, state): return None - # To be implemented by subclasses: # ################################ diff --git a/src/Mod/Fem/femviewprovider/view_post_histogram.py b/src/Mod/Fem/femviewprovider/view_post_histogram.py index 337ad62f86..f6323cafc6 100644 --- a/src/Mod/Fem/femviewprovider/view_post_histogram.py +++ b/src/Mod/Fem/femviewprovider/view_post_histogram.py @@ -48,6 +48,7 @@ from . import view_base_fempostvisualization from femtaskpanels import task_post_histogram from . import view_base_femobject + _GuiPropHelper = view_base_femobject._GuiPropHelper @@ -92,12 +93,11 @@ class EditViewWidget(QtGui.QWidget): self.widget.HatchDensity.setMaximumHeight(self.widget.Hatch.sizeHint().height()) self.widget.LineWidth.setMaximumHeight(self.widget.LineStyle.sizeHint().height()) - def _setup_color_button(self, button, fcColor, callback): - barColor = QtGui.QColor(*[v*255 for v in fcColor]) + barColor = QtGui.QColor(*[v * 255 for v in fcColor]) icon_size = button.iconSize() - icon_size.setWidth(icon_size.width()*2) + icon_size.setWidth(icon_size.width() * 2) button.setIconSize(icon_size) pixmap = QtGui.QPixmap(icon_size) pixmap.fill(barColor) @@ -114,7 +114,6 @@ class EditViewWidget(QtGui.QWidget): button.addAction(action) button.setPopupMode(QtGui.QToolButton.InstantPopup) - @QtCore.Slot(QtGui.QColor) def lineColorChanged(self, color): @@ -199,6 +198,7 @@ class EditFieldAppWidget(QtGui.QWidget): self._object.ExtractFrames = extract self._post_dialog._recompute() + class EditIndexAppWidget(QtGui.QWidget): def __init__(self, obj, post_dialog): @@ -279,7 +279,7 @@ class VPPostHistogramFieldData(view_base_fempostextractors.VPPostExtractor): name="Hatch", group="HistogramBar", doc=QT_TRANSLATE_NOOP("FEM", "The hatch pattern drawn in the bar"), - value=['None', '/', '\\', '|', '-', '+', 'x', 'o', 'O', '.', '*'], + value=["None", "/", "\\", "|", "-", "+", "x", "o", "O", ".", "*"], ), _GuiPropHelper( type="App::PropertyIntegerConstraint", @@ -293,13 +293,15 @@ class VPPostHistogramFieldData(view_base_fempostextractors.VPPostExtractor): name="LineColor", group="HistogramLine", doc=QT_TRANSLATE_NOOP("FEM", "The color the data bin area is drawn with"), - value=(0, 0, 0, 1), # black + value=(0, 0, 0, 1), # black ), _GuiPropHelper( type="App::PropertyFloatConstraint", name="LineWidth", group="HistogramLine", - doc=QT_TRANSLATE_NOOP("FEM", "The width of the bar, between 0 and 1 (1 being without gaps)"), + doc=QT_TRANSLATE_NOOP( + "FEM", "The width of the bar, between 0 and 1 (1 being without gaps)" + ), value=(1, 0, 99, 0.1), ), _GuiPropHelper( @@ -307,7 +309,7 @@ class VPPostHistogramFieldData(view_base_fempostextractors.VPPostExtractor): name="LineStyle", group="HistogramLine", doc=QT_TRANSLATE_NOOP("FEM", "The style the line is drawn in"), - value=['None', '-', '--', '-.', ':'], + value=["None", "-", "--", "-.", ":"], ), ] return super()._get_properties() + prop @@ -327,13 +329,13 @@ class VPPostHistogramFieldData(view_base_fempostextractors.VPPostExtractor): def get_preview(self): - fig = mpl.pyplot.figure(figsize=(0.4,0.2), dpi=500) - ax = mpl.pyplot.Axes(fig, [0., 0., 2, 1]) + fig = mpl.pyplot.figure(figsize=(0.4, 0.2), dpi=500) + ax = mpl.pyplot.Axes(fig, [0.0, 0.0, 2, 1]) ax.set_axis_off() fig.add_axes(ax) kwargs = self.get_kw_args() - patch = mpl.patches.Rectangle(xy=(0,0), width=2, height=1, **kwargs) + patch = mpl.patches.Rectangle(xy=(0, 0), width=2, height=1, **kwargs) ax.add_patch(patch) data = io.BytesIO() @@ -355,7 +357,7 @@ class VPPostHistogramFieldData(view_base_fempostextractors.VPPostExtractor): kwargs["linestyle"] = self.ViewObject.LineStyle kwargs["linewidth"] = self.ViewObject.LineWidth if self.ViewObject.Hatch != "None": - kwargs["hatch"] = self.ViewObject.Hatch*self.ViewObject.HatchDensity + kwargs["hatch"] = self.ViewObject.Hatch * self.ViewObject.HatchDensity return kwargs @@ -386,7 +388,6 @@ class VPPostHistogram(view_base_fempostvisualization.VPPostVisualization): def __init__(self, vobj): super().__init__(vobj) - def _get_properties(self): prop = [ @@ -394,7 +395,9 @@ class VPPostHistogram(view_base_fempostvisualization.VPPostVisualization): type="App::PropertyBool", name="Cumulative", group="Histogram", - doc=QT_TRANSLATE_NOOP("FEM", "If be the bars shoud show the cumulative sum left to rigth"), + doc=QT_TRANSLATE_NOOP( + "FEM", "If be the bars shoud show the cumulative sum left to rigth" + ), value=False, ), _GuiPropHelper( @@ -402,13 +405,15 @@ class VPPostHistogram(view_base_fempostvisualization.VPPostVisualization): name="Type", group="Histogram", doc=QT_TRANSLATE_NOOP("FEM", "The type of histogram plotted"), - value=["bar","barstacked", "step", "stepfilled"], + value=["bar", "barstacked", "step", "stepfilled"], ), _GuiPropHelper( type="App::PropertyFloatConstraint", name="BarWidth", group="Histogram", - doc=QT_TRANSLATE_NOOP("FEM", "The width of the bar, between 0 and 1 (1 being without gaps)"), + doc=QT_TRANSLATE_NOOP( + "FEM", "The width of the bar, between 0 and 1 (1 being without gaps)" + ), value=(0.9, 0, 1, 0.05), ), _GuiPropHelper( @@ -458,29 +463,36 @@ class VPPostHistogram(view_base_fempostvisualization.VPPostVisualization): name="LegendLocation", group="Plot", doc=QT_TRANSLATE_NOOP("FEM", "Determines if the legend is plotted"), - value=['best','upper right','upper left','lower left','lower right','right', - 'center left','center right','lower center','upper center','center'], + value=[ + "best", + "upper right", + "upper left", + "lower left", + "lower right", + "right", + "center left", + "center right", + "lower center", + "upper center", + "center", + ], ), - ] return prop - def getIcon(self): return ":/icons/FEM_PostHistogram.svg" - def setEdit(self, vobj, mode): # build up the task panel taskd = task_post_histogram._TaskPanel(vobj) - #show it + # show it FreeCADGui.Control.showDialog(taskd) return True - def show_visualization(self): if not hasattr(self, "_plot") or not self._plot: @@ -489,12 +501,11 @@ class VPPostHistogram(view_base_fempostvisualization.VPPostVisualization): self._plot.setWindowTitle(self.Object.Label) self._plot.setParent(main) self._plot.setWindowFlags(QtGui.Qt.Dialog) - self._plot.resize(main.size().height()/2, main.size().height()/3) # keep it square + self._plot.resize(main.size().height() / 2, main.size().height() / 3) # keep it square self.update_visualization() self._plot.show() - def get_kw_args(self, obj): view = obj.ViewObject if not view or not hasattr(view, "Proxy"): @@ -503,7 +514,6 @@ class VPPostHistogram(view_base_fempostvisualization.VPPostVisualization): return {} return view.Proxy.get_kw_args() - def update_visualization(self): if not hasattr(self, "_plot") or not self._plot: @@ -523,7 +533,7 @@ class VPPostHistogram(view_base_fempostvisualization.VPPostVisualization): kwargs = self.get_kw_args(child) # iterate over the table and plot all - color_factor = np.linspace(1,0.5,table.GetNumberOfColumns()) + color_factor = np.linspace(1, 0.5, table.GetNumberOfColumns()) legend_multiframe = table.GetNumberOfColumns() > 1 for i in range(table.GetNumberOfColumns()): @@ -532,7 +542,7 @@ class VPPostHistogram(view_base_fempostvisualization.VPPostVisualization): for key in kwargs: if "color" in key: - value = np.array(kwargs[key])*color_factor[i] + value = np.array(kwargs[key]) * color_factor[i] args[key] = mpl.colors.to_hex(value) full_args.append(args) @@ -581,7 +591,7 @@ class VPPostHistogram(view_base_fempostvisualization.VPPostVisualization): self._plot.axes.set_ylabel(self.ViewObject.YLabel) if self.ViewObject.Legend and labels: - self._plot.axes.legend(loc = self.ViewObject.LegendLocation) + self._plot.axes.legend(loc=self.ViewObject.LegendLocation) self._plot.update() diff --git a/src/Mod/Fem/femviewprovider/view_post_lineplot.py b/src/Mod/Fem/femviewprovider/view_post_lineplot.py index 3066e009b7..f73d0aad7b 100644 --- a/src/Mod/Fem/femviewprovider/view_post_lineplot.py +++ b/src/Mod/Fem/femviewprovider/view_post_lineplot.py @@ -48,8 +48,10 @@ from . import view_base_fempostvisualization from femtaskpanels import task_post_lineplot from . import view_base_femobject + _GuiPropHelper = view_base_femobject._GuiPropHelper + class EditViewWidget(QtGui.QWidget): def __init__(self, obj, post_dialog): @@ -91,9 +93,9 @@ class EditViewWidget(QtGui.QWidget): def _setup_color_button(self, button, fcColor, callback): - barColor = QtGui.QColor(*[v*255 for v in fcColor]) + barColor = QtGui.QColor(*[v * 255 for v in fcColor]) icon_size = button.iconSize() - icon_size.setWidth(icon_size.width()*2) + icon_size.setWidth(icon_size.width() * 2) button.setIconSize(icon_size) pixmap = QtGui.QPixmap(icon_size) pixmap.fill(barColor) @@ -110,7 +112,6 @@ class EditViewWidget(QtGui.QWidget): button.addAction(action) button.setPopupMode(QtGui.QToolButton.InstantPopup) - @QtCore.Slot(QtGui.QColor) def colorChanged(self, color): @@ -163,9 +164,13 @@ class EditFieldAppWidget(QtGui.QWidget): # set the other properties self._post_dialog._enumPropertyToCombobox(self._object, "XField", self.widget.XField) - self._post_dialog._enumPropertyToCombobox(self._object, "XComponent", self.widget.XComponent) + self._post_dialog._enumPropertyToCombobox( + self._object, "XComponent", self.widget.XComponent + ) self._post_dialog._enumPropertyToCombobox(self._object, "YField", self.widget.YField) - self._post_dialog._enumPropertyToCombobox(self._object, "YComponent", self.widget.YComponent) + self._post_dialog._enumPropertyToCombobox( + self._object, "YComponent", self.widget.YComponent + ) self.widget.Extract.setChecked(self._object.ExtractFrames) self.widget.XField.activated.connect(self.xFieldChanged) @@ -177,7 +182,9 @@ class EditFieldAppWidget(QtGui.QWidget): @QtCore.Slot(int) def xFieldChanged(self, index): self._object.XField = index - self._post_dialog._enumPropertyToCombobox(self._object, "XComponent", self.widget.XComponent) + self._post_dialog._enumPropertyToCombobox( + self._object, "XComponent", self.widget.XComponent + ) self._post_dialog._recompute() @QtCore.Slot(int) @@ -188,7 +195,9 @@ class EditFieldAppWidget(QtGui.QWidget): @QtCore.Slot(int) def yFieldChanged(self, index): self._object.YField = index - self._post_dialog._enumPropertyToCombobox(self._object, "YComponent", self.widget.YComponent) + self._post_dialog._enumPropertyToCombobox( + self._object, "YComponent", self.widget.YComponent + ) self._post_dialog._recompute() @QtCore.Slot(int) @@ -225,7 +234,9 @@ class EditIndexAppWidget(QtGui.QWidget): self.widget.Index.setValue(self._object.Index) self._post_dialog._enumPropertyToCombobox(self._object, "YField", self.widget.YField) - self._post_dialog._enumPropertyToCombobox(self._object, "YComponent", self.widget.YComponent) + self._post_dialog._enumPropertyToCombobox( + self._object, "YComponent", self.widget.YComponent + ) self.widget.Index.valueChanged.connect(self.indexChanged) self.widget.YField.activated.connect(self.yFieldChanged) @@ -242,7 +253,9 @@ class EditIndexAppWidget(QtGui.QWidget): @QtCore.Slot(int) def yFieldChanged(self, index): self._object.YField = index - self._post_dialog._enumPropertyToCombobox(self._object, "YComponent", self.widget.YComponent) + self._post_dialog._enumPropertyToCombobox( + self._object, "YComponent", self.widget.YComponent + ) self._post_dialog._recompute() @QtCore.Slot(int) @@ -282,7 +295,7 @@ class VPPostLineplotFieldData(view_base_fempostextractors.VPPostExtractor): name="LineStyle", group="Lineplot", doc=QT_TRANSLATE_NOOP("FEM", "The style the line is drawn in"), - value=['-', '--', '-.', ':', 'None'], + value=["-", "--", "-.", ":", "None"], ), _GuiPropHelper( type="App::PropertyFloatConstraint", @@ -296,7 +309,7 @@ class VPPostLineplotFieldData(view_base_fempostextractors.VPPostExtractor): name="MarkerStyle", group="Lineplot", doc=QT_TRANSLATE_NOOP("FEM", "The style the data markers are drawn with"), - value=['None', '*', '+', 's', '.', 'o', 'x'], + value=["None", "*", "+", "s", ".", "o", "x"], ), _GuiPropHelper( type="App::PropertyFloatConstraint", @@ -325,13 +338,13 @@ class VPPostLineplotFieldData(view_base_fempostextractors.VPPostExtractor): # Returns the preview tuple of icon and label: (QPixmap, str) # Note: QPixmap in ratio 2:1 - fig = mpl.pyplot.figure(figsize=(0.2,0.1), dpi=1000) - ax = mpl.pyplot.Axes(fig, [0., 0., 1., 1.]) + fig = mpl.pyplot.figure(figsize=(0.2, 0.1), dpi=1000) + ax = mpl.pyplot.Axes(fig, [0.0, 0.0, 1.0, 1.0]) ax.set_axis_off() fig.add_axes(ax) kwargs = self.get_kw_args() kwargs["markevery"] = [1] - ax.plot([0,0.5,1],[0.5,0.5,0.5], **kwargs) + ax.plot([0, 0.5, 1], [0.5, 0.5, 0.5], **kwargs) data = io.BytesIO() mpl.pyplot.savefig(data, bbox_inches=0, transparent=True) mpl.pyplot.close() @@ -341,7 +354,6 @@ class VPPostLineplotFieldData(view_base_fempostextractors.VPPostExtractor): return (pixmap, self.ViewObject.Legend) - def get_kw_args(self): # builds kw args from the properties kwargs = {} @@ -383,7 +395,6 @@ class VPPostLineplot(view_base_fempostvisualization.VPPostVisualization): def __init__(self, vobj): super().__init__(vobj) - def _get_properties(self): prop = [ @@ -391,7 +402,9 @@ class VPPostLineplot(view_base_fempostvisualization.VPPostVisualization): type="App::PropertyBool", name="Grid", group="Lineplot", - doc=QT_TRANSLATE_NOOP("FEM", "If be the bars shoud show the cumulative sum left to rigth"), + doc=QT_TRANSLATE_NOOP( + "FEM", "If be the bars shoud show the cumulative sum left to rigth" + ), value=True, ), _GuiPropHelper( @@ -399,9 +412,9 @@ class VPPostLineplot(view_base_fempostvisualization.VPPostVisualization): name="Scale", group="Lineplot", doc=QT_TRANSLATE_NOOP("FEM", "The scale the axis are drawn in"), - value=["linear","semi-log x", "semi-log y", "log"], + value=["linear", "semi-log x", "semi-log y", "log"], ), - _GuiPropHelper( + _GuiPropHelper( type="App::PropertyString", name="Title", group="Plot", @@ -434,29 +447,36 @@ class VPPostLineplot(view_base_fempostvisualization.VPPostVisualization): name="LegendLocation", group="Plot", doc=QT_TRANSLATE_NOOP("FEM", "Determines if the legend is plotted"), - value=['best','upper right','upper left','lower left','lower right','right', - 'center left','center right','lower center','upper center','center'], + value=[ + "best", + "upper right", + "upper left", + "lower left", + "lower right", + "right", + "center left", + "center right", + "lower center", + "upper center", + "center", + ], ), - ] return prop - def getIcon(self): return ":/icons/FEM_PostLineplot.svg" - def setEdit(self, vobj, mode): # build up the task panel taskd = task_post_lineplot._TaskPanel(vobj) - #show it + # show it FreeCADGui.Control.showDialog(taskd) return True - def show_visualization(self): if not hasattr(self, "_plot") or not self._plot: @@ -465,12 +485,13 @@ class VPPostLineplot(view_base_fempostvisualization.VPPostVisualization): self._plot.setWindowTitle(self.Object.Label) self._plot.setParent(main) self._plot.setWindowFlags(QtGui.Qt.Dialog) - self._plot.resize(main.size().height()/2, main.size().height()/3) # keep the aspect ratio + self._plot.resize( + main.size().height() / 2, main.size().height() / 3 + ) # keep the aspect ratio self.update_visualization() self._plot.show() - def get_kw_args(self, obj): view = obj.ViewObject if not view or not hasattr(view, "Proxy"): @@ -479,7 +500,6 @@ class VPPostLineplot(view_base_fempostvisualization.VPPostVisualization): return {} return view.Proxy.get_kw_args() - def update_visualization(self): if not hasattr(self, "_plot") or not self._plot: @@ -496,10 +516,10 @@ class VPPostLineplot(view_base_fempostvisualization.VPPostVisualization): kwargs = self.get_kw_args(child) # iterate over the table and plot all (note: column 0 is always X!) - color_factor = np.linspace(1,0.5,int(table.GetNumberOfColumns()/2)) + color_factor = np.linspace(1, 0.5, int(table.GetNumberOfColumns() / 2)) legend_multiframe = table.GetNumberOfColumns() > 2 - for i in range(0,table.GetNumberOfColumns(),2): + for i in range(0, table.GetNumberOfColumns(), 2): plotted = True @@ -507,13 +527,13 @@ class VPPostLineplot(view_base_fempostvisualization.VPPostVisualization): tmp_args = {} for key in kwargs: if "color" in key: - value = np.array(kwargs[key])*color_factor[int(i/2)] + value = np.array(kwargs[key]) * color_factor[int(i / 2)] tmp_args[key] = mpl.colors.to_hex(value) else: tmp_args[key] = kwargs[key] xdata = VTKArray(table.GetColumn(i)) - ydata = VTKArray(table.GetColumn(i+1)) + ydata = VTKArray(table.GetColumn(i + 1)) # ensure points are visible if it is a single datapoint if len(xdata) == 1 and tmp_args["marker"] == "None": @@ -524,13 +544,13 @@ class VPPostLineplot(view_base_fempostvisualization.VPPostVisualization): if not legend_multiframe: label = child.ViewObject.Legend else: - postfix = table.GetColumnName(i+1).split("-")[-1] + postfix = table.GetColumnName(i + 1).split("-")[-1] label = child.ViewObject.Legend + " - " + postfix else: legend_prefix = "" if len(self.Object.Group) > 1: legend_prefix = child.Source.Label + ": " - label = legend_prefix + table.GetColumnName(i+1) + label = legend_prefix + table.GetColumnName(i + 1) match self.ViewObject.Scale: case "log": @@ -550,7 +570,7 @@ class VPPostLineplot(view_base_fempostvisualization.VPPostVisualization): self._plot.axes.set_ylabel(self.ViewObject.YLabel) if self.ViewObject.Legend and plotted: - self._plot.axes.legend(loc = self.ViewObject.LegendLocation) + self._plot.axes.legend(loc=self.ViewObject.LegendLocation) self._plot.axes.grid(self.ViewObject.Grid) self._plot.update() @@ -561,4 +581,3 @@ class VPPostLineplot(view_base_fempostvisualization.VPPostVisualization): i = len(self.Object.Group) cmap = mpl.pyplot.get_cmap("tab10") return cmap(i) - diff --git a/src/Mod/Fem/femviewprovider/view_post_table.py b/src/Mod/Fem/femviewprovider/view_post_table.py index 536a3665e2..3c22d8b999 100644 --- a/src/Mod/Fem/femviewprovider/view_post_table.py +++ b/src/Mod/Fem/femviewprovider/view_post_table.py @@ -41,8 +41,10 @@ from femtaskpanels import task_post_table from femguiutils import vtk_table_view as vtv from . import view_base_femobject + _GuiPropHelper = view_base_femobject._GuiPropHelper + class EditViewWidget(QtGui.QWidget): def __init__(self, obj, post_dialog): @@ -116,6 +118,7 @@ class EditFieldAppWidget(QtGui.QWidget): self._object.ExtractFrames = extract self._post_dialog._recompute() + class EditIndexAppWidget(QtGui.QWidget): def __init__(self, obj, post_dialog): @@ -180,7 +183,9 @@ class VPPostTableFieldData(view_base_fempostextractors.VPPostExtractor): type="App::PropertyString", name="Name", group="Table", - doc=QT_TRANSLATE_NOOP("FEM", "The name used in the table header. Default name is used if empty"), + doc=QT_TRANSLATE_NOOP( + "FEM", "The name used in the table header. Default name is used if empty" + ), value="", ), ] @@ -232,22 +237,19 @@ class VPPostTable(view_base_fempostvisualization.VPPostVisualization): def __init__(self, vobj): super().__init__(vobj) - def getIcon(self): return ":/icons/FEM_PostSpreadsheet.svg" - def setEdit(self, vobj, mode): # build up the task panel taskd = task_post_table._TaskPanel(vobj) - #show it + # show it FreeCADGui.Control.showDialog(taskd) return True - def show_visualization(self): if not hasattr(self, "_tableview") or not self._tableview: @@ -257,13 +259,14 @@ class VPPostTable(view_base_fempostvisualization.VPPostVisualization): self._tableview.setWindowTitle(self.Object.Label) self._tableview.setParent(main) self._tableview.setWindowFlags(QtGui.Qt.Dialog) - self._tableview.resize(main.size().height()/2, main.size().height()/3) # keep the aspect ratio + self._tableview.resize( + main.size().height() / 2, main.size().height() / 3 + ) # keep the aspect ratio self.update_visualization() self._tableview.show() - def update_visualization(self): if not hasattr(self, "_tableModel") or not self._tableModel: @@ -286,5 +289,3 @@ class VPPostTable(view_base_fempostvisualization.VPPostVisualization): header[table.GetColumnName(i)] = new_name self._tableModel.setTable(self.Object.Table, header) - -