FEM: Add index over frames visualizations
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>293</width>
|
||||
<height>126</height>
|
||||
<width>278</width>
|
||||
<height>110</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -68,7 +68,7 @@
|
||||
<item row="1" column="2">
|
||||
<widget class="QComboBox" name="Hatch">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
@@ -93,7 +93,7 @@
|
||||
<item row="1" column="3">
|
||||
<widget class="QSpinBox" name="HatchDensity">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
@@ -113,10 +113,20 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="3">
|
||||
<widget class="QLineEdit" name="Legend"/>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Legend:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="Gui::ColorButton" name="LineColor">
|
||||
<widget class="QToolButton" name="LineColor">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
@@ -127,7 +137,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="Gui::ColorButton" name="BarColor">
|
||||
<widget class="QToolButton" name="BarColor">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
@@ -139,27 +149,10 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="3">
|
||||
<widget class="QLineEdit" name="Legend"/>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Legend:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>Gui::ColorButton</class>
|
||||
<extends>QPushButton</extends>
|
||||
<header>Gui/Widgets.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>Legend</tabstop>
|
||||
<tabstop>BarColor</tabstop>
|
||||
|
||||
81
src/Mod/Fem/Gui/Resources/ui/PostHistogramIndexAppEdit.ui
Normal file
81
src/Mod/Fem/Gui/Resources/ui/PostHistogramIndexAppEdit.ui
Normal file
@@ -0,0 +1,81 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Form</class>
|
||||
<widget class="QWidget" name="Form">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>261</width>
|
||||
<height>110</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Field:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="Field">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="Component">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Index:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSpinBox" name="Index">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>296</width>
|
||||
<height>186</height>
|
||||
<width>271</width>
|
||||
<height>174</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>335</width>
|
||||
<height>124</height>
|
||||
<width>274</width>
|
||||
<height>114</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -28,25 +28,6 @@
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="2" column="4">
|
||||
<widget class="QDoubleSpinBox" name="MarkerSize">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Width of all lines (outline and hatch)</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>99.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
@@ -57,7 +38,7 @@
|
||||
<item row="1" column="2">
|
||||
<widget class="QComboBox" name="LineStyle">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
@@ -107,18 +88,8 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="4">
|
||||
<widget class="QDoubleSpinBox" name="LineWidth">
|
||||
<property name="maximum">
|
||||
<double>99.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="Gui::ColorButton" name="Color">
|
||||
<widget class="QToolButton" name="Color">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
@@ -130,24 +101,50 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="QDoubleSpinBox" name="LineWidth">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>99.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="3">
|
||||
<widget class="QDoubleSpinBox" name="MarkerSize">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Width of all lines (outline and hatch)</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>99.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>Gui::ColorButton</class>
|
||||
<extends>QPushButton</extends>
|
||||
<header>Gui/Widgets.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>Legend</tabstop>
|
||||
<tabstop>Color</tabstop>
|
||||
<tabstop>LineStyle</tabstop>
|
||||
<tabstop>LineWidth</tabstop>
|
||||
<tabstop>MarkerStyle</tabstop>
|
||||
<tabstop>MarkerSize</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
||||
85
src/Mod/Fem/Gui/Resources/ui/PostLineplotIndexAppEdit.ui
Normal file
85
src/Mod/Fem/Gui/Resources/ui/PostLineplotIndexAppEdit.ui
Normal file
@@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Form</class>
|
||||
<widget class="QWidget" name="Form">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>310</width>
|
||||
<height>108</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Y Field:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="YField">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="YComponent">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Index:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSpinBox" name="Index">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>99999999</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>Index</tabstop>
|
||||
<tabstop>YField</tabstop>
|
||||
<tabstop>YComponent</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -408,6 +408,21 @@ void TaskDlgPost::modifyStandardButtons(QDialogButtonBox* box)
|
||||
}
|
||||
}
|
||||
|
||||
void TaskDlgPost::processCollapsedWidgets() {
|
||||
|
||||
for (auto& widget : Content) {
|
||||
if(auto task_box = dynamic_cast<Gui::TaskView::TaskBox*>(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<TaskPostWidget*>(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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1007,6 +1007,7 @@ bool ViewProviderFemPostObject::setEdit(int ModNum)
|
||||
postDlg = new TaskDlgPost(this);
|
||||
setupTaskDialog(postDlg);
|
||||
postDlg->connectSlots();
|
||||
postDlg->processCollapsedWidgets();
|
||||
Gui::Control().showDialog(postDlg);
|
||||
}
|
||||
|
||||
|
||||
@@ -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]):
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"):
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user