diff --git a/src/Mod/Fem/Gui/CMakeLists.txt b/src/Mod/Fem/Gui/CMakeLists.txt
index e1523956ea..7337afd833 100755
--- a/src/Mod/Fem/Gui/CMakeLists.txt
+++ b/src/Mod/Fem/Gui/CMakeLists.txt
@@ -445,11 +445,12 @@ SET(FemGuiPythonUI_SRCS
Resources/ui/TaskPostExtraction.ui
Resources/ui/TaskPostHistogram.ui
Resources/ui/TaskPostLineplot.ui
- Resources/ui/PostExtractionSummaryWidget.ui
Resources/ui/PostHistogramFieldViewEdit.ui
Resources/ui/PostHistogramFieldAppEdit.ui
+ Resources/ui/PostHistogramIndexAppEdit.ui
Resources/ui/PostLineplotFieldViewEdit.ui
Resources/ui/PostLineplotFieldAppEdit.ui
+ Resources/ui/PostLineplotIndexAppEdit.ui
)
ADD_CUSTOM_TARGET(FemPythonUi ALL
diff --git a/src/Mod/Fem/Gui/Resources/ui/PostHistogramFieldViewEdit.ui b/src/Mod/Fem/Gui/Resources/ui/PostHistogramFieldViewEdit.ui
index 744e5a6240..5fe4a7d3dc 100644
--- a/src/Mod/Fem/Gui/Resources/ui/PostHistogramFieldViewEdit.ui
+++ b/src/Mod/Fem/Gui/Resources/ui/PostHistogramFieldViewEdit.ui
@@ -6,8 +6,8 @@
0
0
- 293
- 126
+ 278
+ 110
@@ -68,7 +68,7 @@
-
-
+
0
0
@@ -93,7 +93,7 @@
-
-
+
0
0
@@ -113,10 +113,20 @@
+ -
+
+
+ -
+
+
+ Legend:
+
+
+
-
-
+
-
+
0
0
@@ -127,7 +137,7 @@
-
-
+
0
@@ -139,27 +149,10 @@
- -
-
-
- -
-
-
- Legend:
-
-
-
-
-
- Gui::ColorButton
- QPushButton
-
-
-
Legend
BarColor
diff --git a/src/Mod/Fem/Gui/Resources/ui/PostHistogramIndexAppEdit.ui b/src/Mod/Fem/Gui/Resources/ui/PostHistogramIndexAppEdit.ui
new file mode 100644
index 0000000000..e9dd2a2b3d
--- /dev/null
+++ b/src/Mod/Fem/Gui/Resources/ui/PostHistogramIndexAppEdit.ui
@@ -0,0 +1,81 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 261
+ 110
+
+
+
+ Form
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
-
+
+
+ Field:
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+ Index:
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Mod/Fem/Gui/Resources/ui/PostLineplotFieldAppEdit.ui b/src/Mod/Fem/Gui/Resources/ui/PostLineplotFieldAppEdit.ui
index 00b8ab4f55..b0d1830852 100644
--- a/src/Mod/Fem/Gui/Resources/ui/PostLineplotFieldAppEdit.ui
+++ b/src/Mod/Fem/Gui/Resources/ui/PostLineplotFieldAppEdit.ui
@@ -6,8 +6,8 @@
0
0
- 296
- 186
+ 271
+ 174
diff --git a/src/Mod/Fem/Gui/Resources/ui/PostLineplotFieldViewEdit.ui b/src/Mod/Fem/Gui/Resources/ui/PostLineplotFieldViewEdit.ui
index 720eb96c6e..f197016d12 100644
--- a/src/Mod/Fem/Gui/Resources/ui/PostLineplotFieldViewEdit.ui
+++ b/src/Mod/Fem/Gui/Resources/ui/PostLineplotFieldViewEdit.ui
@@ -6,8 +6,8 @@
0
0
- 335
- 124
+ 274
+ 114
@@ -28,25 +28,6 @@
-
-
-
-
-
-
- 0
- 0
-
-
-
- Width of all lines (outline and hatch)
-
-
- 99.000000000000000
-
-
- 0.100000000000000
-
-
-
-
@@ -57,7 +38,7 @@
-
-
+
0
0
@@ -107,18 +88,8 @@
- -
-
-
- 99.000000000000000
-
-
- 0.100000000000000
-
-
-
-
-
+
0
@@ -130,24 +101,50 @@
+ -
+
+
+
+ 0
+ 0
+
+
+
+ 99.000000000000000
+
+
+ 0.100000000000000
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Width of all lines (outline and hatch)
+
+
+ 99.000000000000000
+
+
+ 0.100000000000000
+
+
+
-
-
- Gui::ColorButton
- QPushButton
-
-
-
Legend
Color
LineStyle
- LineWidth
MarkerStyle
- MarkerSize
diff --git a/src/Mod/Fem/Gui/Resources/ui/PostLineplotIndexAppEdit.ui b/src/Mod/Fem/Gui/Resources/ui/PostLineplotIndexAppEdit.ui
new file mode 100644
index 0000000000..ba4ab0ead3
--- /dev/null
+++ b/src/Mod/Fem/Gui/Resources/ui/PostLineplotIndexAppEdit.ui
@@ -0,0 +1,85 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 310
+ 108
+
+
+
+ Form
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Y Field:
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+ Index:
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ 99999999
+
+
+
+
+
+
+ Index
+ YField
+ YComponent
+
+
+
+
diff --git a/src/Mod/Fem/Gui/TaskPostBoxes.cpp b/src/Mod/Fem/Gui/TaskPostBoxes.cpp
index d880f73d33..1a2c244943 100644
--- a/src/Mod/Fem/Gui/TaskPostBoxes.cpp
+++ b/src/Mod/Fem/Gui/TaskPostBoxes.cpp
@@ -408,6 +408,21 @@ void TaskDlgPost::modifyStandardButtons(QDialogButtonBox* box)
}
}
+void TaskDlgPost::processCollapsedWidgets() {
+
+ for (auto& widget : Content) {
+ if(auto task_box = dynamic_cast(widget)) {
+ // get the task widget and check if it is a post widget
+ auto widget = task_box->groupLayout()->itemAt(0)->widget();
+ if(auto post_widget = dynamic_cast(widget)) {
+ if(post_widget->initiallyCollapsed()) {
+ post_widget->setGeometry(QRect(QPoint(0,0), post_widget->sizeHint()));
+ task_box->hideGroupBox();
+ }
+ }
+ }
+ }
+}
// ***************************************************************************
// box to set the coloring
@@ -571,6 +586,10 @@ void TaskPostFrames::applyPythonCode()
// we apply the views widgets python code
}
+bool TaskPostFrames::initiallyCollapsed() {
+
+ return (ui->FrameTable->rowCount() == 0);
+}
// ***************************************************************************
// in the following, the different filters sorted alphabetically
diff --git a/src/Mod/Fem/Gui/TaskPostBoxes.h b/src/Mod/Fem/Gui/TaskPostBoxes.h
index 9b24eb314f..3c60fe8ddf 100644
--- a/src/Mod/Fem/Gui/TaskPostBoxes.h
+++ b/src/Mod/Fem/Gui/TaskPostBoxes.h
@@ -156,6 +156,11 @@ public:
// executed when the apply button is pressed in the task dialog
virtual void apply() {};
+ // returns if the widget shall be collapsed when opening the task dialog
+ virtual bool initiallyCollapsed() {
+ return false;
+ };
+
protected:
App::DocumentObject* getObject() const
{
@@ -235,6 +240,9 @@ public:
/// returns for Close and Help button
QDialogButtonBox::StandardButtons getStandardButtons() const override;
+ /// makes sure all widgets are collapsed, if they want to be
+ void processCollapsedWidgets();
+
protected:
void recompute();
@@ -300,6 +308,8 @@ public:
void applyPythonCode() override;
+ bool initiallyCollapsed() override;
+
private:
void setupConnections();
void onSelectionChanged();
diff --git a/src/Mod/Fem/Gui/TaskPostExtraction.cpp b/src/Mod/Fem/Gui/TaskPostExtraction.cpp
index ef70109462..57f39a70a2 100644
--- a/src/Mod/Fem/Gui/TaskPostExtraction.cpp
+++ b/src/Mod/Fem/Gui/TaskPostExtraction.cpp
@@ -166,4 +166,22 @@ void TaskPostExtraction::apply()
}
}
+bool TaskPostExtraction::initiallyCollapsed()
+{
+ Base::PyGILStateLocker lock;
+ try {
+ if (m_panel.hasAttr(std::string("initiallyCollapsed"))) {
+ Py::Callable method(m_panel.getAttr(std::string("initiallyCollapsed")));
+ auto result = Py::Boolean(method.apply());
+ return result.as_bool();
+ }
+ }
+ catch (Py::Exception&) {
+ Base::PyException e; // extract the Python error text
+ e.ReportException();
+ }
+
+ return false;
+}
+
#include "moc_TaskPostExtraction.cpp"
diff --git a/src/Mod/Fem/Gui/TaskPostExtraction.h b/src/Mod/Fem/Gui/TaskPostExtraction.h
index 5423a83d00..5fe2518760 100644
--- a/src/Mod/Fem/Gui/TaskPostExtraction.h
+++ b/src/Mod/Fem/Gui/TaskPostExtraction.h
@@ -56,6 +56,7 @@ protected:
bool isGuiTaskOnly() override;
void apply() override;
void onPostDataChanged(Fem::FemPostObject* obj) override;
+ bool initiallyCollapsed() override;
private:
Py::Object m_panel;
diff --git a/src/Mod/Fem/Gui/ViewProviderFemPostObject.cpp b/src/Mod/Fem/Gui/ViewProviderFemPostObject.cpp
index bc4dd1d953..2a34070c72 100644
--- a/src/Mod/Fem/Gui/ViewProviderFemPostObject.cpp
+++ b/src/Mod/Fem/Gui/ViewProviderFemPostObject.cpp
@@ -1007,6 +1007,7 @@ bool ViewProviderFemPostObject::setEdit(int ModNum)
postDlg = new TaskDlgPost(this);
setupTaskDialog(postDlg);
postDlg->connectSlots();
+ postDlg->processCollapsedWidgets();
Gui::Control().showDialog(postDlg);
}
diff --git a/src/Mod/Fem/ObjectsFem.py b/src/Mod/Fem/ObjectsFem.py
index c01cc8f8e6..d40558f4bd 100644
--- a/src/Mod/Fem/ObjectsFem.py
+++ b/src/Mod/Fem/ObjectsFem.py
@@ -699,6 +699,7 @@ def makePostLineplot(doc, name="Lineplot"):
view_post_lineplot.VPPostLineplot(obj.ViewObject)
return obj
+
def makePostLineplotFieldData(doc, name="FieldData2D"):
"""makePostLineplotFieldData(document, [name]):
creates a FEM post processing data extractor for 2D Field data
@@ -712,6 +713,21 @@ def makePostLineplotFieldData(doc, name="FieldData2D"):
view_post_lineplot.VPPostLineplotFieldData(obj.ViewObject)
return obj
+
+def makePostLineplotIndexOverFrames(doc, name="IndexOverFrames2D"):
+ """makePostLineplotIndexOverFrames(document, [name]):
+ creates a FEM post processing data extractor for 2D index data
+ """
+ obj = doc.addObject("App::FeaturePython", name)
+ from femobjects import post_lineplot
+
+ post_lineplot.PostLineplotIndexOverFrames(obj)
+ if FreeCAD.GuiUp:
+ from femviewprovider import view_post_lineplot
+ view_post_lineplot.VPPostLineplotIndexOverFrames(obj.ViewObject)
+ return obj
+
+
def makePostHistogram(doc, name="Histogram"):
"""makePostHistogram(document, [name]):
creates a FEM post processing histogram plot
@@ -725,8 +741,9 @@ def makePostHistogram(doc, name="Histogram"):
view_post_histogram.VPPostHistogram(obj.ViewObject)
return obj
+
def makePostHistogramFieldData(doc, name="FieldData1D"):
- """makePostHistogramFieldData1D(document, [name]):
+ """makePostHistogramFieldData(document, [name]):
creates a FEM post processing data extractor for 1D Field data
"""
obj = doc.addObject("App::FeaturePython", name)
@@ -739,6 +756,20 @@ def makePostHistogramFieldData(doc, name="FieldData1D"):
return obj
+def makePostHistogramIndexOverFrames(doc, name="IndexOverFrames1D"):
+ """makePostHistogramIndexOverFrames(document, [name]):
+ creates a FEM post processing data extractor for 1D Field data
+ """
+ obj = doc.addObject("App::FeaturePython", name)
+ from femobjects import post_histogram
+
+ post_histogram.PostHistogramIndexOverFrames(obj)
+ if FreeCAD.GuiUp:
+ from femviewprovider import view_post_histogram
+ view_post_histogram.VPPostHistogramIndexOverFrames(obj.ViewObject)
+ return obj
+
+
# ********* solver objects ***********************************************************************
def makeEquationDeformation(doc, base_solver=None, name="Deformation"):
"""makeEquationDeformation(document, [base_solver], [name]):
diff --git a/src/Mod/Fem/femcommands/commands.py b/src/Mod/Fem/femcommands/commands.py
index befcd48f30..f4edc1586e 100644
--- a/src/Mod/Fem/femcommands/commands.py
+++ b/src/Mod/Fem/femcommands/commands.py
@@ -1291,6 +1291,6 @@ if "BUILD_FEM_VTK_PYTHON" in FreeCAD.__cmake__:
FreeCADGui.addCommand("FEM_PostFilterGlyph", _PostFilterGlyph())
# setup all visualization commands (register by importing)
- import femobjects.post_histogram
import femobjects.post_lineplot
+ import femobjects.post_histogram
post_visualization.setup_commands("FEM_PostVisualization")
diff --git a/src/Mod/Fem/femguiutils/data_extraction.py b/src/Mod/Fem/femguiutils/data_extraction.py
index 4eeffbcef4..23a1bb784b 100644
--- a/src/Mod/Fem/femguiutils/data_extraction.py
+++ b/src/Mod/Fem/femguiutils/data_extraction.py
@@ -40,11 +40,13 @@ from vtkmodules.vtkFiltersGeneral import vtkSplitColumnComponents
import FreeCAD
import FreeCADGui
+import femobjects.base_fempostextractors as extr
from femtaskpanels.base_fempostpanel import _BasePostTaskPanel
from . import extract_link_view
ExtractLinkView = extract_link_view.ExtractLinkView
+
class DataExtraction(_BasePostTaskPanel):
# The class is not a widget itself, but provides a widget. It implements
# all required callbacks for the widget and the task dialog.
@@ -137,3 +139,12 @@ class DataExtraction(_BasePostTaskPanel):
def apply(self):
pass
+
+ def initiallyCollapsed(self):
+ # if we do not have any extractions to show we hide initially to remove clutter
+
+ for obj in self.Object.InList:
+ if extr.is_extractor_object(obj):
+ return False
+
+ return True
diff --git a/src/Mod/Fem/femguiutils/extract_link_view.py b/src/Mod/Fem/femguiutils/extract_link_view.py
index 9c984b537e..e1611f8609 100644
--- a/src/Mod/Fem/femguiutils/extract_link_view.py
+++ b/src/Mod/Fem/femguiutils/extract_link_view.py
@@ -198,16 +198,10 @@ class _SettingsPopup(QtGui.QGroupBox):
close = QtCore.Signal()
- def __init__(self, setting):
-
- toplevel = QtGui.QApplication.topLevelWidgets()
- for i in toplevel:
- if i.metaObject().className() == "Gui::MainWindow":
- main = i
- break
-
- super().__init__(main)
+ def __init__(self, setting, parent):
+ super().__init__(parent)
+ self.setWindowFlags(QtGui.Qt.Popup)
self.setFocusPolicy(QtGui.Qt.ClickFocus)
vbox = QtGui.QVBoxLayout()
@@ -217,20 +211,22 @@ class _SettingsPopup(QtGui.QGroupBox):
buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok)
vbox.addWidget(buttonBox)
- buttonBox.accepted.connect(self.accept)
+ buttonBox.accepted.connect(self.hide)
self.setLayout(vbox)
- @QtCore.Slot()
- def accept(self):
- self.close.emit()
-
def showEvent(self, event):
+ # required to get keyboard events
self.setFocus()
- def keyPressEvent(self, event):
- if event.key() == QtGui.Qt.Key_Enter or event.key() == QtGui.Qt.Key_Return:
- self.accept()
+ def hideEvent(self, event):
+ # emit on hide: this happens for OK button as well as
+ # "click away" closing of the popup
+ self.close.emit()
+ def keyPressEvent(self, event):
+ # close on hitting enter
+ if event.key() == QtGui.Qt.Key_Enter or event.key() == QtGui.Qt.Key_Return:
+ self.hide()
class _SummaryWidget(QtGui.QWidget):
@@ -284,14 +280,12 @@ class _SummaryWidget(QtGui.QWidget):
# make sure initial drawing happened
self._redraw()
+
def _button(self, text):
btn = QtGui.QPushButton(self)
btn.full_text = text
- #size = btn.sizeHint()
- #size.setWidth(size.width()*2)
btn.setMinimumSize(btn.sizeHint())
-
btn.setFlat(True)
btn.setText(text)
btn.setStyleSheet("text-align:left;padding:6px");
@@ -313,7 +307,7 @@ class _SummaryWidget(QtGui.QWidget):
for i, btn in enumerate(btns):
btn_size = btn_total_size*btn_rel_size[i]
- txt_size = btn_size - btn.iconSize().width() - btn_margin/2*3
+ txt_size = btn_size - btn.iconSize().width() - btn_margin
# we elide only if there is enough space for a meaningful text
if txt_size >= min_text_width:
@@ -355,24 +349,28 @@ class _SummaryWidget(QtGui.QWidget):
def _position_dialog(self, dialog):
- main = dialog.parent()
- list_widget = self.parent().parent().parent()
- widget_rect = list_widget.geometry()
- diag_size = dialog.sizeHint()
- # default is towards main window center
- if main.geometry().center().x() >= list_widget.mapToGlobal(widget_rect.center()).x():
- rigth_point = list_widget.mapToGlobal(widget_rect.topRight())
- dialog.setGeometry(QtCore.QRect(rigth_point, diag_size))
- else:
- left_point = list_widget.mapToGlobal(widget_rect.topLeft())
- left_point -= QtCore.QPoint(diag_size.width(), 0)
- dialog.setGeometry(QtCore.QRect(left_point, diag_size))
+ # the scroll area does mess the mapping to global up, somehow
+ # the transformation from the widget ot the scroll area gives
+ # very weird values. Hence we build the coords of the widget
+ # ourself
+
+ 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.sizeHint().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()))
@QtCore.Slot()
def editApp(self):
if not hasattr(self, "appDialog"):
widget = self._extractor.ViewObject.Proxy.get_app_edit_widget(self._post_dialog)
- self.appDialog = _SettingsPopup(widget)
+ self.appDialog = _SettingsPopup(widget, self)
self.appDialog.close.connect(self.appAccept)
if not self.appDialog.isVisible():
@@ -386,7 +384,7 @@ class _SummaryWidget(QtGui.QWidget):
if not hasattr(self, "viewDialog"):
widget = self._extractor.ViewObject.Proxy.get_view_edit_widget(self._post_dialog)
- self.viewDialog = _SettingsPopup(widget)
+ self.viewDialog = _SettingsPopup(widget, self)
self.viewDialog.close.connect(self.viewAccept)
if not self.viewDialog.isVisible():
@@ -402,8 +400,6 @@ class _SummaryWidget(QtGui.QWidget):
@QtCore.Slot()
def viewAccept(self):
- self.viewDialog.hide()
-
# update the preview
extr_repr = self._extractor.ViewObject.Proxy.get_preview()
self.viewButton.setIcon(extr_repr[0])
@@ -414,8 +410,6 @@ class _SummaryWidget(QtGui.QWidget):
@QtCore.Slot()
def appAccept(self):
- self.appDialog.hide()
-
# update the preview
extr_label = self._extractor.Proxy.get_representive_fieldname(self._extractor)
self.extrButton.full_text = extr_label
diff --git a/src/Mod/Fem/femobjects/base_fempostextractors.py b/src/Mod/Fem/femobjects/base_fempostextractors.py
index 33ef4b4935..9e4ddac24f 100644
--- a/src/Mod/Fem/femobjects/base_fempostextractors.py
+++ b/src/Mod/Fem/femobjects/base_fempostextractors.py
@@ -116,6 +116,8 @@ class Extractor(base_fempythonobject.BaseFemPythonObject):
return ["X", "Y"]
case 3:
return ["X", "Y", "Z"]
+ case 6:
+ return ["XX", "YY", "ZZ", "XY", "XZ", "YZ"]
case _:
return ["Not a vector"]
@@ -217,11 +219,13 @@ class Extractor1D(Extractor):
component_array.SetName(array.GetName())
table.AddColumn(component_array)
- def _x_array_from_dataset(self, obj, dataset):
+ def _x_array_from_dataset(self, obj, dataset, copy=True):
# extracts the relevant array from the dataset and returns a copy
+ # indices = None uses all indices, otherwise the values in this list
match obj.XField:
case "Index":
+ # index needs always to be build, ignore copy argument
num = dataset.GetPoints().GetNumberOfPoints()
array = vtkIntArray()
array.SetNumberOfTuples(num)
@@ -230,15 +234,22 @@ class Extractor1D(Extractor):
array.SetValue(i,i)
case "Position":
+
orig_array = dataset.GetPoints().GetData()
- array = vtkDoubleArray()
- array.DeepCopy(orig_array)
+ if copy:
+ array = vtkDoubleArray()
+ array.DeepCopy(orig_array)
+ else:
+ array = orig_array
case _:
point_data = dataset.GetPointData()
orig_array = point_data.GetAbstractArray(obj.XField)
- array = vtkDoubleArray()
- array.DeepCopy(orig_array)
+ if copy:
+ array = vtkDoubleArray()
+ array.DeepCopy(orig_array)
+ else:
+ array = orig_array
return array
@@ -343,28 +354,29 @@ class Extractor2D(Extractor1D):
component_array.SetName(array.GetName())
table.AddColumn(component_array)
- def _y_array_from_dataset(self, obj, dataset):
+ def _y_array_from_dataset(self, obj, dataset, copy=True):
# extracts the relevant array from the dataset and returns a copy
+ # indices = None uses all indices, otherwise the values in this list
match obj.YField:
- case "Index":
- num = dataset.GetPoints().GetNumberOfPoints()
- array = vtkIntArray()
- array.SetNumberOfTuples(num)
- array.SetNumberOfComponents(1)
- for i in range(num):
- array.SetValue(i,i)
-
case "Position":
+
orig_array = dataset.GetPoints().GetData()
- array = vtkDoubleArray()
- array.DeepCopy(orig_array)
+ if copy:
+ array = vtkDoubleArray()
+ array.DeepCopy(orig_array)
+ else:
+ array = orig_array
case _:
point_data = dataset.GetPointData()
orig_array = point_data.GetAbstractArray(obj.YField)
- array = vtkDoubleArray()
- array.DeepCopy(orig_array)
+
+ if copy:
+ array = vtkDoubleArray()
+ array.DeepCopy(orig_array)
+ else:
+ array = orig_array
return array
diff --git a/src/Mod/Fem/femobjects/post_extract1D.py b/src/Mod/Fem/femobjects/post_extract1D.py
index f7a450c181..3540b6706a 100644
--- a/src/Mod/Fem/femobjects/post_extract1D.py
+++ b/src/Mod/Fem/femobjects/post_extract1D.py
@@ -33,6 +33,7 @@ from . import base_fempostextractors
from . import base_fempythonobject
_PropHelper = base_fempythonobject._PropHelper
+from vtkmodules.vtkCommonCore import vtkDoubleArray
from vtkmodules.vtkCommonDataModel import vtkTable
from vtkmodules.vtkCommonExecutionModel import vtkStreamingDemandDrivenPipeline
@@ -108,7 +109,7 @@ class PostFieldData1D(base_fempostextractors.Extractor1D):
obj.Table = table
-class PostIndexData1D(base_fempostextractors.Extractor1D):
+class PostIndexOverFrames1D(base_fempostextractors.Extractor1D):
"""
A post processing extraction of one dimensional index data
"""
@@ -119,19 +120,67 @@ class PostIndexData1D(base_fempostextractors.Extractor1D):
super().__init__(obj)
def _get_properties(self):
- prop =[ _PropHelper(
- type="App::PropertyBool",
- name="ExtractFrames",
- group="Multiframe",
- doc="Specify if the data at the index should be extracted for each frame",
- value=False,
- ),
- _PropHelper(
+ prop =[_PropHelper(
type="App::PropertyInteger",
- name="XIndex",
+ name="Index",
group="X Data",
- doc="Specify for which point index the data should be extracted",
+ doc="Specify for which index the data should be extracted",
value=0,
),
]
return super()._get_properties() + prop
+
+ def execute(self, obj):
+
+ # on execution we populate the vtk table
+ table = vtkTable()
+
+ if not obj.Source:
+ obj.Table = table
+ return
+
+ dataset = obj.Source.getDataSet()
+ if not dataset:
+ obj.Table = table
+ return
+
+ # check if we have timesteps (required!)
+ abort = True
+ info = obj.Source.getOutputAlgorithm().GetOutputInformation(0)
+ if info.Has(vtkStreamingDemandDrivenPipeline.TIME_STEPS()):
+ timesteps = info.Get(vtkStreamingDemandDrivenPipeline.TIME_STEPS())
+ if len(timesteps) > 1:
+ abort = False
+
+ if abort:
+ FreeCAD.Console.PrintWarning("Not sufficient frames available in data, cannot extract data")
+ obj.Table = table
+ return
+
+ algo = obj.Source.getOutputAlgorithm()
+ setup = False
+ frame_array = vtkDoubleArray()
+
+ idx = obj.Index
+ for i, timestep in enumerate(timesteps):
+
+ algo.UpdateTimeStep(timestep)
+ dataset = algo.GetOutputDataObject(0)
+ array = self._x_array_from_dataset(obj, dataset, copy=False)
+
+ if not setup:
+ frame_array.SetNumberOfComponents(array.GetNumberOfComponents())
+ frame_array.SetNumberOfTuples(len(timesteps))
+ setup = True
+
+ frame_array.SetTuple(i, idx, array)
+
+ if frame_array.GetNumberOfComponents() > 1:
+ frame_array.SetName(f"{obj.XField} ({obj.XComponent})")
+ else:
+ frame_array.SetName(f"{obj.XField}")
+
+ self._x_array_component_to_table(obj, frame_array, table)
+
+ # set the final table
+ obj.Table = table
diff --git a/src/Mod/Fem/femobjects/post_extract2D.py b/src/Mod/Fem/femobjects/post_extract2D.py
index b25b809655..60c9ac2df2 100644
--- a/src/Mod/Fem/femobjects/post_extract2D.py
+++ b/src/Mod/Fem/femobjects/post_extract2D.py
@@ -33,6 +33,7 @@ from . import base_fempostextractors
from . import base_fempythonobject
_PropHelper = base_fempythonobject._PropHelper
+from vtkmodules.vtkCommonCore import vtkDoubleArray
from vtkmodules.vtkCommonDataModel import vtkTable
from vtkmodules.vtkCommonExecutionModel import vtkStreamingDemandDrivenPipeline
@@ -124,9 +125,9 @@ class PostFieldData2D(base_fempostextractors.Extractor2D):
obj.Table = table
-class PostIndexData2D(base_fempostextractors.Extractor2D):
+class PostIndexOverFrames2D(base_fempostextractors.Extractor2D):
"""
- A post processing extraction of one dimensional index data
+ A post processing extraction for two dimensional data with X always being the frames
"""
ExtractionType = "Index"
@@ -135,19 +136,83 @@ class PostIndexData2D(base_fempostextractors.Extractor2D):
super().__init__(obj)
def _get_properties(self):
- prop =[ _PropHelper(
- type="App::PropertyBool",
- name="ExtractFrames",
- group="Multiframe",
- doc="Specify if the data at the index should be extracted for each frame",
- value=False,
- ),
- _PropHelper(
+ prop =[_PropHelper(
type="App::PropertyInteger",
- name="XIndex",
- group="X Data",
+ name="Index",
+ group="Data",
doc="Specify for which point index the data should be extracted",
value=0,
),
]
return super()._get_properties() + prop
+
+ def _setup_x_component_property(self, obj, point_data):
+ # override to only allow "Frames" as X data
+ obj.XComponent = ["Not a vector"]
+
+ def _setup_x_properties(self, obj, dataset):
+ # override to only allow "Frames" as X data
+ obj.XField = ["Frames"]
+
+ def execute(self, obj):
+
+ # on execution we populate the vtk table
+ table = vtkTable()
+
+ if not obj.Source:
+ obj.Table = table
+ return
+
+ dataset = obj.Source.getDataSet()
+ if not dataset:
+ obj.Table = table
+ return
+
+ # check if we have timesteps (required!)
+ abort = True
+ info = obj.Source.getOutputAlgorithm().GetOutputInformation(0)
+ if info.Has(vtkStreamingDemandDrivenPipeline.TIME_STEPS()):
+ timesteps = info.Get(vtkStreamingDemandDrivenPipeline.TIME_STEPS())
+ if len(timesteps) > 1:
+ abort = False
+
+ if abort:
+ FreeCAD.Console.PrintWarning("Not sufficient frames available in data, cannot extract data")
+ obj.Table = table
+ return
+
+ algo = obj.Source.getOutputAlgorithm()
+
+ frame_x_array = vtkDoubleArray()
+ frame_x_array.SetNumberOfTuples(len(timesteps))
+ frame_x_array.SetNumberOfComponents(1)
+
+
+ frame_y_array = vtkDoubleArray()
+ idx = obj.Index
+ setup = False
+ for i, timestep in enumerate(timesteps):
+
+ frame_x_array.SetTuple1(i, timestep)
+
+ algo.UpdateTimeStep(timestep)
+ dataset = algo.GetOutputDataObject(0)
+ array = self._y_array_from_dataset(obj, dataset, copy=False)
+ if not setup:
+ frame_y_array.SetNumberOfComponents(array.GetNumberOfComponents())
+ frame_y_array.SetNumberOfTuples(len(timesteps))
+ setup = True
+
+ frame_y_array.SetTuple(i, idx, array)
+
+ frame_x_array.SetName("Frames")
+ if frame_y_array.GetNumberOfComponents() > 1:
+ frame_y_array.SetName(f"{obj.YField} ({obj.YComponent})")
+ else:
+ frame_y_array.SetName(obj.YField)
+
+ table.AddColumn(frame_x_array)
+ self._y_array_component_to_table(obj, frame_y_array, table)
+
+ # set the final table
+ obj.Table = table
diff --git a/src/Mod/Fem/femobjects/post_histogram.py b/src/Mod/Fem/femobjects/post_histogram.py
index 8cbd72b41c..bdcb4ad553 100644
--- a/src/Mod/Fem/femobjects/post_histogram.py
+++ b/src/Mod/Fem/femobjects/post_histogram.py
@@ -57,6 +57,14 @@ post_visualization.register_extractor("Histogram",
"makePostHistogramFieldData")
+post_visualization.register_extractor("Histogram",
+ "HistogramIndexOverFrames",
+ ":/icons/FEM_PostIndex.svg",
+ "1D",
+ "Index",
+ "ObjectsFem",
+ "makePostHistogramIndexOverFrames")
+
# Implementation
# ##############
@@ -77,7 +85,11 @@ class PostHistogramFieldData(post_extract1D.PostFieldData1D):
"""
VisualizationType = "Histogram"
-
+class PostHistogramIndexOverFrames(post_extract1D.PostIndexOverFrames1D):
+ """
+ A 1D index extraction for histogram.
+ """
+ VisualizationType = "Histogram"
class PostHistogram(base_fempostvisualizations.PostVisualization):
diff --git a/src/Mod/Fem/femobjects/post_lineplot.py b/src/Mod/Fem/femobjects/post_lineplot.py
index 272aaea74c..06f844ebac 100644
--- a/src/Mod/Fem/femobjects/post_lineplot.py
+++ b/src/Mod/Fem/femobjects/post_lineplot.py
@@ -56,6 +56,14 @@ post_visualization.register_extractor("Lineplot",
"ObjectsFem",
"makePostLineplotFieldData")
+post_visualization.register_extractor("Lineplot",
+ "LineplotIndexOverFrames",
+ ":/icons/FEM_PostIndex.svg",
+ "2D",
+ "Index",
+ "ObjectsFem",
+ "makePostLineplotIndexOverFrames")
+
# Implementation
# ##############
@@ -77,6 +85,12 @@ class PostLineplotFieldData(post_extract2D.PostFieldData2D):
"""
VisualizationType = "Lineplot"
+class PostLineplotIndexOverFrames(post_extract2D.PostIndexOverFrames2D):
+ """
+ A 2D index extraction for lineplot.
+ """
+ VisualizationType = "Lineplot"
+
class PostLineplot(base_fempostvisualizations.PostVisualization):
diff --git a/src/Mod/Fem/femtaskpanels/task_post_histogram.py b/src/Mod/Fem/femtaskpanels/task_post_histogram.py
index 593f177a94..79b4e4eeab 100644
--- a/src/Mod/Fem/femtaskpanels/task_post_histogram.py
+++ b/src/Mod/Fem/femtaskpanels/task_post_histogram.py
@@ -64,6 +64,7 @@ class _TaskPanel(base_fempostpanel._BasePostTaskPanel):
self.data_widget.setLayout(vbox)
self.data_widget.setWindowTitle("Histogram data")
+ self.data_widget.setWindowIcon(FreeCADGui.getIcon(":/icons/FEM_PostHistogram.svg"))
# histogram parameter widget
@@ -71,6 +72,8 @@ class _TaskPanel(base_fempostpanel._BasePostTaskPanel):
FreeCAD.getHomePath() + "Mod/Fem/Resources/ui/TaskPostHistogram.ui"
)
self.view_widget.setWindowTitle("Histogram view settings")
+ self.view_widget.setWindowIcon(FreeCADGui.getIcon(":/icons/FEM_PostHistogram.svg"))
+
self.__init_widgets()
# form made from param and selection widget
diff --git a/src/Mod/Fem/femtaskpanels/task_post_lineplot.py b/src/Mod/Fem/femtaskpanels/task_post_lineplot.py
index 650cdd70a1..507e6b3cbf 100644
--- a/src/Mod/Fem/femtaskpanels/task_post_lineplot.py
+++ b/src/Mod/Fem/femtaskpanels/task_post_lineplot.py
@@ -64,6 +64,7 @@ class _TaskPanel(base_fempostpanel._BasePostTaskPanel):
self.data_widget.setLayout(vbox)
self.data_widget.setWindowTitle("Lineplot data")
+ self.data_widget.setWindowIcon(FreeCADGui.getIcon(":/icons/FEM_PostLineplot.svg"))
# lineplot parameter widget
@@ -71,6 +72,8 @@ class _TaskPanel(base_fempostpanel._BasePostTaskPanel):
FreeCAD.getHomePath() + "Mod/Fem/Resources/ui/TaskPostLineplot.ui"
)
self.view_widget.setWindowTitle("Lineplot view settings")
+ self.view_widget.setWindowIcon(FreeCADGui.getIcon(":/icons/FEM_PostLineplot.svg"))
+
self.__init_widgets()
# form made from param and selection widget
diff --git a/src/Mod/Fem/femviewprovider/view_post_histogram.py b/src/Mod/Fem/femviewprovider/view_post_histogram.py
index 8fe2fa6b4c..777e3d15c0 100644
--- a/src/Mod/Fem/femviewprovider/view_post_histogram.py
+++ b/src/Mod/Fem/femviewprovider/view_post_histogram.py
@@ -36,6 +36,7 @@ import Plot
import FemGui
from PySide import QtGui, QtCore
+import io
import numpy as np
import matplotlib as mpl
@@ -73,25 +74,59 @@ class EditViewWidget(QtGui.QWidget):
self._post_dialog._enumPropertyToCombobox(vobj, "LineStyle", self.widget.LineStyle)
self.widget.LineWidth.setValue(vobj.LineWidth)
self.widget.HatchDensity.setValue(vobj.HatchDensity)
- self.widget.BarColor.setProperty("color", QtGui.QColor(*[v*255 for v in vobj.BarColor]))
- self.widget.LineColor.setProperty("color", QtGui.QColor(*[v*255 for v in vobj.LineColor]))
+
+ # setup the color buttons (don't use FreeCADs color button, as this does not work in popups!)
+ self._setup_color_button(self.widget.BarColor, vobj.BarColor, self.barColorChanged)
+ self._setup_color_button(self.widget.LineColor, vobj.LineColor, self.lineColorChanged)
self.widget.Legend.editingFinished.connect(self.legendChanged)
self.widget.Hatch.activated.connect(self.hatchPatternChanged)
self.widget.LineStyle.activated.connect(self.lineStyleChanged)
self.widget.HatchDensity.valueChanged.connect(self.hatchDensityChanged)
self.widget.LineWidth.valueChanged.connect(self.lineWidthChanged)
- self.widget.LineColor.changed.connect(self.lineColorChanged)
- self.widget.BarColor.changed.connect(self.barColorChanged)
- @QtCore.Slot()
- def lineColorChanged(self):
- color = self.widget.LineColor.property("color")
+ # sometimes wierd sizes occur with spinboxes
+ 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])
+ icon_size = button.iconSize()
+ icon_size.setWidth(icon_size.width()*2)
+ button.setIconSize(icon_size)
+ pixmap = QtGui.QPixmap(icon_size)
+ pixmap.fill(barColor)
+ button.setIcon(pixmap)
+
+ action = QtGui.QWidgetAction(button)
+ diag = QtGui.QColorDialog(barColor, parent=button)
+ diag.accepted.connect(action.trigger)
+ diag.rejected.connect(action.trigger)
+ diag.colorSelected.connect(callback)
+
+ action.setDefaultWidget(diag)
+ button.addAction(action)
+ button.setPopupMode(QtGui.QToolButton.InstantPopup)
+
+
+ @QtCore.Slot(QtGui.QColor)
+ def lineColorChanged(self, color):
+
+ pixmap = QtGui.QPixmap(self.widget.LineColor.iconSize())
+ pixmap.fill(color)
+ self.widget.LineColor.setIcon(pixmap)
+
self._object.ViewObject.LineColor = color.getRgb()
- @QtCore.Slot()
- def barColorChanged(self):
- color = self.widget.BarColor.property("color")
+ @QtCore.Slot(QtGui.QColor)
+ def barColorChanged(self, color):
+
+ pixmap = QtGui.QPixmap(self.widget.BarColor.iconSize())
+ pixmap.fill(color)
+ self.widget.BarColor.setIcon(pixmap)
+
self._object.ViewObject.BarColor = color.getRgb()
@QtCore.Slot(float)
@@ -115,7 +150,7 @@ class EditViewWidget(QtGui.QWidget):
self._object.ViewObject.Legend = self.widget.Legend.text()
-class EditAppWidget(QtGui.QWidget):
+class EditFieldAppWidget(QtGui.QWidget):
def __init__(self, obj, post_dialog):
super().__init__()
@@ -160,6 +195,54 @@ class EditAppWidget(QtGui.QWidget):
self._object.ExtractFrames = extract
self._post_dialog._recompute()
+class EditIndexAppWidget(QtGui.QWidget):
+
+ def __init__(self, obj, post_dialog):
+ super().__init__()
+
+ self._object = obj
+ self._post_dialog = post_dialog
+
+ # load the ui and set it up
+ self.widget = FreeCADGui.PySideUic.loadUi(
+ FreeCAD.getHomePath() + "Mod/Fem/Resources/ui/PostHistogramIndexAppEdit.ui"
+ )
+ layout = QtGui.QVBoxLayout()
+ layout.addWidget(self.widget)
+ self.setLayout(layout)
+
+ self.__init_widget()
+
+ def __init_widget(self):
+ # set the other properties
+
+ self.widget.Index.setValue(self._object.Index)
+ self._post_dialog._enumPropertyToCombobox(self._object, "XField", self.widget.Field)
+ self._post_dialog._enumPropertyToCombobox(self._object, "XComponent", self.widget.Component)
+
+ self.widget.Index.valueChanged.connect(self.indexChanged)
+ self.widget.Field.activated.connect(self.fieldChanged)
+ self.widget.Component.activated.connect(self.componentChanged)
+
+ # sometimes wierd sizes occur with spinboxes
+ self.widget.Index.setMaximumHeight(self.widget.Field.sizeHint().height())
+
+ @QtCore.Slot(int)
+ def fieldChanged(self, index):
+ self._object.XField = index
+ self._post_dialog._enumPropertyToCombobox(self._object, "XComponent", self.widget.Component)
+ self._post_dialog._recompute()
+
+ @QtCore.Slot(int)
+ def componentChanged(self, index):
+ self._object.XComponent = index
+ self._post_dialog._recompute()
+
+ @QtCore.Slot(int)
+ def indexChanged(self, value):
+ self._object.Index = value
+ self._post_dialog._recompute()
+
class VPPostHistogramFieldData(view_post_extract.VPPostExtractor):
"""
@@ -233,13 +316,30 @@ class VPPostHistogramFieldData(view_post_extract.VPPostExtractor):
return ":/icons/FEM_PostField.svg"
def get_app_edit_widget(self, post_dialog):
- return EditAppWidget(self.Object, post_dialog)
+ return EditFieldAppWidget(self.Object, post_dialog)
def get_view_edit_widget(self, post_dialog):
return EditViewWidget(self.Object, post_dialog)
def get_preview(self):
- return (QtGui.QPixmap(), self.ViewObject.Legend)
+
+ fig = mpl.pyplot.figure(figsize=(0.4,0.2), dpi=500)
+ ax = mpl.pyplot.Axes(fig, [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)
+ ax.add_patch(patch)
+
+ data = io.BytesIO()
+ mpl.pyplot.savefig(data, bbox_inches=0, transparent=True)
+ mpl.pyplot.close()
+
+ pixmap = QtGui.QPixmap()
+ pixmap.loadFromData(data.getvalue())
+
+ return (pixmap, self.ViewObject.Legend)
def get_kw_args(self):
# builds kw args from the properties
@@ -256,6 +356,21 @@ class VPPostHistogramFieldData(view_post_extract.VPPostExtractor):
return kwargs
+class VPPostHistogramIndexOverFrames(VPPostHistogramFieldData):
+ """
+ A View Provider for extraction of 1D index over frames data
+ """
+
+ def __init__(self, vobj):
+ super().__init__(vobj)
+
+ def getIcon(self):
+ return ":/icons/FEM_PostIndex.svg"
+
+ def get_app_edit_widget(self, post_dialog):
+ return EditIndexAppWidget(self.Object, post_dialog)
+
+
class VPPostHistogram(view_base_fempostvisualization.VPPostVisualization):
"""
A View Provider for Histogram plots
@@ -365,15 +480,26 @@ class VPPostHistogram(view_base_fempostvisualization.VPPostVisualization):
def show_visualization(self):
if not hasattr(self, "_plot") or not self._plot:
+ main = Plot.getMainWindow()
self._plot = Plot.Plot()
- self._dialog = QtGui.QDialog(Plot.getMainWindow())
+ self._plot.destroyed.connect(self.destroyed)
+ self._dialog = QtGui.QDialog(main)
box = QtGui.QVBoxLayout()
box.addWidget(self._plot)
+ self._dialog.resize(main.size().height()/2, main.size().height()/3) # keep it square
self._dialog.setLayout(box)
self.drawPlot()
self._dialog.show()
+
+ def destroyed(self, obj):
+ print("*********************************************************")
+ print("**************** ******************")
+ print("**************** destroy ******************")
+ print("**************** ******************")
+ print("*********************************************************")
+
def get_kw_args(self, obj):
view = obj.ViewObject
if not view or not hasattr(view, "Proxy"):
diff --git a/src/Mod/Fem/femviewprovider/view_post_lineplot.py b/src/Mod/Fem/femviewprovider/view_post_lineplot.py
index 54fe5439f3..f98a5bf1e4 100644
--- a/src/Mod/Fem/femviewprovider/view_post_lineplot.py
+++ b/src/Mod/Fem/femviewprovider/view_post_lineplot.py
@@ -39,7 +39,6 @@ from PySide import QtGui, QtCore
import io
import numpy as np
import matplotlib as mpl
-import matplotlib.pyplot as plt
from vtkmodules.numpy_interface.dataset_adapter import VTKArray
@@ -75,18 +74,47 @@ class EditViewWidget(QtGui.QWidget):
self._post_dialog._enumPropertyToCombobox(vobj, "MarkerStyle", self.widget.MarkerStyle)
self.widget.LineWidth.setValue(vobj.LineWidth)
self.widget.MarkerSize.setValue(vobj.MarkerSize)
- self.widget.Color.setProperty("color", QtGui.QColor(*[v*255 for v in vobj.Color]))
+
+ self._setup_color_button(self.widget.Color, vobj.Color, self.colorChanged)
self.widget.Legend.editingFinished.connect(self.legendChanged)
self.widget.MarkerStyle.activated.connect(self.markerStyleChanged)
self.widget.LineStyle.activated.connect(self.lineStyleChanged)
self.widget.MarkerSize.valueChanged.connect(self.markerSizeChanged)
self.widget.LineWidth.valueChanged.connect(self.lineWidthChanged)
- self.widget.Color.changed.connect(self.colorChanged)
- @QtCore.Slot()
- def colorChanged(self):
- color = self.widget.Color.property("color")
+ # sometimes wierd sizes occur with spinboxes
+ self.widget.MarkerSize.setMaximumHeight(self.widget.MarkerStyle.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])
+ icon_size = button.iconSize()
+ icon_size.setWidth(icon_size.width()*2)
+ button.setIconSize(icon_size)
+ pixmap = QtGui.QPixmap(icon_size)
+ pixmap.fill(barColor)
+ button.setIcon(pixmap)
+
+ action = QtGui.QWidgetAction(button)
+ diag = QtGui.QColorDialog(barColor, parent=button)
+ diag.accepted.connect(action.trigger)
+ diag.rejected.connect(action.trigger)
+ diag.colorSelected.connect(callback)
+
+ action.setDefaultWidget(diag)
+ button.addAction(action)
+ button.setPopupMode(QtGui.QToolButton.InstantPopup)
+
+
+ @QtCore.Slot(QtGui.QColor)
+ def colorChanged(self, color):
+
+ pixmap = QtGui.QPixmap(self.widget.Color.iconSize())
+ pixmap.fill(color)
+ self.widget.Color.setIcon(pixmap)
+
self._object.ViewObject.Color = color.getRgb()
@QtCore.Slot(float)
@@ -110,7 +138,7 @@ class EditViewWidget(QtGui.QWidget):
self._object.ViewObject.Legend = self.widget.Legend.text()
-class EditAppWidget(QtGui.QWidget):
+class EditFieldAppWidget(QtGui.QWidget):
def __init__(self, obj, post_dialog):
super().__init__()
@@ -171,9 +199,58 @@ class EditAppWidget(QtGui.QWidget):
self._post_dialog._recompute()
+class EditIndexAppWidget(QtGui.QWidget):
+
+ def __init__(self, obj, post_dialog):
+ super().__init__()
+
+ self._object = obj
+ self._post_dialog = post_dialog
+
+ # load the ui and set it up
+ self.widget = FreeCADGui.PySideUic.loadUi(
+ FreeCAD.getHomePath() + "Mod/Fem/Resources/ui/PostLineplotIndexAppEdit.ui"
+ )
+ layout = QtGui.QVBoxLayout()
+ layout.addWidget(self.widget)
+ self.setLayout(layout)
+
+ self.__init_widget()
+
+ def __init_widget(self):
+ # set the other properties
+
+ 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.widget.Index.valueChanged.connect(self.indexChanged)
+ self.widget.YField.activated.connect(self.yFieldChanged)
+ self.widget.YComponent.activated.connect(self.yComponentChanged)
+
+ # sometimes wierd sizes occur with spinboxes
+ self.widget.Index.setMaximumHeight(self.widget.YField.sizeHint().height())
+
+ @QtCore.Slot(int)
+ def indexChanged(self, value):
+ self._object.Index = value
+ self._post_dialog._recompute()
+
+ @QtCore.Slot(int)
+ def yFieldChanged(self, index):
+ self._object.YField = index
+ self._post_dialog._enumPropertyToCombobox(self._object, "YComponent", self.widget.YComponent)
+ self._post_dialog._recompute()
+
+ @QtCore.Slot(int)
+ def yComponentChanged(self, index):
+ self._object.YComponent = index
+ self._post_dialog._recompute()
+
+
class VPPostLineplotFieldData(view_post_extract.VPPostExtractor):
"""
- A View Provider for extraction of 1D field data specialy for histograms
+ A View Provider for extraction of 2D field data specialy for histograms
"""
def __init__(self, vobj):
@@ -236,7 +313,7 @@ class VPPostLineplotFieldData(view_post_extract.VPPostExtractor):
return ":/icons/FEM_PostField.svg"
def get_app_edit_widget(self, post_dialog):
- return EditAppWidget(self.Object, post_dialog)
+ return EditFieldAppWidget(self.Object, post_dialog)
def get_view_edit_widget(self, post_dialog):
return EditViewWidget(self.Object, post_dialog)
@@ -245,16 +322,16 @@ class VPPostLineplotFieldData(view_post_extract.VPPostExtractor):
# Returns the preview tuple of icon and label: (QPixmap, str)
# Note: QPixmap in ratio 2:1
- fig = plt.figure(figsize=(0.2,0.1), dpi=1000)
- ax = plt.Axes(fig, [0., 0., 1., 1.])
+ fig = mpl.pyplot.figure(figsize=(0.2,0.1), dpi=1000)
+ ax = mpl.pyplot.Axes(fig, [0., 0., 1., 1.])
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)
data = io.BytesIO()
- plt.savefig(data, bbox_inches=0, transparent=True)
- plt.close()
+ mpl.pyplot.savefig(data, bbox_inches=0, transparent=True)
+ mpl.pyplot.close()
pixmap = QtGui.QPixmap()
pixmap.loadFromData(data.getvalue())
@@ -277,6 +354,21 @@ class VPPostLineplotFieldData(view_post_extract.VPPostExtractor):
return kwargs
+class VPPostLineplotIndexOverFrames(VPPostLineplotFieldData):
+ """
+ A View Provider for extraction of 2D index over frames data
+ """
+
+ def __init__(self, vobj):
+ super().__init__(vobj)
+
+ def getIcon(self):
+ return ":/icons/FEM_PostIndex.svg"
+
+ def get_app_edit_widget(self, post_dialog):
+ return EditIndexAppWidget(self.Object, post_dialog)
+
+
class VPPostLineplot(view_base_fempostvisualization.VPPostVisualization):
"""
A View Provider for Lineplot plots
@@ -366,9 +458,11 @@ class VPPostLineplot(view_base_fempostvisualization.VPPostVisualization):
if not hasattr(self, "_plot") or not self._plot:
self._plot = Plot.Plot()
- self._dialog = QtGui.QDialog(Plot.getMainWindow())
+ main = Plot.getMainWindow()
+ self._dialog = QtGui.QDialog(main)
box = QtGui.QVBoxLayout()
box.addWidget(self._plot)
+ self._dialog.resize(main.size().height()/2, main.size().height()/3) # keep aspect ratio constant
self._dialog.setLayout(box)
self.drawPlot()
diff --git a/src/Mod/Plot/Plot.py b/src/Mod/Plot/Plot.py
index 4f5a360745..a3423ae93d 100644
--- a/src/Mod/Plot/Plot.py
+++ b/src/Mod/Plot/Plot.py
@@ -28,7 +28,7 @@ import sys
try:
import matplotlib
- matplotlib.use("Qt5Agg")
+ matplotlib.use("QtAgg")
# Force matplotlib to use PySide backend by temporarily unloading PyQt
if "PyQt5.QtCore" in sys.modules:
@@ -36,10 +36,11 @@ try:
import matplotlib.pyplot as plt
import PyQt5.QtCore
else:
+ print("default matplotlib import")
import matplotlib.pyplot as plt
- from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
- from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
+ from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas
+ from matplotlib.backends.backend_qtagg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
except ImportError: