FEM: Draft architecture of post data extraction with histogram example
This commit is contained in:
@@ -49,6 +49,13 @@ Note: Can lead to a full recompute of the whole pipeline, hence best to call thi
|
||||
<UserDocu>
|
||||
Returns the names of all scalar fields available on this filter's input.
|
||||
Note: Can lead to a full recompute of the whole pipeline, hence best to call this only in "execute", where the user expects long calculation cycles.
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getOutputAlgorithm">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
Returns the filters vtk algorithm currently used as output (the one generating the Data field). Note that the output algorithm may change depending on filter settings.
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>"
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#ifdef FC_USE_VTK_PYTHON
|
||||
#include <vtkUnstructuredGrid.h>
|
||||
#include <vtkPythonUtil.h>
|
||||
#include <vtkPolyData.h>
|
||||
#endif // BUILD_FEM_VTK
|
||||
|
||||
using namespace Fem;
|
||||
@@ -129,6 +130,9 @@ PyObject* FemPostFilterPy::getInputData(PyObject* args)
|
||||
case VTK_UNSTRUCTURED_GRID:
|
||||
copy = vtkUnstructuredGrid::New();
|
||||
break;
|
||||
case VTK_POLY_DATA:
|
||||
copy = vtkPolyData::New();
|
||||
break;
|
||||
default:
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"cannot return datatype object; not unstructured grid");
|
||||
@@ -183,6 +187,25 @@ PyObject* FemPostFilterPy::getInputScalarFields(PyObject* args)
|
||||
return Py::new_reference_to(list);
|
||||
}
|
||||
|
||||
PyObject* FemPostFilterPy::getOutputAlgorithm(PyObject* args)
|
||||
{
|
||||
#ifdef BUILD_FEM_VTK_WRAPPER
|
||||
// we take no arguments
|
||||
if (!PyArg_ParseTuple(args, "")) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// return python object for the algorithm
|
||||
auto algorithm = getFemPostFilterPtr()->getFilterOutput();
|
||||
PyObject* py_algorithm = vtkPythonUtil::GetObjectFromPointer(algorithm);
|
||||
|
||||
return Py::new_reference_to(py_algorithm);
|
||||
#else
|
||||
PyErr_SetString(PyExc_NotImplementedError, "VTK python wrapper not available");
|
||||
Py_Return;
|
||||
#endif
|
||||
}
|
||||
|
||||
PyObject* FemPostFilterPy::getCustomAttributes(const char* /*attr*/) const
|
||||
{
|
||||
return nullptr;
|
||||
|
||||
@@ -23,6 +23,13 @@ filename: str
|
||||
File extension is automatically detected from data type.</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getDataSet">
|
||||
<Documentation>
|
||||
<UserDocu>getDataset() -> vtkDataSet
|
||||
|
||||
Returns the current output dataset. For normal filters this is equal to the objects Data property output. However, a pipelines Data property could store multiple frames, and hence Data can be of type vtkCompositeData, which is not a vtkDataset. To simplify implementations this function always returns a vtkDataSet, and for a pipeline it will be the dataset of the currently selected frame. Note that the returned value could be None, if no data is set at all.</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
|
||||
</PythonExport>
|
||||
</GenerateModel>
|
||||
|
||||
@@ -29,6 +29,10 @@
|
||||
#include "FemPostObjectPy.h"
|
||||
#include "FemPostObjectPy.cpp"
|
||||
|
||||
#ifdef BUILD_FEM_VTK_WRAPPER
|
||||
#include <vtkDataSet.h>
|
||||
#include <vtkPythonUtil.h>
|
||||
#endif //BUILD_FEM_VTK
|
||||
|
||||
using namespace Fem;
|
||||
|
||||
@@ -55,6 +59,27 @@ PyObject* FemPostObjectPy::writeVTK(PyObject* args)
|
||||
Py_Return;
|
||||
}
|
||||
|
||||
PyObject* FemPostObjectPy::getDataSet(PyObject* args)
|
||||
{
|
||||
#ifdef BUILD_FEM_VTK_WRAPPER
|
||||
// we take no arguments
|
||||
if (!PyArg_ParseTuple(args, "")) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// return python object for the dataset
|
||||
auto dataset = getFemPostObjectPtr()->getDataSet();
|
||||
if (dataset) {
|
||||
PyObject* py_algorithm = vtkPythonUtil::GetObjectFromPointer(dataset);
|
||||
return Py::new_reference_to(py_algorithm);
|
||||
}
|
||||
return Py_None;
|
||||
#else
|
||||
PyErr_SetString(PyExc_NotImplementedError, "VTK python wrapper not available");
|
||||
Py_Return;
|
||||
#endif
|
||||
}
|
||||
|
||||
PyObject* FemPostObjectPy::getCustomAttributes(const char* /*attr*/) const
|
||||
{
|
||||
return nullptr;
|
||||
|
||||
@@ -118,6 +118,12 @@ public:
|
||||
unsigned int getFrameNumber();
|
||||
std::vector<double> getFrameValues();
|
||||
|
||||
// output algorithm handling
|
||||
vtkSmartPointer<vtkAlgorithm> getOutputAlgorithm()
|
||||
{
|
||||
return m_source_algorithm;
|
||||
}
|
||||
|
||||
protected:
|
||||
void onChanged(const App::Property* prop) override;
|
||||
bool allowObject(App::DocumentObject* obj) override;
|
||||
|
||||
@@ -71,5 +71,12 @@ Load a single result object or create a multiframe result by loading multiple re
|
||||
<UserDocu>Change name of data arrays</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getOutputAlgorithm">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
Returns the pipeline vtk algorithm, which generates the data passed to the pipelines filters. Note that the output algorithm may change depending on pipeline settings.
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>"
|
||||
</PythonExport>
|
||||
</GenerateModel>
|
||||
|
||||
@@ -34,6 +34,10 @@
|
||||
#include "FemPostPipelinePy.cpp"
|
||||
// clang-format on
|
||||
|
||||
#ifdef BUILD_FEM_VTK_WRAPPER
|
||||
#include <vtkPythonUtil.h>
|
||||
#endif //BUILD_FEM_VTK
|
||||
|
||||
|
||||
using namespace Fem;
|
||||
|
||||
@@ -313,6 +317,25 @@ PyObject* FemPostPipelinePy::renameArrays(PyObject* args)
|
||||
Py_Return;
|
||||
}
|
||||
|
||||
PyObject* FemPostPipelinePy::getOutputAlgorithm(PyObject* args)
|
||||
{
|
||||
#ifdef BUILD_FEM_VTK_WRAPPER
|
||||
// we take no arguments
|
||||
if (!PyArg_ParseTuple(args, "")) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// return python object for the algorithm
|
||||
auto algorithm = getFemPostPipelinePtr()->getOutputAlgorithm();
|
||||
PyObject* py_algorithm = vtkPythonUtil::GetObjectFromPointer(algorithm);
|
||||
|
||||
return Py::new_reference_to(py_algorithm);
|
||||
#else
|
||||
PyErr_SetString(PyExc_NotImplementedError, "VTK python wrapper not available");
|
||||
Py_Return;
|
||||
#endif
|
||||
}
|
||||
|
||||
PyObject* FemPostPipelinePy::getCustomAttributes(const char* /*attr*/) const
|
||||
{
|
||||
return nullptr;
|
||||
|
||||
@@ -32,8 +32,11 @@
|
||||
#include <vtkStructuredGrid.h>
|
||||
#include <vtkUniformGrid.h>
|
||||
#include <vtkUnstructuredGrid.h>
|
||||
#include <vtkTable.h>
|
||||
#include <vtkXMLTableWriter.h>
|
||||
#include <vtkXMLDataSetWriter.h>
|
||||
#include <vtkXMLMultiBlockDataWriter.h>
|
||||
#include <vtkXMLTableReader.h>
|
||||
#include <vtkXMLMultiBlockDataReader.h>
|
||||
#include <vtkXMLImageDataReader.h>
|
||||
#include <vtkXMLPolyDataReader.h>
|
||||
@@ -243,6 +246,9 @@ void PropertyPostDataObject::createDataObjectByExternalType(vtkSmartPointer<vtkD
|
||||
case VTK_MULTIPIECE_DATA_SET:
|
||||
m_dataObject = vtkSmartPointer<vtkMultiPieceDataSet>::New();
|
||||
break;
|
||||
case VTK_TABLE:
|
||||
m_dataObject = vtkSmartPointer<vtkTable>::New();
|
||||
break;
|
||||
default:
|
||||
throw Base::TypeError("Unsupported VTK data type");
|
||||
};
|
||||
@@ -313,6 +319,9 @@ void PropertyPostDataObject::Save(Base::Writer& writer) const
|
||||
case VTK_MULTIBLOCK_DATA_SET:
|
||||
extension = "zip";
|
||||
break;
|
||||
case VTK_TABLE:
|
||||
extension = ".vtt";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
@@ -382,13 +391,16 @@ void PropertyPostDataObject::SaveDocFile(Base::Writer& writer) const
|
||||
xmlWriter = vtkSmartPointer<vtkXMLMultiBlockDataWriter>::New();
|
||||
xmlWriter->SetInputDataObject(m_dataObject);
|
||||
xmlWriter->SetFileName(datafile.filePath().c_str());
|
||||
xmlWriter->SetDataModeToBinary();
|
||||
}
|
||||
else if (m_dataObject->IsA("vtkTable")) {
|
||||
xmlWriter = vtkSmartPointer<vtkXMLTableWriter>::New();
|
||||
xmlWriter->SetInputDataObject(m_dataObject);
|
||||
xmlWriter->SetFileName(fi.filePath().c_str());
|
||||
}
|
||||
else {
|
||||
xmlWriter = vtkSmartPointer<vtkXMLDataSetWriter>::New();
|
||||
xmlWriter->SetInputDataObject(m_dataObject);
|
||||
xmlWriter->SetFileName(fi.filePath().c_str());
|
||||
xmlWriter->SetDataModeToBinary();
|
||||
|
||||
#ifdef VTK_CELL_ARRAY_V2
|
||||
// Looks like an invalid data object that causes a crash with vtk9
|
||||
@@ -399,6 +411,7 @@ void PropertyPostDataObject::SaveDocFile(Base::Writer& writer) const
|
||||
}
|
||||
#endif
|
||||
}
|
||||
xmlWriter->SetDataModeToBinary();
|
||||
|
||||
if (xmlWriter->Write() != 1) {
|
||||
// Note: Do NOT throw an exception here because if the tmp. file could
|
||||
@@ -481,6 +494,9 @@ void PropertyPostDataObject::RestoreDocFile(Base::Reader& reader)
|
||||
else if (extension == "vti") {
|
||||
xmlReader = vtkSmartPointer<vtkXMLImageDataReader>::New();
|
||||
}
|
||||
else if (extension == "vtt") {
|
||||
xmlReader = vtkSmartPointer<vtkXMLTableReader>::New();
|
||||
}
|
||||
else if (extension == "zip") {
|
||||
|
||||
// first unzip the file into a datafolder
|
||||
|
||||
@@ -36,7 +36,7 @@ SET(FemBaseModules_SRCS
|
||||
coding_conventions.md
|
||||
Init.py
|
||||
InitGui.py
|
||||
ObjectsFem.py
|
||||
# ObjectsFem.py
|
||||
TestFemApp.py
|
||||
CreateLabels.py
|
||||
)
|
||||
@@ -182,6 +182,8 @@ SET(FemObjects_SRCS
|
||||
femobjects/base_femelement.py
|
||||
femobjects/base_femmeshelement.py
|
||||
femobjects/base_fempythonobject.py
|
||||
femobjects/base_fempostextractors.py
|
||||
femobjects/base_fempostvisualizations.py
|
||||
femobjects/constant_vacuumpermittivity.py
|
||||
femobjects/constraint_bodyheatsource.py
|
||||
femobjects/constraint_centrif.py
|
||||
@@ -217,6 +219,8 @@ if(BUILD_FEM_VTK_PYTHON)
|
||||
SET(FemObjects_SRCS
|
||||
${FemObjects_SRCS}
|
||||
femobjects/post_glyphfilter.py
|
||||
femobjects/post_extract1D.py
|
||||
femobjects/post_histogram.py
|
||||
)
|
||||
endif(BUILD_FEM_VTK_PYTHON)
|
||||
|
||||
@@ -597,6 +601,7 @@ SET(FemGuiTaskPanels_SRCS
|
||||
femtaskpanels/__init__.py
|
||||
femtaskpanels/base_femtaskpanel.py
|
||||
femtaskpanels/base_femlogtaskpanel.py
|
||||
femtaskpanels/base_fempostpanel.py
|
||||
femtaskpanels/task_constraint_bodyheatsource.py
|
||||
femtaskpanels/task_constraint_centrif.py
|
||||
femtaskpanels/task_constraint_currentdensity.py
|
||||
@@ -628,6 +633,8 @@ if(BUILD_FEM_VTK_PYTHON)
|
||||
SET(FemGuiTaskPanels_SRCS
|
||||
${FemGuiTaskPanels_SRCS}
|
||||
femtaskpanels/task_post_glyphfilter.py
|
||||
femtaskpanels/task_post_histogram.py
|
||||
femtaskpanels/task_post_extractor.py
|
||||
)
|
||||
endif(BUILD_FEM_VTK_PYTHON)
|
||||
|
||||
@@ -642,6 +649,10 @@ SET(FemGuiUtils_SRCS
|
||||
femguiutils/migrate_gui.py
|
||||
femguiutils/selection_widgets.py
|
||||
femguiutils/vtk_module_handling.py
|
||||
femguiutils/vtk_table_view.py
|
||||
femguiutils/data_extraction.py
|
||||
femguiutils/extract_link_view.py
|
||||
femguiutils/post_visualization.py
|
||||
)
|
||||
|
||||
SET(FemGuiViewProvider_SRCS
|
||||
@@ -651,6 +662,7 @@ SET(FemGuiViewProvider_SRCS
|
||||
femviewprovider/view_base_femmaterial.py
|
||||
femviewprovider/view_base_femmeshelement.py
|
||||
femviewprovider/view_base_femobject.py
|
||||
femviewprovider/view_base_fempostvisualization.py
|
||||
femviewprovider/view_constant_vacuumpermittivity.py
|
||||
femviewprovider/view_constraint_bodyheatsource.py
|
||||
femviewprovider/view_constraint_centrif.py
|
||||
@@ -686,6 +698,8 @@ if(BUILD_FEM_VTK_PYTHON)
|
||||
SET(FemGuiViewProvider_SRCS
|
||||
${FemGuiViewProvider_SRCS}
|
||||
femviewprovider/view_post_glyphfilter.py
|
||||
femviewprovider/view_post_extract.py
|
||||
femviewprovider/view_post_histogram.py
|
||||
)
|
||||
endif(BUILD_FEM_VTK_PYTHON)
|
||||
|
||||
|
||||
@@ -291,6 +291,8 @@ if(BUILD_FEM_VTK)
|
||||
SphereWidget.ui
|
||||
TaskPostBoxes.h
|
||||
TaskPostBoxes.cpp
|
||||
TaskPostExtraction.h
|
||||
TaskPostExtraction.cpp
|
||||
TaskPostCalculator.ui
|
||||
TaskPostClip.ui
|
||||
TaskPostContours.ui
|
||||
@@ -440,6 +442,10 @@ SET(FemGuiPythonUI_SRCS
|
||||
Resources/ui/SolverCalculiX.ui
|
||||
Resources/ui/SolverCcxTools.ui
|
||||
Resources/ui/TaskPostGlyph.ui
|
||||
Resources/ui/TaskPostExtraction.ui
|
||||
Resources/ui/TaskPostHistogram.ui
|
||||
Resources/ui/PostHistogramFieldViewEdit.ui
|
||||
Resources/ui/PostHistogramFieldAppEdit.ui
|
||||
)
|
||||
|
||||
ADD_CUSTOM_TARGET(FemPythonUi ALL
|
||||
|
||||
@@ -86,6 +86,12 @@
|
||||
<file>icons/FEM_PostFrames.svg</file>
|
||||
<file>icons/FEM_PostBranchFilter.svg</file>
|
||||
<file>icons/FEM_PostPipelineFromResult.svg</file>
|
||||
<file>icons/FEM_PostLineplot.svg</file>
|
||||
<file>icons/FEM_PostPlotline.svg</file>
|
||||
<file>icons/FEM_PostHistogram.svg</file>
|
||||
<file>icons/FEM_PostSpreadsheet.svg</file>
|
||||
<file>icons/FEM_PostField.svg</file>
|
||||
<file>icons/FEM_PostIndex.svg</file>
|
||||
<file>icons/FEM_ResultShow.svg</file>
|
||||
<file>icons/FEM_ResultsPurge.svg</file>
|
||||
|
||||
|
||||
60
src/Mod/Fem/Gui/Resources/icons/FEM_PostField.svg
Normal file
60
src/Mod/Fem/Gui/Resources/icons/FEM_PostField.svg
Normal file
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
fill="currentColor"
|
||||
class="bi bi-table"
|
||||
viewBox="0 0 16 16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="FEM_PostField.svg"
|
||||
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="34.537747"
|
||||
inkscape:cx="8.8743484"
|
||||
inkscape:cy="9.6850556"
|
||||
inkscape:window-width="3132"
|
||||
inkscape:window-height="1772"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<circle
|
||||
style="fill:#800080;stroke:none;stroke-width:14.2999;stroke-linejoin:round"
|
||||
id="path2"
|
||||
cx="4.0849209"
|
||||
cy="4.0125365"
|
||||
r="3.9000001" />
|
||||
<circle
|
||||
style="fill:#800080;stroke:none;stroke-width:14.2999;stroke-linejoin:round"
|
||||
id="circle2"
|
||||
cx="11.960362"
|
||||
cy="4.0414896"
|
||||
r="3.9000001" />
|
||||
<circle
|
||||
style="fill:#800080;stroke:none;stroke-width:14.2999;stroke-linejoin:round"
|
||||
id="circle4"
|
||||
cx="4.0270133"
|
||||
cy="12.003795"
|
||||
r="3.9000001" />
|
||||
<circle
|
||||
style="fill:#800080;stroke:none;stroke-width:14.2999;stroke-linejoin:round"
|
||||
id="circle5"
|
||||
cx="11.902454"
|
||||
cy="12.061702"
|
||||
r="3.9000001" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
40
src/Mod/Fem/Gui/Resources/icons/FEM_PostHistogram.svg
Normal file
40
src/Mod/Fem/Gui/Resources/icons/FEM_PostHistogram.svg
Normal file
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
fill="currentColor"
|
||||
class="bi bi-bar-chart"
|
||||
viewBox="0 0 16 16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="FEM_PostHistogram.svg"
|
||||
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="97.6875"
|
||||
inkscape:cx="8"
|
||||
inkscape:cy="8"
|
||||
inkscape:window-width="3132"
|
||||
inkscape:window-height="1772"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<path
|
||||
d="M4 11H2v3h2zm5-4H7v7h2zm5-5v12h-2V2zm-2-1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zM6 7a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v7a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1zm-5 4a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v3a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1z"
|
||||
id="path1"
|
||||
style="fill:#800080;fill-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
42
src/Mod/Fem/Gui/Resources/icons/FEM_PostIndex.svg
Normal file
42
src/Mod/Fem/Gui/Resources/icons/FEM_PostIndex.svg
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
fill="currentColor"
|
||||
class="bi bi-table"
|
||||
viewBox="0 0 16 16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="FEM_PostIndex.svg"
|
||||
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="34.537747"
|
||||
inkscape:cx="8.8453946"
|
||||
inkscape:cy="9.6561018"
|
||||
inkscape:window-width="3132"
|
||||
inkscape:window-height="1772"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<circle
|
||||
style="fill:#800080;stroke:none;stroke-width:14.2999;stroke-linejoin:round"
|
||||
id="path2"
|
||||
cx="8.0515957"
|
||||
cy="7.9792109"
|
||||
r="3.9000001" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
41
src/Mod/Fem/Gui/Resources/icons/FEM_PostLineplot.svg
Normal file
41
src/Mod/Fem/Gui/Resources/icons/FEM_PostLineplot.svg
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
fill="currentColor"
|
||||
class="bi bi-graph-up"
|
||||
viewBox="0 0 16 16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="FEM_PostLineplot.svg"
|
||||
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="97.6875"
|
||||
inkscape:cx="8"
|
||||
inkscape:cy="8"
|
||||
inkscape:window-width="3132"
|
||||
inkscape:window-height="1772"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M0 0h1v15h15v1H0zm14.817 3.113a.5.5 0 0 1 .07.704l-4.5 5.5a.5.5 0 0 1-.74.037L7.06 6.767l-3.656 5.027a.5.5 0 0 1-.808-.588l4-5.5a.5.5 0 0 1 .758-.06l2.609 2.61 4.15-5.073a.5.5 0 0 1 .704-.07"
|
||||
id="path1"
|
||||
style="fill:#800080;fill-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
41
src/Mod/Fem/Gui/Resources/icons/FEM_PostPlotline.svg
Normal file
41
src/Mod/Fem/Gui/Resources/icons/FEM_PostPlotline.svg
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
fill="currentColor"
|
||||
class="bi bi-graph-up"
|
||||
viewBox="0 0 16 16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="Fem_PostPlotline.svg"
|
||||
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="69.075494"
|
||||
inkscape:cx="11.458478"
|
||||
inkscape:cy="9.6343864"
|
||||
inkscape:window-width="3132"
|
||||
inkscape:window-height="1772"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="m 14.817,3.113 c 0.21387,0.1750014 0.245222,0.4903105 0.07,0.704 l -4.5,5.5 C 10.199729,9.5455781 9.8561383,9.5627576 9.647,9.354 L 7.06,6.767 3.404,11.794 C 3.006988,12.298831 2.2377371,11.73903 2.596,11.206 l 4,-5.5 C 6.7765369,5.4573046 7.1365852,5.4288047 7.354,5.646 l 2.609,2.61 4.15,-5.073 c 0.175001,-0.2138702 0.49031,-0.245222 0.704,-0.07"
|
||||
id="path1"
|
||||
sodipodi:nodetypes="cccccccccccc" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
40
src/Mod/Fem/Gui/Resources/icons/FEM_PostSpreadsheet.svg
Normal file
40
src/Mod/Fem/Gui/Resources/icons/FEM_PostSpreadsheet.svg
Normal file
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
fill="currentColor"
|
||||
class="bi bi-table"
|
||||
viewBox="0 0 16 16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="FEM_PostSpreadsheet.svg"
|
||||
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="97.6875"
|
||||
inkscape:cx="8"
|
||||
inkscape:cy="8"
|
||||
inkscape:window-width="3132"
|
||||
inkscape:window-height="1772"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<path
|
||||
d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm15 2h-4v3h4zm0 4h-4v3h4zm0 4h-4v3h3a1 1 0 0 0 1-1zm-5 3v-3H6v3zm-5 0v-3H1v2a1 1 0 0 0 1 1zm-4-4h4V8H1zm0-4h4V4H1zm5-3v3h4V4zm4 4H6v3h4z"
|
||||
id="path1"
|
||||
style="fill:#800080;fill-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
66
src/Mod/Fem/Gui/Resources/ui/PostHistogramFieldAppEdit.ui
Normal file
66
src/Mod/Fem/Gui/Resources/ui/PostHistogramFieldAppEdit.ui
Normal file
@@ -0,0 +1,66 @@
|
||||
<?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>317</width>
|
||||
<height>118</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Field:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" 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="1" 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="2" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Frames:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="Extract">
|
||||
<property name="text">
|
||||
<string>One field for all frames</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
162
src/Mod/Fem/Gui/Resources/ui/PostHistogramFieldViewEdit.ui
Normal file
162
src/Mod/Fem/Gui/Resources/ui/PostHistogramFieldViewEdit.ui
Normal file
@@ -0,0 +1,162 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PostHistogramEdit</class>
|
||||
<widget class="QWidget" name="PostHistogramEdit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>293</width>
|
||||
<height>126</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="2" column="2">
|
||||
<widget class="QComboBox" name="LineStyle">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Outline draw style (None does not draw outlines)</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>None</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="3">
|
||||
<widget class="QDoubleSpinBox" name="LineWidth">
|
||||
<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="1" column="2">
|
||||
<widget class="QComboBox" name="Hatch">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Hatch pattern</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>None</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Lines:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="QSpinBox" name="HatchDensity">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Density of hatch pattern</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Bars:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="Gui::ColorButton" name="LineColor">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Color of all lines (bar outline and hatches)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="Gui::ColorButton" name="BarColor">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Color of the bars in histogram</string>
|
||||
</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>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>Gui::ColorButton</class>
|
||||
<extends>QPushButton</extends>
|
||||
<header>Gui/Widgets.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
54
src/Mod/Fem/Gui/Resources/ui/TaskPostExtraction.ui
Normal file
54
src/Mod/Fem/Gui/Resources/ui/TaskPostExtraction.ui
Normal file
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TaskPostExtraction</class>
|
||||
<widget class="QWidget" name="TaskPostExtraction">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>515</width>
|
||||
<height>36</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<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="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QPushButton" name="Summary">
|
||||
<property name="text">
|
||||
<string>Data Summary</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="Data">
|
||||
<property name="text">
|
||||
<string>Show Data</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
223
src/Mod/Fem/Gui/Resources/ui/TaskPostHistogram.ui
Normal file
223
src/Mod/Fem/Gui/Resources/ui/TaskPostHistogram.ui
Normal file
@@ -0,0 +1,223 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TaskPostGlyph</class>
|
||||
<widget class="QWidget" name="TaskPostGlyph">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>343</width>
|
||||
<height>498</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Glyph settings</string>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LayoutDirection::LeftToRight</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout_4">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="toolTip">
|
||||
<string>The form of the glyph</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Bins</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSpinBox" name="Bins">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LayoutDirection::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>1000</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="toolTip">
|
||||
<string>Which vector field is used to orient the glyphs</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="Type">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Which vector field is used to orient the glyphs</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>None</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="Cumulative">
|
||||
<property name="text">
|
||||
<string>Cumulative</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>Legend</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="LegendShow">
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LayoutDirection::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Show</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="LegendPos">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="Scale">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Labels</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="Title"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Y Axis</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="YLabel"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="toolTip">
|
||||
<string>If the scale data is a vector this property decides if the glyph is scaled by vector magnitude or by the individual components</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>X Axis</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="toolTip">
|
||||
<string>A constant multiplier the glyphs are scaled with</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Title</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="XLabel"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Visuals</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="2" column="0">
|
||||
<widget class="QDoubleSpinBox" name="BarWidth">
|
||||
<property name="maximum">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.050000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Hatch Line Width</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Bar width</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QDoubleSpinBox" name="HatchWidth"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -64,7 +64,6 @@
|
||||
#include "ui_TaskPostFrames.h"
|
||||
#include "ui_TaskPostBranch.h"
|
||||
|
||||
|
||||
#include "FemSettings.h"
|
||||
#include "TaskPostBoxes.h"
|
||||
#include "ViewProviderFemPostFilter.h"
|
||||
@@ -72,6 +71,9 @@
|
||||
#include "ViewProviderFemPostObject.h"
|
||||
#include "ViewProviderFemPostBranchFilter.h"
|
||||
|
||||
#include <vtkQtTableView.h>
|
||||
#include <vtkQtTableModelAdapter.h>
|
||||
#include <vtkAttributeDataToTableFilter.h>
|
||||
|
||||
using namespace FemGui;
|
||||
using namespace Gui;
|
||||
@@ -214,9 +216,14 @@ TaskPostWidget::TaskPostWidget(Gui::ViewProviderDocumentObject* view,
|
||||
setWindowTitle(title);
|
||||
setWindowIcon(icon);
|
||||
m_icon = icon;
|
||||
|
||||
m_connection = m_object->signalChanged.connect(boost::bind(&TaskPostWidget::handlePropertyChange, this, boost::placeholders::_1, boost::placeholders::_2));
|
||||
}
|
||||
|
||||
TaskPostWidget::~TaskPostWidget() = default;
|
||||
TaskPostWidget::~TaskPostWidget()
|
||||
{
|
||||
m_connection.disconnect();
|
||||
};
|
||||
|
||||
bool TaskPostWidget::autoApply()
|
||||
{
|
||||
@@ -256,6 +263,14 @@ void TaskPostWidget::updateEnumerationList(App::PropertyEnumeration& prop, QComb
|
||||
box->setCurrentIndex(index);
|
||||
}
|
||||
|
||||
void TaskPostWidget::handlePropertyChange(const App::DocumentObject& obj, const App::Property& prop)
|
||||
{
|
||||
if (auto postobj = m_object.get<Fem::FemPostObject>()) {
|
||||
if (&prop == &postobj->Data) {
|
||||
this->onPostDataChanged(postobj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ***************************************************************************
|
||||
// simulation dialog for the TaskView
|
||||
@@ -475,7 +490,6 @@ void TaskPostDisplay::onTransparencyValueChanged(int i)
|
||||
void TaskPostDisplay::applyPythonCode()
|
||||
{}
|
||||
|
||||
|
||||
// ***************************************************************************
|
||||
// functions
|
||||
TaskPostFunction::TaskPostFunction(ViewProviderFemPostFunction* view, QWidget* parent)
|
||||
|
||||
@@ -42,6 +42,7 @@ class Ui_TaskPostWarpVector;
|
||||
class Ui_TaskPostCut;
|
||||
class Ui_TaskPostFrames;
|
||||
class Ui_TaskPostBranch;
|
||||
class Ui_TaskPostExtraction;
|
||||
|
||||
class SoFontStyle;
|
||||
class SoText2;
|
||||
@@ -187,10 +188,15 @@ protected:
|
||||
|
||||
static void updateEnumerationList(App::PropertyEnumeration&, QComboBox* box);
|
||||
|
||||
// object update handling
|
||||
void handlePropertyChange(const App::DocumentObject&, const App::Property&);
|
||||
virtual void onPostDataChanged(Fem::FemPostObject*) {};
|
||||
|
||||
private:
|
||||
QPixmap m_icon;
|
||||
App::DocumentObjectWeakPtrT m_object;
|
||||
Gui::ViewProviderWeakPtrT m_view;
|
||||
boost::signals2::connection m_connection;
|
||||
};
|
||||
|
||||
|
||||
@@ -267,7 +273,6 @@ private:
|
||||
std::unique_ptr<Ui_TaskPostDisplay> ui;
|
||||
};
|
||||
|
||||
|
||||
// ***************************************************************************
|
||||
// functions
|
||||
class ViewProviderFemPostFunction;
|
||||
|
||||
169
src/Mod/Fem/Gui/TaskPostExtraction.cpp
Normal file
169
src/Mod/Fem/Gui/TaskPostExtraction.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2015 Stefan Tröger <stefantroeger@gmx.net> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
#ifndef _PreComp_
|
||||
|
||||
#endif
|
||||
|
||||
#include <Gui/PythonWrapper.h>
|
||||
#include <Gui/BitmapFactory.h>
|
||||
#include <Mod/Fem/App/FemPostFilter.h>
|
||||
#include <Mod/Fem/App/FemPostPipeline.h>
|
||||
|
||||
#include <QString>
|
||||
#include <QTableView>
|
||||
#include <QHeaderView>
|
||||
#include <QDialog>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include <vtkTable.h>
|
||||
#include <vtkAttributeDataToTableFilter.h>
|
||||
#include <vtkSplitColumnComponents.h>
|
||||
#include <vtkAbstractArray.h>
|
||||
|
||||
#include "ViewProviderFemPostObject.h"
|
||||
#include "TaskPostExtraction.h"
|
||||
|
||||
using namespace FemGui;
|
||||
using namespace Gui;
|
||||
|
||||
|
||||
// ***************************************************************************
|
||||
// box to handle data extractions
|
||||
|
||||
TaskPostExtraction::TaskPostExtraction(ViewProviderFemPostObject* view, QWidget* parent)
|
||||
: TaskPostWidget(view,
|
||||
Gui::BitmapFactory().pixmap("FEM_PostHistogram"), QString(),
|
||||
parent)
|
||||
{
|
||||
// we load the python implementation, and try to get the widget from it, to add
|
||||
// directly our widget
|
||||
|
||||
setWindowTitle(tr("Data and extractions"));
|
||||
|
||||
Base::PyGILStateLocker lock;
|
||||
|
||||
Py::Module mod(PyImport_ImportModule("femguiutils.data_extraction"), true);
|
||||
if (mod.isNull())
|
||||
throw Base::ImportError("Unable to import data extraction widget");
|
||||
|
||||
try {
|
||||
Py::Callable method(mod.getAttr(std::string("DataExtraction")));
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(view->getPyObject()));
|
||||
m_panel = Py::Object(method.apply(args));
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
}
|
||||
|
||||
if (m_panel.hasAttr(std::string("widget"))) {
|
||||
Py::Object pywidget(m_panel.getAttr(std::string("widget")));
|
||||
|
||||
Gui::PythonWrapper wrap;
|
||||
if (wrap.loadCoreModule()) {
|
||||
QObject* object = wrap.toQObject(pywidget);
|
||||
if (object) {
|
||||
QWidget* widget = qobject_cast<QWidget*>(object);
|
||||
if (widget) {
|
||||
// finally we have the usable QWidget. Add to us!
|
||||
|
||||
auto layout = new QVBoxLayout();
|
||||
layout->addWidget(widget);
|
||||
setLayout(layout);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// if we are here somethign went wrong!
|
||||
throw Base::ImportError("Unable to import data extraction widget");
|
||||
};
|
||||
|
||||
TaskPostExtraction::~TaskPostExtraction() {
|
||||
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (m_panel.hasAttr(std::string("widget"))) {
|
||||
m_panel.setAttr(std::string("widget"), Py::None());
|
||||
}
|
||||
m_panel = Py::None();
|
||||
}
|
||||
catch (Py::AttributeError& e) {
|
||||
e.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void TaskPostExtraction::onPostDataChanged(Fem::FemPostObject* obj)
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (m_panel.hasAttr(std::string("onPostDataChanged"))) {
|
||||
Py::Callable method(m_panel.getAttr(std::string("onPostDataChanged")));
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(obj->getPyObject()));
|
||||
method.apply(args);
|
||||
}
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
}
|
||||
};
|
||||
|
||||
bool TaskPostExtraction::isGuiTaskOnly()
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (m_panel.hasAttr(std::string("isGuiTaskOnly"))) {
|
||||
Py::Callable method(m_panel.getAttr(std::string("isGuiTaskOnly")));
|
||||
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;
|
||||
};
|
||||
|
||||
void TaskPostExtraction::apply()
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (m_panel.hasAttr(std::string("apply"))) {
|
||||
Py::Callable method(m_panel.getAttr(std::string("apply")));
|
||||
method.apply();
|
||||
}
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_TaskPostExtraction.cpp"
|
||||
67
src/Mod/Fem/Gui/TaskPostExtraction.h
Normal file
67
src/Mod/Fem/Gui/TaskPostExtraction.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2025 Stefan Tröger <stefantroeger@gmx.net> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef GUI_TASKVIEW_TaskPostExtraction_H
|
||||
#define GUI_TASKVIEW_TaskPostExtraction_H
|
||||
|
||||
#include <Gui/DocumentObserver.h>
|
||||
#include <Gui/TaskView/TaskDialog.h>
|
||||
#include <Gui/TaskView/TaskView.h>
|
||||
#include <Gui/ViewProviderDocumentObject.h>
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
|
||||
#include "TaskPostBoxes.h"
|
||||
|
||||
#include <vtkSmartPointer.h>
|
||||
#include <vtkTableAlgorithm.h>
|
||||
|
||||
class Ui_TaskPostExtraction;
|
||||
|
||||
|
||||
namespace FemGui
|
||||
{
|
||||
|
||||
// ***************************************************************************
|
||||
// box to handle data extractions: It is implemented in python, the c++
|
||||
// code is used to access it and manage it for the c++ task panels
|
||||
class TaskPostExtraction: public TaskPostWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TaskPostExtraction(ViewProviderFemPostObject* view, QWidget* parent = nullptr);
|
||||
~TaskPostExtraction();
|
||||
|
||||
protected:
|
||||
bool isGuiTaskOnly() override;
|
||||
void apply() override;
|
||||
void onPostDataChanged(Fem::FemPostObject* obj) override;
|
||||
|
||||
private:
|
||||
Py::Object m_panel;
|
||||
};
|
||||
|
||||
|
||||
} // namespace FemGui
|
||||
|
||||
#endif // GUI_TASKVIEW_TaskPostExtraction_H
|
||||
135
src/Mod/Fem/Gui/TaskPostExtraction.ui
Normal file
135
src/Mod/Fem/Gui/TaskPostExtraction.ui
Normal file
@@ -0,0 +1,135 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TaskPostExtraction</class>
|
||||
<widget class="QWidget" name="TaskPostExtraction">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>375</width>
|
||||
<height>302</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QPushButton" name="Summary">
|
||||
<property name="text">
|
||||
<string>Data Summary</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="Data">
|
||||
<property name="text">
|
||||
<string>Show Data</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Policy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>10</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Data used in:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="AddBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Add data to</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="CreateBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Create and add</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="ExtractionArea">
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="ExtractionContent">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>359</width>
|
||||
<height>188</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -20,5 +20,10 @@
|
||||
<UserDocu>Returns the display option task panel for a post processing edit task dialog.</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="createExtractionTaskWidget">
|
||||
<Documentation>
|
||||
<UserDocu>Returns the data extraction task panel for a post processing edit task dialog.</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
</PythonExport>
|
||||
</GenerateModel>
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <Gui/PythonWrapper.h>
|
||||
#include "ViewProviderFemPostFilter.h"
|
||||
#include "TaskPostBoxes.h"
|
||||
#include "TaskPostExtraction.h"
|
||||
// inclusion of the generated files (generated out of ViewProviderFemPostFilterPy.xml)
|
||||
#include "ViewProviderFemPostFilterPy.h"
|
||||
#include "ViewProviderFemPostFilterPy.cpp"
|
||||
@@ -60,6 +61,24 @@ PyObject* ViewProviderFemPostFilterPy::createDisplayTaskWidget(PyObject* args)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PyObject* ViewProviderFemPostFilterPy::createExtractionTaskWidget(PyObject* args)
|
||||
{
|
||||
// we take no arguments
|
||||
if (!PyArg_ParseTuple(args, "")) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto panel = new TaskPostExtraction(getViewProviderFemPostObjectPtr());
|
||||
|
||||
Gui::PythonWrapper wrap;
|
||||
if (wrap.loadCoreModule()) {
|
||||
return Py::new_reference_to(wrap.fromQWidget(panel));
|
||||
}
|
||||
|
||||
PyErr_SetString(PyExc_TypeError, "creating the panel failed");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PyObject* ViewProviderFemPostFilterPy::getCustomAttributes(const char* /*attr*/) const
|
||||
{
|
||||
return nullptr;
|
||||
|
||||
@@ -67,6 +67,7 @@
|
||||
#include <Mod/Fem/App/FemPostFilter.h>
|
||||
|
||||
#include "TaskPostBoxes.h"
|
||||
#include "TaskPostExtraction.h"
|
||||
#include "ViewProviderAnalysis.h"
|
||||
#include "ViewProviderFemPostObject.h"
|
||||
|
||||
@@ -1019,8 +1020,11 @@ bool ViewProviderFemPostObject::setEdit(int ModNum)
|
||||
void ViewProviderFemPostObject::setupTaskDialog(TaskDlgPost* dlg)
|
||||
{
|
||||
assert(dlg->getView() == this);
|
||||
auto panel = new TaskPostDisplay(this);
|
||||
dlg->addTaskBox(panel->windowIcon().pixmap(32), panel);
|
||||
auto disp_panel = new TaskPostDisplay(this);
|
||||
dlg->addTaskBox(disp_panel->windowIcon().pixmap(32), disp_panel);
|
||||
|
||||
auto extr_panel = new TaskPostExtraction(this);
|
||||
dlg->addTaskBox(extr_panel->windowIcon().pixmap(32), extr_panel);
|
||||
}
|
||||
|
||||
void ViewProviderFemPostObject::unsetEdit(int ModNum)
|
||||
|
||||
@@ -214,7 +214,11 @@ Gui::ToolBarItem* Workbench::setupToolBars() const
|
||||
<< "FEM_PostFilterDataAtPoint"
|
||||
<< "FEM_PostFilterCalculator"
|
||||
<< "Separator"
|
||||
<< "FEM_PostCreateFunctions";
|
||||
<< "FEM_PostCreateFunctions"
|
||||
#ifdef BUILD_FEM_VTK_WRAPPER
|
||||
<< "FEM_PostVisualization"
|
||||
#endif
|
||||
;
|
||||
#endif
|
||||
|
||||
Gui::ToolBarItem* utils = new Gui::ToolBarItem(root);
|
||||
@@ -366,7 +370,11 @@ Gui::MenuItem* Workbench::setupMenuBar() const
|
||||
<< "FEM_PostFilterDataAtPoint"
|
||||
<< "FEM_PostFilterCalculator"
|
||||
<< "Separator"
|
||||
<< "FEM_PostCreateFunctions";
|
||||
<< "FEM_PostCreateFunctions"
|
||||
#ifdef BUILD_FEM_VTK_WRAPPER
|
||||
<< "FEM_PostVisualization"
|
||||
#endif
|
||||
;
|
||||
#endif
|
||||
|
||||
Gui::MenuItem* utils = new Gui::MenuItem;
|
||||
|
||||
@@ -686,6 +686,48 @@ def makePostVtkResult(doc, result_data, name="VtkResult"):
|
||||
return obj
|
||||
|
||||
|
||||
def makePostVtkLinePlot(doc, name="Lineplot"):
|
||||
"""makePostVtkLineplot(document, [name]):
|
||||
creates a FEM post processing line plot
|
||||
"""
|
||||
obj = doc.addObject("App::FeaturePython", name)
|
||||
from femobjects import post_lineplot
|
||||
|
||||
post_lineplot.PostLinePlot(obj)
|
||||
if FreeCAD.GuiUp:
|
||||
from femviewprovider import view_post_lineplot
|
||||
view_post_lineplot.VPPostLinePlot(obj.ViewObject)
|
||||
return
|
||||
|
||||
|
||||
def makePostVtkHistogramFieldData(doc, name="FieldData1D"):
|
||||
"""makePostVtkFieldData1D(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.PostHistogramFieldData(obj)
|
||||
if FreeCAD.GuiUp:
|
||||
from femviewprovider import view_post_histogram
|
||||
view_post_histogram.VPPostHistogramFieldData(obj.ViewObject)
|
||||
return obj
|
||||
|
||||
|
||||
def makePostVtkHistogram(doc, name="Histogram"):
|
||||
"""makePostVtkHistogram(document, [name]):
|
||||
creates a FEM post processing histogram plot
|
||||
"""
|
||||
obj = doc.addObject("App::FeaturePython", name)
|
||||
from femobjects import post_histogram
|
||||
|
||||
post_histogram.PostHistogram(obj)
|
||||
if FreeCAD.GuiUp:
|
||||
from femviewprovider import view_post_histogram
|
||||
view_post_histogram.VPPostHistogram(obj.ViewObject)
|
||||
return obj
|
||||
|
||||
|
||||
# ********* solver objects ***********************************************************************
|
||||
def makeEquationDeformation(doc, base_solver=None, name="Deformation"):
|
||||
"""makeEquationDeformation(document, [base_solver], [name]):
|
||||
|
||||
@@ -40,6 +40,7 @@ from .manager import CommandManager
|
||||
from femtools.femutils import expandParentObject
|
||||
from femtools.femutils import is_of_type
|
||||
from femsolver.settings import get_default_solver
|
||||
from femguiutils import post_visualization
|
||||
|
||||
# Python command definitions:
|
||||
# for C++ command definitions see src/Mod/Fem/Command.cpp
|
||||
@@ -1231,7 +1232,6 @@ class _PostFilterGlyph(CommandManager):
|
||||
self.is_active = "with_vtk_selresult"
|
||||
self.do_activated = "add_filter_set_edit"
|
||||
|
||||
|
||||
# the string in add command will be the page name on FreeCAD wiki
|
||||
FreeCADGui.addCommand("FEM_Analysis", _Analysis())
|
||||
FreeCADGui.addCommand("FEM_ClippingPlaneAdd", _ClippingPlaneAdd())
|
||||
@@ -1289,3 +1289,8 @@ FreeCADGui.addCommand("FEM_SolverZ88", _SolverZ88())
|
||||
|
||||
if "BUILD_FEM_VTK_PYTHON" in FreeCAD.__cmake__:
|
||||
FreeCADGui.addCommand("FEM_PostFilterGlyph", _PostFilterGlyph())
|
||||
|
||||
# setup all visualization commands (register by importing)
|
||||
import femobjects.post_histogram
|
||||
post_visualization.setup_commands("FEM_PostVisualization")
|
||||
|
||||
|
||||
139
src/Mod/Fem/femguiutils/data_extraction.py
Normal file
139
src/Mod/Fem/femguiutils/data_extraction.py
Normal file
@@ -0,0 +1,139 @@
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2025 Stefan Tröger <stefantroeger@gmx.net> *
|
||||
# * *
|
||||
# * This file is part of the FreeCAD CAx development system. *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
# * as published by the Free Software Foundation; either version 2 of *
|
||||
# * the License, or (at your option) any later version. *
|
||||
# * for detail see the LICENCE text file. *
|
||||
# * *
|
||||
# * This program is distributed in the hope that it will be useful, *
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
# * GNU Library General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with this program; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
__title__ = "FreeCAD FEM postprocessing ldata view and extraction widget"
|
||||
__author__ = "Stefan Tröger"
|
||||
__url__ = "https://www.freecad.org"
|
||||
|
||||
## @package data_extraction
|
||||
# \ingroup FEM
|
||||
# \brief A widget for data extraction. Used in the PostObject task panel.
|
||||
|
||||
from . import vtk_table_view
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
|
||||
from vtkmodules.vtkCommonDataModel import vtkTable
|
||||
from vtkmodules.vtkFiltersCore import vtkAttributeDataToTableFilter
|
||||
from vtkmodules.vtkFiltersGeneral import vtkSplitColumnComponents
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
|
||||
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.
|
||||
# Note: This object is created and used from c++! See PostTaskExtraction
|
||||
|
||||
def __init__(self, vobj):
|
||||
|
||||
super().__init__(vobj.Object)
|
||||
|
||||
self.ViewObject = vobj
|
||||
self.Object = vobj.Object
|
||||
|
||||
self.widget = FreeCADGui.PySideUic.loadUi(
|
||||
FreeCAD.getHomePath() + "Mod/Fem/Resources/ui/TaskPostExtraction.ui"
|
||||
)
|
||||
|
||||
# connect all signals as required
|
||||
self.widget.Data.clicked.connect(self.showData)
|
||||
self.widget.Summary.clicked.connect(self.showSummary)
|
||||
|
||||
# setup the data models
|
||||
self.data_model = vtk_table_view.VtkTableModel()
|
||||
self.summary_model = vtk_table_view.VtkTableSummaryModel()
|
||||
|
||||
# generate the data
|
||||
self.onPostDataChanged(self.Object)
|
||||
|
||||
# setup the extraction widget
|
||||
self._extraction_view = ExtractLinkView(self.Object, True, self)
|
||||
self.widget.layout().addSpacing(self.widget.Data.size().height()/3)
|
||||
self.widget.layout().addWidget(self._extraction_view)
|
||||
self._extraction_view.repopulate()
|
||||
|
||||
|
||||
@QtCore.Slot()
|
||||
def showData(self):
|
||||
|
||||
dialog = QtGui.QDialog(self.widget)
|
||||
widget = vtk_table_view.VtkTableView(self.data_model)
|
||||
layout = QtGui.QVBoxLayout()
|
||||
layout.addWidget(widget)
|
||||
layout.setContentsMargins(0,0,0,0)
|
||||
|
||||
dialog.setLayout(layout)
|
||||
dialog.resize(1500, 900)
|
||||
dialog.show()
|
||||
|
||||
@QtCore.Slot()
|
||||
def showSummary(self):
|
||||
|
||||
dialog = QtGui.QDialog(self.widget)
|
||||
widget = vtk_table_view.VtkTableView(self.summary_model)
|
||||
layout = QtGui.QVBoxLayout()
|
||||
layout.addWidget(widget)
|
||||
layout.setContentsMargins(0,0,0,0)
|
||||
|
||||
dialog.setLayout(layout)
|
||||
dialog.resize(600, 900)
|
||||
dialog.show()
|
||||
|
||||
def isGuiTaskOnly(self):
|
||||
# If all panels return true it omits the Apply button in the dialog
|
||||
return True
|
||||
|
||||
def onPostDataChanged(self, obj):
|
||||
|
||||
algo = obj.getOutputAlgorithm()
|
||||
if not algo:
|
||||
self.data_model.setTable(vtkTable())
|
||||
|
||||
filter = vtkAttributeDataToTableFilter()
|
||||
filter.SetInputConnection(0, algo.GetOutputPort(0))
|
||||
filter.Update()
|
||||
table = filter.GetOutputDataObject(0)
|
||||
|
||||
# add the points
|
||||
points = algo.GetOutputDataObject(0).GetPoints().GetData()
|
||||
table.InsertColumn(points, 0)
|
||||
|
||||
# split the components
|
||||
splitter = vtkSplitColumnComponents()
|
||||
splitter.SetNamingModeToNamesWithParens()
|
||||
splitter.SetInputData(0, table)
|
||||
|
||||
splitter.Update()
|
||||
table = splitter.GetOutputDataObject(0)
|
||||
|
||||
self.data_model.setTable(table)
|
||||
self.summary_model.setTable(table)
|
||||
|
||||
def apply(self):
|
||||
pass
|
||||
490
src/Mod/Fem/femguiutils/extract_link_view.py
Normal file
490
src/Mod/Fem/femguiutils/extract_link_view.py
Normal file
@@ -0,0 +1,490 @@
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2025 Stefan Tröger <stefantroeger@gmx.net> *
|
||||
# * *
|
||||
# * This file is part of the FreeCAD CAx development system. *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
# * as published by the Free Software Foundation; either version 2 of *
|
||||
# * the License, or (at your option) any later version. *
|
||||
# * for detail see the LICENCE text file. *
|
||||
# * *
|
||||
# * This program is distributed in the hope that it will be useful, *
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
# * GNU Library General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with this program; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
__title__ = "FreeCAD FEM postprocessing view for summarizing extractor links"
|
||||
__author__ = "Stefan Tröger"
|
||||
__url__ = "https://www.freecad.org"
|
||||
|
||||
## @package data_extraction
|
||||
# \ingroup FEM
|
||||
# \brief A widget that shows summaries of all available links to extractors
|
||||
|
||||
from PySide import QtGui, QtCore
|
||||
|
||||
import femobjects.base_fempostextractors as extr
|
||||
import femobjects.base_fempostvisualizations as vis
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
|
||||
from . import post_visualization as pv
|
||||
|
||||
# a model showing available visualizations and possible extractions
|
||||
# #################################################################
|
||||
|
||||
def build_new_visualization_tree_model():
|
||||
# model that shows all options to create new visualizations
|
||||
|
||||
model = QtGui.QStandardItemModel()
|
||||
|
||||
visualizations = pv.get_registered_visualizations()
|
||||
for vis_name in visualizations:
|
||||
vis_icon = FreeCADGui.getIcon(visualizations[vis_name].icon)
|
||||
vis_item = QtGui.QStandardItem(vis_icon, f"New {vis_name}")
|
||||
vis_item.setFlags(QtGui.Qt.ItemIsEnabled)
|
||||
vis_item.setData(visualizations[vis_name])
|
||||
|
||||
for ext in visualizations[vis_name].extractions:
|
||||
icon = FreeCADGui.getIcon(ext.icon)
|
||||
name = ext.name.removeprefix(vis_name)
|
||||
ext_item = QtGui.QStandardItem(icon, f"with {name}")
|
||||
ext_item.setData(ext)
|
||||
vis_item.appendRow(ext_item)
|
||||
model.appendRow(vis_item)
|
||||
|
||||
return model
|
||||
|
||||
def build_add_to_visualization_tree_model():
|
||||
# model that shows all possible visualization objects to add data to
|
||||
|
||||
visualizations = pv.get_registered_visualizations()
|
||||
model = QtGui.QStandardItemModel()
|
||||
|
||||
for obj in FreeCAD.ActiveDocument.Objects:
|
||||
if obj.isDerivedFrom("Fem::FemAnalysis"):
|
||||
ana_item = QtGui.QStandardItem(obj.ViewObject.Icon, obj.Label)
|
||||
ana_item.setFlags(QtGui.Qt.ItemIsEnabled)
|
||||
|
||||
# check all children it it is a visualization
|
||||
for child in obj.Group:
|
||||
if vis.is_visualization_object(child):
|
||||
|
||||
vis_item = QtGui.QStandardItem(child.ViewObject.Icon, child.Label)
|
||||
vis_type = vis.get_visualization_type(child)
|
||||
vis_item.setFlags(QtGui.Qt.ItemIsEnabled)
|
||||
vis_item.setData(child)
|
||||
ana_item.appendRow(vis_item)
|
||||
|
||||
# add extractor items
|
||||
for ext in visualizations[vis_type].extractions:
|
||||
icon = FreeCADGui.getIcon(ext.icon)
|
||||
name = ext.name.removeprefix(vis_type)
|
||||
ext_item = QtGui.QStandardItem(icon, f"Add {name}")
|
||||
ext_item.setData(ext)
|
||||
vis_item.appendRow(ext_item)
|
||||
|
||||
if ana_item.rowCount():
|
||||
model.appendRow(ana_item)
|
||||
|
||||
return model
|
||||
|
||||
def build_post_object_item(post_object, extractions, vis_type):
|
||||
|
||||
# definitely build a item and add the extractions
|
||||
post_item = QtGui.QStandardItem(post_object.ViewObject.Icon, f"From {post_object.Label}")
|
||||
post_item.setFlags(QtGui.Qt.ItemIsEnabled)
|
||||
post_item.setData(post_object)
|
||||
|
||||
# add extractor items
|
||||
for ext in extractions:
|
||||
icon = FreeCADGui.getIcon(ext.icon)
|
||||
name = ext.name.removeprefix(vis_type)
|
||||
ext_item = QtGui.QStandardItem(icon, f"add {name}")
|
||||
ext_item.setData(ext)
|
||||
post_item.appendRow(ext_item)
|
||||
|
||||
# if we are a post group, we need to add the children
|
||||
if post_object.hasExtension("Fem::FemPostGroupExtension"):
|
||||
|
||||
for child in post_object.Group:
|
||||
if child.isDerivedFrom("Fem::FemPostObject"):
|
||||
item = build_post_object_item(child, extractions, vis_type)
|
||||
post_item.appendRow(item)
|
||||
|
||||
return post_item
|
||||
|
||||
|
||||
def build_add_from_data_tree_model(vis_type):
|
||||
# model that shows all Post data objects from which data can be extracted
|
||||
extractions = pv.get_registered_visualizations()[vis_type].extractions
|
||||
|
||||
model = QtGui.QStandardItemModel()
|
||||
for obj in FreeCAD.ActiveDocument.Objects:
|
||||
if obj.isDerivedFrom("Fem::FemAnalysis"):
|
||||
ana_item = QtGui.QStandardItem(obj.ViewObject.Icon, obj.Label)
|
||||
ana_item.setFlags(QtGui.Qt.ItemIsEnabled)
|
||||
|
||||
# check all children if it is a post object
|
||||
for child in obj.Group:
|
||||
if child.isDerivedFrom("Fem::FemPostObject"):
|
||||
item = build_post_object_item(child, extractions, vis_type)
|
||||
ana_item.appendRow(item)
|
||||
|
||||
if ana_item.rowCount():
|
||||
model.appendRow(ana_item)
|
||||
|
||||
return model
|
||||
|
||||
class TreeChoiceButton(QtGui.QToolButton):
|
||||
|
||||
selection = QtCore.Signal(object,object)
|
||||
|
||||
def __init__(self, model):
|
||||
super().__init__()
|
||||
|
||||
self.model = model
|
||||
self.setEnabled(bool(model.rowCount()))
|
||||
|
||||
self.__skip_next_hide = False
|
||||
|
||||
self.tree_view = QtGui.QTreeView(self)
|
||||
self.tree_view.setModel(model)
|
||||
|
||||
self.tree_view.setFrameShape(QtGui.QFrame.NoFrame)
|
||||
self.tree_view.setHeaderHidden(True)
|
||||
self.tree_view.setEditTriggers(QtGui.QTreeView.EditTriggers.NoEditTriggers)
|
||||
self.tree_view.setSelectionBehavior(QtGui.QTreeView.SelectionBehavior.SelectRows)
|
||||
self.tree_view.expandAll()
|
||||
self.tree_view.activated.connect(self.selectIndex)
|
||||
|
||||
# set a complex menu
|
||||
self.popup = QtGui.QWidgetAction(self)
|
||||
self.popup.setDefaultWidget(self.tree_view)
|
||||
self.setPopupMode(QtGui.QToolButton.InstantPopup)
|
||||
self.addAction(self.popup);
|
||||
|
||||
QtCore.Slot(QtCore.QModelIndex)
|
||||
def selectIndex(self, index):
|
||||
item = self.model.itemFromIndex(index)
|
||||
|
||||
if item and not item.hasChildren():
|
||||
extraction = item.data()
|
||||
parent = item.parent().data()
|
||||
self.selection.emit(parent, extraction)
|
||||
self.popup.trigger()
|
||||
|
||||
def setModel(self, model):
|
||||
self.model = model
|
||||
self.tree_view.setModel(model)
|
||||
self.tree_view.expandAll()
|
||||
|
||||
# check if we should be disabled
|
||||
self.setEnabled(bool(model.rowCount()))
|
||||
|
||||
|
||||
# implementationof GUI and its functionality
|
||||
# ##########################################
|
||||
|
||||
class _ShowVisualization:
|
||||
def __init__(self, st_object):
|
||||
self._st_object = st_object
|
||||
|
||||
def __call__(self):
|
||||
if vis.is_visualization_object(self._st_object):
|
||||
# show the visualization
|
||||
self._st_object.ViewObject.Proxy.show_visualization()
|
||||
else:
|
||||
# for now just select the thing
|
||||
FreeCADGui.Selection.clearSelection()
|
||||
FreeCADGui.Selection.addSelection(self._st_object)
|
||||
|
||||
class _ShowEditDialog:
|
||||
def __init__(self, extractor, post_dialog, widget):
|
||||
self._extractor = extractor
|
||||
self._post_dialog = post_dialog
|
||||
self._widget = widget
|
||||
|
||||
widgets = self._extractor.ViewObject.Proxy.get_edit_widgets(self._post_dialog)
|
||||
vbox = QtGui.QVBoxLayout()
|
||||
|
||||
buttonBox = QtGui.QDialogButtonBox()
|
||||
buttonBox.setCenterButtons(True)
|
||||
buttonBox.setStandardButtons(self._post_dialog.getStandardButtons())
|
||||
vbox.addWidget(buttonBox)
|
||||
|
||||
started = False
|
||||
for widget in widgets:
|
||||
|
||||
if started:
|
||||
# add a seperator line
|
||||
frame = QtGui.QFrame()
|
||||
frame.setFrameShape(QtGui.QFrame.HLine);
|
||||
vbox.addWidget(frame);
|
||||
else:
|
||||
started = True
|
||||
|
||||
vbox.addWidget(widget)
|
||||
|
||||
vbox.addStretch()
|
||||
|
||||
self.dialog = QtGui.QDialog(self._widget)
|
||||
buttonBox.accepted.connect(self.accept)
|
||||
buttonBox.rejected.connect(self.dialog.close)
|
||||
buttonBox.button(QtGui.QDialogButtonBox.Apply).clicked.connect(self.apply)
|
||||
self.dialog.setLayout(vbox)
|
||||
|
||||
|
||||
def accept(self):
|
||||
# recompute and close
|
||||
self._extractor.Document.recompute()
|
||||
self.dialog.close()
|
||||
|
||||
def apply(self):
|
||||
self._extractor.Document.recompute()
|
||||
|
||||
def __call__(self):
|
||||
# create the widgets, add it to dialog
|
||||
self.dialog.show()
|
||||
|
||||
class _DeleteExtractor:
|
||||
def __init__(self, extractor, widget):
|
||||
self._extractor = extractor
|
||||
self._widget = widget
|
||||
|
||||
def __call__(self):
|
||||
# remove the document object
|
||||
doc = self._extractor.Document
|
||||
doc.removeObject(self._extractor.Name)
|
||||
doc.recompute()
|
||||
|
||||
# remove the widget
|
||||
self._widget.deleteLater()
|
||||
|
||||
class ExtractLinkView(QtGui.QWidget):
|
||||
|
||||
def __init__(self, obj, is_source, post_dialog):
|
||||
# initializes the view.
|
||||
# obj: The object for which the links should be shown / summarized
|
||||
# is_source: Bool, if the object is the data source (e.g. postobject), or the target (e.g. plots)
|
||||
|
||||
super().__init__()
|
||||
|
||||
self._object = obj
|
||||
self._is_source = is_source
|
||||
self._post_dialog = post_dialog
|
||||
self._widgets = []
|
||||
|
||||
# build the layout:
|
||||
self._scroll_view = QtGui.QScrollArea(self)
|
||||
self._scroll_view.setHorizontalScrollBarPolicy(QtGui.Qt.ScrollBarAlwaysOff)
|
||||
self._scroll_view.setWidgetResizable(True)
|
||||
|
||||
hbox = QtGui.QHBoxLayout()
|
||||
label = QtGui.QLabel("Data used in:")
|
||||
if not self._is_source:
|
||||
label.setText("Data used from:")
|
||||
|
||||
label.setAlignment(QtGui.Qt.AlignBottom)
|
||||
hbox.addWidget(label)
|
||||
hbox.addStretch()
|
||||
|
||||
if self._is_source:
|
||||
|
||||
self._add = TreeChoiceButton(build_add_to_visualization_tree_model())
|
||||
self._add.setText("Add data to")
|
||||
self._add.selection.connect(self.addExtractionToVisualization)
|
||||
hbox.addWidget(self._add)
|
||||
|
||||
self._create = TreeChoiceButton(build_new_visualization_tree_model())
|
||||
self._create.setText("New")
|
||||
self._create.selection.connect(self.newVisualization)
|
||||
hbox.addWidget(self._create)
|
||||
|
||||
else:
|
||||
vis_type = vis.get_visualization_type(self._object)
|
||||
self._add = TreeChoiceButton(build_add_from_data_tree_model(vis_type))
|
||||
self._add.setText("Add data from")
|
||||
self._add.selection.connect(self.addExtractionToPostObject)
|
||||
hbox.addWidget(self._add)
|
||||
|
||||
vbox = QtGui.QVBoxLayout()
|
||||
vbox.setContentsMargins(0,0,0,0)
|
||||
vbox.addItem(hbox)
|
||||
vbox.addWidget(self._scroll_view)
|
||||
|
||||
self.setLayout(vbox)
|
||||
|
||||
|
||||
|
||||
# add the content
|
||||
self.repopulate()
|
||||
|
||||
def _build_summary_widget(self, extractor):
|
||||
|
||||
widget = FreeCADGui.PySideUic.loadUi(
|
||||
FreeCAD.getHomePath() + "Mod/Fem/Resources/ui/PostExtractionSummaryWidget.ui"
|
||||
)
|
||||
|
||||
# add the separation line
|
||||
frame = QtGui.QFrame()
|
||||
frame.setFrameShape(QtGui.QFrame.HLine);
|
||||
widget.layout().addWidget(frame);
|
||||
|
||||
if self._is_source:
|
||||
st_object = extractor.getParentGroup()
|
||||
else:
|
||||
st_object = extractor.Source
|
||||
|
||||
widget.RemoveButton.setIcon(QtGui.QIcon.fromTheme("delete"))
|
||||
|
||||
widget.STButton.setIcon(st_object.ViewObject.Icon)
|
||||
widget.STButton.setText(st_object.Label)
|
||||
|
||||
widget.ExtractButton.setIcon(extractor.ViewObject.Icon)
|
||||
|
||||
extr_label = extr.get_extraction_dimension(extractor)
|
||||
extr_label += " " + extr.get_extraction_type(extractor)
|
||||
widget.ExtractButton.setText(extr_label)
|
||||
|
||||
# connect actions. We add functions to widget, as well as the data we need,
|
||||
# and use those as callback. This way every widget knows which objects to use
|
||||
widget.STButton.clicked.connect(_ShowVisualization(st_object))
|
||||
widget.ExtractButton.clicked.connect(_ShowEditDialog(extractor, self._post_dialog, widget))
|
||||
widget.RemoveButton.clicked.connect(_DeleteExtractor(extractor, widget))
|
||||
|
||||
return widget
|
||||
|
||||
def repopulate(self):
|
||||
# collect all links that are available and shows them
|
||||
|
||||
# clear the view
|
||||
for widget in self._widgets:
|
||||
widget.hide()
|
||||
widget.deleteLater()
|
||||
|
||||
self._widgets = []
|
||||
|
||||
# rebuild the widgets
|
||||
|
||||
if self._is_source:
|
||||
candidates = self._object.InList
|
||||
else:
|
||||
candidates = self._object.OutList
|
||||
|
||||
# get all widgets from the candidates
|
||||
extractors = []
|
||||
for candidate in candidates:
|
||||
if extr.is_extractor_object(candidate):
|
||||
summary = self._build_summary_widget(candidate)
|
||||
self._widgets.append(summary)
|
||||
|
||||
# fill the scroll area
|
||||
vbox = QtGui.QVBoxLayout()
|
||||
for widget in self._widgets:
|
||||
vbox.addWidget(widget)
|
||||
|
||||
vbox.addStretch()
|
||||
widget = QtGui.QWidget()
|
||||
widget.setLayout(vbox)
|
||||
|
||||
self._scroll_view.setWidget(widget)
|
||||
|
||||
# also reset the add button model
|
||||
if self._is_source:
|
||||
self._add.setModel(build_add_to_visualization_tree_model())
|
||||
|
||||
def _find_parent_analysis(self, obj):
|
||||
# iterate upwards, till we find a analysis
|
||||
for parent in obj.InList:
|
||||
if parent.isDerivedFrom("Fem::FemAnalysis"):
|
||||
return parent
|
||||
|
||||
analysis = self._find_parent_analysis(parent)
|
||||
if analysis:
|
||||
return analysis
|
||||
|
||||
return None
|
||||
|
||||
QtCore.Slot(object, object) # visualization data, extraction data
|
||||
def newVisualization(self, vis_data, ext_data):
|
||||
|
||||
doc = self._object.Document
|
||||
|
||||
FreeCADGui.addModule(vis_data.module)
|
||||
FreeCADGui.addModule(ext_data.module)
|
||||
FreeCADGui.addModule("FemGui")
|
||||
|
||||
# create visualization
|
||||
FreeCADGui.doCommand(
|
||||
f"visualization = {vis_data.module}.{vis_data.factory}(FreeCAD.ActiveDocument)"
|
||||
)
|
||||
analysis = self._find_parent_analysis(self._object)
|
||||
if analysis:
|
||||
FreeCADGui.doCommand(
|
||||
f"FreeCAD.ActiveDocument.{analysis.Name}.addObject(visualization)"
|
||||
)
|
||||
|
||||
# create extraction and add it
|
||||
FreeCADGui.doCommand(
|
||||
f"extraction = {ext_data.module}.{ext_data.factory}(FreeCAD.ActiveDocument)"
|
||||
)
|
||||
FreeCADGui.doCommand(
|
||||
f"extraction.Source = FreeCAD.ActiveDocument.{self._object.Name}"
|
||||
)
|
||||
FreeCADGui.doCommand(
|
||||
f"visualization.addObject(extraction)"
|
||||
)
|
||||
|
||||
self._post_dialog._recompute()
|
||||
self.repopulate()
|
||||
|
||||
QtCore.Slot(object, object) # visualization object, extraction data
|
||||
def addExtractionToVisualization(self, vis_obj, ext_data):
|
||||
|
||||
FreeCADGui.addModule(ext_data.module)
|
||||
FreeCADGui.addModule("FemGui")
|
||||
|
||||
# create extraction and add it
|
||||
FreeCADGui.doCommand(
|
||||
f"extraction = {ext_data.module}.{ext_data.factory}(FreeCAD.ActiveDocument)"
|
||||
)
|
||||
FreeCADGui.doCommand(
|
||||
f"extraction.Source = FreeCAD.ActiveDocument.{self._object.Name}"
|
||||
)
|
||||
FreeCADGui.doCommand(
|
||||
f"App.ActiveDocument.{vis_obj.Name}.addObject(extraction)"
|
||||
)
|
||||
|
||||
self._post_dialog._recompute()
|
||||
self.repopulate()
|
||||
|
||||
QtCore.Slot(object, object) # post object, extraction data
|
||||
def addExtractionToPostObject(self, post_obj, ext_data):
|
||||
|
||||
FreeCADGui.addModule(ext_data.module)
|
||||
FreeCADGui.addModule("FemGui")
|
||||
|
||||
# create extraction and add it
|
||||
FreeCADGui.doCommand(
|
||||
f"extraction = {ext_data.module}.{ext_data.factory}(FreeCAD.ActiveDocument)"
|
||||
)
|
||||
FreeCADGui.doCommand(
|
||||
f"extraction.Source = FreeCAD.ActiveDocument.{post_obj.Name}"
|
||||
)
|
||||
FreeCADGui.doCommand(
|
||||
f"App.ActiveDocument.{self._object.Name}.addObject(extraction)"
|
||||
)
|
||||
|
||||
self._post_dialog._recompute()
|
||||
self.repopulate()
|
||||
|
||||
162
src/Mod/Fem/femguiutils/post_visualization.py
Normal file
162
src/Mod/Fem/femguiutils/post_visualization.py
Normal file
@@ -0,0 +1,162 @@
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2025 Stefan Tröger <stefantroeger@gmx.net> *
|
||||
# * *
|
||||
# * This file is part of the FreeCAD CAx development system. *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
# * as published by the Free Software Foundation; either version 2 of *
|
||||
# * the License, or (at your option) any later version. *
|
||||
# * for detail see the LICENCE text file. *
|
||||
# * *
|
||||
# * This program is distributed in the hope that it will be useful, *
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
# * GNU Library General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with this program; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
__title__ = "FreeCAD visualization registry"
|
||||
__author__ = "Stefan Tröger"
|
||||
__url__ = "https://www.freecad.org"
|
||||
|
||||
## @package post_visualization
|
||||
# \ingroup FEM
|
||||
# \brief A registry to collect visualizations for use in menus
|
||||
|
||||
import copy
|
||||
from dataclasses import dataclass
|
||||
|
||||
from PySide import QtGui, QtCore
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import FemGui
|
||||
|
||||
# Registry to handle visulization commands
|
||||
# ########################################
|
||||
|
||||
_registry = {}
|
||||
|
||||
@dataclass
|
||||
class _Extraction:
|
||||
|
||||
name: str
|
||||
icon: str
|
||||
dimension: str
|
||||
extracttype: str
|
||||
module: str
|
||||
factory: str
|
||||
|
||||
@dataclass
|
||||
class _Visualization:
|
||||
|
||||
name: str
|
||||
icon: str
|
||||
module: str
|
||||
factory: str
|
||||
extractions: list[_Extraction]
|
||||
|
||||
# Register a visualization by type, icon and factory function
|
||||
def register_visualization(visualization_type, icon, module, factory):
|
||||
if visualization_type in _registry:
|
||||
raise ValueError("Visualization type already registered")
|
||||
|
||||
_registry[visualization_type] = _Visualization(visualization_type, icon, module, factory, [])
|
||||
|
||||
def register_extractor(visualization_type, extraction_type, icon, dimension, etype, module, factory):
|
||||
|
||||
if not visualization_type in _registry:
|
||||
raise ValueError("visualization not registered yet")
|
||||
|
||||
extraction = _Extraction(extraction_type, icon, dimension, etype, module, factory)
|
||||
_registry[visualization_type].extractions.append(extraction)
|
||||
|
||||
def get_registered_visualizations():
|
||||
return copy.deepcopy(_registry)
|
||||
|
||||
|
||||
def _to_command_name(name):
|
||||
return "FEM_PostVisualization" + name
|
||||
|
||||
class _VisualizationGroupCommand:
|
||||
|
||||
def GetCommands(self):
|
||||
visus = _registry.keys()
|
||||
cmds = [_to_command_name(v) for v in visus]
|
||||
return cmds
|
||||
|
||||
def GetDefaultCommand(self):
|
||||
return 0
|
||||
|
||||
def GetResources(self):
|
||||
return { 'MenuText': 'Data Visualizations', 'ToolTip': 'Different visualizations to show post processing data in'}
|
||||
|
||||
def IsActive(self):
|
||||
if not FreeCAD.ActiveDocument:
|
||||
return False
|
||||
|
||||
return bool(FemGui.getActiveAnalysis())
|
||||
|
||||
|
||||
class _VisualizationCommand:
|
||||
|
||||
def __init__(self, visualization_type):
|
||||
self._visualization_type = visualization_type
|
||||
|
||||
def GetResources(self):
|
||||
|
||||
cmd = _to_command_name(self._visualization_type)
|
||||
vis = _registry[self._visualization_type]
|
||||
tooltip = f"Create a {self._visualization_type} post processing data visualization"
|
||||
|
||||
return {
|
||||
"Pixmap": vis.icon,
|
||||
"MenuText": QtCore.QT_TRANSLATE_NOOP(cmd, f"{self._visualization_type}"),
|
||||
"Accel": "",
|
||||
"ToolTip": QtCore.QT_TRANSLATE_NOOP(cmd, tooltip),
|
||||
"CmdType": "AlterDoc"
|
||||
}
|
||||
|
||||
def IsActive(self):
|
||||
# active analysis available
|
||||
if not FreeCAD.ActiveDocument:
|
||||
return False
|
||||
|
||||
return bool(FemGui.getActiveAnalysis())
|
||||
|
||||
def Activated(self):
|
||||
|
||||
vis = _registry[self._visualization_type]
|
||||
FreeCAD.ActiveDocument.openTransaction(f"Create {vis.name}")
|
||||
|
||||
FreeCADGui.addModule(vis.module)
|
||||
FreeCADGui.addModule("FemGui")
|
||||
|
||||
FreeCADGui.doCommand(
|
||||
f"obj = {vis.module}.{vis.factory}(FreeCAD.ActiveDocument)"
|
||||
)
|
||||
FreeCADGui.doCommand(
|
||||
f"FemGui.getActiveAnalysis().addObject(obj)"
|
||||
)
|
||||
|
||||
FreeCADGui.Selection.clearSelection()
|
||||
FreeCADGui.doCommand(
|
||||
"FreeCADGui.ActiveDocument.setEdit(obj)"
|
||||
)
|
||||
|
||||
def setup_commands(toplevel_name):
|
||||
# creates all visualization commands and registers them. The
|
||||
# toplevel group command will have the name provided to this function.
|
||||
|
||||
# first all visualization and extraction commands
|
||||
for vis in _registry:
|
||||
FreeCADGui.addCommand(_to_command_name(vis), _VisualizationCommand(vis))
|
||||
|
||||
# build the group command!
|
||||
FreeCADGui.addCommand("FEM_PostVisualization", _VisualizationGroupCommand())
|
||||
138
src/Mod/Fem/femguiutils/vtk_table_view.py
Normal file
138
src/Mod/Fem/femguiutils/vtk_table_view.py
Normal file
@@ -0,0 +1,138 @@
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2025 Stefan Tröger <stefantroeger@gmx.net> *
|
||||
# * *
|
||||
# * This file is part of the FreeCAD CAx development system. *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
# * as published by the Free Software Foundation; either version 2 of *
|
||||
# * the License, or (at your option) any later version. *
|
||||
# * for detail see the LICENCE text file. *
|
||||
# * *
|
||||
# * This program is distributed in the hope that it will be useful, *
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
# * GNU Library General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with this program; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
__title__ = "FreeCAD table view widget to visualize vtkTable"
|
||||
__author__ = "Stefan Tröger"
|
||||
__url__ = "https://www.freecad.org"
|
||||
|
||||
## @package vtk_table_view
|
||||
# \ingroup FEM
|
||||
# \brief A Qt widget to show a vtkTable
|
||||
|
||||
from PySide import QtGui
|
||||
from PySide import QtCore
|
||||
|
||||
class VtkTableModel(QtCore.QAbstractTableModel):
|
||||
# Simple table model. Only supports single component columns
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._table = None
|
||||
|
||||
def setTable(self, table):
|
||||
self.beginResetModel()
|
||||
self._table = table
|
||||
self.endResetModel()
|
||||
|
||||
def rowCount(self, index):
|
||||
|
||||
if not self._table:
|
||||
return 0
|
||||
|
||||
return self._table.GetNumberOfRows()
|
||||
|
||||
def columnCount(self, index):
|
||||
|
||||
if not self._table:
|
||||
return 0
|
||||
|
||||
return self._table.GetNumberOfColumns()
|
||||
|
||||
def data(self, index, role):
|
||||
|
||||
if not self._table:
|
||||
return None
|
||||
|
||||
if role == QtCore.Qt.DisplayRole:
|
||||
col = self._table.GetColumn(index.column())
|
||||
return col.GetTuple(index.row())[0]
|
||||
|
||||
def headerData(self, section, orientation, role):
|
||||
|
||||
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
|
||||
return self._table.GetColumnName(section)
|
||||
|
||||
if orientation == QtCore.Qt.Vertical and role == QtCore.Qt.DisplayRole:
|
||||
return section
|
||||
|
||||
class VtkTableSummaryModel(QtCore.QAbstractTableModel):
|
||||
# Simple model showing a summary of the table.
|
||||
# Only supports single component columns
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._table = None
|
||||
|
||||
def setTable(self, table):
|
||||
self.beginResetModel()
|
||||
self._table = table
|
||||
self.endResetModel()
|
||||
|
||||
def rowCount(self, index):
|
||||
|
||||
if not self._table:
|
||||
return 0
|
||||
|
||||
return self._table.GetNumberOfColumns()
|
||||
|
||||
def columnCount(self, index):
|
||||
return 2 # min, max
|
||||
|
||||
def data(self, index, role):
|
||||
|
||||
if not self._table:
|
||||
return None
|
||||
|
||||
if role == QtCore.Qt.DisplayRole:
|
||||
col = self._table.GetColumn(index.row())
|
||||
range = col.GetRange()
|
||||
return range[index.column()]
|
||||
|
||||
def headerData(self, section, orientation, role):
|
||||
|
||||
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
|
||||
return ["Min","Max"][section]
|
||||
|
||||
if orientation == QtCore.Qt.Vertical and role == QtCore.Qt.DisplayRole:
|
||||
return self._table.GetColumnName(section)
|
||||
|
||||
|
||||
class VtkTableView(QtGui.QWidget):
|
||||
|
||||
def __init__(self, model):
|
||||
super().__init__()
|
||||
|
||||
self.model = model
|
||||
self.table_view = QtGui.QTableView()
|
||||
self.table_view.setModel(model)
|
||||
|
||||
# fast initial resize and manual resizing still allowed!
|
||||
header = self.table_view.horizontalHeader()
|
||||
header.setResizeContentsPrecision(10)
|
||||
self.table_view.resizeColumnsToContents()
|
||||
|
||||
layout = QtGui.QVBoxLayout()
|
||||
layout.setContentsMargins(0,0,0,0)
|
||||
layout.addWidget(self.table_view)
|
||||
self.setLayout(layout)
|
||||
|
||||
199
src/Mod/Fem/femobjects/base_fempostextractors.py
Normal file
199
src/Mod/Fem/femobjects/base_fempostextractors.py
Normal file
@@ -0,0 +1,199 @@
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2025 Stefan Tröger <stefantroeger@gmx.net> *
|
||||
# * *
|
||||
# * This file is part of the FreeCAD CAx development system. *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
# * as published by the Free Software Foundation; either version 2 of *
|
||||
# * the License, or (at your option) any later version. *
|
||||
# * for detail see the LICENCE text file. *
|
||||
# * *
|
||||
# * This program is distributed in the hope that it will be useful, *
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
# * GNU Library General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with this program; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
__title__ = "FreeCAD FEM postprocessing data exxtractor base objcts"
|
||||
__author__ = "Stefan Tröger"
|
||||
__url__ = "https://www.freecad.org"
|
||||
|
||||
## @package base_fempostextractors
|
||||
# \ingroup FEM
|
||||
# \brief base objects for data extractors
|
||||
|
||||
from vtkmodules.vtkCommonDataModel import vtkTable
|
||||
|
||||
from . import base_fempythonobject
|
||||
_PropHelper = base_fempythonobject._PropHelper
|
||||
|
||||
# helper functions
|
||||
# ################
|
||||
|
||||
def is_extractor_object(obj):
|
||||
if not hasattr(obj, "Proxy"):
|
||||
return False
|
||||
|
||||
return hasattr(obj.Proxy, "ExtractionType")
|
||||
|
||||
def get_extraction_type(obj):
|
||||
# returns the extractor type string, or throws exception if
|
||||
# not a extractor
|
||||
return obj.Proxy.ExtractionType
|
||||
|
||||
def get_extraction_dimension(obj):
|
||||
# returns the extractor dimension string, or throws exception if
|
||||
# not a extractor
|
||||
return obj.Proxy.ExtractionDimension
|
||||
|
||||
|
||||
# Base class for all extractors with common source and table handling functionality
|
||||
# Note: Never use directly, always subclass! This class does not create a
|
||||
# ExtractionType/Dimension variable, hence will not work correctly.
|
||||
class Extractor(base_fempythonobject.BaseFemPythonObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._setup_properties(obj)
|
||||
|
||||
def _setup_properties(self, obj):
|
||||
pl = obj.PropertiesList
|
||||
for prop in self._get_properties():
|
||||
if not prop.name in pl:
|
||||
prop.add_to_object(obj)
|
||||
|
||||
def _get_properties(self):
|
||||
|
||||
prop = [
|
||||
_PropHelper(
|
||||
type="Fem::PropertyPostDataObject",
|
||||
name="Table",
|
||||
group="Base",
|
||||
doc="The data table that stores the extracted data",
|
||||
value=vtkTable(),
|
||||
),
|
||||
_PropHelper(
|
||||
type="App::PropertyLink",
|
||||
name="Source",
|
||||
group="Base",
|
||||
doc="The data source from which the data is extracted",
|
||||
value=None,
|
||||
),
|
||||
]
|
||||
return prop
|
||||
|
||||
def onDocumentRestored(self, obj):
|
||||
self._setup_properties(obj)
|
||||
|
||||
def onChanged(self, obj, prop):
|
||||
|
||||
if prop == "Source":
|
||||
# check if the source is a Post object
|
||||
if obj.Source and not obj.Source.isDerivedFrom("Fem::FemPostObject"):
|
||||
FreeCAD.Console.PrintWarning("Invalid object: Line source must be FemPostObject")
|
||||
obj.Source = None
|
||||
|
||||
|
||||
def get_vtk_table(self, obj):
|
||||
if not obj.DataTable:
|
||||
obj.DataTable = vtkTable()
|
||||
|
||||
return obj.DataTable
|
||||
|
||||
def component_options(self, num):
|
||||
|
||||
match num:
|
||||
case 2:
|
||||
return ["X", "Y"]
|
||||
case 3:
|
||||
return ["X", "Y", "Z"]
|
||||
case _:
|
||||
return ["Not a vector"]
|
||||
|
||||
|
||||
class Extractor1D(Extractor):
|
||||
|
||||
ExtractionDimension = "1D"
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
|
||||
|
||||
def _get_properties(self):
|
||||
prop = [
|
||||
_PropHelper(
|
||||
type="App::PropertyEnumeration",
|
||||
name="XField",
|
||||
group="X Data",
|
||||
doc="The field to use as X data",
|
||||
value=[],
|
||||
),
|
||||
_PropHelper(
|
||||
type="App::PropertyEnumeration",
|
||||
name="XComponent",
|
||||
group="X Data",
|
||||
doc="Which part of the X field vector to use for the X axis",
|
||||
value=[],
|
||||
),
|
||||
]
|
||||
|
||||
return super()._get_properties() + prop
|
||||
|
||||
|
||||
def onChanged(self, obj, prop):
|
||||
|
||||
super().onChanged(obj, prop)
|
||||
|
||||
if prop == "XField" and obj.Source and obj.Source.getDataSet():
|
||||
point_data = obj.Source.getDataSet().GetPointData()
|
||||
self._setup_x_component_property(obj, point_data)
|
||||
|
||||
if prop == "Source":
|
||||
if obj.Source:
|
||||
dset = obj.Source.getDataSet()
|
||||
if dset:
|
||||
self._setup_x_properties(obj, dset)
|
||||
else:
|
||||
self._clear_x_properties(obj)
|
||||
else:
|
||||
self._clear_x_properties(obj)
|
||||
|
||||
def _setup_x_component_property(self, obj, point_data):
|
||||
|
||||
if obj.XField == "Index":
|
||||
obj.XComponent = self.component_options(1)
|
||||
elif obj.XField == "Position":
|
||||
obj.XComponent = self.component_options(3)
|
||||
else:
|
||||
array = point_data.GetAbstractArray(obj.XField)
|
||||
obj.XComponent = self.component_options(array.GetNumberOfComponents())
|
||||
|
||||
def _clear_x_properties(self, obj):
|
||||
if hasattr(obj, "XComponent"):
|
||||
obj.XComponent = []
|
||||
if hasattr(obj, "XField"):
|
||||
obj.XField = []
|
||||
|
||||
def _setup_x_properties(self, obj, dataset):
|
||||
# Set all X Data properties correctly for the given dataset
|
||||
fields = ["Index", "Position"]
|
||||
point_data = dataset.GetPointData()
|
||||
|
||||
for i in range(point_data.GetNumberOfArrays()):
|
||||
fields.append(point_data.GetArrayName(i))
|
||||
|
||||
current_field = obj.XField
|
||||
obj.XField = fields
|
||||
if current_field in fields:
|
||||
obj.XField = current_field
|
||||
|
||||
self._setup_x_component_property(obj, point_data)
|
||||
|
||||
|
||||
71
src/Mod/Fem/femobjects/base_fempostvisualizations.py
Normal file
71
src/Mod/Fem/femobjects/base_fempostvisualizations.py
Normal file
@@ -0,0 +1,71 @@
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2025 Stefan Tröger <stefantroeger@gmx.net> *
|
||||
# * *
|
||||
# * This file is part of the FreeCAD CAx development system. *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
# * as published by the Free Software Foundation; either version 2 of *
|
||||
# * the License, or (at your option) any later version. *
|
||||
# * for detail see the LICENCE text file. *
|
||||
# * *
|
||||
# * This program is distributed in the hope that it will be useful, *
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
# * GNU Library General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with this program; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
__title__ = "FreeCAD FEM postprocessing data exxtractor base objcts"
|
||||
__author__ = "Stefan Tröger"
|
||||
__url__ = "https://www.freecad.org"
|
||||
|
||||
## @package base_fempostextractors
|
||||
# \ingroup FEM
|
||||
# \brief base objects for data extractors
|
||||
|
||||
from vtkmodules.vtkCommonDataModel import vtkTable
|
||||
|
||||
from . import base_fempythonobject
|
||||
|
||||
# helper functions
|
||||
# ################
|
||||
|
||||
def is_visualization_object(obj):
|
||||
if not hasattr(obj, "Proxy"):
|
||||
return False
|
||||
|
||||
return hasattr(obj.Proxy, "VisualizationType")
|
||||
|
||||
def get_visualization_type(obj):
|
||||
# returns the extractor type string, or throws exception if
|
||||
# not a extractor
|
||||
return obj.Proxy.VisualizationType
|
||||
|
||||
|
||||
# Base class for all visualizations
|
||||
# Note: Never use directly, always subclass! This class does not create a
|
||||
# Visualization variable, hence will not work correctly.
|
||||
class PostVisualization(base_fempythonobject.BaseFemPythonObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._setup_properties(obj)
|
||||
|
||||
def _setup_properties(self, obj):
|
||||
pl = obj.PropertiesList
|
||||
for prop in self._get_properties():
|
||||
if not prop.name in pl:
|
||||
prop.add_to_object(obj)
|
||||
|
||||
|
||||
def onDocumentRestored(self, obj):
|
||||
self._setup_properties(obj)
|
||||
|
||||
def _get_properties(self):
|
||||
return []
|
||||
@@ -54,6 +54,7 @@ class _PropHelper:
|
||||
Helper class to manage property data inside proxy objects.
|
||||
Initialization keywords are the same used with PropertyContainer
|
||||
to add dynamics properties plus "value" for the initial value.
|
||||
Note: Is used as base for a GUI version, be aware when refactoring
|
||||
"""
|
||||
|
||||
def __init__(self, **kwds):
|
||||
|
||||
178
src/Mod/Fem/femobjects/post_extract1D.py
Normal file
178
src/Mod/Fem/femobjects/post_extract1D.py
Normal file
@@ -0,0 +1,178 @@
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2025 Stefan Tröger <stefantroeger@gmx.net> *
|
||||
# * *
|
||||
# * This file is part of the FreeCAD CAx development system. *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
# * as published by the Free Software Foundation; either version 2 of *
|
||||
# * the License, or (at your option) any later version. *
|
||||
# * for detail see the LICENCE text file. *
|
||||
# * *
|
||||
# * This program is distributed in the hope that it will be useful, *
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
# * GNU Library General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with this program; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
__title__ = "FreeCAD post line plot"
|
||||
__author__ = "Stefan Tröger"
|
||||
__url__ = "https://www.freecad.org"
|
||||
|
||||
## @package post_histogram
|
||||
# \ingroup FEM
|
||||
# \brief Post processing plot displaying lines
|
||||
|
||||
from . import base_fempostextractors
|
||||
from . import base_fempythonobject
|
||||
_PropHelper = base_fempythonobject._PropHelper
|
||||
|
||||
from vtkmodules.vtkCommonCore import vtkDoubleArray
|
||||
from vtkmodules.vtkCommonCore import vtkIntArray
|
||||
from vtkmodules.vtkCommonDataModel import vtkTable
|
||||
from vtkmodules.vtkCommonDataModel import vtkDataObject
|
||||
from vtkmodules.vtkCommonExecutionModel import vtkStreamingDemandDrivenPipeline
|
||||
|
||||
class PostFieldData1D(base_fempostextractors.Extractor1D):
|
||||
"""
|
||||
A post processing extraction of one dimensional field data
|
||||
"""
|
||||
|
||||
ExtractionType = "Field"
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
|
||||
def _get_properties(self):
|
||||
prop =[ _PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="ExtractFrames",
|
||||
group="Multiframe",
|
||||
doc="Specify if the field shall be extracted for every available frame",
|
||||
value=False,
|
||||
),
|
||||
]
|
||||
return super()._get_properties() + prop
|
||||
|
||||
def __array_to_table(self, obj, array, table):
|
||||
if array.GetNumberOfComponents() == 1:
|
||||
table.AddColumn(array)
|
||||
else:
|
||||
component_array = vtkDoubleArray();
|
||||
component_array.SetNumberOfComponents(1)
|
||||
component_array.SetNumberOfTuples(array.GetNumberOfTuples())
|
||||
c_idx = obj.getEnumerationsOfProperty("XComponent").index(obj.XComponent)
|
||||
component_array.CopyComponent(0, array, c_idx)
|
||||
component_array.SetName(array.GetName())
|
||||
table.AddColumn(component_array)
|
||||
|
||||
def __array_from_dataset(self, obj, dataset):
|
||||
# extracts the relevant array from the dataset and returns a copy
|
||||
|
||||
match obj.XField:
|
||||
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)
|
||||
|
||||
case _:
|
||||
point_data = dataset.GetPointData()
|
||||
orig_array = point_data.GetAbstractArray(obj.XField)
|
||||
array = vtkDoubleArray()
|
||||
array.DeepCopy(orig_array)
|
||||
|
||||
return array
|
||||
|
||||
|
||||
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
|
||||
|
||||
frames = False
|
||||
if obj.ExtractFrames:
|
||||
# check if we have timesteps
|
||||
info = obj.Source.getOutputAlgorithm().GetOutputInformation(0)
|
||||
if info.Has(vtkStreamingDemandDrivenPipeline.TIME_STEPS()):
|
||||
timesteps = info.Get(vtkStreamingDemandDrivenPipeline.TIME_STEPS())
|
||||
frames = True
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning("No frames available in data, ignoring \"ExtractFrames\" property")
|
||||
|
||||
if not frames:
|
||||
# get the dataset and extract the correct array
|
||||
array = self.__array_from_dataset(obj, dataset)
|
||||
if array.GetNumberOfComponents() > 1:
|
||||
array.SetName(obj.XField + " (" + obj.XComponent + ")")
|
||||
else:
|
||||
array.SetName(obj.XField)
|
||||
|
||||
self.__array_to_table(obj, array, table)
|
||||
|
||||
else:
|
||||
algo = obj.Source.getOutputAlgorithm()
|
||||
for timestep in timesteps:
|
||||
algo.UpdateTimeStep(timestep)
|
||||
dataset = algo.GetOutputDataObject(0)
|
||||
array = self.__array_from_dataset(obj, dataset)
|
||||
|
||||
if array.GetNumberOfComponents() > 1:
|
||||
array.SetName(f"{obj.XField} ({obj.XComponent}) - {timestep}")
|
||||
else:
|
||||
array.SetName(f"{obj.XField} - {timestep}")
|
||||
self.__array_to_table(obj, array, table)
|
||||
|
||||
# set the final table
|
||||
obj.Table = table
|
||||
|
||||
|
||||
class PostIndexData1D(base_fempostextractors.Extractor1D):
|
||||
"""
|
||||
A post processing extraction of one dimensional index data
|
||||
"""
|
||||
|
||||
ExtractionType = "Index"
|
||||
|
||||
def __init__(self, obj):
|
||||
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(
|
||||
type="App::PropertyInteger",
|
||||
name="XIndex",
|
||||
group="X Data",
|
||||
doc="Specify for which point index the data should be extracted",
|
||||
value=0,
|
||||
),
|
||||
]
|
||||
return super()._get_properties() + prop
|
||||
142
src/Mod/Fem/femobjects/post_histogram.py
Normal file
142
src/Mod/Fem/femobjects/post_histogram.py
Normal file
@@ -0,0 +1,142 @@
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2025 Stefan Tröger <stefantroeger@gmx.net> *
|
||||
# * *
|
||||
# * This file is part of the FreeCAD CAx development system. *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
# * as published by the Free Software Foundation; either version 2 of *
|
||||
# * the License, or (at your option) any later version. *
|
||||
# * for detail see the LICENCE text file. *
|
||||
# * *
|
||||
# * This program is distributed in the hope that it will be useful, *
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
# * GNU Library General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with this program; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
__title__ = "FreeCAD post line plot"
|
||||
__author__ = "Stefan Tröger"
|
||||
__url__ = "https://www.freecad.org"
|
||||
|
||||
## @package post_histogram
|
||||
# \ingroup FEM
|
||||
# \brief Post processing plot displaying lines
|
||||
|
||||
from . import base_fempythonobject
|
||||
_PropHelper = base_fempythonobject._PropHelper
|
||||
|
||||
from . import base_fempostextractors
|
||||
from . import base_fempostvisualizations
|
||||
from . import post_extract1D
|
||||
|
||||
from vtkmodules.vtkCommonCore import vtkDoubleArray
|
||||
from vtkmodules.vtkCommonDataModel import vtkTable
|
||||
|
||||
|
||||
from femguiutils import post_visualization
|
||||
|
||||
# register visualization and extractors
|
||||
post_visualization.register_visualization("Histogram",
|
||||
":/icons/FEM_PostHistogram.svg",
|
||||
"ObjectsFem",
|
||||
"makePostVtkHistogram")
|
||||
|
||||
post_visualization.register_extractor("Histogram",
|
||||
"HistogramFieldData",
|
||||
":/icons/FEM_PostField.svg",
|
||||
"1D",
|
||||
"Field",
|
||||
"ObjectsFem",
|
||||
"makePostVtkHistogramFieldData")
|
||||
|
||||
|
||||
# Implementation
|
||||
# ##############
|
||||
|
||||
def is_histogram_extractor(obj):
|
||||
|
||||
if not base_fempostextractors.is_extractor_object(obj):
|
||||
return False
|
||||
|
||||
if not hasattr(obj.Proxy, "VisualizationType"):
|
||||
return False
|
||||
|
||||
return obj.Proxy.VisualizationType == "Histogram"
|
||||
|
||||
|
||||
class PostHistogramFieldData(post_extract1D.PostFieldData1D):
|
||||
"""
|
||||
A 1D Field extraction for histograms.
|
||||
"""
|
||||
VisualizationType = "Histogram"
|
||||
|
||||
|
||||
|
||||
|
||||
class PostHistogram(base_fempostvisualizations.PostVisualization):
|
||||
"""
|
||||
A post processing plot for showing extracted data as histograms
|
||||
"""
|
||||
|
||||
VisualizationType = "Histogram"
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
obj.addExtension("App::GroupExtensionPython")
|
||||
|
||||
def _get_properties(self):
|
||||
prop = [
|
||||
_PropHelper(
|
||||
type="Fem::PropertyPostDataObject",
|
||||
name="Table",
|
||||
group="Base",
|
||||
doc="The data table that stores the plotted data, one column per histogram",
|
||||
value=vtkTable(),
|
||||
),
|
||||
]
|
||||
return super()._get_properties() + prop
|
||||
|
||||
|
||||
def onChanged(self, obj, prop):
|
||||
|
||||
if prop == "Group":
|
||||
# check if all objects are allowed
|
||||
|
||||
children = obj.Group
|
||||
for child in obj.Group:
|
||||
if not is_histogram_extractor(child):
|
||||
FreeCAD.Console.PrintWarning(f"{child.Label} is not a data histogram data extraction object, cannot be added")
|
||||
children.remove(child)
|
||||
|
||||
if len(obj.Group) != len(children):
|
||||
obj.Group = children
|
||||
|
||||
def execute(self, obj):
|
||||
|
||||
# during execution we collect all child data into our table
|
||||
table = vtkTable()
|
||||
for child in obj.Group:
|
||||
|
||||
c_table = child.Table
|
||||
for i in range(c_table.GetNumberOfColumns()):
|
||||
c_array = c_table.GetColumn(i)
|
||||
# TODO: check which array type it is and use that one
|
||||
array = vtkDoubleArray()
|
||||
array.DeepCopy(c_array)
|
||||
array.SetName(f"{child.Source.Label}: {c_array.GetName()}")
|
||||
table.AddColumn(array)
|
||||
|
||||
obj.Table = table
|
||||
return False
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
211
src/Mod/Fem/femobjects/post_lineplot.py
Normal file
211
src/Mod/Fem/femobjects/post_lineplot.py
Normal file
@@ -0,0 +1,211 @@
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2025 Stefan Tröger <stefantroeger@gmx.net> *
|
||||
# * *
|
||||
# * This file is part of the FreeCAD CAx development system. *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
# * as published by the Free Software Foundation; either version 2 of *
|
||||
# * the License, or (at your option) any later version. *
|
||||
# * for detail see the LICENCE text file. *
|
||||
# * *
|
||||
# * This program is distributed in the hope that it will be useful, *
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
# * GNU Library General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with this program; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
__title__ = "FreeCAD post line plot"
|
||||
__author__ = "Stefan Tröger"
|
||||
__url__ = "https://www.freecad.org"
|
||||
|
||||
## @package post_lineplot
|
||||
# \ingroup FEM
|
||||
# \brief Post processing plot displaying lines
|
||||
|
||||
from . import base_fempythonobject
|
||||
_PropHelper = base_fempythonobject._PropHelper
|
||||
|
||||
# helper function to extract plot object type
|
||||
def _get_extraction_subtype(obj):
|
||||
if hasattr(obj, 'Proxy') and hasattr(obj.Proxy, "Type"):
|
||||
return obj.Proxy.Type
|
||||
|
||||
return "unknown"
|
||||
|
||||
|
||||
class PostLinePlot(base_fempythonobject.BaseFemPythonObject):
|
||||
"""
|
||||
A post processing extraction for plotting lines
|
||||
"""
|
||||
|
||||
Type = "App::FeaturePython"
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
obj.addExtension("App::GroupExtension")
|
||||
self._setup_properties(obj)
|
||||
|
||||
def _setup_properties(self, obj):
|
||||
|
||||
self.ExtractionType = "LinePlot"
|
||||
|
||||
pl = obj.PropertiesList
|
||||
for prop in self._get_properties():
|
||||
if not prop.Name in pl:
|
||||
prop.add_to_object(obj)
|
||||
|
||||
def _get_properties(self):
|
||||
prop = []
|
||||
return prop
|
||||
|
||||
def onDocumentRestored(self, obj):
|
||||
self._setup_properties(self, obj):
|
||||
|
||||
def onChanged(self, obj, prop):
|
||||
|
||||
if prop == "Group":
|
||||
# check if all objects are allowed
|
||||
|
||||
children = obj.Group
|
||||
for child in obj.Group:
|
||||
if _get_extraction_subtype(child) not in ["Line"]:
|
||||
children.remove(child)
|
||||
|
||||
if len(obj.Group) != len(children):
|
||||
obj.Group = children
|
||||
|
||||
|
||||
class PostPlotLine(base_fempythonobject.BaseFemPythonObject):
|
||||
|
||||
Type = "App::FeaturePython"
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._setup_properties(obj)
|
||||
|
||||
def _setup_properties(self, obj):
|
||||
|
||||
self.ExtractionType = "Line"
|
||||
|
||||
pl = obj.PropertiesList
|
||||
for prop in self._get_properties():
|
||||
if not prop.Name in pl:
|
||||
prop.add_to_object(obj)
|
||||
|
||||
def _get_properties(self):
|
||||
|
||||
prop = [
|
||||
_PropHelper(
|
||||
type="App::PropertyLink",
|
||||
name="Source",
|
||||
group="Line",
|
||||
doc="The data source, the line uses",
|
||||
value=None,
|
||||
),
|
||||
_PropHelper(
|
||||
type="App::PropertyEnumeration",
|
||||
name="XField",
|
||||
group="X Data",
|
||||
doc="The field to use as X data",
|
||||
value=None,
|
||||
),
|
||||
_PropHelper(
|
||||
type="App::PropertyEnumeration",
|
||||
name="XComponent",
|
||||
group="X Data",
|
||||
doc="Which part of the X field vector to use for the X axis",
|
||||
value=None,
|
||||
),
|
||||
_PropHelper(
|
||||
type="App::PropertyEnumeration",
|
||||
name="YField",
|
||||
group="Y Data",
|
||||
doc="The field to use as Y data for the line plot",
|
||||
value=None,
|
||||
),
|
||||
_PropHelper(
|
||||
type="App::PropertyEnumeration",
|
||||
name="YComponent",
|
||||
group="Y Data",
|
||||
doc="Which part of the Y field vector to use for the X axis",
|
||||
value=None,
|
||||
),
|
||||
]
|
||||
return prop
|
||||
|
||||
def onDocumentRestored(self, obj):
|
||||
self._setup_properties(self, obj):
|
||||
|
||||
def onChanged(self, obj, prop):
|
||||
|
||||
if prop == "Source":
|
||||
# check if the source is a Post object
|
||||
if obj.Source and not obj.Source.isDerivedFrom("Fem::FemPostObject"):
|
||||
FreeCAD.Console.PrintWarning("Invalid object: Line source must be FemPostObject")
|
||||
obj.XField = []
|
||||
obj.YField = []
|
||||
obj.Source = None
|
||||
|
||||
if prop == "XField":
|
||||
if not obj.Source:
|
||||
obj.XComponent = []
|
||||
return
|
||||
|
||||
point_data = obj.Source.Data.GetPointData()
|
||||
if not point_data.HasArray(obj.XField):
|
||||
obj.XComponent = []
|
||||
return
|
||||
|
||||
match point_data.GetArray(fields.index(obj.XField)).GetNumberOfComponents:
|
||||
case 1:
|
||||
obj.XComponent = ["Not a vector"]
|
||||
case 2:
|
||||
obj.XComponent = ["Magnitude", "X", "Y"]
|
||||
case 3:
|
||||
obj.XComponent = ["Magnitude", "X", "Y", "Z"]
|
||||
|
||||
if prop == "YField":
|
||||
if not obj.Source:
|
||||
obj.YComponent = []
|
||||
return
|
||||
|
||||
point_data = obj.Source.Data.GetPointData()
|
||||
if not point_data.HasArray(obj.YField):
|
||||
obj.YComponent = []
|
||||
return
|
||||
|
||||
match point_data.GetArray(fields.index(obj.YField)).GetNumberOfComponents:
|
||||
case 1:
|
||||
obj.YComponent = ["Not a vector"]
|
||||
case 2:
|
||||
obj.YComponent = ["Magnitude", "X", "Y"]
|
||||
case 3:
|
||||
obj.YComponent = ["Magnitude", "X", "Y", "Z"]
|
||||
|
||||
def onExecute(self, obj):
|
||||
# we need to make sure that we show the correct fields to the user as option for data extraction
|
||||
|
||||
fields = []
|
||||
if obj.Source:
|
||||
point_data = obj.Source.Data.GetPointData()
|
||||
fields = [point_data.GetArrayName(i) for i in range(point_data.GetNumberOfArrays())]
|
||||
|
||||
current_X = obj.XField
|
||||
obj.XField = fields
|
||||
if current_X in fields:
|
||||
obj.XField = current_X
|
||||
|
||||
current_Y = obj.YField
|
||||
obj.YField = fields
|
||||
if current_Y in fields:
|
||||
obj.YField = current_Y
|
||||
|
||||
return True
|
||||
|
||||
83
src/Mod/Fem/femtaskpanels/base_fempostpanel.py
Normal file
83
src/Mod/Fem/femtaskpanels/base_fempostpanel.py
Normal file
@@ -0,0 +1,83 @@
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2025 Stefan Tröger <stefantroeger@gmx.net> *
|
||||
# * *
|
||||
# * This file is part of the FreeCAD CAx development system. *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
# * as published by the Free Software Foundation; either version 2 of *
|
||||
# * the License, or (at your option) any later version. *
|
||||
# * for detail see the LICENCE text file. *
|
||||
# * *
|
||||
# * This program is distributed in the hope that it will be useful, *
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
# * GNU Library General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with this program; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
__title__ = "FreeCAD task panel base for post object task panels"
|
||||
__author__ = "Stefan Tröger"
|
||||
__url__ = "https://www.freecad.org"
|
||||
|
||||
## @package base_fempostpanel
|
||||
# \ingroup FEM
|
||||
# \brief task panel base for post objects
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
|
||||
from femguiutils import selection_widgets
|
||||
from . import base_femtaskpanel
|
||||
|
||||
|
||||
class _BasePostTaskPanel(base_femtaskpanel._BaseTaskPanel):
|
||||
"""
|
||||
The TaskPanel for post objects, mimicing the c++ functionality
|
||||
"""
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
|
||||
# get the settings group
|
||||
self.__settings_grp = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem")
|
||||
|
||||
# Implement parent functions
|
||||
# ##########################
|
||||
|
||||
def getStandardButtons(self):
|
||||
return QtGui.QDialogButtonBox.Apply | QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel
|
||||
|
||||
def clicked(self, button):
|
||||
# apply button hit?
|
||||
if button == QtGui.QDialogButtonBox.Apply:
|
||||
self.obj.Document.recompute()
|
||||
|
||||
|
||||
# Helper functions
|
||||
# ################
|
||||
|
||||
def _recompute(self):
|
||||
# only recompute if the user wants automatic recompute
|
||||
if self.__settings_grp.GetBool("PostAutoRecompute", True):
|
||||
self.obj.Document.recompute()
|
||||
|
||||
def _enumPropertyToCombobox(self, obj, prop, cbox):
|
||||
cbox.blockSignals(True)
|
||||
cbox.clear()
|
||||
entries = obj.getEnumerationsOfProperty(prop)
|
||||
for entry in entries:
|
||||
cbox.addItem(entry)
|
||||
|
||||
cbox.setCurrentText(getattr(obj, prop))
|
||||
cbox.blockSignals(False)
|
||||
|
||||
|
||||
|
||||
54
src/Mod/Fem/femtaskpanels/task_post_extractor.py
Normal file
54
src/Mod/Fem/femtaskpanels/task_post_extractor.py
Normal file
@@ -0,0 +1,54 @@
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2025 Stefan Tröger <stefantroeger@gmx.net> *
|
||||
# * *
|
||||
# * This file is part of the FreeCAD CAx development system. *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
# * as published by the Free Software Foundation; either version 2 of *
|
||||
# * the License, or (at your option) any later version. *
|
||||
# * for detail see the LICENCE text file. *
|
||||
# * *
|
||||
# * This program is distributed in the hope that it will be useful, *
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
# * GNU Library General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with this program; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
__title__ = "FreeCAD FEM post extractor object task panel"
|
||||
__author__ = "Stefan Tröger"
|
||||
__url__ = "https://www.freecad.org"
|
||||
|
||||
## @package task_post_extractor
|
||||
# \ingroup FEM
|
||||
# \brief universal task dialog for extractor objects.
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
|
||||
from femguiutils import selection_widgets
|
||||
from . import base_fempostpanel
|
||||
|
||||
|
||||
class _ExtractorTaskPanel(base_fempostpanel._BasePostTaskPanel):
|
||||
"""
|
||||
The TaskPanel for editing properties extractor objects. The actual UI is
|
||||
provided by the viewproviders. This allows using a universal task panel
|
||||
"""
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
|
||||
# form is used to display individual task panels
|
||||
self.form = obj.ViewObject.Proxy.get_edit_widgets(self)
|
||||
|
||||
|
||||
|
||||
@@ -35,10 +35,10 @@ import FreeCAD
|
||||
import FreeCADGui
|
||||
|
||||
from femguiutils import selection_widgets
|
||||
from . import base_femtaskpanel
|
||||
from . import base_fempostpanel
|
||||
|
||||
|
||||
class _TaskPanel(base_femtaskpanel._BaseTaskPanel):
|
||||
class _TaskPanel(base_fempostpanel._BasePostTaskPanel):
|
||||
"""
|
||||
The TaskPanel for editing properties of glyph filter
|
||||
"""
|
||||
|
||||
180
src/Mod/Fem/femtaskpanels/task_post_histogram.py
Normal file
180
src/Mod/Fem/femtaskpanels/task_post_histogram.py
Normal file
@@ -0,0 +1,180 @@
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2025 Stefan Tröger <stefantroeger@gmx.net> *
|
||||
# * *
|
||||
# * This file is part of the FreeCAD CAx development system. *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
# * as published by the Free Software Foundation; either version 2 of *
|
||||
# * the License, or (at your option) any later version. *
|
||||
# * for detail see the LICENCE text file. *
|
||||
# * *
|
||||
# * This program is distributed in the hope that it will be useful, *
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
# * GNU Library General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with this program; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
__title__ = "FreeCAD FEM histogram plot task panel"
|
||||
__author__ = "Stefan Tröger"
|
||||
__url__ = "https://www.freecad.org"
|
||||
|
||||
## @package task_post_histogram
|
||||
# \ingroup FEM
|
||||
# \brief task panel for post histogram plot
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
|
||||
from . import base_fempostpanel
|
||||
from femguiutils import extract_link_view as elv
|
||||
from femguiutils import vtk_table_view
|
||||
|
||||
class _TaskPanel(base_fempostpanel._BasePostTaskPanel):
|
||||
"""
|
||||
The TaskPanel for editing properties of glyph filter
|
||||
"""
|
||||
|
||||
def __init__(self, vobj):
|
||||
super().__init__(vobj.Object)
|
||||
|
||||
# data widget
|
||||
self.data_widget = QtGui.QWidget()
|
||||
hbox = QtGui.QHBoxLayout()
|
||||
self.data_widget.show_plot = QtGui.QPushButton()
|
||||
self.data_widget.show_plot.setText("Show plot")
|
||||
hbox.addWidget(self.data_widget.show_plot)
|
||||
self.data_widget.show_table = QtGui.QPushButton()
|
||||
self.data_widget.show_table.setText("Show data")
|
||||
hbox.addWidget(self.data_widget.show_table)
|
||||
vbox = QtGui.QVBoxLayout()
|
||||
vbox.addItem(hbox)
|
||||
vbox.addSpacing(10)
|
||||
|
||||
extracts = elv.ExtractLinkView(self.obj, False, self)
|
||||
vbox.addWidget(extracts)
|
||||
|
||||
self.data_widget.setLayout(vbox)
|
||||
self.data_widget.setWindowTitle("Histogram data")
|
||||
|
||||
|
||||
# histogram parameter widget
|
||||
self.view_widget = FreeCADGui.PySideUic.loadUi(
|
||||
FreeCAD.getHomePath() + "Mod/Fem/Resources/ui/TaskPostHistogram.ui"
|
||||
)
|
||||
self.view_widget.setWindowTitle("Histogram view settings")
|
||||
self.__init_widgets()
|
||||
|
||||
# form made from param and selection widget
|
||||
self.form = [self.data_widget, self.view_widget]
|
||||
|
||||
|
||||
# Setup functions
|
||||
# ###############
|
||||
|
||||
def __init_widgets(self):
|
||||
|
||||
# connect data widget
|
||||
self.data_widget.show_plot.clicked.connect(self.showPlot)
|
||||
self.data_widget.show_table.clicked.connect(self.showTable)
|
||||
|
||||
# set current values to view widget
|
||||
viewObj = self.obj.ViewObject
|
||||
|
||||
self.view_widget.Bins.setValue(viewObj.Bins)
|
||||
self._enumPropertyToCombobox(viewObj, "Type", self.view_widget.Type)
|
||||
self.view_widget.Cumulative.setChecked(viewObj.Cumulative)
|
||||
|
||||
self.view_widget.Title.setText(viewObj.Title)
|
||||
self.view_widget.XLabel.setText(viewObj.XLabel)
|
||||
self.view_widget.YLabel.setText(viewObj.YLabel)
|
||||
|
||||
self.view_widget.LegendShow.setChecked(viewObj.Legend)
|
||||
self._enumPropertyToCombobox(viewObj, "LegendLocation", self.view_widget.LegendPos)
|
||||
self.view_widget.BarWidth.setValue(viewObj.BarWidth)
|
||||
self.view_widget.HatchWidth.setValue(viewObj.HatchLineWidth)
|
||||
|
||||
# connect callbacks
|
||||
self.view_widget.Bins.valueChanged.connect(self.binsChanged)
|
||||
self.view_widget.Type.activated.connect(self.typeChanged)
|
||||
self.view_widget.Cumulative.toggled.connect(self.comulativeChanged)
|
||||
|
||||
self.view_widget.Title.editingFinished.connect(self.titleChanged)
|
||||
self.view_widget.XLabel.editingFinished.connect(self.xLabelChanged)
|
||||
self.view_widget.YLabel.editingFinished.connect(self.yLabelChanged)
|
||||
|
||||
self.view_widget.LegendShow.toggled.connect(self.legendShowChanged)
|
||||
self.view_widget.LegendPos.activated.connect(self.legendPosChanged)
|
||||
self.view_widget.BarWidth.valueChanged.connect(self.barWidthChanged)
|
||||
self.view_widget.HatchWidth.valueChanged.connect(self.hatchWidthChanged)
|
||||
|
||||
|
||||
QtCore.Slot()
|
||||
def showPlot(self):
|
||||
self.obj.ViewObject.Proxy.show_visualization()
|
||||
|
||||
QtCore.Slot()
|
||||
def showTable(self):
|
||||
|
||||
# TODO: make data model update when object is recomputed
|
||||
data_model = vtk_table_view.VtkTableModel()
|
||||
data_model.setTable(self.obj.Table)
|
||||
|
||||
dialog = QtGui.QDialog(self.data_widget)
|
||||
widget = vtk_table_view.VtkTableView(data_model)
|
||||
layout = QtGui.QVBoxLayout()
|
||||
layout.addWidget(widget)
|
||||
layout.setContentsMargins(0,0,0,0)
|
||||
|
||||
dialog.setLayout(layout)
|
||||
dialog.resize(1500, 900)
|
||||
dialog.show()
|
||||
|
||||
|
||||
QtCore.Slot(int)
|
||||
def binsChanged(self, bins):
|
||||
self.obj.ViewObject.Bins = bins
|
||||
|
||||
QtCore.Slot(int)
|
||||
def typeChanged(self, idx):
|
||||
self.obj.ViewObject.Type = idx
|
||||
|
||||
QtCore.Slot(bool)
|
||||
def comulativeChanged(self, state):
|
||||
self.obj.ViewObject.Cumulative = state
|
||||
|
||||
QtCore.Slot()
|
||||
def titleChanged(self):
|
||||
self.obj.ViewObject.Title = self.view_widget.Title.text()
|
||||
|
||||
QtCore.Slot()
|
||||
def xLabelChanged(self):
|
||||
self.obj.ViewObject.XLabel = self.view_widget.XLabel.text()
|
||||
|
||||
QtCore.Slot()
|
||||
def yLabelChanged(self):
|
||||
self.obj.ViewObject.YLabel = self.view_widget.YLabel.text()
|
||||
|
||||
QtCore.Slot(int)
|
||||
def legendPosChanged(self, idx):
|
||||
self.obj.ViewObject.LegendLocation = idx
|
||||
|
||||
QtCore.Slot(bool)
|
||||
def legendShowChanged(self, state):
|
||||
self.obj.ViewObject.Legend = state
|
||||
|
||||
QtCore.Slot(float)
|
||||
def barWidthChanged(self, value):
|
||||
self.obj.ViewObject.BarWidth = value
|
||||
|
||||
QtCore.Slot(float)
|
||||
def hatchWidthChanged(self, value):
|
||||
self.obj.ViewObject.HatchLineWidth = value
|
||||
@@ -36,8 +36,25 @@ import FreeCADGui
|
||||
|
||||
import FemGui # needed to display the icons in TreeView
|
||||
|
||||
from femobjects.base_fempythonobject import _PropHelper
|
||||
|
||||
False if FemGui.__name__ else True # flake8, dummy FemGui usage
|
||||
|
||||
class _GuiPropHelper(_PropHelper):
|
||||
"""
|
||||
Helper class to manage property data inside proxy objects.
|
||||
Based on the App verison, but viewprovider addProperty does
|
||||
not take keyword args, hence we use positional arguments here
|
||||
"""
|
||||
|
||||
def __init__(self, **kwds):
|
||||
super().__init__(**kwds)
|
||||
|
||||
def add_to_object(self, obj):
|
||||
obj.addProperty(self.info["type"], self.info["name"], self.info["group"], self.info["doc"])
|
||||
obj.setPropertyStatus(self.name, "LockDynamic")
|
||||
setattr(obj, self.name, self.value)
|
||||
|
||||
|
||||
class VPBaseFemObject:
|
||||
"""Proxy View Provider for FEM FeaturePythons base constraint."""
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2025 Stefan Tröger <stefantroeger@gmx.net> *
|
||||
# * *
|
||||
# * This file is part of the FreeCAD CAx development system. *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
# * as published by the Free Software Foundation; either version 2 of *
|
||||
# * the License, or (at your option) any later version. *
|
||||
# * for detail see the LICENCE text file. *
|
||||
# * *
|
||||
# * This program is distributed in the hope that it will be useful, *
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
# * GNU Library General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with this program; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
__title__ = "FreeCAD FEM postprocessing visualization base ViewProvider"
|
||||
__author__ = "Stefan Tröger"
|
||||
__url__ = "https://www.freecad.org"
|
||||
|
||||
## @package view_base_fempostvisualizations
|
||||
# \ingroup FEM
|
||||
# \brief view provider for post visualization object
|
||||
|
||||
from PySide import QtGui, QtCore
|
||||
|
||||
import Plot
|
||||
import FreeCADGui
|
||||
|
||||
from . import view_base_femobject
|
||||
_GuiPropHelper = view_base_femobject._GuiPropHelper
|
||||
|
||||
class VPPostVisualization:
|
||||
"""
|
||||
A View Provider for visualization objects
|
||||
"""
|
||||
|
||||
def __init__(self, vobj):
|
||||
vobj.Proxy = self
|
||||
self._setup_properties(vobj)
|
||||
|
||||
def _setup_properties(self, vobj):
|
||||
pl = vobj.PropertiesList
|
||||
for prop in self._get_properties():
|
||||
if not prop.name in pl:
|
||||
prop.add_to_object(vobj)
|
||||
|
||||
def _get_properties(self):
|
||||
return []
|
||||
|
||||
def attach(self, vobj):
|
||||
self.Object = vobj.Object
|
||||
self.ViewObject = vobj
|
||||
|
||||
def isShow(self):
|
||||
return True
|
||||
|
||||
def doubleClicked(self,vobj):
|
||||
|
||||
guidoc = FreeCADGui.getDocument(vobj.Object.Document)
|
||||
|
||||
# check if another VP is in edit mode and close it then
|
||||
if guidoc.getInEdit():
|
||||
FreeCADGui.Control.closeDialog()
|
||||
guidoc.resetEdit()
|
||||
|
||||
guidoc.setEdit(vobj.Object.Name)
|
||||
return True
|
||||
|
||||
def show_visualization(self):
|
||||
# shows the visualization without going into edit mode
|
||||
# to be implemented by subclasses
|
||||
pass
|
||||
|
||||
def get_kw_args(self, obj):
|
||||
# returns a dictionary with all visualization options needed for plotting
|
||||
# based on the view provider properties
|
||||
return {}
|
||||
|
||||
def dumps(self):
|
||||
return None
|
||||
|
||||
def loads(self, state):
|
||||
return None
|
||||
129
src/Mod/Fem/femviewprovider/view_post_extract.py
Normal file
129
src/Mod/Fem/femviewprovider/view_post_extract.py
Normal file
@@ -0,0 +1,129 @@
|
||||
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2025 Stefan Tröger <stefantroeger@gmx.net> *
|
||||
# * *
|
||||
# * This file is part of the FreeCAD CAx development system. *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
# * as published by the Free Software Foundation; either version 2 of *
|
||||
# * the License, or (at your option) any later version. *
|
||||
# * for detail see the LICENCE text file. *
|
||||
# * *
|
||||
# * This program is distributed in the hope that it will be useful, *
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
# * GNU Library General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with this program; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
__title__ = "FreeCAD FEM postprocessing line plot ViewProvider for the document object"
|
||||
__author__ = "Stefan Tröger"
|
||||
__url__ = "https://www.freecad.org"
|
||||
|
||||
## @package view_post_lineplot
|
||||
# \ingroup FEM
|
||||
# \brief view provider for post line plot object
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
|
||||
import FemGui
|
||||
from PySide import QtGui
|
||||
|
||||
import femobjects.base_fempostextractors as fpe
|
||||
from femtaskpanels import task_post_extractor
|
||||
|
||||
class VPPostExtractor:
|
||||
"""
|
||||
A View Provider for extraction of data
|
||||
"""
|
||||
|
||||
def __init__(self, vobj):
|
||||
vobj.Proxy = self
|
||||
self._setup_properties(vobj)
|
||||
|
||||
def _setup_properties(self, vobj):
|
||||
pl = vobj.PropertiesList
|
||||
for prop in self._get_properties():
|
||||
if not prop.name in pl:
|
||||
prop.add_to_object(vobj)
|
||||
|
||||
def _get_properties(self):
|
||||
return []
|
||||
|
||||
def attach(self, vobj):
|
||||
self.Object = vobj.Object # used on various places, claim childreens, get icon, etc.
|
||||
self.ViewObject = vobj
|
||||
|
||||
def isShow(self):
|
||||
return True
|
||||
|
||||
def onChanged(self, vobj, prop):
|
||||
|
||||
# one of our view properties was changed. Lets inform our parent plot
|
||||
# that this happend, as this is the one that needs to redraw
|
||||
|
||||
if prop == "Proxy":
|
||||
return
|
||||
|
||||
group = vobj.Object.getParentGroup()
|
||||
if not group:
|
||||
return
|
||||
|
||||
if (hasattr(group.ViewObject, "Proxy") and
|
||||
hasattr(group.ViewObject.Proxy, "childViewPropertyChanged")):
|
||||
|
||||
group.ViewObject.Proxy.childViewPropertyChanged(vobj, prop)
|
||||
|
||||
|
||||
def setEdit(self, vobj, mode):
|
||||
|
||||
# build up the task panel
|
||||
taskd = task_post_extractor._ExtractorTaskPanel(vobj.Object)
|
||||
|
||||
#show it
|
||||
FreeCADGui.Control.showDialog(taskd)
|
||||
|
||||
return True
|
||||
|
||||
def doubleClicked(self, vobj):
|
||||
guidoc = FreeCADGui.getDocument(vobj.Object.Document)
|
||||
|
||||
# check if another VP is in edit mode and close it then
|
||||
if guidoc.getInEdit():
|
||||
FreeCADGui.Control.closeDialog()
|
||||
guidoc.resetEdit()
|
||||
|
||||
guidoc.setEdit(vobj.Object.Name)
|
||||
|
||||
return True
|
||||
|
||||
def get_kw_args(self):
|
||||
# should return the plot keyword arguments that represent the properties
|
||||
# of the object
|
||||
return {}
|
||||
|
||||
def get_edit_widgets(self, post_dialog):
|
||||
# Returns a list of widgets for editing the object/viewprovider.
|
||||
# The widget will be part of the provided post_dialog, and
|
||||
# should use its functionality to inform of changes.
|
||||
raise FreeCAD.Base.FreeCADError("Not implemented")
|
||||
|
||||
def get_preview_widget(self, post_dialog):
|
||||
# Returns a widget for editing the object/viewprovider.
|
||||
# The widget will be part of the provided post_dialog, and
|
||||
# should use its functionality to inform of changes.
|
||||
raise FreeCAD.Base.FreeCADError("Not implemented")
|
||||
|
||||
|
||||
def dumps(self):
|
||||
return None
|
||||
|
||||
def loads(self, state):
|
||||
return None
|
||||
476
src/Mod/Fem/femviewprovider/view_post_histogram.py
Normal file
476
src/Mod/Fem/femviewprovider/view_post_histogram.py
Normal file
@@ -0,0 +1,476 @@
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2025 Stefan Tröger <stefantroeger@gmx.net> *
|
||||
# * *
|
||||
# * This file is part of the FreeCAD CAx development system. *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
# * as published by the Free Software Foundation; either version 2 of *
|
||||
# * the License, or (at your option) any later version. *
|
||||
# * for detail see the LICENCE text file. *
|
||||
# * *
|
||||
# * This program is distributed in the hope that it will be useful, *
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
# * GNU Library General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with this program; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
__title__ = "FreeCAD FEM postprocessing line plot ViewProvider for the document object"
|
||||
__author__ = "Stefan Tröger"
|
||||
__url__ = "https://www.freecad.org"
|
||||
|
||||
## @package view_post_lineplot
|
||||
# \ingroup FEM
|
||||
# \brief view provider for post line plot object
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
|
||||
import Plot
|
||||
import FemGui
|
||||
from PySide import QtGui, QtCore
|
||||
|
||||
import numpy as np
|
||||
import matplotlib as mpl
|
||||
|
||||
from vtkmodules.numpy_interface.dataset_adapter import VTKArray
|
||||
|
||||
from . import view_post_extract
|
||||
from . import view_base_fempostvisualization
|
||||
from femtaskpanels import task_post_histogram
|
||||
|
||||
_GuiPropHelper = view_base_fempostvisualization._GuiPropHelper
|
||||
|
||||
class EditViewWidget(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/PostHistogramFieldViewEdit.ui"
|
||||
)
|
||||
layout = QtGui.QVBoxLayout()
|
||||
layout.addWidget(self.widget)
|
||||
self.setLayout(layout)
|
||||
|
||||
self.__init_widget()
|
||||
|
||||
def __init_widget(self):
|
||||
vobj = self._object.ViewObject
|
||||
|
||||
self.widget.Legend.setText(vobj.Legend)
|
||||
self._post_dialog._enumPropertyToCombobox(vobj, "Hatch", self.widget.Hatch)
|
||||
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]))
|
||||
|
||||
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")
|
||||
self._object.ViewObject.LineColor = color.getRgb()
|
||||
|
||||
@QtCore.Slot()
|
||||
def barColorChanged(self):
|
||||
color = self.widget.BarColor.property("color")
|
||||
self._object.ViewObject.BarColor = color.getRgb()
|
||||
|
||||
@QtCore.Slot(float)
|
||||
def lineWidthChanged(self, value):
|
||||
self._object.ViewObject.LineWidth = value
|
||||
|
||||
@QtCore.Slot(float)
|
||||
def hatchDensityChanged(self, value):
|
||||
self._object.ViewObject.HatchDensity = value
|
||||
|
||||
@QtCore.Slot(int)
|
||||
def hatchPatternChanged(self, index):
|
||||
self._object.ViewObject.Hatch = index
|
||||
|
||||
@QtCore.Slot(int)
|
||||
def lineStyleChanged(self, index):
|
||||
self._object.ViewObject.LineStyle = index
|
||||
|
||||
@QtCore.Slot()
|
||||
def legendChanged(self):
|
||||
self._object.ViewObject.Legend = self.widget.Legend.text()
|
||||
|
||||
|
||||
class EditAppWidget(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/PostHistogramFieldAppEdit.ui"
|
||||
)
|
||||
layout = QtGui.QVBoxLayout()
|
||||
layout.addWidget(self.widget)
|
||||
self.setLayout(layout)
|
||||
|
||||
self.__init_widget()
|
||||
|
||||
def __init_widget(self):
|
||||
# set the other properties
|
||||
|
||||
self._post_dialog._enumPropertyToCombobox(self._object, "XField", self.widget.Field)
|
||||
self._post_dialog._enumPropertyToCombobox(self._object, "XComponent", self.widget.Component)
|
||||
self.widget.Extract.setChecked(self._object.ExtractFrames)
|
||||
|
||||
self.widget.Field.activated.connect(self.fieldChanged)
|
||||
self.widget.Component.activated.connect(self.componentChanged)
|
||||
self.widget.Extract.toggled.connect(self.extractionChanged)
|
||||
|
||||
@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(bool)
|
||||
def extractionChanged(self, extract):
|
||||
self._object.ExtractFrames = extract
|
||||
self._post_dialog._recompute()
|
||||
|
||||
|
||||
class VPPostHistogramFieldData(view_post_extract.VPPostExtractor):
|
||||
"""
|
||||
A View Provider for extraction of 1D field data specialy for histograms
|
||||
"""
|
||||
|
||||
def __init__(self, vobj):
|
||||
super().__init__(vobj)
|
||||
vobj.Proxy = self
|
||||
|
||||
def _get_properties(self):
|
||||
|
||||
prop = [
|
||||
_GuiPropHelper(
|
||||
type="App::PropertyString",
|
||||
name="Legend",
|
||||
group="HistogramPlot",
|
||||
doc="The name used in the plots legend",
|
||||
value="",
|
||||
),
|
||||
_GuiPropHelper(
|
||||
type="App::PropertyColor",
|
||||
name="BarColor",
|
||||
group="HistogramBar",
|
||||
doc="The color the data bin area is drawn with",
|
||||
value=(0, 85, 255, 255),
|
||||
),
|
||||
_GuiPropHelper(
|
||||
type="App::PropertyEnumeration",
|
||||
name="Hatch",
|
||||
group="HistogramBar",
|
||||
doc="The hatch pattern drawn in the bar",
|
||||
value=['None', '/', '\\', '|', '-', '+', 'x', 'o', 'O', '.', '*'],
|
||||
),
|
||||
_GuiPropHelper(
|
||||
type="App::PropertyIntegerConstraint",
|
||||
name="HatchDensity",
|
||||
group="HistogramBar",
|
||||
doc="The line width of the hatch",
|
||||
value=(1, 1, 99, 1),
|
||||
),
|
||||
_GuiPropHelper(
|
||||
type="App::PropertyColor",
|
||||
name="LineColor",
|
||||
group="HistogramLine",
|
||||
doc="The color the data bin area is drawn with",
|
||||
value=(0, 85, 255, 255),
|
||||
),
|
||||
_GuiPropHelper(
|
||||
type="App::PropertyFloatConstraint",
|
||||
name="LineWidth",
|
||||
group="HistogramLine",
|
||||
doc="The width of the bar, between 0 and 1 (1 being without gaps)",
|
||||
value=(1, 0, 99, 0.1),
|
||||
),
|
||||
_GuiPropHelper(
|
||||
type="App::PropertyEnumeration",
|
||||
name="LineStyle",
|
||||
group="HistogramLine",
|
||||
doc="The style the line is drawn in",
|
||||
value=['None', '-', '--', '-.', ':'],
|
||||
),
|
||||
]
|
||||
return super()._get_properties() + prop
|
||||
|
||||
def attach(self, vobj):
|
||||
self.Object = vobj.Object
|
||||
self.ViewObject = vobj
|
||||
|
||||
def getIcon(self):
|
||||
return ":/icons/FEM_PostField.svg"
|
||||
|
||||
def get_edit_widgets(self, post_dialog):
|
||||
return [ EditAppWidget(self.Object, post_dialog),
|
||||
EditViewWidget(self.Object, post_dialog)]
|
||||
|
||||
def get_preview_widget(self, post_dialog):
|
||||
return QtGui.QComboBox()
|
||||
|
||||
def get_kw_args(self):
|
||||
# builds kw args from the properties
|
||||
kwargs = {}
|
||||
|
||||
# colors need a workaround, some error occurs with rgba tuple
|
||||
kwargs["edgecolor"] = self.ViewObject.LineColor
|
||||
kwargs["facecolor"] = self.ViewObject.BarColor
|
||||
kwargs["linestyle"] = self.ViewObject.LineStyle
|
||||
kwargs["linewidth"] = self.ViewObject.LineWidth
|
||||
if self.ViewObject.Hatch != "None":
|
||||
kwargs["hatch"] = self.ViewObject.Hatch*self.ViewObject.HatchDensity
|
||||
|
||||
return kwargs
|
||||
|
||||
|
||||
class VPPostHistogram(view_base_fempostvisualization.VPPostVisualization):
|
||||
"""
|
||||
A View Provider for Histogram plots
|
||||
"""
|
||||
|
||||
def __init__(self, vobj):
|
||||
super().__init__(vobj)
|
||||
vobj.addExtension("Gui::ViewProviderGroupExtensionPython")
|
||||
|
||||
def _get_properties(self):
|
||||
|
||||
prop = [
|
||||
_GuiPropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="Cumulative",
|
||||
group="Histogram",
|
||||
doc="If be the bars shoud show the cumulative sum left to rigth",
|
||||
value=False,
|
||||
),
|
||||
_GuiPropHelper(
|
||||
type="App::PropertyEnumeration",
|
||||
name="Type",
|
||||
group="Histogram",
|
||||
doc="The type of histogram plotted",
|
||||
value=["bar","barstacked", "step", "stepfilled"],
|
||||
),
|
||||
_GuiPropHelper(
|
||||
type="App::PropertyFloatConstraint",
|
||||
name="BarWidth",
|
||||
group="Histogram",
|
||||
doc="The width of the bar, between 0 and 1 (1 being without gaps)",
|
||||
value=(0.9, 0, 1, 0.05),
|
||||
),
|
||||
_GuiPropHelper(
|
||||
type="App::PropertyFloatConstraint",
|
||||
name="HatchLineWidth",
|
||||
group="Histogram",
|
||||
doc="The line width of all drawn hatch patterns",
|
||||
value=(1, 0, 99, 0.1),
|
||||
),
|
||||
_GuiPropHelper(
|
||||
type="App::PropertyInteger",
|
||||
name="Bins",
|
||||
group="Histogram",
|
||||
doc="The number of bins the data is split into",
|
||||
value=10,
|
||||
),
|
||||
_GuiPropHelper(
|
||||
type="App::PropertyString",
|
||||
name="Title",
|
||||
group="Plot",
|
||||
doc="The histogram plot title",
|
||||
value="",
|
||||
),
|
||||
_GuiPropHelper(
|
||||
type="App::PropertyString",
|
||||
name="XLabel",
|
||||
group="Plot",
|
||||
doc="The label shown for the histogram X axis",
|
||||
value="",
|
||||
),
|
||||
_GuiPropHelper(
|
||||
type="App::PropertyString",
|
||||
name="YLabel",
|
||||
group="Plot",
|
||||
doc="The label shown for the histogram Y axis",
|
||||
value="",
|
||||
),
|
||||
_GuiPropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="Legend",
|
||||
group="Plot",
|
||||
doc="Determines if the legend is plotted",
|
||||
value=True,
|
||||
),
|
||||
_GuiPropHelper(
|
||||
type="App::PropertyEnumeration",
|
||||
name="LegendLocation",
|
||||
group="Plot",
|
||||
doc="Determines if the legend is plotted",
|
||||
value=['best','upper right','upper left','lower left','lower right','right',
|
||||
'center left','center right','lower center','upper center','center'],
|
||||
),
|
||||
|
||||
]
|
||||
return prop
|
||||
|
||||
def getIcon(self):
|
||||
return ":/icons/FEM_PostHistogram.svg"
|
||||
|
||||
def doubleClicked(self,vobj):
|
||||
|
||||
self.show_visualization()
|
||||
super().doubleClicked(vobj)
|
||||
|
||||
def setEdit(self, vobj, mode):
|
||||
|
||||
# build up the task panel
|
||||
taskd = task_post_histogram._TaskPanel(vobj)
|
||||
|
||||
#show it
|
||||
FreeCADGui.Control.showDialog(taskd)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def show_visualization(self):
|
||||
|
||||
if not hasattr(self, "_plot") or not self._plot:
|
||||
self._plot = Plot.Plot()
|
||||
self._dialog = QtGui.QDialog(Plot.getMainWindow())
|
||||
box = QtGui.QVBoxLayout()
|
||||
box.addWidget(self._plot)
|
||||
self._dialog.setLayout(box)
|
||||
|
||||
self.drawPlot()
|
||||
self._dialog.show()
|
||||
|
||||
def get_kw_args(self, obj):
|
||||
view = obj.ViewObject
|
||||
if not view or not hasattr(view, "Proxy"):
|
||||
return {}
|
||||
if not hasattr(view.Proxy, "get_kw_args"):
|
||||
return {}
|
||||
return view.Proxy.get_kw_args()
|
||||
|
||||
def drawPlot(self):
|
||||
|
||||
if not hasattr(self, "_plot") or not self._plot:
|
||||
return
|
||||
|
||||
self._plot.axes.clear()
|
||||
bins = self.ViewObject.Bins
|
||||
|
||||
# we do not iterate the table, but iterate the children. This makes it possible
|
||||
# to attribute the correct styles
|
||||
full_args = {}
|
||||
full_data = []
|
||||
labels = []
|
||||
for child in self.Object.Group:
|
||||
|
||||
table = child.Table
|
||||
kwargs = self.get_kw_args(child)
|
||||
|
||||
# iterate over the table and plot all
|
||||
color_factor = np.linspace(1,0.5,table.GetNumberOfColumns())
|
||||
legend_multiframe = table.GetNumberOfColumns() > 1
|
||||
for i in range(table.GetNumberOfColumns()):
|
||||
|
||||
# add the kw args, with some slide change over color for multiple frames
|
||||
for key in kwargs:
|
||||
if not (key in full_args):
|
||||
full_args[key] = []
|
||||
|
||||
if "color" in key:
|
||||
value = np.array(kwargs[key])*color_factor[i]
|
||||
full_args[key].append(mpl.colors.to_hex(value))
|
||||
else:
|
||||
full_args[key].append(kwargs[key])
|
||||
|
||||
data = VTKArray(table.GetColumn(i))
|
||||
full_data.append(data)
|
||||
|
||||
# legend labels
|
||||
if child.ViewObject.Legend:
|
||||
if not legend_multiframe:
|
||||
labels.append(child.ViewObject.Legend)
|
||||
else:
|
||||
postfix = table.GetColumnName(i).split("-")[-1]
|
||||
labels.append(child.ViewObject.Legend + " - " + postfix)
|
||||
else:
|
||||
legend_prefix = ""
|
||||
if len(self.Object.Group) > 1:
|
||||
legend_prefix = child.Source.Label + ": "
|
||||
labels.append(legend_prefix + table.GetColumnName(i))
|
||||
|
||||
|
||||
full_args["hatch_linewidth"] = self.ViewObject.HatchLineWidth
|
||||
full_args["rwidth"] = self.ViewObject.BarWidth
|
||||
full_args["cumulative"] = self.ViewObject.Cumulative
|
||||
full_args["histtype"] = self.ViewObject.Type
|
||||
full_args["label"] = labels
|
||||
|
||||
self._plot.axes.hist(full_data, bins, **full_args)
|
||||
|
||||
if self.ViewObject.Title:
|
||||
self._plot.axes.set_title(self.ViewObject.Title)
|
||||
if self.ViewObject.XLabel:
|
||||
self._plot.axes.set_xlabel(self.ViewObject.XLabel)
|
||||
if self.ViewObject.YLabel:
|
||||
self._plot.axes.set_ylabel(self.ViewObject.YLabel)
|
||||
|
||||
if self.ViewObject.Legend and labels:
|
||||
self._plot.axes.legend(loc = self.ViewObject.LegendLocation)
|
||||
|
||||
self._plot.update()
|
||||
|
||||
|
||||
def updateData(self, obj, prop):
|
||||
# we only react if the table changed, as then know that new data is available
|
||||
if prop == "Table":
|
||||
self.drawPlot()
|
||||
|
||||
|
||||
def onChanged(self, vobj, prop):
|
||||
|
||||
# for all property changes we need to redraw the plot
|
||||
self.drawPlot()
|
||||
|
||||
def childViewPropertyChanged(self, vobj, prop):
|
||||
|
||||
# on of our extractors has a changed view property.
|
||||
self.drawPlot()
|
||||
|
||||
def dumps(self):
|
||||
return None
|
||||
|
||||
|
||||
def loads(self, state):
|
||||
return None
|
||||
71
src/Mod/Fem/femviewprovider/view_post_lineplot.py
Normal file
71
src/Mod/Fem/femviewprovider/view_post_lineplot.py
Normal file
@@ -0,0 +1,71 @@
|
||||
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2025 Stefan Tröger <stefantroeger@gmx.net> *
|
||||
# * *
|
||||
# * This file is part of the FreeCAD CAx development system. *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
# * as published by the Free Software Foundation; either version 2 of *
|
||||
# * the License, or (at your option) any later version. *
|
||||
# * for detail see the LICENCE text file. *
|
||||
# * *
|
||||
# * This program is distributed in the hope that it will be useful, *
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
# * GNU Library General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with this program; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
__title__ = "FreeCAD FEM postprocessing line plot ViewProvider for the document object"
|
||||
__author__ = "Stefan Tröger"
|
||||
__url__ = "https://www.freecad.org"
|
||||
|
||||
## @package view_post_lineplot
|
||||
# \ingroup FEM
|
||||
# \brief view provider for post line plot object
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
|
||||
import FemGui
|
||||
from PySide import QtGui
|
||||
|
||||
|
||||
class VPPostLinePlot:
|
||||
"""
|
||||
A View Provider for the Post LinePlot object
|
||||
"""
|
||||
|
||||
def __init__(self, vobj):
|
||||
vobj.Proxy = self
|
||||
|
||||
def getIcon(self):
|
||||
return ":/icons/FEM_PostLineplot.svg"
|
||||
|
||||
def setEdit(self, vobj, mode):
|
||||
# make sure we see what we edit
|
||||
vobj.show()
|
||||
|
||||
# build up the task panel
|
||||
#taskd = task_post_glyphfilter._TaskPanel(vobj)
|
||||
|
||||
#show it
|
||||
#FreeCADGui.Control.showDialog(taskd)
|
||||
|
||||
return True
|
||||
|
||||
def unsetEdit(self, vobj, mode):
|
||||
FreeCADGui.Control.closeDialog()
|
||||
return True
|
||||
|
||||
def dumps(self):
|
||||
return None
|
||||
|
||||
def loads(self, state):
|
||||
return None
|
||||
Reference in New Issue
Block a user