Merge pull request #20891 from ickby/FEM_python_filter
FEM: Enabling postprocessing filters written in python, and adding a glyph example
This commit is contained in:
@@ -42,7 +42,7 @@ macro(SetupSalomeSMESH)
|
|||||||
endif()
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
else()
|
else()
|
||||||
set(VTK_COMPONENTS "CommonCore;CommonDataModel;FiltersVerdict;IOXML;FiltersCore;FiltersGeneral;IOLegacy;FiltersExtraction;FiltersSources;FiltersGeometry")
|
set(VTK_COMPONENTS "CommonCore;CommonDataModel;FiltersVerdict;IOXML;FiltersCore;FiltersGeneral;IOLegacy;FiltersExtraction;FiltersSources;FiltersGeometry;WrappingPythonCore")
|
||||||
list(APPEND VTK_COMPONENTS "IOMPIParallel;ParallelMPI;hdf5;FiltersParallelDIY2;RenderingCore;InteractionStyle;RenderingFreeType;RenderingOpenGL2")
|
list(APPEND VTK_COMPONENTS "IOMPIParallel;ParallelMPI;hdf5;FiltersParallelDIY2;RenderingCore;InteractionStyle;RenderingFreeType;RenderingOpenGL2")
|
||||||
foreach(_module ${VTK_COMPONENTS})
|
foreach(_module ${VTK_COMPONENTS})
|
||||||
list (FIND VTK_AVAILABLE_COMPONENTS ${_module} _index)
|
list (FIND VTK_AVAILABLE_COMPONENTS ${_module} _index)
|
||||||
@@ -63,6 +63,17 @@ macro(SetupSalomeSMESH)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(BUILD_FEM_VTK ON)
|
set(BUILD_FEM_VTK ON)
|
||||||
|
|
||||||
|
# Check if PythonWrapperCore was found
|
||||||
|
# Note: VTK 9 only, as the implementations use the VTK modules introduced in 8.1
|
||||||
|
# VTK_WrappingPythonCore_FOUND is named differently for versions <9.0
|
||||||
|
if (${VTK_WrappingPythonCore_FOUND})
|
||||||
|
set(BUILD_FEM_VTK_PYTHON 1)
|
||||||
|
message(STATUS "VTK python wrapper: available")
|
||||||
|
else()
|
||||||
|
message(STATUS "VTK python wrapper: NOT available")
|
||||||
|
endif()
|
||||||
|
|
||||||
if(${VTK_MAJOR_VERSION} LESS 6)
|
if(${VTK_MAJOR_VERSION} LESS 6)
|
||||||
message( FATAL_ERROR "Found VTK version is <6, this is not compatible" )
|
message( FATAL_ERROR "Found VTK version is <6, this is not compatible" )
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -206,6 +206,8 @@ PyMOD_INIT_FUNC(Fem)
|
|||||||
Fem::FemPostSphereFunction ::init();
|
Fem::FemPostSphereFunction ::init();
|
||||||
|
|
||||||
Fem::PropertyPostDataObject ::init();
|
Fem::PropertyPostDataObject ::init();
|
||||||
|
|
||||||
|
Fem::PostFilterPython ::init();
|
||||||
#endif
|
#endif
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,11 @@
|
|||||||
#ifdef FC_USE_VTK
|
#ifdef FC_USE_VTK
|
||||||
#include "FemPostPipeline.h"
|
#include "FemPostPipeline.h"
|
||||||
#include "FemVTKTools.h"
|
#include "FemVTKTools.h"
|
||||||
|
#include <LibraryVersions.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef FC_USE_VTK_PYTHON
|
||||||
|
#include <vtkPythonUtil.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@@ -75,6 +80,15 @@ public:
|
|||||||
&Module::writeResult,
|
&Module::writeResult,
|
||||||
"write a CFD or FEM result (auto detect) to a file (file format "
|
"write a CFD or FEM result (auto detect) to a file (file format "
|
||||||
"detected from file suffix)");
|
"detected from file suffix)");
|
||||||
|
add_varargs_method("getVtkVersion",
|
||||||
|
&Module::getVtkVersion,
|
||||||
|
"Returns the VTK version FreeCAD is linked against");
|
||||||
|
#ifdef FC_USE_VTK_PYTHON
|
||||||
|
add_varargs_method(
|
||||||
|
"isVtkCompatible",
|
||||||
|
&Module::isVtkCompatible,
|
||||||
|
"Checks if the passed vtkObject is compatible with the c++ VTK version FreeCAD uses");
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
add_varargs_method("show",
|
add_varargs_method("show",
|
||||||
&Module::show,
|
&Module::show,
|
||||||
@@ -318,6 +332,34 @@ private:
|
|||||||
|
|
||||||
return Py::None();
|
return Py::None();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Py::Object getVtkVersion(const Py::Tuple& args)
|
||||||
|
{
|
||||||
|
if (!PyArg_ParseTuple(args.ptr(), "")) {
|
||||||
|
throw Py::Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Py::String(fcVtkVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef FC_USE_VTK_PYTHON
|
||||||
|
Py::Object isVtkCompatible(const Py::Tuple& args)
|
||||||
|
{
|
||||||
|
PyObject* pcObj = nullptr;
|
||||||
|
if (!PyArg_ParseTuple(args.ptr(), "O", &pcObj)) {
|
||||||
|
throw Py::Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
// if non is returned the VTK object was created by annother VTK library, and the
|
||||||
|
// python api used to create it cannot be used with FreeCAD
|
||||||
|
vtkObjectBase* obj = vtkPythonUtil::GetPointerFromObject(pcObj, "vtkObject");
|
||||||
|
if (!obj) {
|
||||||
|
PyErr_Clear();
|
||||||
|
return Py::False();
|
||||||
|
}
|
||||||
|
return Py::True();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Py::Object show(const Py::Tuple& args)
|
Py::Object show(const Py::Tuple& args)
|
||||||
|
|||||||
@@ -67,11 +67,14 @@ if(BUILD_FEM_VTK)
|
|||||||
FemPostObjectPyImp.cpp
|
FemPostObjectPyImp.cpp
|
||||||
FemPostPipelinePy.xml
|
FemPostPipelinePy.xml
|
||||||
FemPostPipelinePyImp.cpp
|
FemPostPipelinePyImp.cpp
|
||||||
|
FemPostFilterPy.xml
|
||||||
|
FemPostFilterPyImp.cpp
|
||||||
FemPostBranchFilterPy.xml
|
FemPostBranchFilterPy.xml
|
||||||
FemPostBranchFilterPyImp.cpp
|
FemPostBranchFilterPyImp.cpp
|
||||||
)
|
)
|
||||||
generate_from_xml(FemPostObjectPy)
|
generate_from_xml(FemPostObjectPy)
|
||||||
generate_from_xml(FemPostPipelinePy)
|
generate_from_xml(FemPostPipelinePy)
|
||||||
|
generate_from_xml(FemPostFilterPy)
|
||||||
generate_from_xml(FemPostBranchFilterPy)
|
generate_from_xml(FemPostBranchFilterPy)
|
||||||
endif(BUILD_FEM_VTK)
|
endif(BUILD_FEM_VTK)
|
||||||
SOURCE_GROUP("Python" FILES ${Python_SRCS})
|
SOURCE_GROUP("Python" FILES ${Python_SRCS})
|
||||||
|
|||||||
@@ -31,10 +31,13 @@
|
|||||||
#include <vtkUnstructuredGrid.h>
|
#include <vtkUnstructuredGrid.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <App/FeaturePythonPyImp.h>
|
||||||
#include <App/Document.h>
|
#include <App/Document.h>
|
||||||
#include <Base/Console.h>
|
#include <Base/Console.h>
|
||||||
|
|
||||||
#include "FemPostFilter.h"
|
#include "FemPostFilter.h"
|
||||||
|
#include "FemPostFilterPy.h"
|
||||||
|
|
||||||
#include "FemPostPipeline.h"
|
#include "FemPostPipeline.h"
|
||||||
#include "FemPostBranchFilter.h"
|
#include "FemPostBranchFilter.h"
|
||||||
|
|
||||||
@@ -52,22 +55,42 @@ FemPostFilter::FemPostFilter()
|
|||||||
"Data",
|
"Data",
|
||||||
App::Prop_ReadOnly,
|
App::Prop_ReadOnly,
|
||||||
"The step used to calculate the data");
|
"The step used to calculate the data");
|
||||||
|
|
||||||
|
// the default pipeline: just a passthrough
|
||||||
|
// this is used to simplify the python filter handling,
|
||||||
|
// as those do not have filter pipelines setup till later
|
||||||
|
// in the document loading process.
|
||||||
|
auto filter = vtkPassThrough::New();
|
||||||
|
auto pipeline = FemPostFilter::FilterPipeline();
|
||||||
|
pipeline.algorithmStorage.push_back(filter);
|
||||||
|
pipeline.source = filter;
|
||||||
|
pipeline.target = filter;
|
||||||
|
addFilterPipeline(pipeline, "__passthrough__");
|
||||||
}
|
}
|
||||||
|
|
||||||
FemPostFilter::~FemPostFilter() = default;
|
FemPostFilter::~FemPostFilter() = default;
|
||||||
|
|
||||||
|
|
||||||
void FemPostFilter::addFilterPipeline(const FemPostFilter::FilterPipeline& p, std::string name)
|
void FemPostFilter::addFilterPipeline(const FemPostFilter::FilterPipeline& p, std::string name)
|
||||||
{
|
{
|
||||||
m_pipelines[name] = p;
|
m_pipelines[name] = p;
|
||||||
|
|
||||||
|
if (m_activePipeline.empty()) {
|
||||||
|
m_activePipeline = name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FemPostFilter::FilterPipeline& FemPostFilter::getFilterPipeline(std::string name)
|
FemPostFilter::FilterPipeline& FemPostFilter::getFilterPipeline(std::string name)
|
||||||
{
|
{
|
||||||
return m_pipelines[name];
|
return m_pipelines.at(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FemPostFilter::setActiveFilterPipeline(std::string name)
|
void FemPostFilter::setActiveFilterPipeline(std::string name)
|
||||||
{
|
{
|
||||||
|
if (m_pipelines.count(name) == 0) {
|
||||||
|
throw Base::ValueError("Not a filter pipline name");
|
||||||
|
}
|
||||||
|
|
||||||
if (m_activePipeline != name && isValid()) {
|
if (m_activePipeline != name && isValid()) {
|
||||||
|
|
||||||
// disable all inputs of current pipeline
|
// disable all inputs of current pipeline
|
||||||
@@ -129,6 +152,7 @@ void FemPostFilter::onChanged(const App::Property* prop)
|
|||||||
{
|
{
|
||||||
|
|
||||||
if (prop == &Placement) {
|
if (prop == &Placement) {
|
||||||
|
|
||||||
if (Placement.getValue().isIdentity() && m_use_transform) {
|
if (Placement.getValue().isIdentity() && m_use_transform) {
|
||||||
// remove transform from pipeline
|
// remove transform from pipeline
|
||||||
if (m_transform_location == TransformLocation::output) {
|
if (m_transform_location == TransformLocation::output) {
|
||||||
@@ -191,7 +215,6 @@ DocumentObjectExecReturn* FemPostFilter::execute()
|
|||||||
|
|
||||||
Data.setValue(output->GetOutputDataObject(0));
|
Data.setValue(output->GetOutputDataObject(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
return StdReturn;
|
return StdReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,8 +226,19 @@ vtkSmartPointer<vtkDataSet> FemPostFilter::getInputData()
|
|||||||
}
|
}
|
||||||
|
|
||||||
vtkAlgorithmOutput* output = active.source->GetInputConnection(0, 0);
|
vtkAlgorithmOutput* output = active.source->GetInputConnection(0, 0);
|
||||||
|
if (!output) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
vtkAlgorithm* algo = output->GetProducer();
|
vtkAlgorithm* algo = output->GetProducer();
|
||||||
algo->Update();
|
if (!algo) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (Frame.getValue() > 0) {
|
||||||
|
algo->UpdateTimeStep(Frame.getValue());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
algo->Update();
|
||||||
|
}
|
||||||
return vtkDataSet::SafeDownCast(algo->GetOutputDataObject(0));
|
return vtkDataSet::SafeDownCast(algo->GetOutputDataObject(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,6 +285,45 @@ void FemPostFilter::setTransformLocation(TransformLocation loc)
|
|||||||
m_transform_location = loc;
|
m_transform_location = loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyObject* FemPostFilter::getPyObject()
|
||||||
|
{
|
||||||
|
if (PythonObject.is(Py::_None())) {
|
||||||
|
// ref counter is set to 1
|
||||||
|
PythonObject = Py::Object(new FemPostFilterPy(this), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Py::new_reference_to(PythonObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Python Filter feature ---------------------------------------------------------
|
||||||
|
|
||||||
|
namespace App
|
||||||
|
{
|
||||||
|
/// @cond DOXERR
|
||||||
|
PROPERTY_SOURCE_TEMPLATE(Fem::PostFilterPython, Fem::FemPostFilter)
|
||||||
|
template<>
|
||||||
|
const char* Fem::PostFilterPython::getViewProviderName(void) const
|
||||||
|
{
|
||||||
|
return "FemGui::ViewProviderPostFilterPython";
|
||||||
|
}
|
||||||
|
template<>
|
||||||
|
PyObject* Fem::PostFilterPython::getPyObject()
|
||||||
|
{
|
||||||
|
if (PythonObject.is(Py::_None())) {
|
||||||
|
// ref counter is set to 1
|
||||||
|
PythonObject = Py::Object(new App::FeaturePythonPyT<FemPostFilterPy>(this), true);
|
||||||
|
}
|
||||||
|
return Py::new_reference_to(PythonObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
// explicit template instantiation
|
||||||
|
template class FemExport FeaturePythonT<Fem::FemPostFilter>;
|
||||||
|
} // namespace App
|
||||||
|
|
||||||
|
|
||||||
// ***************************************************************************
|
// ***************************************************************************
|
||||||
// in the following, the different filters sorted alphabetically
|
// in the following, the different filters sorted alphabetically
|
||||||
// ***************************************************************************
|
// ***************************************************************************
|
||||||
|
|||||||
@@ -40,6 +40,7 @@
|
|||||||
|
|
||||||
#include <App/PropertyUnits.h>
|
#include <App/PropertyUnits.h>
|
||||||
#include <App/DocumentObjectExtension.h>
|
#include <App/DocumentObjectExtension.h>
|
||||||
|
#include <App/FeaturePython.h>
|
||||||
|
|
||||||
#include "FemPostObject.h"
|
#include "FemPostObject.h"
|
||||||
|
|
||||||
@@ -53,6 +54,8 @@ enum class TransformLocation : size_t
|
|||||||
output
|
output
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FemPostFilterPy;
|
||||||
|
|
||||||
class FemExport FemPostFilter: public Fem::FemPostObject
|
class FemExport FemPostFilter: public Fem::FemPostObject
|
||||||
{
|
{
|
||||||
PROPERTY_HEADER_WITH_OVERRIDE(Fem::FemPostFilter);
|
PROPERTY_HEADER_WITH_OVERRIDE(Fem::FemPostFilter);
|
||||||
@@ -69,12 +72,16 @@ protected:
|
|||||||
std::vector<vtkSmartPointer<vtkAlgorithm>> algorithmStorage;
|
std::vector<vtkSmartPointer<vtkAlgorithm>> algorithmStorage;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// pipeline handling
|
||||||
void addFilterPipeline(const FilterPipeline& p, std::string name);
|
void addFilterPipeline(const FilterPipeline& p, std::string name);
|
||||||
void setActiveFilterPipeline(std::string name);
|
|
||||||
FilterPipeline& getFilterPipeline(std::string name);
|
FilterPipeline& getFilterPipeline(std::string name);
|
||||||
|
void setActiveFilterPipeline(std::string name);
|
||||||
|
|
||||||
|
// Transformation handling
|
||||||
void setTransformLocation(TransformLocation loc);
|
void setTransformLocation(TransformLocation loc);
|
||||||
|
|
||||||
|
friend class FemPostFilterPy;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// Constructor
|
/// Constructor
|
||||||
FemPostFilter();
|
FemPostFilter();
|
||||||
@@ -88,16 +95,21 @@ public:
|
|||||||
vtkSmartPointer<vtkAlgorithm> getFilterInput();
|
vtkSmartPointer<vtkAlgorithm> getFilterInput();
|
||||||
vtkSmartPointer<vtkAlgorithm> getFilterOutput();
|
vtkSmartPointer<vtkAlgorithm> getFilterOutput();
|
||||||
|
|
||||||
|
PyObject* getPyObject() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// handling of multiple pipelines which can be the filter
|
// handling of multiple pipelines which can be the filter
|
||||||
std::map<std::string, FilterPipeline> m_pipelines;
|
std::map<std::string, FilterPipeline> m_pipelines;
|
||||||
std::string m_activePipeline;
|
std::string m_activePipeline;
|
||||||
bool m_use_transform = false;
|
bool m_use_transform = false;
|
||||||
|
bool m_running_setup = false;
|
||||||
TransformLocation m_transform_location = TransformLocation::output;
|
TransformLocation m_transform_location = TransformLocation::output;
|
||||||
|
|
||||||
void pipelineChanged(); // inform parents that the pipeline changed
|
void pipelineChanged(); // inform parents that the pipeline changed
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using PostFilterPython = App::FeaturePythonT<FemPostFilter>;
|
||||||
|
|
||||||
class FemExport FemPostSmoothFilterExtension: public App::DocumentObjectExtension
|
class FemExport FemPostSmoothFilterExtension: public App::DocumentObjectExtension
|
||||||
{
|
{
|
||||||
EXTENSION_PROPERTY_HEADER_WITH_OVERRIDE(Fem::FemPostSmoothFilterExtension);
|
EXTENSION_PROPERTY_HEADER_WITH_OVERRIDE(Fem::FemPostSmoothFilterExtension);
|
||||||
|
|||||||
56
src/Mod/Fem/App/FemPostFilterPy.xml
Normal file
56
src/Mod/Fem/App/FemPostFilterPy.xml
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
|
||||||
|
<PythonExport
|
||||||
|
Father="FemPostObjectPy"
|
||||||
|
Name="FemPostFilterPy"
|
||||||
|
Twin="FemPostFilter"
|
||||||
|
TwinPointer="FemPostFilter"
|
||||||
|
Include="Mod/Fem/App/FemPostFilter.h"
|
||||||
|
Namespace="Fem"
|
||||||
|
FatherInclude="Mod/Fem/App/FemPostObjectPy.h"
|
||||||
|
FatherNamespace="Fem">
|
||||||
|
<Documentation>
|
||||||
|
<Author Licence="LGPL" Name="Stefan Tröger" EMail="stefantroeger@gmx.net" />
|
||||||
|
<UserDocu>The FemPostFilter class.</UserDocu>
|
||||||
|
</Documentation>
|
||||||
|
<Methode Name="addFilterPipeline">
|
||||||
|
<Documentation>
|
||||||
|
<UserDocu>Registers a new vtk filter pipeline for data processing. Arguments are (name, source algorithm, target algorithm).</UserDocu>
|
||||||
|
</Documentation>
|
||||||
|
</Methode>
|
||||||
|
<Methode Name="setActiveFilterPipeline">
|
||||||
|
<Documentation>
|
||||||
|
<UserDocu>Sets the filter pipeline that shall be used for data processing. Argument is the name of the filter pipeline to activate.</UserDocu>
|
||||||
|
</Documentation>
|
||||||
|
</Methode>
|
||||||
|
<Methode Name="getParentPostGroup">
|
||||||
|
<Documentation>
|
||||||
|
<UserDocu>Returns the postprocessing group the filter is in (e.g. a pipeline or branch object). None is returned if not in any.</UserDocu>
|
||||||
|
</Documentation>
|
||||||
|
</Methode>
|
||||||
|
<Methode Name="getInputData">
|
||||||
|
<Documentation>
|
||||||
|
<UserDocu>
|
||||||
|
Returns the dataset available at the 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="getInputVectorFields">
|
||||||
|
<Documentation>
|
||||||
|
<UserDocu>
|
||||||
|
Returns the names of all vector 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="getInputScalarFields">
|
||||||
|
<Documentation>
|
||||||
|
<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>"
|
||||||
|
</PythonExport>
|
||||||
|
</GenerateModel>
|
||||||
194
src/Mod/Fem/App/FemPostFilterPyImp.cpp
Normal file
194
src/Mod/Fem/App/FemPostFilterPyImp.cpp
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
* Copyright (c) 2017 Werner Mayer <wmayer[at]users.sourceforge.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_
|
||||||
|
#include <Python.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <Base/FileInfo.h>
|
||||||
|
#include <Base/UnitPy.h>
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
#include "FemPostGroupExtension.h"
|
||||||
|
#include "FemPostFilter.h"
|
||||||
|
#include "FemPostFilterPy.h"
|
||||||
|
#include "FemPostFilterPy.cpp"
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
#ifdef FC_USE_VTK_PYTHON
|
||||||
|
#include <vtkUnstructuredGrid.h>
|
||||||
|
#include <vtkPythonUtil.h>
|
||||||
|
#endif // BUILD_FEM_VTK
|
||||||
|
|
||||||
|
using namespace Fem;
|
||||||
|
|
||||||
|
// returns a string which represents the object e.g. when printed in python
|
||||||
|
std::string FemPostFilterPy::representation() const
|
||||||
|
{
|
||||||
|
std::stringstream str;
|
||||||
|
str << "<FemPostFilter object at " << getFemPostFilterPtr() << ">";
|
||||||
|
|
||||||
|
return str.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PyObject* FemPostFilterPy::addFilterPipeline(PyObject* args)
|
||||||
|
{
|
||||||
|
#ifdef FC_USE_VTK_PYTHON
|
||||||
|
const char* name;
|
||||||
|
PyObject* source = nullptr;
|
||||||
|
PyObject* target = nullptr;
|
||||||
|
|
||||||
|
if (PyArg_ParseTuple(args, "sOO", &name, &source, &target)) {
|
||||||
|
|
||||||
|
// extract the algorithms
|
||||||
|
vtkObjectBase* obj = vtkPythonUtil::GetPointerFromObject(source, "vtkAlgorithm");
|
||||||
|
if (!obj) {
|
||||||
|
// error marker is set by PythonUtil
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto source_algo = static_cast<vtkAlgorithm*>(obj);
|
||||||
|
|
||||||
|
obj = vtkPythonUtil::GetPointerFromObject(target, "vtkAlgorithm");
|
||||||
|
if (!obj) {
|
||||||
|
// error marker is set by PythonUtil
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto target_algo = static_cast<vtkAlgorithm*>(obj);
|
||||||
|
|
||||||
|
// add the pipeline
|
||||||
|
FemPostFilter::FilterPipeline pipe;
|
||||||
|
pipe.source = source_algo;
|
||||||
|
pipe.target = target_algo;
|
||||||
|
getFemPostFilterPtr()->addFilterPipeline(pipe, name);
|
||||||
|
}
|
||||||
|
Py_Return;
|
||||||
|
#else
|
||||||
|
PyErr_SetString(PyExc_NotImplementedError, "VTK python wrapper not available");
|
||||||
|
Py_Return;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* FemPostFilterPy::setActiveFilterPipeline(PyObject* args)
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
if (PyArg_ParseTuple(args, "s", &name)) {
|
||||||
|
getFemPostFilterPtr()->setActiveFilterPipeline(std::string(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_Return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* FemPostFilterPy::getParentPostGroup(PyObject* args)
|
||||||
|
{
|
||||||
|
// we take no arguments
|
||||||
|
if (!PyArg_ParseTuple(args, "")) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto group = Fem::FemPostGroupExtension::getGroupOfObject(getFemPostFilterPtr());
|
||||||
|
if (group) {
|
||||||
|
return group->getPyObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Py_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* FemPostFilterPy::getInputData(PyObject* args)
|
||||||
|
{
|
||||||
|
#ifdef FC_USE_VTK_PYTHON
|
||||||
|
// we take no arguments
|
||||||
|
if (!PyArg_ParseTuple(args, "")) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make a copy of the dataset
|
||||||
|
auto dataset = getFemPostFilterPtr()->getInputData();
|
||||||
|
vtkDataSet* copy;
|
||||||
|
switch (dataset->GetDataObjectType()) {
|
||||||
|
case VTK_UNSTRUCTURED_GRID:
|
||||||
|
copy = vtkUnstructuredGrid::New();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
PyErr_SetString(PyExc_TypeError,
|
||||||
|
"cannot return datatype object; not unstructured grid");
|
||||||
|
Py_Return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the python wrapper
|
||||||
|
copy->DeepCopy(dataset);
|
||||||
|
PyObject* py_dataset = vtkPythonUtil::GetObjectFromPointer(copy);
|
||||||
|
|
||||||
|
return Py::new_reference_to(py_dataset);
|
||||||
|
#else
|
||||||
|
PyErr_SetString(PyExc_NotImplementedError, "VTK python wrapper not available");
|
||||||
|
Py_Return;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* FemPostFilterPy::getInputVectorFields(PyObject* args)
|
||||||
|
{
|
||||||
|
// we take no arguments
|
||||||
|
if (!PyArg_ParseTuple(args, "")) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> vector_fields = getFemPostFilterPtr()->getInputVectorFields();
|
||||||
|
|
||||||
|
// convert to python list of strings
|
||||||
|
Py::List list;
|
||||||
|
for (std::string& field : vector_fields) {
|
||||||
|
list.append(Py::String(field));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Py::new_reference_to(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PyObject* FemPostFilterPy::getInputScalarFields(PyObject* args)
|
||||||
|
{
|
||||||
|
// we take no arguments
|
||||||
|
if (!PyArg_ParseTuple(args, "")) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> scalar_fields = getFemPostFilterPtr()->getInputScalarFields();
|
||||||
|
|
||||||
|
// convert to python list of strings
|
||||||
|
Py::List list;
|
||||||
|
for (std::string& field : scalar_fields) {
|
||||||
|
list.append(Py::String(field));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Py::new_reference_to(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* FemPostFilterPy::getCustomAttributes(const char* /*attr*/) const
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FemPostFilterPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -42,6 +42,12 @@
|
|||||||
#include <vtkXMLUnstructuredGridReader.h>
|
#include <vtkXMLUnstructuredGridReader.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef FC_USE_VTK_PYTHON
|
||||||
|
#include <vtkPythonUtil.h>
|
||||||
|
#else
|
||||||
|
#include <Base/PyObjectBase.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <App/Application.h>
|
#include <App/Application.h>
|
||||||
#include <App/DocumentObject.h>
|
#include <App/DocumentObject.h>
|
||||||
#include <Base/Console.h>
|
#include <Base/Console.h>
|
||||||
@@ -162,12 +168,39 @@ int PropertyPostDataObject::getDataType()
|
|||||||
|
|
||||||
PyObject* PropertyPostDataObject::getPyObject()
|
PyObject* PropertyPostDataObject::getPyObject()
|
||||||
{
|
{
|
||||||
// TODO: fetch the vtk python object from the data set and return it
|
#ifdef FC_USE_VTK_PYTHON
|
||||||
return Py::new_reference_to(Py::None());
|
// create a copy first
|
||||||
|
auto copy = static_cast<PropertyPostDataObject*>(Copy());
|
||||||
|
|
||||||
|
// get the data python wrapper
|
||||||
|
PyObject* py_dataset = vtkPythonUtil::GetObjectFromPointer(copy->getValue());
|
||||||
|
auto result = Py::new_reference_to(py_dataset);
|
||||||
|
delete copy;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
#else
|
||||||
|
PyErr_SetString(PyExc_NotImplementedError, "VTK python wrapper not available");
|
||||||
|
Py_Return;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertyPostDataObject::setPyObject(PyObject* /*value*/)
|
void PropertyPostDataObject::setPyObject(PyObject* value)
|
||||||
{}
|
{
|
||||||
|
#ifdef FC_USE_VTK_PYTHON
|
||||||
|
vtkObjectBase* obj = vtkPythonUtil::GetPointerFromObject(value, "vtkDataObject");
|
||||||
|
if (!obj) {
|
||||||
|
throw Base::TypeError("Can only set vtkDataObject");
|
||||||
|
}
|
||||||
|
auto dobj = static_cast<vtkDataObject*>(obj);
|
||||||
|
createDataObjectByExternalType(dobj);
|
||||||
|
|
||||||
|
aboutToSetValue();
|
||||||
|
m_dataObject->DeepCopy(dobj);
|
||||||
|
hasSetValue();
|
||||||
|
#else
|
||||||
|
throw Base::NotImplementedError();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
App::Property* PropertyPostDataObject::Copy() const
|
App::Property* PropertyPostDataObject::Copy() const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
|
|
||||||
if(BUILD_FEM_VTK)
|
if(BUILD_FEM_VTK)
|
||||||
add_definitions(-DFC_USE_VTK)
|
add_definitions(-DFC_USE_VTK)
|
||||||
|
|
||||||
|
# we may use VTK but do not have the python wrappers available
|
||||||
|
if(BUILD_FEM_VTK_PYTHON)
|
||||||
|
add_definitions(-DFC_USE_VTK_PYTHON)
|
||||||
|
endif(BUILD_FEM_VTK_PYTHON)
|
||||||
|
|
||||||
endif(BUILD_FEM_VTK)
|
endif(BUILD_FEM_VTK)
|
||||||
|
|
||||||
|
|
||||||
@@ -207,6 +213,13 @@ SET(FemObjects_SRCS
|
|||||||
femobjects/solver_ccxtools.py
|
femobjects/solver_ccxtools.py
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(BUILD_FEM_VTK_PYTHON)
|
||||||
|
SET(FemObjects_SRCS
|
||||||
|
${FemObjects_SRCS}
|
||||||
|
femobjects/post_glyphfilter.py
|
||||||
|
)
|
||||||
|
endif(BUILD_FEM_VTK_PYTHON)
|
||||||
|
|
||||||
SET(FemResult_SRCS
|
SET(FemResult_SRCS
|
||||||
femresult/__init__.py
|
femresult/__init__.py
|
||||||
femresult/resulttools.py
|
femresult/resulttools.py
|
||||||
@@ -609,6 +622,13 @@ SET(FemGuiTaskPanels_SRCS
|
|||||||
femtaskpanels/task_solver_ccxtools.py
|
femtaskpanels/task_solver_ccxtools.py
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(BUILD_FEM_VTK_PYTHON)
|
||||||
|
SET(FemGuiTaskPanels_SRCS
|
||||||
|
${FemGuiTaskPanels_SRCS}
|
||||||
|
femtaskpanels/task_post_glyphfilter.py
|
||||||
|
)
|
||||||
|
endif(BUILD_FEM_VTK_PYTHON)
|
||||||
|
|
||||||
SET(FemGuiTests_SRCS
|
SET(FemGuiTests_SRCS
|
||||||
femtest/gui/__init__.py
|
femtest/gui/__init__.py
|
||||||
femtest/gui/test_open.py
|
femtest/gui/test_open.py
|
||||||
@@ -619,6 +639,7 @@ SET(FemGuiUtils_SRCS
|
|||||||
femguiutils/disambiguate_solid_selection.py
|
femguiutils/disambiguate_solid_selection.py
|
||||||
femguiutils/migrate_gui.py
|
femguiutils/migrate_gui.py
|
||||||
femguiutils/selection_widgets.py
|
femguiutils/selection_widgets.py
|
||||||
|
femguiutils/vtk_module_handling.py
|
||||||
)
|
)
|
||||||
|
|
||||||
SET(FemGuiViewProvider_SRCS
|
SET(FemGuiViewProvider_SRCS
|
||||||
@@ -659,6 +680,13 @@ SET(FemGuiViewProvider_SRCS
|
|||||||
femviewprovider/view_solver_ccxtools.py
|
femviewprovider/view_solver_ccxtools.py
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(BUILD_FEM_VTK_PYTHON)
|
||||||
|
SET(FemGuiViewProvider_SRCS
|
||||||
|
${FemGuiViewProvider_SRCS}
|
||||||
|
femviewprovider/view_post_glyphfilter.py
|
||||||
|
)
|
||||||
|
endif(BUILD_FEM_VTK_PYTHON)
|
||||||
|
|
||||||
SET(FemGuiPreferencePages_SRCS
|
SET(FemGuiPreferencePages_SRCS
|
||||||
fempreferencepages/__init__.py
|
fempreferencepages/__init__.py
|
||||||
fempreferencepages/dlg_settings_netgen.py
|
fempreferencepages/dlg_settings_netgen.py
|
||||||
|
|||||||
@@ -161,6 +161,8 @@ PyMOD_INIT_FUNC(FemGui)
|
|||||||
#ifdef FC_USE_VTK
|
#ifdef FC_USE_VTK
|
||||||
FemGui::ViewProviderFemPostObject ::init();
|
FemGui::ViewProviderFemPostObject ::init();
|
||||||
FemGui::ViewProviderFemPostPipeline ::init();
|
FemGui::ViewProviderFemPostPipeline ::init();
|
||||||
|
FemGui::ViewProviderFemPostFilterPythonBase ::init();
|
||||||
|
FemGui::ViewProviderPostFilterPython ::init();
|
||||||
FemGui::ViewProviderFemPostBranchFilter ::init();
|
FemGui::ViewProviderFemPostBranchFilter ::init();
|
||||||
FemGui::ViewProviderFemPostCalculator ::init();
|
FemGui::ViewProviderFemPostCalculator ::init();
|
||||||
FemGui::ViewProviderFemPostClip ::init();
|
FemGui::ViewProviderFemPostClip ::init();
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ set(FemGui_LIBS
|
|||||||
generate_from_xml(ViewProviderFemConstraintPy)
|
generate_from_xml(ViewProviderFemConstraintPy)
|
||||||
generate_from_xml(ViewProviderFemMeshPy)
|
generate_from_xml(ViewProviderFemMeshPy)
|
||||||
generate_from_xml(ViewProviderFemPostPipelinePy)
|
generate_from_xml(ViewProviderFemPostPipelinePy)
|
||||||
|
generate_from_xml(ViewProviderFemPostFilterPy)
|
||||||
|
|
||||||
SET(Python_SRCS
|
SET(Python_SRCS
|
||||||
ViewProviderFemConstraintPy.xml
|
ViewProviderFemConstraintPy.xml
|
||||||
@@ -44,6 +45,8 @@ SET(Python_SRCS
|
|||||||
ViewProviderFemMeshPyImp.cpp
|
ViewProviderFemMeshPyImp.cpp
|
||||||
ViewProviderFemPostPipelinePy.xml
|
ViewProviderFemPostPipelinePy.xml
|
||||||
ViewProviderFemPostPipelinePyImp.cpp
|
ViewProviderFemPostPipelinePyImp.cpp
|
||||||
|
ViewProviderFemPostFilterPy.xml
|
||||||
|
ViewProviderFemPostFilterPyImp.cpp
|
||||||
)
|
)
|
||||||
SOURCE_GROUP("Python" FILES ${Python_SRCS})
|
SOURCE_GROUP("Python" FILES ${Python_SRCS})
|
||||||
|
|
||||||
@@ -430,6 +433,7 @@ SET(FemGuiPythonUI_SRCS
|
|||||||
Resources/ui/ResultShow.ui
|
Resources/ui/ResultShow.ui
|
||||||
Resources/ui/SolverCalculiX.ui
|
Resources/ui/SolverCalculiX.ui
|
||||||
Resources/ui/SolverCcxTools.ui
|
Resources/ui/SolverCcxTools.ui
|
||||||
|
Resources/ui/TaskPostGlyph.ui
|
||||||
)
|
)
|
||||||
|
|
||||||
ADD_CUSTOM_TARGET(FemPythonUi ALL
|
ADD_CUSTOM_TARGET(FemPythonUi ALL
|
||||||
|
|||||||
@@ -82,6 +82,7 @@
|
|||||||
<file>icons/FEM_PostFilterDataAtPoint.svg</file>
|
<file>icons/FEM_PostFilterDataAtPoint.svg</file>
|
||||||
<file>icons/FEM_PostFilterLinearizedStresses.svg</file>
|
<file>icons/FEM_PostFilterLinearizedStresses.svg</file>
|
||||||
<file>icons/FEM_PostFilterWarp.svg</file>
|
<file>icons/FEM_PostFilterWarp.svg</file>
|
||||||
|
<file>icons/FEM_PostFilterGlyph.svg</file>
|
||||||
<file>icons/FEM_PostFrames.svg</file>
|
<file>icons/FEM_PostFrames.svg</file>
|
||||||
<file>icons/FEM_PostBranchFilter.svg</file>
|
<file>icons/FEM_PostBranchFilter.svg</file>
|
||||||
<file>icons/FEM_PostPipelineFromResult.svg</file>
|
<file>icons/FEM_PostPipelineFromResult.svg</file>
|
||||||
@@ -150,5 +151,6 @@
|
|||||||
<file>ui/ResultShow.ui</file>
|
<file>ui/ResultShow.ui</file>
|
||||||
<file>ui/SolverCalculiX.ui</file>
|
<file>ui/SolverCalculiX.ui</file>
|
||||||
<file>ui/SolverCcxTools.ui</file>
|
<file>ui/SolverCcxTools.ui</file>
|
||||||
|
<file>ui/TaskPostGlyph.ui</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
111
src/Mod/Fem/Gui/Resources/icons/FEM_PostFilterGlyph.svg
Normal file
111
src/Mod/Fem/Gui/Resources/icons/FEM_PostFilterGlyph.svg
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="64"
|
||||||
|
height="64"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
sodipodi:docname="FEM_PostFilterGlyph.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:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#505050"
|
||||||
|
inkscape:zoom="8.6344367"
|
||||||
|
inkscape:cx="33.238995"
|
||||||
|
inkscape:cy="26.232169"
|
||||||
|
inkscape:window-width="3132"
|
||||||
|
inkscape:window-height="1772"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<defs
|
||||||
|
id="defs4">
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient3802">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#4e9a06;stop-opacity:1"
|
||||||
|
offset="0"
|
||||||
|
id="stop3804" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#73d216;stop-opacity:1"
|
||||||
|
offset="1"
|
||||||
|
id="stop3806" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
xlink:href="#linearGradient3802"
|
||||||
|
id="linearGradient3808"
|
||||||
|
x1="49"
|
||||||
|
y1="58"
|
||||||
|
x2="47"
|
||||||
|
y2="42"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="translate(-77.65959,-38.104787)" />
|
||||||
|
</defs>
|
||||||
|
<metadata
|
||||||
|
id="metadata7">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:creator>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>[Alexander Gryson]</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:creator>
|
||||||
|
<dc:title>fem-warp</dc:title>
|
||||||
|
<dc:date>2017-03-11</dc:date>
|
||||||
|
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
|
||||||
|
<dc:publisher>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>FreeCAD</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:publisher>
|
||||||
|
<dc:identifier>FreeCAD/src/Mod/</dc:identifier>
|
||||||
|
<dc:rights>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>FreeCAD LGPL2+</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:rights>
|
||||||
|
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
|
||||||
|
<dc:contributor>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>[agryson] Alexander Gryson</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:contributor>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<path
|
||||||
|
id="path5"
|
||||||
|
style="fill:#5bae0c;stroke:#172a04;stroke-width:0.783709;stroke-linecap:round;stroke-linejoin:round;stroke-dashoffset:9.6"
|
||||||
|
d="M 28.643627,7.4834885 14.376899,11.359125 26.588343,21.800077 Z M 23.279624,18.970903 17.46429,13.999367 0.84260476,32.570907 6.6625341,37.537309 Z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" />
|
||||||
|
<path
|
||||||
|
id="path6"
|
||||||
|
style="fill:#5bae0c;stroke:#172a04;stroke-width:0.709324;stroke-linecap:round;stroke-linejoin:round;stroke-dashoffset:9.6"
|
||||||
|
d="m 54.160189,35.642295 -12.759634,3.549837 10.921464,9.563249 z m -4.797366,10.521743 -5.20102,-4.553612 -14.865822,17.01035 5.20513,4.548909 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" />
|
||||||
|
<path
|
||||||
|
id="path6-6"
|
||||||
|
style="fill:#5bae0c;stroke:#172a04;stroke-width:1.43581;stroke-linecap:round;stroke-linejoin:round;stroke-dashoffset:9.6"
|
||||||
|
d="M 59.446346,1.4589408 33.618389,8.6444946 55.725536,28.002353 Z M 49.735554,22.756974 39.207689,13.539587 9.1164014,47.971811 19.652586,57.179679 Z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.9 KiB |
355
src/Mod/Fem/Gui/Resources/ui/TaskPostGlyph.ui
Normal file
355
src/Mod/Fem/Gui/Resources/ui/TaskPostGlyph.ui
Normal file
@@ -0,0 +1,355 @@
|
|||||||
|
<?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>440</width>
|
||||||
|
<height>428</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string notr="true">Glyph settings</string>
|
||||||
|
</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>Form</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QComboBox" name="FormComboBox">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>The form of the glyph</string>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Arrow</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Cube</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</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>Orientation</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QComboBox" name="OrientationComboBox">
|
||||||
|
<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>
|
||||||
|
</layout>
|
||||||
|
</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>Sca&le</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="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>Data</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="ScaleComboBox">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||||
|
<horstretch>1</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<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>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>None</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="2" 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>Factor</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QDoubleSpinBox" name="ScaleFactorBox">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>A constant multiplier the glyphs are scaled with</string>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>99999999999.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="singleStep">
|
||||||
|
<double>0.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="stepType">
|
||||||
|
<enum>QAbstractSpinBox::StepType::AdaptiveDecimalStepType</enum>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<double>1.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QSlider" name="ScaleSlider">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Changes the scale factor by +/- 50% of the set scale factor</string>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>100</number>
|
||||||
|
</property>
|
||||||
|
<property name="singleStep">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>50</number>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QComboBox" name="VectorModeComboBox">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||||
|
<horstretch>1</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Which data field is used to scale the glyphs</string>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Not a vector</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>By magnitude</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>By components</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="Mask">
|
||||||
|
<property name="title">
|
||||||
|
<string>Vertex Mas&king</string>
|
||||||
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<layout class="QFormLayout" name="formLayout_3">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Which vertices are used as glyph locations</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Mode</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QSpinBox" name="MaxBox">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Defines the maximal number of vertices used for "Uniform Sampling" masking mode</string>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>999999999</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="StrideLabel">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Define the stride for "Every Nth" masking mode</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Stride</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="MaxLabel">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Defines the maximal number of vertices used for "Uniform Sampling" masking mode</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Max </string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QSpinBox" name="StrideBox">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Define the stride for "Every Nth" masking mode</string>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>999999999</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QComboBox" name="MaskModeComboBox">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Which vertices are used as glyph locations</string>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>All</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Every Nth</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Uniform Sampling</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
@@ -203,29 +203,33 @@ void DataAlongLineMarker::customEvent(QEvent*)
|
|||||||
|
|
||||||
// ***************************************************************************
|
// ***************************************************************************
|
||||||
// main task dialog
|
// main task dialog
|
||||||
TaskPostBox::TaskPostBox(Gui::ViewProviderDocumentObject* view,
|
TaskPostWidget::TaskPostWidget(Gui::ViewProviderDocumentObject* view,
|
||||||
const QPixmap& icon,
|
const QPixmap& icon,
|
||||||
const QString& title,
|
const QString& title,
|
||||||
QWidget* parent)
|
QWidget* parent)
|
||||||
: TaskBox(icon, title, true, parent)
|
: QWidget(parent)
|
||||||
, m_object(view->getObject())
|
, m_object(view->getObject())
|
||||||
, m_view(view)
|
, m_view(view)
|
||||||
{}
|
{
|
||||||
|
setWindowTitle(title);
|
||||||
|
setWindowIcon(icon);
|
||||||
|
m_icon = icon;
|
||||||
|
}
|
||||||
|
|
||||||
TaskPostBox::~TaskPostBox() = default;
|
TaskPostWidget::~TaskPostWidget() = default;
|
||||||
|
|
||||||
bool TaskPostBox::autoApply()
|
bool TaskPostWidget::autoApply()
|
||||||
{
|
{
|
||||||
return FemSettings().getPostAutoRecompute();
|
return FemSettings().getPostAutoRecompute();
|
||||||
}
|
}
|
||||||
|
|
||||||
App::Document* TaskPostBox::getDocument() const
|
App::Document* TaskPostWidget::getDocument() const
|
||||||
{
|
{
|
||||||
App::DocumentObject* obj = getObject();
|
App::DocumentObject* obj = getObject();
|
||||||
return (obj ? obj->getDocument() : nullptr);
|
return (obj ? obj->getDocument() : nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TaskPostBox::recompute()
|
void TaskPostWidget::recompute()
|
||||||
{
|
{
|
||||||
if (autoApply()) {
|
if (autoApply()) {
|
||||||
App::Document* doc = getDocument();
|
App::Document* doc = getDocument();
|
||||||
@@ -235,7 +239,7 @@ void TaskPostBox::recompute()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TaskPostBox::updateEnumerationList(App::PropertyEnumeration& prop, QComboBox* box)
|
void TaskPostWidget::updateEnumerationList(App::PropertyEnumeration& prop, QComboBox* box)
|
||||||
{
|
{
|
||||||
QStringList list;
|
QStringList list;
|
||||||
std::vector<std::string> vec = prop.getEnumVector();
|
std::vector<std::string> vec = prop.getEnumVector();
|
||||||
@@ -266,10 +270,20 @@ TaskDlgPost::~TaskDlgPost() = default;
|
|||||||
|
|
||||||
QDialogButtonBox::StandardButtons TaskDlgPost::getStandardButtons() const
|
QDialogButtonBox::StandardButtons TaskDlgPost::getStandardButtons() const
|
||||||
{
|
{
|
||||||
// check if we only have gui task boxes
|
|
||||||
bool guionly = true;
|
bool guionly = true;
|
||||||
for (auto it : m_boxes) {
|
for (auto& widget : Content) {
|
||||||
guionly = guionly && it->isGuiTaskOnly();
|
if (auto task_box = dynamic_cast<Gui::TaskView::TaskBox*>(widget)) {
|
||||||
|
|
||||||
|
// get the task widget and check if it is a post widget
|
||||||
|
auto widget = task_box->groupLayout()->itemAt(0)->widget();
|
||||||
|
if (auto post_widget = dynamic_cast<TaskPostWidget*>(widget)) {
|
||||||
|
guionly = guionly && post_widget->isGuiTaskOnly();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// unknown panel, we can only assume
|
||||||
|
guionly = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!guionly) {
|
if (!guionly) {
|
||||||
@@ -285,7 +299,7 @@ void TaskDlgPost::connectSlots()
|
|||||||
// Connect emitAddedFunction() with slotAddedFunction()
|
// Connect emitAddedFunction() with slotAddedFunction()
|
||||||
QObject* sender = nullptr;
|
QObject* sender = nullptr;
|
||||||
int indexSignal = 0;
|
int indexSignal = 0;
|
||||||
for (const auto dlg : m_boxes) {
|
for (const auto dlg : Content) {
|
||||||
indexSignal = dlg->metaObject()->indexOfSignal(
|
indexSignal = dlg->metaObject()->indexOfSignal(
|
||||||
QMetaObject::normalizedSignature("emitAddedFunction()"));
|
QMetaObject::normalizedSignature("emitAddedFunction()"));
|
||||||
if (indexSignal >= 0) {
|
if (indexSignal >= 0) {
|
||||||
@@ -295,7 +309,7 @@ void TaskDlgPost::connectSlots()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sender) {
|
if (sender) {
|
||||||
for (const auto dlg : m_boxes) {
|
for (const auto dlg : Content) {
|
||||||
int indexSlot = dlg->metaObject()->indexOfSlot(
|
int indexSlot = dlg->metaObject()->indexOfSlot(
|
||||||
QMetaObject::normalizedSignature("slotAddedFunction()"));
|
QMetaObject::normalizedSignature("slotAddedFunction()"));
|
||||||
if (indexSlot >= 0) {
|
if (indexSlot >= 0) {
|
||||||
@@ -308,12 +322,6 @@ void TaskDlgPost::connectSlots()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TaskDlgPost::appendBox(TaskPostBox* box)
|
|
||||||
{
|
|
||||||
m_boxes.push_back(box);
|
|
||||||
Content.push_back(box);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TaskDlgPost::open()
|
void TaskDlgPost::open()
|
||||||
{
|
{
|
||||||
// only open a new command if none is pending (e.g. if the object was newly created)
|
// only open a new command if none is pending (e.g. if the object was newly created)
|
||||||
@@ -326,8 +334,14 @@ void TaskDlgPost::open()
|
|||||||
void TaskDlgPost::clicked(int button)
|
void TaskDlgPost::clicked(int button)
|
||||||
{
|
{
|
||||||
if (button == QDialogButtonBox::Apply) {
|
if (button == QDialogButtonBox::Apply) {
|
||||||
for (auto box : m_boxes) {
|
for (auto& widget : Content) {
|
||||||
box->apply();
|
if (auto task_box = dynamic_cast<Gui::TaskView::TaskBox*>(widget)) {
|
||||||
|
// get the task widget and check if it is a post widget
|
||||||
|
auto widget = task_box->groupLayout()->itemAt(0)->widget();
|
||||||
|
if (auto post_widget = dynamic_cast<TaskPostWidget*>(widget)) {
|
||||||
|
post_widget->apply();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
recompute();
|
recompute();
|
||||||
}
|
}
|
||||||
@@ -336,8 +350,14 @@ void TaskDlgPost::clicked(int button)
|
|||||||
bool TaskDlgPost::accept()
|
bool TaskDlgPost::accept()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
for (auto& box : m_boxes) {
|
for (auto& widget : Content) {
|
||||||
box->applyPythonCode();
|
if (auto task_box = dynamic_cast<Gui::TaskView::TaskBox*>(widget)) {
|
||||||
|
// get the task widget and check if it is a post widget
|
||||||
|
auto widget = task_box->groupLayout()->itemAt(0)->widget();
|
||||||
|
if (auto post_widget = dynamic_cast<TaskPostWidget*>(widget)) {
|
||||||
|
post_widget->applyPythonCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (const Base::Exception& e) {
|
catch (const Base::Exception& e) {
|
||||||
@@ -377,19 +397,15 @@ void TaskDlgPost::modifyStandardButtons(QDialogButtonBox* box)
|
|||||||
// ***************************************************************************
|
// ***************************************************************************
|
||||||
// box to set the coloring
|
// box to set the coloring
|
||||||
TaskPostDisplay::TaskPostDisplay(ViewProviderFemPostObject* view, QWidget* parent)
|
TaskPostDisplay::TaskPostDisplay(ViewProviderFemPostObject* view, QWidget* parent)
|
||||||
: TaskPostBox(view,
|
: TaskPostWidget(view, Gui::BitmapFactory().pixmap("FEM_ResultShow"), QString(), parent)
|
||||||
Gui::BitmapFactory().pixmap("FEM_ResultShow"),
|
|
||||||
tr("Result display options"),
|
|
||||||
parent)
|
|
||||||
, ui(new Ui_TaskPostDisplay)
|
, ui(new Ui_TaskPostDisplay)
|
||||||
{
|
{
|
||||||
// we need a separate container widget to add all controls to
|
// setup the ui
|
||||||
proxy = new QWidget(this);
|
ui->setupUi(this);
|
||||||
ui->setupUi(proxy);
|
setWindowTitle(
|
||||||
|
tr("Result display options")); // set title here as setupUi overrides the constructor title
|
||||||
setupConnections();
|
setupConnections();
|
||||||
|
|
||||||
this->groupLayout()->addWidget(proxy);
|
|
||||||
|
|
||||||
// update all fields
|
// update all fields
|
||||||
updateEnumerationList(getTypedView<ViewProviderFemPostObject>()->DisplayMode,
|
updateEnumerationList(getTypedView<ViewProviderFemPostObject>()->DisplayMode,
|
||||||
ui->Representation);
|
ui->Representation);
|
||||||
@@ -463,16 +479,19 @@ void TaskPostDisplay::applyPythonCode()
|
|||||||
// ***************************************************************************
|
// ***************************************************************************
|
||||||
// functions
|
// functions
|
||||||
TaskPostFunction::TaskPostFunction(ViewProviderFemPostFunction* view, QWidget* parent)
|
TaskPostFunction::TaskPostFunction(ViewProviderFemPostFunction* view, QWidget* parent)
|
||||||
: TaskPostBox(view,
|
: TaskPostWidget(view,
|
||||||
Gui::BitmapFactory().pixmap("fem-post-geo-plane"),
|
Gui::BitmapFactory().pixmap("fem-post-geo-plane"),
|
||||||
tr("Implicit function"),
|
tr("Implicit function"),
|
||||||
parent)
|
parent)
|
||||||
{
|
{
|
||||||
// we load the views widget
|
// we load the views widget
|
||||||
FunctionWidget* w = getTypedView<ViewProviderFemPostFunction>()->createControlWidget();
|
FunctionWidget* w = getTypedView<ViewProviderFemPostFunction>()->createControlWidget();
|
||||||
w->setParent(this);
|
w->setParent(this);
|
||||||
w->setViewProvider(getTypedView<ViewProviderFemPostFunction>());
|
w->setViewProvider(getTypedView<ViewProviderFemPostFunction>());
|
||||||
this->groupLayout()->addWidget(w);
|
|
||||||
|
QVBoxLayout* layout = new QVBoxLayout;
|
||||||
|
layout->addWidget(w);
|
||||||
|
setLayout(layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
TaskPostFunction::~TaskPostFunction() = default;
|
TaskPostFunction::~TaskPostFunction() = default;
|
||||||
@@ -486,13 +505,12 @@ void TaskPostFunction::applyPythonCode()
|
|||||||
// ***************************************************************************
|
// ***************************************************************************
|
||||||
// Frames
|
// Frames
|
||||||
TaskPostFrames::TaskPostFrames(ViewProviderFemPostObject* view, QWidget* parent)
|
TaskPostFrames::TaskPostFrames(ViewProviderFemPostObject* view, QWidget* parent)
|
||||||
: TaskPostBox(view, Gui::BitmapFactory().pixmap("FEM_PostFrames"), tr("Result Frames"), parent)
|
: TaskPostWidget(view, Gui::BitmapFactory().pixmap("FEM_PostFrames"), QString(), parent)
|
||||||
, ui(new Ui_TaskPostFrames)
|
, ui(new Ui_TaskPostFrames)
|
||||||
{
|
{
|
||||||
// we load the views widget
|
// setup the ui
|
||||||
proxy = new QWidget(this);
|
ui->setupUi(this);
|
||||||
ui->setupUi(proxy);
|
setWindowTitle(tr("Result Frames"));
|
||||||
this->groupLayout()->addWidget(proxy);
|
|
||||||
setupConnections();
|
setupConnections();
|
||||||
|
|
||||||
// populate the data
|
// populate the data
|
||||||
@@ -548,16 +566,12 @@ void TaskPostFrames::applyPythonCode()
|
|||||||
// ***************************************************************************
|
// ***************************************************************************
|
||||||
// Branch
|
// Branch
|
||||||
TaskPostBranch::TaskPostBranch(ViewProviderFemPostBranchFilter* view, QWidget* parent)
|
TaskPostBranch::TaskPostBranch(ViewProviderFemPostBranchFilter* view, QWidget* parent)
|
||||||
: TaskPostBox(view,
|
: TaskPostWidget(view, Gui::BitmapFactory().pixmap("FEM_PostBranchFilter"), QString(), parent)
|
||||||
Gui::BitmapFactory().pixmap("FEM_PostBranchFilter"),
|
|
||||||
tr("Branch behaviour"),
|
|
||||||
parent)
|
|
||||||
, ui(new Ui_TaskPostBranch)
|
, ui(new Ui_TaskPostBranch)
|
||||||
{
|
{
|
||||||
// we load the views widget
|
// setup the ui
|
||||||
proxy = new QWidget(this);
|
ui->setupUi(this);
|
||||||
ui->setupUi(proxy);
|
setWindowTitle(tr("Branch behaviour"));
|
||||||
this->groupLayout()->addWidget(proxy);
|
|
||||||
setupConnections();
|
setupConnections();
|
||||||
|
|
||||||
// populate the data
|
// populate the data
|
||||||
@@ -603,19 +617,17 @@ void TaskPostBranch::applyPythonCode()
|
|||||||
// data along line filter
|
// data along line filter
|
||||||
TaskPostDataAlongLine::TaskPostDataAlongLine(ViewProviderFemPostDataAlongLine* view,
|
TaskPostDataAlongLine::TaskPostDataAlongLine(ViewProviderFemPostDataAlongLine* view,
|
||||||
QWidget* parent)
|
QWidget* parent)
|
||||||
: TaskPostBox(view,
|
: TaskPostWidget(view,
|
||||||
Gui::BitmapFactory().pixmap("FEM_PostFilterDataAlongLine"),
|
Gui::BitmapFactory().pixmap("FEM_PostFilterDataAlongLine"),
|
||||||
tr("Data along a line options"),
|
QString(),
|
||||||
parent)
|
parent)
|
||||||
, ui(new Ui_TaskPostDataAlongLine)
|
, ui(new Ui_TaskPostDataAlongLine)
|
||||||
, marker(nullptr)
|
, marker(nullptr)
|
||||||
{
|
{
|
||||||
// we load the views widget
|
// setup the ui
|
||||||
proxy = new QWidget(this);
|
ui->setupUi(this);
|
||||||
ui->setupUi(proxy);
|
setWindowTitle(tr("Data along a line options"));
|
||||||
|
|
||||||
setupConnectionsStep1();
|
setupConnectionsStep1();
|
||||||
this->groupLayout()->addWidget(proxy);
|
|
||||||
|
|
||||||
QSize size = ui->point1X->sizeForText(QStringLiteral("000000000000"));
|
QSize size = ui->point1X->sizeForText(QStringLiteral("000000000000"));
|
||||||
ui->point1X->setMinimumWidth(size.width());
|
ui->point1X->setMinimumWidth(size.width());
|
||||||
@@ -1025,21 +1037,19 @@ plt.show()\n";
|
|||||||
// ***************************************************************************
|
// ***************************************************************************
|
||||||
// data at point filter
|
// data at point filter
|
||||||
TaskPostDataAtPoint::TaskPostDataAtPoint(ViewProviderFemPostDataAtPoint* view, QWidget* parent)
|
TaskPostDataAtPoint::TaskPostDataAtPoint(ViewProviderFemPostDataAtPoint* view, QWidget* parent)
|
||||||
: TaskPostBox(view,
|
: TaskPostWidget(view,
|
||||||
Gui::BitmapFactory().pixmap("FEM_PostFilterDataAtPoint"),
|
Gui::BitmapFactory().pixmap("FEM_PostFilterDataAtPoint"),
|
||||||
tr("Data at point options"),
|
QString(),
|
||||||
parent)
|
parent)
|
||||||
, viewer(nullptr)
|
, viewer(nullptr)
|
||||||
, connSelectPoint(QMetaObject::Connection())
|
, connSelectPoint(QMetaObject::Connection())
|
||||||
, ui(new Ui_TaskPostDataAtPoint)
|
, ui(new Ui_TaskPostDataAtPoint)
|
||||||
{
|
{
|
||||||
// we load the views widget
|
// setup the ui
|
||||||
proxy = new QWidget(this);
|
ui->setupUi(this);
|
||||||
ui->setupUi(proxy);
|
setWindowTitle(tr("Data at point options"));
|
||||||
setupConnections();
|
setupConnections();
|
||||||
|
|
||||||
this->groupLayout()->addWidget(proxy);
|
|
||||||
|
|
||||||
QSize size = ui->centerX->sizeForText(QStringLiteral("000000000000"));
|
QSize size = ui->centerX->sizeForText(QStringLiteral("000000000000"));
|
||||||
ui->centerX->setMinimumWidth(size.width());
|
ui->centerX->setMinimumWidth(size.width());
|
||||||
ui->centerY->setMinimumWidth(size.width());
|
ui->centerY->setMinimumWidth(size.width());
|
||||||
@@ -1382,10 +1392,10 @@ std::string TaskPostDataAtPoint::toString(double val) const
|
|||||||
TaskPostClip::TaskPostClip(ViewProviderFemPostClip* view,
|
TaskPostClip::TaskPostClip(ViewProviderFemPostClip* view,
|
||||||
App::PropertyLink* function,
|
App::PropertyLink* function,
|
||||||
QWidget* parent)
|
QWidget* parent)
|
||||||
: TaskPostBox(view,
|
: TaskPostWidget(view,
|
||||||
Gui::BitmapFactory().pixmap("FEM_PostFilterClipRegion"),
|
Gui::BitmapFactory().pixmap("FEM_PostFilterClipRegion"),
|
||||||
tr("Clip region, choose implicit function"),
|
QString(),
|
||||||
parent)
|
parent)
|
||||||
, ui(new Ui_TaskPostClip)
|
, ui(new Ui_TaskPostClip)
|
||||||
{
|
{
|
||||||
assert(function);
|
assert(function);
|
||||||
@@ -1393,11 +1403,10 @@ TaskPostClip::TaskPostClip(ViewProviderFemPostClip* view,
|
|||||||
|
|
||||||
fwidget = nullptr;
|
fwidget = nullptr;
|
||||||
|
|
||||||
// we load the views widget
|
// setup the ui
|
||||||
proxy = new QWidget(this);
|
ui->setupUi(this);
|
||||||
ui->setupUi(proxy);
|
setWindowTitle(tr("Clip region, choose implicit function"));
|
||||||
setupConnections();
|
setupConnections();
|
||||||
this->groupLayout()->addWidget(proxy);
|
|
||||||
|
|
||||||
// the layout for the container widget
|
// the layout for the container widget
|
||||||
QVBoxLayout* layout = new QVBoxLayout();
|
QVBoxLayout* layout = new QVBoxLayout();
|
||||||
@@ -1542,17 +1551,13 @@ void TaskPostClip::onInsideOutToggled(bool val)
|
|||||||
// ***************************************************************************
|
// ***************************************************************************
|
||||||
// contours filter
|
// contours filter
|
||||||
TaskPostContours::TaskPostContours(ViewProviderFemPostContours* view, QWidget* parent)
|
TaskPostContours::TaskPostContours(ViewProviderFemPostContours* view, QWidget* parent)
|
||||||
: TaskPostBox(view,
|
: TaskPostWidget(view, Gui::BitmapFactory().pixmap("FEM_PostFilterContours"), QString(), parent)
|
||||||
Gui::BitmapFactory().pixmap("FEM_PostFilterContours"),
|
|
||||||
tr("Contours filter options"),
|
|
||||||
parent)
|
|
||||||
, ui(new Ui_TaskPostContours)
|
, ui(new Ui_TaskPostContours)
|
||||||
{
|
{
|
||||||
// load the views widget
|
// setup the ui
|
||||||
proxy = new QWidget(this);
|
ui->setupUi(this);
|
||||||
ui->setupUi(proxy);
|
setWindowTitle(tr("Contours filter options"));
|
||||||
QMetaObject::connectSlotsByName(this);
|
QMetaObject::connectSlotsByName(this);
|
||||||
this->groupLayout()->addWidget(proxy);
|
|
||||||
|
|
||||||
auto obj = getObject<Fem::FemPostContoursFilter>();
|
auto obj = getObject<Fem::FemPostContoursFilter>();
|
||||||
|
|
||||||
@@ -1697,10 +1702,10 @@ void TaskPostContours::onRelaxationChanged(double value)
|
|||||||
// ***************************************************************************
|
// ***************************************************************************
|
||||||
// cut filter
|
// cut filter
|
||||||
TaskPostCut::TaskPostCut(ViewProviderFemPostCut* view, App::PropertyLink* function, QWidget* parent)
|
TaskPostCut::TaskPostCut(ViewProviderFemPostCut* view, App::PropertyLink* function, QWidget* parent)
|
||||||
: TaskPostBox(view,
|
: TaskPostWidget(view,
|
||||||
Gui::BitmapFactory().pixmap("FEM_PostFilterCutFunction"),
|
Gui::BitmapFactory().pixmap("FEM_PostFilterCutFunction"),
|
||||||
tr("Function cut, choose implicit function"),
|
QString(),
|
||||||
parent)
|
parent)
|
||||||
, ui(new Ui_TaskPostCut)
|
, ui(new Ui_TaskPostCut)
|
||||||
{
|
{
|
||||||
assert(function);
|
assert(function);
|
||||||
@@ -1708,11 +1713,10 @@ TaskPostCut::TaskPostCut(ViewProviderFemPostCut* view, App::PropertyLink* functi
|
|||||||
|
|
||||||
fwidget = nullptr;
|
fwidget = nullptr;
|
||||||
|
|
||||||
// we load the views widget
|
// setup the ui
|
||||||
proxy = new QWidget(this);
|
ui->setupUi(this);
|
||||||
ui->setupUi(proxy);
|
setWindowTitle(tr("Function cut, choose implicit function"));
|
||||||
setupConnections();
|
setupConnections();
|
||||||
this->groupLayout()->addWidget(proxy);
|
|
||||||
|
|
||||||
// the layout for the container widget
|
// the layout for the container widget
|
||||||
QVBoxLayout* layout = new QVBoxLayout();
|
QVBoxLayout* layout = new QVBoxLayout();
|
||||||
@@ -1836,17 +1840,16 @@ void TaskPostCut::onFunctionBoxCurrentIndexChanged(int idx)
|
|||||||
// ***************************************************************************
|
// ***************************************************************************
|
||||||
// scalar clip filter
|
// scalar clip filter
|
||||||
TaskPostScalarClip::TaskPostScalarClip(ViewProviderFemPostScalarClip* view, QWidget* parent)
|
TaskPostScalarClip::TaskPostScalarClip(ViewProviderFemPostScalarClip* view, QWidget* parent)
|
||||||
: TaskPostBox(view,
|
: TaskPostWidget(view,
|
||||||
Gui::BitmapFactory().pixmap("FEM_PostFilterClipScalar"),
|
Gui::BitmapFactory().pixmap("FEM_PostFilterClipScalar"),
|
||||||
tr("Scalar clip options"),
|
QString(),
|
||||||
parent)
|
parent)
|
||||||
, ui(new Ui_TaskPostScalarClip)
|
, ui(new Ui_TaskPostScalarClip)
|
||||||
{
|
{
|
||||||
// we load the views widget
|
// setup the ui
|
||||||
proxy = new QWidget(this);
|
ui->setupUi(this);
|
||||||
ui->setupUi(proxy);
|
setWindowTitle(tr("Scalar clip options"));
|
||||||
setupConnections();
|
setupConnections();
|
||||||
this->groupLayout()->addWidget(proxy);
|
|
||||||
|
|
||||||
// load the default values
|
// load the default values
|
||||||
updateEnumerationList(getTypedObject<Fem::FemPostScalarClipFilter>()->Scalars, ui->Scalar);
|
updateEnumerationList(getTypedObject<Fem::FemPostScalarClipFilter>()->Scalars, ui->Scalar);
|
||||||
@@ -1961,17 +1964,13 @@ void TaskPostScalarClip::onInsideOutToggled(bool val)
|
|||||||
// ***************************************************************************
|
// ***************************************************************************
|
||||||
// warp vector filter
|
// warp vector filter
|
||||||
TaskPostWarpVector::TaskPostWarpVector(ViewProviderFemPostWarpVector* view, QWidget* parent)
|
TaskPostWarpVector::TaskPostWarpVector(ViewProviderFemPostWarpVector* view, QWidget* parent)
|
||||||
: TaskPostBox(view,
|
: TaskPostWidget(view, Gui::BitmapFactory().pixmap("FEM_PostFilterWarp"), QString(), parent)
|
||||||
Gui::BitmapFactory().pixmap("FEM_PostFilterWarp"),
|
|
||||||
tr("Warp options"),
|
|
||||||
parent)
|
|
||||||
, ui(new Ui_TaskPostWarpVector)
|
, ui(new Ui_TaskPostWarpVector)
|
||||||
{
|
{
|
||||||
// we load the views widget
|
// setup the ui
|
||||||
proxy = new QWidget(this);
|
ui->setupUi(this);
|
||||||
ui->setupUi(proxy);
|
setWindowTitle(tr("Warp options"));
|
||||||
setupConnections();
|
setupConnections();
|
||||||
this->groupLayout()->addWidget(proxy);
|
|
||||||
|
|
||||||
// load the default values for warp display
|
// load the default values for warp display
|
||||||
updateEnumerationList(getTypedObject<Fem::FemPostWarpVectorFilter>()->Vector, ui->Vector);
|
updateEnumerationList(getTypedObject<Fem::FemPostWarpVectorFilter>()->Vector, ui->Vector);
|
||||||
@@ -2136,17 +2135,15 @@ static const std::vector<std::string> calculatorOperators = {
|
|||||||
"log", "pow", "sqrt", "iHat", "jHat", "kHat", "cross", "dot", "mag", "norm"};
|
"log", "pow", "sqrt", "iHat", "jHat", "kHat", "cross", "dot", "mag", "norm"};
|
||||||
|
|
||||||
TaskPostCalculator::TaskPostCalculator(ViewProviderFemPostCalculator* view, QWidget* parent)
|
TaskPostCalculator::TaskPostCalculator(ViewProviderFemPostCalculator* view, QWidget* parent)
|
||||||
: TaskPostBox(view,
|
: TaskPostWidget(view,
|
||||||
Gui::BitmapFactory().pixmap("FEM_PostFilterCalculator"),
|
Gui::BitmapFactory().pixmap("FEM_PostFilterCalculator"),
|
||||||
tr("Calculator options"),
|
tr("Calculator options"),
|
||||||
parent)
|
parent)
|
||||||
, ui(new Ui_TaskPostCalculator)
|
, ui(new Ui_TaskPostCalculator)
|
||||||
{
|
{
|
||||||
// we load the views widget
|
// we load the views widget
|
||||||
proxy = new QWidget(this);
|
ui->setupUi(this);
|
||||||
ui->setupUi(proxy);
|
|
||||||
setupConnections();
|
setupConnections();
|
||||||
this->groupLayout()->addWidget(proxy);
|
|
||||||
|
|
||||||
// load the default values
|
// load the default values
|
||||||
auto obj = getObject<Fem::FemPostCalculatorFilter>();
|
auto obj = getObject<Fem::FemPostCalculatorFilter>();
|
||||||
|
|||||||
@@ -131,18 +131,22 @@ protected:
|
|||||||
|
|
||||||
// ***************************************************************************
|
// ***************************************************************************
|
||||||
// main task dialog
|
// main task dialog
|
||||||
class TaskPostBox: public Gui::TaskView::TaskBox
|
class TaskPostWidget: public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
// Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TaskPostBox(Gui::ViewProviderDocumentObject* view,
|
TaskPostWidget(Gui::ViewProviderDocumentObject* view,
|
||||||
const QPixmap& icon,
|
const QPixmap& icon,
|
||||||
const QString& title,
|
const QString& title = QString(),
|
||||||
QWidget* parent = nullptr);
|
QWidget* parent = nullptr);
|
||||||
~TaskPostBox() override;
|
~TaskPostWidget() override;
|
||||||
|
|
||||||
virtual void applyPythonCode() {};
|
virtual void applyPythonCode() {};
|
||||||
|
QPixmap getIcon()
|
||||||
|
{
|
||||||
|
return m_icon;
|
||||||
|
}
|
||||||
virtual bool isGuiTaskOnly()
|
virtual bool isGuiTaskOnly()
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -184,6 +188,7 @@ protected:
|
|||||||
static void updateEnumerationList(App::PropertyEnumeration&, QComboBox* box);
|
static void updateEnumerationList(App::PropertyEnumeration&, QComboBox* box);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QPixmap m_icon;
|
||||||
App::DocumentObjectWeakPtrT m_object;
|
App::DocumentObjectWeakPtrT m_object;
|
||||||
Gui::ViewProviderWeakPtrT m_view;
|
Gui::ViewProviderWeakPtrT m_view;
|
||||||
};
|
};
|
||||||
@@ -200,7 +205,6 @@ public:
|
|||||||
~TaskDlgPost() override;
|
~TaskDlgPost() override;
|
||||||
void connectSlots();
|
void connectSlots();
|
||||||
|
|
||||||
void appendBox(TaskPostBox* box);
|
|
||||||
Gui::ViewProviderDocumentObject* getView() const
|
Gui::ViewProviderDocumentObject* getView() const
|
||||||
{
|
{
|
||||||
return *m_view;
|
return *m_view;
|
||||||
@@ -230,7 +234,6 @@ protected:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
Gui::ViewProviderWeakPtrT m_view;
|
Gui::ViewProviderWeakPtrT m_view;
|
||||||
std::vector<TaskPostBox*> m_boxes;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -238,7 +241,7 @@ protected:
|
|||||||
// box to set the coloring
|
// box to set the coloring
|
||||||
class ViewProviderFemPostObject;
|
class ViewProviderFemPostObject;
|
||||||
|
|
||||||
class TaskPostDisplay: public TaskPostBox
|
class TaskPostDisplay: public TaskPostWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@@ -261,7 +264,6 @@ private:
|
|||||||
void slotAddedFunction();
|
void slotAddedFunction();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QWidget* proxy;
|
|
||||||
std::unique_ptr<Ui_TaskPostDisplay> ui;
|
std::unique_ptr<Ui_TaskPostDisplay> ui;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -270,7 +272,7 @@ private:
|
|||||||
// functions
|
// functions
|
||||||
class ViewProviderFemPostFunction;
|
class ViewProviderFemPostFunction;
|
||||||
|
|
||||||
class TaskPostFunction: public TaskPostBox
|
class TaskPostFunction: public TaskPostWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@@ -283,7 +285,7 @@ public:
|
|||||||
|
|
||||||
// ***************************************************************************
|
// ***************************************************************************
|
||||||
// frames
|
// frames
|
||||||
class TaskPostFrames: public TaskPostBox
|
class TaskPostFrames: public TaskPostWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@@ -297,7 +299,6 @@ private:
|
|||||||
void setupConnections();
|
void setupConnections();
|
||||||
void onSelectionChanged();
|
void onSelectionChanged();
|
||||||
|
|
||||||
QWidget* proxy;
|
|
||||||
std::unique_ptr<Ui_TaskPostFrames> ui;
|
std::unique_ptr<Ui_TaskPostFrames> ui;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -311,7 +312,7 @@ private:
|
|||||||
// branch
|
// branch
|
||||||
class ViewProviderFemPostBranchFilter;
|
class ViewProviderFemPostBranchFilter;
|
||||||
|
|
||||||
class TaskPostBranch: public TaskPostBox
|
class TaskPostBranch: public TaskPostWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@@ -326,7 +327,6 @@ private:
|
|||||||
void onModeIndexChanged(int);
|
void onModeIndexChanged(int);
|
||||||
void onOutputIndexChanged(int);
|
void onOutputIndexChanged(int);
|
||||||
|
|
||||||
QWidget* proxy;
|
|
||||||
std::unique_ptr<Ui_TaskPostBranch> ui;
|
std::unique_ptr<Ui_TaskPostBranch> ui;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -334,7 +334,7 @@ private:
|
|||||||
// data along line filter
|
// data along line filter
|
||||||
class ViewProviderFemPostDataAlongLine;
|
class ViewProviderFemPostDataAlongLine;
|
||||||
|
|
||||||
class TaskPostDataAlongLine: public TaskPostBox
|
class TaskPostDataAlongLine: public TaskPostWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@@ -362,7 +362,6 @@ private:
|
|||||||
private:
|
private:
|
||||||
std::string Plot();
|
std::string Plot();
|
||||||
std::string ObjectVisible();
|
std::string ObjectVisible();
|
||||||
QWidget* proxy;
|
|
||||||
std::unique_ptr<Ui_TaskPostDataAlongLine> ui;
|
std::unique_ptr<Ui_TaskPostDataAlongLine> ui;
|
||||||
DataAlongLineMarker* marker;
|
DataAlongLineMarker* marker;
|
||||||
};
|
};
|
||||||
@@ -372,7 +371,7 @@ private:
|
|||||||
// data at point filter
|
// data at point filter
|
||||||
class ViewProviderFemPostDataAtPoint;
|
class ViewProviderFemPostDataAtPoint;
|
||||||
|
|
||||||
class TaskPostDataAtPoint: public TaskPostBox
|
class TaskPostDataAtPoint: public TaskPostWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@@ -400,7 +399,6 @@ private:
|
|||||||
std::string toString(double val) const;
|
std::string toString(double val) const;
|
||||||
void showValue(double value, const char* unit);
|
void showValue(double value, const char* unit);
|
||||||
std::string objectVisible(bool visible) const;
|
std::string objectVisible(bool visible) const;
|
||||||
QWidget* proxy;
|
|
||||||
std::unique_ptr<Ui_TaskPostDataAtPoint> ui;
|
std::unique_ptr<Ui_TaskPostDataAtPoint> ui;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -409,7 +407,7 @@ private:
|
|||||||
// clip filter
|
// clip filter
|
||||||
class ViewProviderFemPostClip;
|
class ViewProviderFemPostClip;
|
||||||
|
|
||||||
class TaskPostClip: public TaskPostBox
|
class TaskPostClip: public TaskPostWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@@ -435,7 +433,6 @@ private:
|
|||||||
void collectImplicitFunctions();
|
void collectImplicitFunctions();
|
||||||
|
|
||||||
// App::PropertyLink* m_functionProperty;
|
// App::PropertyLink* m_functionProperty;
|
||||||
QWidget* proxy;
|
|
||||||
std::unique_ptr<Ui_TaskPostClip> ui;
|
std::unique_ptr<Ui_TaskPostClip> ui;
|
||||||
FunctionWidget* fwidget;
|
FunctionWidget* fwidget;
|
||||||
};
|
};
|
||||||
@@ -445,7 +442,7 @@ private:
|
|||||||
// contours filter
|
// contours filter
|
||||||
class ViewProviderFemPostContours;
|
class ViewProviderFemPostContours;
|
||||||
|
|
||||||
class TaskPostContours: public TaskPostBox
|
class TaskPostContours: public TaskPostWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@@ -464,7 +461,6 @@ private:
|
|||||||
void onRelaxationChanged(double v);
|
void onRelaxationChanged(double v);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QWidget* proxy;
|
|
||||||
std::unique_ptr<Ui_TaskPostContours> ui;
|
std::unique_ptr<Ui_TaskPostContours> ui;
|
||||||
bool blockVectorUpdate = false;
|
bool blockVectorUpdate = false;
|
||||||
void updateFields();
|
void updateFields();
|
||||||
@@ -475,7 +471,7 @@ private:
|
|||||||
// cut filter
|
// cut filter
|
||||||
class ViewProviderFemPostCut;
|
class ViewProviderFemPostCut;
|
||||||
|
|
||||||
class TaskPostCut: public TaskPostBox
|
class TaskPostCut: public TaskPostWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@@ -499,7 +495,6 @@ private:
|
|||||||
void collectImplicitFunctions();
|
void collectImplicitFunctions();
|
||||||
|
|
||||||
// App::PropertyLink* m_functionProperty;
|
// App::PropertyLink* m_functionProperty;
|
||||||
QWidget* proxy;
|
|
||||||
std::unique_ptr<Ui_TaskPostCut> ui;
|
std::unique_ptr<Ui_TaskPostCut> ui;
|
||||||
FunctionWidget* fwidget;
|
FunctionWidget* fwidget;
|
||||||
};
|
};
|
||||||
@@ -509,7 +504,7 @@ private:
|
|||||||
// scalar clip filter
|
// scalar clip filter
|
||||||
class ViewProviderFemPostScalarClip;
|
class ViewProviderFemPostScalarClip;
|
||||||
|
|
||||||
class TaskPostScalarClip: public TaskPostBox
|
class TaskPostScalarClip: public TaskPostWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@@ -527,7 +522,6 @@ private:
|
|||||||
void onInsideOutToggled(bool val);
|
void onInsideOutToggled(bool val);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QWidget* proxy;
|
|
||||||
std::unique_ptr<Ui_TaskPostScalarClip> ui;
|
std::unique_ptr<Ui_TaskPostScalarClip> ui;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -536,7 +530,7 @@ private:
|
|||||||
// warp vector filter
|
// warp vector filter
|
||||||
class ViewProviderFemPostWarpVector;
|
class ViewProviderFemPostWarpVector;
|
||||||
|
|
||||||
class TaskPostWarpVector: public TaskPostBox
|
class TaskPostWarpVector: public TaskPostWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@@ -555,7 +549,6 @@ private:
|
|||||||
void onVectorCurrentIndexChanged(int idx);
|
void onVectorCurrentIndexChanged(int idx);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QWidget* proxy;
|
|
||||||
std::unique_ptr<Ui_TaskPostWarpVector> ui;
|
std::unique_ptr<Ui_TaskPostWarpVector> ui;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -564,7 +557,7 @@ private:
|
|||||||
// calculator filter
|
// calculator filter
|
||||||
class ViewProviderFemPostCalculator;
|
class ViewProviderFemPostCalculator;
|
||||||
|
|
||||||
class TaskPostCalculator: public TaskPostBox
|
class TaskPostCalculator: public TaskPostWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include "TaskPostBoxes.h"
|
#include "TaskPostBoxes.h"
|
||||||
#include "ViewProviderFemPostBranchFilter.h"
|
#include "ViewProviderFemPostBranchFilter.h"
|
||||||
#include <Mod/Fem/App/FemPostGroupExtension.h>
|
#include <Mod/Fem/App/FemPostGroupExtension.h>
|
||||||
|
#include <Gui/BitmapFactory.h>
|
||||||
|
|
||||||
|
|
||||||
using namespace FemGui;
|
using namespace FemGui;
|
||||||
@@ -46,7 +47,8 @@ ViewProviderFemPostBranchFilter::~ViewProviderFemPostBranchFilter()
|
|||||||
void ViewProviderFemPostBranchFilter::setupTaskDialog(TaskDlgPost* dlg)
|
void ViewProviderFemPostBranchFilter::setupTaskDialog(TaskDlgPost* dlg)
|
||||||
{
|
{
|
||||||
// add the branch ui
|
// add the branch ui
|
||||||
dlg->appendBox(new TaskPostBranch(this));
|
auto panel = new TaskPostBranch(this);
|
||||||
|
dlg->addTaskBox(panel->windowIcon().pixmap(32), panel);
|
||||||
|
|
||||||
// add the display options
|
// add the display options
|
||||||
FemGui::ViewProviderFemPostObject::setupTaskDialog(dlg);
|
FemGui::ViewProviderFemPostObject::setupTaskDialog(dlg);
|
||||||
|
|||||||
@@ -30,10 +30,43 @@
|
|||||||
|
|
||||||
#include "TaskPostBoxes.h"
|
#include "TaskPostBoxes.h"
|
||||||
#include "ViewProviderFemPostFilter.h"
|
#include "ViewProviderFemPostFilter.h"
|
||||||
|
#include "ViewProviderFemPostFilterPy.h"
|
||||||
|
|
||||||
|
|
||||||
using namespace FemGui;
|
using namespace FemGui;
|
||||||
|
|
||||||
|
PROPERTY_SOURCE(FemGui::ViewProviderFemPostFilterPythonBase, FemGui::ViewProviderFemPostObject)
|
||||||
|
|
||||||
|
ViewProviderFemPostFilterPythonBase::ViewProviderFemPostFilterPythonBase()
|
||||||
|
{}
|
||||||
|
|
||||||
|
ViewProviderFemPostFilterPythonBase::~ViewProviderFemPostFilterPythonBase() = default;
|
||||||
|
|
||||||
|
std::vector<std::string> ViewProviderFemPostFilterPythonBase::getDisplayModes() const
|
||||||
|
{
|
||||||
|
return std::vector<std::string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Gui
|
||||||
|
{
|
||||||
|
PROPERTY_SOURCE_TEMPLATE(FemGui::ViewProviderPostFilterPython,
|
||||||
|
FemGui::ViewProviderFemPostFilterPythonBase)
|
||||||
|
|
||||||
|
template<>
|
||||||
|
PyObject* FemGui::ViewProviderPostFilterPython::getPyObject()
|
||||||
|
{
|
||||||
|
if (!pyViewObject) {
|
||||||
|
pyViewObject = new ViewProviderFemPostFilterPy(this);
|
||||||
|
}
|
||||||
|
pyViewObject->IncRef();
|
||||||
|
return pyViewObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
// explicit template instantiation
|
||||||
|
template class FemGuiExport ViewProviderFeaturePythonT<FemGui::ViewProviderFemPostFilterPythonBase>;
|
||||||
|
|
||||||
|
} // namespace Gui
|
||||||
|
|
||||||
// ***************************************************************************
|
// ***************************************************************************
|
||||||
// in the following, the different filters sorted alphabetically
|
// in the following, the different filters sorted alphabetically
|
||||||
// ***************************************************************************
|
// ***************************************************************************
|
||||||
@@ -54,7 +87,8 @@ void ViewProviderFemPostDataAlongLine::setupTaskDialog(TaskDlgPost* dlg)
|
|||||||
{
|
{
|
||||||
// add the function box
|
// add the function box
|
||||||
assert(dlg->getView() == this);
|
assert(dlg->getView() == this);
|
||||||
dlg->appendBox(new TaskPostDataAlongLine(this));
|
auto panel = new TaskPostDataAlongLine(this);
|
||||||
|
dlg->addTaskBox(panel->getIcon(), panel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -102,7 +136,8 @@ void ViewProviderFemPostDataAtPoint::setupTaskDialog(TaskDlgPost* dlg)
|
|||||||
{
|
{
|
||||||
// add the function box
|
// add the function box
|
||||||
assert(dlg->getView() == this);
|
assert(dlg->getView() == this);
|
||||||
dlg->appendBox(new TaskPostDataAtPoint(this));
|
auto panel = new TaskPostDataAtPoint(this);
|
||||||
|
dlg->addTaskBox(panel->getIcon(), panel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -123,8 +158,9 @@ void ViewProviderFemPostClip::setupTaskDialog(TaskDlgPost* dlg)
|
|||||||
|
|
||||||
// add the function box
|
// add the function box
|
||||||
assert(dlg->getView() == this);
|
assert(dlg->getView() == this);
|
||||||
dlg->appendBox(
|
auto panel =
|
||||||
new TaskPostClip(this, &dlg->getView()->getObject<Fem::FemPostClipFilter>()->Function));
|
new TaskPostClip(this, &dlg->getView()->getObject<Fem::FemPostClipFilter>()->Function);
|
||||||
|
dlg->addTaskBox(panel->getIcon(), panel);
|
||||||
|
|
||||||
// add the display options
|
// add the display options
|
||||||
FemGui::ViewProviderFemPostObject::setupTaskDialog(dlg);
|
FemGui::ViewProviderFemPostObject::setupTaskDialog(dlg);
|
||||||
@@ -146,7 +182,8 @@ void ViewProviderFemPostContours::setupTaskDialog(TaskDlgPost* dlg)
|
|||||||
{
|
{
|
||||||
// the filter-specific task panel
|
// the filter-specific task panel
|
||||||
assert(dlg->getView() == this);
|
assert(dlg->getView() == this);
|
||||||
dlg->appendBox(new TaskPostContours(this));
|
auto panel = new TaskPostContours(this);
|
||||||
|
dlg->addTaskBox(panel->getIcon(), panel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -165,8 +202,9 @@ void ViewProviderFemPostCut::setupTaskDialog(TaskDlgPost* dlg)
|
|||||||
{
|
{
|
||||||
// add the function box
|
// add the function box
|
||||||
assert(dlg->getView() == this);
|
assert(dlg->getView() == this);
|
||||||
dlg->appendBox(
|
auto panel =
|
||||||
new TaskPostCut(this, &dlg->getView()->getObject<Fem::FemPostCutFilter>()->Function));
|
new TaskPostCut(this, &dlg->getView()->getObject<Fem::FemPostCutFilter>()->Function);
|
||||||
|
dlg->addTaskBox(panel->getIcon(), panel);
|
||||||
|
|
||||||
// add the display options
|
// add the display options
|
||||||
FemGui::ViewProviderFemPostObject::setupTaskDialog(dlg);
|
FemGui::ViewProviderFemPostObject::setupTaskDialog(dlg);
|
||||||
@@ -188,7 +226,8 @@ void ViewProviderFemPostScalarClip::setupTaskDialog(TaskDlgPost* dlg)
|
|||||||
{
|
{
|
||||||
// add the function box
|
// add the function box
|
||||||
assert(dlg->getView() == this);
|
assert(dlg->getView() == this);
|
||||||
dlg->appendBox(new TaskPostScalarClip(this));
|
auto panel = new TaskPostScalarClip(this);
|
||||||
|
dlg->addTaskBox(panel->getIcon(), panel);
|
||||||
|
|
||||||
// add the display options
|
// add the display options
|
||||||
FemGui::ViewProviderFemPostObject::setupTaskDialog(dlg);
|
FemGui::ViewProviderFemPostObject::setupTaskDialog(dlg);
|
||||||
@@ -210,7 +249,8 @@ void ViewProviderFemPostWarpVector::setupTaskDialog(TaskDlgPost* dlg)
|
|||||||
{
|
{
|
||||||
// add the function box
|
// add the function box
|
||||||
assert(dlg->getView() == this);
|
assert(dlg->getView() == this);
|
||||||
dlg->appendBox(new TaskPostWarpVector(this));
|
auto panel = new TaskPostWarpVector(this);
|
||||||
|
dlg->addTaskBox(panel->getIcon(), panel);
|
||||||
|
|
||||||
// add the display options
|
// add the display options
|
||||||
FemGui::ViewProviderFemPostObject::setupTaskDialog(dlg);
|
FemGui::ViewProviderFemPostObject::setupTaskDialog(dlg);
|
||||||
@@ -245,7 +285,8 @@ void ViewProviderFemPostCalculator::setupTaskDialog(TaskDlgPost* dlg)
|
|||||||
{
|
{
|
||||||
// add the function box
|
// add the function box
|
||||||
assert(dlg->getView() == this);
|
assert(dlg->getView() == this);
|
||||||
dlg->appendBox(new TaskPostCalculator(this));
|
auto panel = new TaskPostCalculator(this);
|
||||||
|
dlg->addTaskBox(panel->getIcon(), panel);
|
||||||
|
|
||||||
// add the display options
|
// add the display options
|
||||||
FemGui::ViewProviderFemPostObject::setupTaskDialog(dlg);
|
FemGui::ViewProviderFemPostObject::setupTaskDialog(dlg);
|
||||||
|
|||||||
@@ -23,12 +23,37 @@
|
|||||||
#ifndef FEM_VIEWPROVIDERFEMPOSTFILTER_H
|
#ifndef FEM_VIEWPROVIDERFEMPOSTFILTER_H
|
||||||
#define FEM_VIEWPROVIDERFEMPOSTFILTER_H
|
#define FEM_VIEWPROVIDERFEMPOSTFILTER_H
|
||||||
|
|
||||||
|
#include <Gui/ViewProviderFeaturePython.h>
|
||||||
#include "ViewProviderFemPostObject.h"
|
#include "ViewProviderFemPostObject.h"
|
||||||
|
|
||||||
|
|
||||||
namespace FemGui
|
namespace FemGui
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// ***************************************************************************
|
||||||
|
// Special classes to enable python filter view providers
|
||||||
|
// ***************************************************************************
|
||||||
|
|
||||||
|
// Special class for the python view providers, which need some special behaviour
|
||||||
|
class FemGuiExport ViewProviderFemPostFilterPythonBase: public ViewProviderFemPostObject
|
||||||
|
{
|
||||||
|
PROPERTY_HEADER_WITH_OVERRIDE(FemGui::ViewProviderFemPostFilterPythonBase);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// constructor.
|
||||||
|
ViewProviderFemPostFilterPythonBase();
|
||||||
|
~ViewProviderFemPostFilterPythonBase() override;
|
||||||
|
|
||||||
|
// we do not use default display modes but let the python implementation choose
|
||||||
|
// Python view provider needs to return a sublist of PostObject supporter DisplayModes
|
||||||
|
std::vector<std::string> getDisplayModes() const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Viewprovider for the python filters
|
||||||
|
using ViewProviderPostFilterPython =
|
||||||
|
Gui::ViewProviderFeaturePythonT<ViewProviderFemPostFilterPythonBase>;
|
||||||
|
|
||||||
|
|
||||||
// ***************************************************************************
|
// ***************************************************************************
|
||||||
// in the following, the different filters sorted alphabetically
|
// in the following, the different filters sorted alphabetically
|
||||||
// ***************************************************************************
|
// ***************************************************************************
|
||||||
|
|||||||
24
src/Mod/Fem/Gui/ViewProviderFemPostFilterPy.xml
Normal file
24
src/Mod/Fem/Gui/ViewProviderFemPostFilterPy.xml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
|
||||||
|
<PythonExport
|
||||||
|
Father="ViewProviderDocumentObjectPy"
|
||||||
|
Name="ViewProviderFemPostFilterPy"
|
||||||
|
Twin="ViewProviderFemPostObject"
|
||||||
|
TwinPointer="ViewProviderFemPostObject"
|
||||||
|
Include="Mod/Fem/Gui/ViewProviderFemPostObject.h"
|
||||||
|
Namespace="FemGui"
|
||||||
|
FatherInclude="Gui/ViewProviderDocumentObjectPy.h"
|
||||||
|
FatherNamespace="Gui"
|
||||||
|
Constructor="false"
|
||||||
|
Delete="false">
|
||||||
|
<Documentation>
|
||||||
|
<Author Licence="LGPL" Name="Stefan Tröger" EMail="stefantroeger@gmx.net" />
|
||||||
|
<UserDocu>ViewProviderFemPostPipeline class</UserDocu>
|
||||||
|
</Documentation>
|
||||||
|
<Methode Name="createDisplayTaskWidget">
|
||||||
|
<Documentation>
|
||||||
|
<UserDocu>Returns the display option task panel for a post processing edit task dialog.</UserDocu>
|
||||||
|
</Documentation>
|
||||||
|
</Methode>
|
||||||
|
</PythonExport>
|
||||||
|
</GenerateModel>
|
||||||
71
src/Mod/Fem/Gui/ViewProviderFemPostFilterPyImp.cpp
Normal file
71
src/Mod/Fem/Gui/ViewProviderFemPostFilterPyImp.cpp
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 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"
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
#include <Gui/Control.h>
|
||||||
|
#include <Gui/PythonWrapper.h>
|
||||||
|
#include "ViewProviderFemPostFilter.h"
|
||||||
|
#include "TaskPostBoxes.h"
|
||||||
|
// inclusion of the generated files (generated out of ViewProviderFemPostFilterPy.xml)
|
||||||
|
#include "ViewProviderFemPostFilterPy.h"
|
||||||
|
#include "ViewProviderFemPostFilterPy.cpp"
|
||||||
|
#include <Base/PyWrapParseTupleAndKeywords.h>
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
|
||||||
|
using namespace FemGui;
|
||||||
|
|
||||||
|
// returns a string which represents the object e.g. when printed in python
|
||||||
|
std::string ViewProviderFemPostFilterPy::representation() const
|
||||||
|
{
|
||||||
|
return {"<ViewProviderFemPostFilter object>"};
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* ViewProviderFemPostFilterPy::createDisplayTaskWidget(PyObject* args)
|
||||||
|
{
|
||||||
|
// we take no arguments
|
||||||
|
if (!PyArg_ParseTuple(args, "")) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto panel = new TaskPostDisplay(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ViewProviderFemPostFilterPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -343,7 +343,8 @@ bool ViewProviderFemPostFunction::setEdit(int ModNum)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
postDlg = new TaskDlgPost(this);
|
postDlg = new TaskDlgPost(this);
|
||||||
postDlg->appendBox(new TaskPostFunction(this));
|
auto panel = new TaskPostFunction(this);
|
||||||
|
postDlg->addTaskBox(panel->windowIcon().pixmap(32), panel);
|
||||||
Gui::Control().showDialog(postDlg);
|
Gui::Control().showDialog(postDlg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,6 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <App/Document.h>
|
#include <App/Document.h>
|
||||||
#include <Base/Console.h>
|
|
||||||
#include <Gui/Application.h>
|
#include <Gui/Application.h>
|
||||||
#include <Gui/Control.h>
|
#include <Gui/Control.h>
|
||||||
#include <Gui/Document.h>
|
#include <Gui/Document.h>
|
||||||
@@ -187,7 +186,7 @@ ViewProviderFemPostObject::ViewProviderFemPostObject()
|
|||||||
LineWidth.setConstraints(&sizeRange);
|
LineWidth.setConstraints(&sizeRange);
|
||||||
PointSize.setConstraints(&sizeRange);
|
PointSize.setConstraints(&sizeRange);
|
||||||
|
|
||||||
sPixmap = "fem-femmesh-from-shape";
|
sPixmap = "FEM_PostPipelineFromResult";
|
||||||
|
|
||||||
// create the subnodes which do the visualization work
|
// create the subnodes which do the visualization work
|
||||||
m_transpType = new SoTransparencyType();
|
m_transpType = new SoTransparencyType();
|
||||||
@@ -408,7 +407,9 @@ void ViewProviderFemPostObject::updateVtk()
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_currentAlgorithm->Update();
|
m_currentAlgorithm->Update();
|
||||||
updateProperties();
|
if (!isRestoring()) {
|
||||||
|
updateProperties();
|
||||||
|
}
|
||||||
update3D();
|
update3D();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -931,7 +932,9 @@ void ViewProviderFemPostObject::onChanged(const App::Property* prop)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (prop == &Field && setupPipeline()) {
|
if (prop == &Field && setupPipeline()) {
|
||||||
updateProperties();
|
if (!isRestoring()) {
|
||||||
|
updateProperties();
|
||||||
|
}
|
||||||
WriteColorData(ResetColorBarRange);
|
WriteColorData(ResetColorBarRange);
|
||||||
}
|
}
|
||||||
else if (prop == &Component && setupPipeline()) {
|
else if (prop == &Component && setupPipeline()) {
|
||||||
@@ -1016,7 +1019,8 @@ bool ViewProviderFemPostObject::setEdit(int ModNum)
|
|||||||
void ViewProviderFemPostObject::setupTaskDialog(TaskDlgPost* dlg)
|
void ViewProviderFemPostObject::setupTaskDialog(TaskDlgPost* dlg)
|
||||||
{
|
{
|
||||||
assert(dlg->getView() == this);
|
assert(dlg->getView() == this);
|
||||||
dlg->appendBox(new TaskPostDisplay(this));
|
auto panel = new TaskPostDisplay(this);
|
||||||
|
dlg->addTaskBox(panel->windowIcon().pixmap(32), panel);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewProviderFemPostObject::unsetEdit(int ModNum)
|
void ViewProviderFemPostObject::unsetEdit(int ModNum)
|
||||||
|
|||||||
@@ -114,27 +114,14 @@ public:
|
|||||||
bool canDelete(App::DocumentObject* obj) const override;
|
bool canDelete(App::DocumentObject* obj) const override;
|
||||||
virtual void onSelectionChanged(const Gui::SelectionChanges& sel);
|
virtual void onSelectionChanged(const Gui::SelectionChanges& sel);
|
||||||
|
|
||||||
/** @name Selection handling
|
// setting up task dialogs
|
||||||
* This group of methods do the selection handling.
|
virtual void setupTaskDialog(TaskDlgPost* dlg);
|
||||||
* Here you can define how the selection for your ViewProvider
|
|
||||||
* works.
|
|
||||||
*/
|
|
||||||
//@{
|
|
||||||
// /// indicates if the ViewProvider use the new Selection model
|
|
||||||
// virtual bool useNewSelectionModel(void) const {return true;}
|
|
||||||
// /// return a hit element to the selection path or 0
|
|
||||||
// virtual std::string getElement(const SoDetail*) const;
|
|
||||||
// virtual SoDetail* getDetail(const char*) const;
|
|
||||||
// /// return the highlight lines for a given element or the whole shape
|
|
||||||
// virtual std::vector<Base::Vector3d> getSelectionShape(const char* Element) const;
|
|
||||||
// //@}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void handleChangedPropertyName(Base::XMLReader& reader,
|
void handleChangedPropertyName(Base::XMLReader& reader,
|
||||||
const char* typeName,
|
const char* typeName,
|
||||||
const char* propName) override;
|
const char* propName) override;
|
||||||
|
|
||||||
virtual void setupTaskDialog(TaskDlgPost* dlg);
|
|
||||||
bool setupPipeline();
|
bool setupPipeline();
|
||||||
void updateVtk();
|
void updateVtk();
|
||||||
void setRangeOfColorBar(float min, float max);
|
void setRangeOfColorBar(float min, float max);
|
||||||
|
|||||||
@@ -221,7 +221,8 @@ void ViewProviderFemPostPipeline::setupTaskDialog(TaskDlgPost* dlg)
|
|||||||
// add the function box
|
// add the function box
|
||||||
assert(dlg->getView() == this);
|
assert(dlg->getView() == this);
|
||||||
ViewProviderFemPostObject::setupTaskDialog(dlg);
|
ViewProviderFemPostObject::setupTaskDialog(dlg);
|
||||||
dlg->appendBox(new TaskPostFrames(this));
|
auto panel = new TaskPostFrames(this);
|
||||||
|
dlg->addTaskBox(panel->windowIcon().pixmap(32), panel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -206,6 +206,9 @@ Gui::ToolBarItem* Workbench::setupToolBars() const
|
|||||||
<< "FEM_PostFilterCutFunction"
|
<< "FEM_PostFilterCutFunction"
|
||||||
<< "FEM_PostFilterClipRegion"
|
<< "FEM_PostFilterClipRegion"
|
||||||
<< "FEM_PostFilterContours"
|
<< "FEM_PostFilterContours"
|
||||||
|
#ifdef FC_USE_VTK_PYTHON
|
||||||
|
<< "FEM_PostFilterGlyph"
|
||||||
|
#endif
|
||||||
<< "FEM_PostFilterDataAlongLine"
|
<< "FEM_PostFilterDataAlongLine"
|
||||||
<< "FEM_PostFilterLinearizedStresses"
|
<< "FEM_PostFilterLinearizedStresses"
|
||||||
<< "FEM_PostFilterDataAtPoint"
|
<< "FEM_PostFilterDataAtPoint"
|
||||||
@@ -355,6 +358,9 @@ Gui::MenuItem* Workbench::setupMenuBar() const
|
|||||||
<< "FEM_PostFilterCutFunction"
|
<< "FEM_PostFilterCutFunction"
|
||||||
<< "FEM_PostFilterClipRegion"
|
<< "FEM_PostFilterClipRegion"
|
||||||
<< "FEM_PostFilterContours"
|
<< "FEM_PostFilterContours"
|
||||||
|
#ifdef FC_USE_VTK_PYTHON
|
||||||
|
<< "FEM_PostFilterGlyph"
|
||||||
|
#endif
|
||||||
<< "FEM_PostFilterDataAlongLine"
|
<< "FEM_PostFilterDataAlongLine"
|
||||||
<< "FEM_PostFilterLinearizedStresses"
|
<< "FEM_PostFilterLinearizedStresses"
|
||||||
<< "FEM_PostFilterDataAtPoint"
|
<< "FEM_PostFilterDataAtPoint"
|
||||||
|
|||||||
@@ -80,6 +80,11 @@ class FemWorkbench(Workbench):
|
|||||||
False if FemGui.__name__ else True
|
False if FemGui.__name__ else True
|
||||||
False if femcommands.commands.__name__ else True
|
False if femcommands.commands.__name__ else True
|
||||||
|
|
||||||
|
# check vtk version to potentially find missmatchs
|
||||||
|
from femguiutils.vtk_module_handling import vtk_module_handling
|
||||||
|
|
||||||
|
vtk_module_handling()
|
||||||
|
|
||||||
def GetClassName(self):
|
def GetClassName(self):
|
||||||
# see https://forum.freecad.org/viewtopic.php?f=10&t=43300
|
# see https://forum.freecad.org/viewtopic.php?f=10&t=43300
|
||||||
return "FemGui::Workbench"
|
return "FemGui::Workbench"
|
||||||
|
|||||||
@@ -653,6 +653,22 @@ def makePostVtkFilterContours(doc, base_vtk_result, name="VtkFilterContours"):
|
|||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
def makePostFilterGlyph(doc, base_vtk_result, name="Glyph"):
|
||||||
|
"""makePostVtkFilterGlyph(document, [name]):
|
||||||
|
creates a FEM post processing filter that visualizes vector fields with glyphs
|
||||||
|
"""
|
||||||
|
obj = doc.addObject("Fem::PostFilterPython", name)
|
||||||
|
from femobjects import post_glyphfilter
|
||||||
|
|
||||||
|
post_glyphfilter.PostGlyphFilter(obj)
|
||||||
|
base_vtk_result.addObject(obj)
|
||||||
|
if FreeCAD.GuiUp:
|
||||||
|
from femviewprovider import view_post_glyphfilter
|
||||||
|
|
||||||
|
view_post_glyphfilter.VPPostGlyphFilter(obj.ViewObject)
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
def makePostVtkResult(doc, result_data, name="VtkResult"):
|
def makePostVtkResult(doc, result_data, name="VtkResult"):
|
||||||
"""makePostVtkResult(document, base_result, [name]):
|
"""makePostVtkResult(document, base_result, [name]):
|
||||||
creates a FEM post processing result data (vtk based) to hold FEM results
|
creates a FEM post processing result data (vtk based) to hold FEM results
|
||||||
|
|||||||
@@ -1217,6 +1217,21 @@ class _SolverZ88(CommandManager):
|
|||||||
self.do_activated = "add_obj_on_gui_expand_noset_edit"
|
self.do_activated = "add_obj_on_gui_expand_noset_edit"
|
||||||
|
|
||||||
|
|
||||||
|
class _PostFilterGlyph(CommandManager):
|
||||||
|
"The FEM_PostFilterGlyph command definition"
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.menutext = Qt.QT_TRANSLATE_NOOP("FEM_PostFilterGlyph", "Glyph filter")
|
||||||
|
self.accel = "F, G"
|
||||||
|
self.tooltip = Qt.QT_TRANSLATE_NOOP(
|
||||||
|
"FEM_PostFilterGlyph",
|
||||||
|
"Post processing filter that adds glyphs to the mesh vertices for vertex data visualization",
|
||||||
|
)
|
||||||
|
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
|
# the string in add command will be the page name on FreeCAD wiki
|
||||||
FreeCADGui.addCommand("FEM_Analysis", _Analysis())
|
FreeCADGui.addCommand("FEM_Analysis", _Analysis())
|
||||||
FreeCADGui.addCommand("FEM_ClippingPlaneAdd", _ClippingPlaneAdd())
|
FreeCADGui.addCommand("FEM_ClippingPlaneAdd", _ClippingPlaneAdd())
|
||||||
@@ -1271,3 +1286,6 @@ FreeCADGui.addCommand("FEM_SolverElmer", _SolverElmer())
|
|||||||
FreeCADGui.addCommand("FEM_SolverMystran", _SolverMystran())
|
FreeCADGui.addCommand("FEM_SolverMystran", _SolverMystran())
|
||||||
FreeCADGui.addCommand("FEM_SolverRun", _SolverRun())
|
FreeCADGui.addCommand("FEM_SolverRun", _SolverRun())
|
||||||
FreeCADGui.addCommand("FEM_SolverZ88", _SolverZ88())
|
FreeCADGui.addCommand("FEM_SolverZ88", _SolverZ88())
|
||||||
|
|
||||||
|
if "BUILD_FEM_VTK_PYTHON" in FreeCAD.__cmake__:
|
||||||
|
FreeCADGui.addCommand("FEM_PostFilterGlyph", _PostFilterGlyph())
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import FreeCAD
|
|||||||
|
|
||||||
from femtools.femutils import expandParentObject
|
from femtools.femutils import expandParentObject
|
||||||
from femtools.femutils import is_of_type
|
from femtools.femutils import is_of_type
|
||||||
|
from femguiutils.vtk_module_handling import vtk_compatibility_abort
|
||||||
|
|
||||||
if FreeCAD.GuiUp:
|
if FreeCAD.GuiUp:
|
||||||
from PySide import QtCore
|
from PySide import QtCore
|
||||||
@@ -89,6 +90,8 @@ class CommandManager:
|
|||||||
FreeCADGui.ActiveDocument is not None
|
FreeCADGui.ActiveDocument is not None
|
||||||
and self.result_selected()
|
and self.result_selected()
|
||||||
)
|
)
|
||||||
|
elif self.is_active == "with_vtk_selresult":
|
||||||
|
active = self.vtk_result_selected()
|
||||||
elif self.is_active == "with_part_feature":
|
elif self.is_active == "with_part_feature":
|
||||||
active = FreeCADGui.ActiveDocument is not None and self.part_feature_selected()
|
active = FreeCADGui.ActiveDocument is not None and self.part_feature_selected()
|
||||||
elif self.is_active == "with_femmesh":
|
elif self.is_active == "with_femmesh":
|
||||||
@@ -144,6 +147,8 @@ class CommandManager:
|
|||||||
self.add_obj_on_gui_selobj_set_edit(self.__class__.__name__.lstrip("_"))
|
self.add_obj_on_gui_selobj_set_edit(self.__class__.__name__.lstrip("_"))
|
||||||
elif self.do_activated == "add_obj_on_gui_selobj_expand_noset_edit":
|
elif self.do_activated == "add_obj_on_gui_selobj_expand_noset_edit":
|
||||||
self.add_obj_on_gui_selobj_expand_noset_edit(self.__class__.__name__.lstrip("_"))
|
self.add_obj_on_gui_selobj_expand_noset_edit(self.__class__.__name__.lstrip("_"))
|
||||||
|
elif self.do_activated == "add_filter_set_edit":
|
||||||
|
self.add_filter_set_edit(self.__class__.__name__.lstrip("_"))
|
||||||
# in all other cases Activated is implemented it the command class
|
# in all other cases Activated is implemented it the command class
|
||||||
|
|
||||||
def results_present(self):
|
def results_present(self):
|
||||||
@@ -169,6 +174,13 @@ class CommandManager:
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def vtk_result_selected(self):
|
||||||
|
sel = FreeCADGui.Selection.getSelection()
|
||||||
|
if len(sel) == 1 and sel[0].isDerivedFrom("Fem::FemPostObject"):
|
||||||
|
self.selobj = sel[0]
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def part_feature_selected(self):
|
def part_feature_selected(self):
|
||||||
sel = FreeCADGui.Selection.getSelection()
|
sel = FreeCADGui.Selection.getSelection()
|
||||||
if len(sel) == 1 and sel[0].isDerivedFrom("Part::Feature"):
|
if len(sel) == 1 and sel[0].isDerivedFrom("Part::Feature"):
|
||||||
@@ -363,3 +375,48 @@ class CommandManager:
|
|||||||
)
|
)
|
||||||
# expand selobj in tree view
|
# expand selobj in tree view
|
||||||
expandParentObject()
|
expandParentObject()
|
||||||
|
|
||||||
|
def add_filter_set_edit(self, filtertype):
|
||||||
|
# like add_obj_on_gui_selobj_noset_edit but the selection is kept
|
||||||
|
# and the selobj is expanded in the tree to see the added obj
|
||||||
|
|
||||||
|
# check if we should use python fitler
|
||||||
|
if vtk_compatibility_abort(True):
|
||||||
|
return
|
||||||
|
|
||||||
|
# Note: we know selobj is a FemPostObject as otherwise the command should not have been active
|
||||||
|
# We also assume the all filters are in PostGroups and not astray
|
||||||
|
group = None
|
||||||
|
if self.selobj.hasExtension("Fem::FemPostGroupExtension"):
|
||||||
|
group = self.selobj
|
||||||
|
else:
|
||||||
|
group = self.selobj.getParentPostGroup()
|
||||||
|
|
||||||
|
FreeCAD.ActiveDocument.openTransaction(f"Create Fem{filtertype}")
|
||||||
|
FreeCADGui.addModule("ObjectsFem")
|
||||||
|
FreeCADGui.doCommand(
|
||||||
|
"ObjectsFem.make{}("
|
||||||
|
"FreeCAD.ActiveDocument, FreeCAD.ActiveDocument.{})".format(filtertype, group.Name)
|
||||||
|
)
|
||||||
|
# set display and selection style to assure the user sees the new object
|
||||||
|
FreeCADGui.doCommand(
|
||||||
|
'FreeCAD.ActiveDocument.ActiveObject.ViewObject.DisplayMode = "Surface"'
|
||||||
|
)
|
||||||
|
FreeCADGui.doCommand(
|
||||||
|
'FreeCAD.ActiveDocument.ActiveObject.ViewObject.SelectionStyle = "BoundBox"'
|
||||||
|
)
|
||||||
|
|
||||||
|
# hide selected filter
|
||||||
|
FreeCADGui.doCommand(
|
||||||
|
"FreeCAD.ActiveDocument.{}.ViewObject.Visibility = False".format(self.selobj.Name)
|
||||||
|
)
|
||||||
|
|
||||||
|
# recompute, expand selobj in tree view
|
||||||
|
expandParentObject()
|
||||||
|
FreeCADGui.doCommand("FreeCAD.ActiveDocument.ActiveObject.recompute()")
|
||||||
|
|
||||||
|
# set edit
|
||||||
|
FreeCADGui.Selection.clearSelection()
|
||||||
|
FreeCADGui.doCommand(
|
||||||
|
"FreeCADGui.ActiveDocument.setEdit(FreeCAD.ActiveDocument.ActiveObject.Name)"
|
||||||
|
)
|
||||||
|
|||||||
258
src/Mod/Fem/femguiutils/vtk_module_handling.py
Normal file
258
src/Mod/Fem/femguiutils/vtk_module_handling.py
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
# ***************************************************************************
|
||||||
|
# * 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 *
|
||||||
|
# * *
|
||||||
|
# ***************************************************************************
|
||||||
|
|
||||||
|
"""Methods to verify if the python VTK module is the correct one
|
||||||
|
|
||||||
|
FreeCAD is linked with VTK libraries during its build process. To use the VTK
|
||||||
|
python module and pass objects between python and c++ the compiled module library
|
||||||
|
needs to be linked to the exact same vtk library as FreeCAD is. This is ensured by
|
||||||
|
installing VTK via linux app managers: All known distros install the python side
|
||||||
|
packages together with vtk libs. Libpack and other OS systems ensure this too.
|
||||||
|
|
||||||
|
However, if a vtk python package is installed manually, e.g. by "pip install vtk",
|
||||||
|
it could be found instead of the system module. This python API brings its own
|
||||||
|
set of VTK libs, and hence object passing in FreeCAD fails. (Note: import and
|
||||||
|
pure vtk python code still works, only passing to c++ fails)
|
||||||
|
|
||||||
|
This file provides functions that detect this situation and inform the user.
|
||||||
|
Additionally we try to find the correct module in the path and offer to use
|
||||||
|
it instead.
|
||||||
|
|
||||||
|
Note that this problem occurs with all "compiled binary" python APIs, also
|
||||||
|
with PySide. It is the users responsibility to handle his python path and keep
|
||||||
|
it clean/working. The functions provided here are a workaround only.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__title__ = "FEM GUI vtk python module check"
|
||||||
|
__author__ = "Stefan Tröger"
|
||||||
|
__url__ = "https://www.freecad.org"
|
||||||
|
|
||||||
|
__user_input_received = False
|
||||||
|
|
||||||
|
|
||||||
|
def vtk_module_compatible():
|
||||||
|
# checks if the VTK library FreeCAD is build against is the one used by
|
||||||
|
# the python module
|
||||||
|
|
||||||
|
# make sure we do not contaminate the modules with vtk to not trick
|
||||||
|
# the check later
|
||||||
|
unload = not _vtk_is_loaded()
|
||||||
|
|
||||||
|
import Fem
|
||||||
|
from vtkmodules.vtkCommonCore import vtkVersion, vtkBitArray
|
||||||
|
|
||||||
|
# simple version check
|
||||||
|
if Fem.getVtkVersion() != vtkVersion.GetVTKVersion():
|
||||||
|
return False
|
||||||
|
|
||||||
|
# check binary compatibility
|
||||||
|
result = Fem.isVtkCompatible(vtkBitArray())
|
||||||
|
|
||||||
|
if unload:
|
||||||
|
# cleanup our own import
|
||||||
|
_unload_vtk_modules()
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def _vtk_is_loaded():
|
||||||
|
import sys
|
||||||
|
|
||||||
|
return any("vtkmodules" in module for module in sys.modules)
|
||||||
|
|
||||||
|
|
||||||
|
def _unload_vtk_modules():
|
||||||
|
# unloads all loaded vtk modules
|
||||||
|
# NOTE: does not remove any stored references in objects
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
for module in sys.modules.copy():
|
||||||
|
if "vtkmodules" in module:
|
||||||
|
del sys.modules[module]
|
||||||
|
|
||||||
|
|
||||||
|
def _find_compatible_module():
|
||||||
|
# Check all python path folders if they contain a vtk module
|
||||||
|
|
||||||
|
import Fem
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# remove module from runtime
|
||||||
|
_unload_vtk_modules()
|
||||||
|
|
||||||
|
path = sys.path.copy()
|
||||||
|
|
||||||
|
for folder in reversed(path):
|
||||||
|
try:
|
||||||
|
# use a single folder as path and try to load vtk
|
||||||
|
sys.path = [folder]
|
||||||
|
if vtk_module_compatible():
|
||||||
|
# we do still unload, to let the user descide if he wants to use it
|
||||||
|
_unload_vtk_modules()
|
||||||
|
sys.path = path
|
||||||
|
return folder
|
||||||
|
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# reset the correct path and indicate that we failed
|
||||||
|
sys.path = path
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _load_vtk_from(folder):
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
path = sys.path
|
||||||
|
try:
|
||||||
|
sys.path = [folder]
|
||||||
|
import vtkmodules
|
||||||
|
finally:
|
||||||
|
sys.path = path
|
||||||
|
|
||||||
|
|
||||||
|
# If FreeCAD is build with VTK python support this function checks if the
|
||||||
|
# used python module is compatible with the c++ lib. Does inform the user
|
||||||
|
# if not so and offers the correct module, if available
|
||||||
|
#
|
||||||
|
# Note: Call this also from Python feature module, as on document load
|
||||||
|
# this can be loaded before initializing FEM workbench.
|
||||||
|
def vtk_module_handling():
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import FreeCAD
|
||||||
|
|
||||||
|
if not "BUILD_FEM_VTK_PYTHON" in FreeCAD.__cmake__:
|
||||||
|
# no VTK python api support in FreeCAD
|
||||||
|
return
|
||||||
|
|
||||||
|
# only ask user once per session
|
||||||
|
global __user_input_received
|
||||||
|
if __user_input_received:
|
||||||
|
return
|
||||||
|
__user_input_received = True
|
||||||
|
|
||||||
|
loaded = _vtk_is_loaded()
|
||||||
|
|
||||||
|
# check if we are compatible
|
||||||
|
if not vtk_module_compatible():
|
||||||
|
|
||||||
|
if not FreeCAD.GuiUp:
|
||||||
|
FreeCAD.Console.PrintError(
|
||||||
|
"FEM: vtk python module is not compatible with internal vtk library"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
import FreeCAD, Fem
|
||||||
|
from vtkmodules.vtkCommonCore import vtkVersion
|
||||||
|
import inspect
|
||||||
|
from PySide import QtGui
|
||||||
|
|
||||||
|
translate = FreeCAD.Qt.translate
|
||||||
|
|
||||||
|
path = inspect.getfile(vtkVersion)
|
||||||
|
path = path[: path.find("vtkmodules")]
|
||||||
|
|
||||||
|
message = translate(
|
||||||
|
"FEM",
|
||||||
|
(
|
||||||
|
"FreeCAD is linked to a different VTK library then the imported "
|
||||||
|
"VTK python module. This is incompatible and will lead to errors."
|
||||||
|
"\n\nWrong python module is imported from: \n{}"
|
||||||
|
),
|
||||||
|
).format(path)
|
||||||
|
|
||||||
|
buttons = QtGui.QMessageBox.Discard
|
||||||
|
|
||||||
|
# check if there is any compatible vtk module
|
||||||
|
compatible_module = _find_compatible_module()
|
||||||
|
|
||||||
|
if compatible_module:
|
||||||
|
# there is a compatible module of VTK available.
|
||||||
|
message += translate("FEM", "\n\nCorrect module found in: \n{}").format(
|
||||||
|
compatible_module
|
||||||
|
)
|
||||||
|
|
||||||
|
if not loaded:
|
||||||
|
# vtk was not loaded beforehand, therefore we can realistically reload
|
||||||
|
message += translate("FEM", "\n\nShould this module be loaded instead?")
|
||||||
|
|
||||||
|
buttons = QtGui.QMessageBox.Yes | QtGui.QMessageBox.No
|
||||||
|
|
||||||
|
else:
|
||||||
|
message += translate(
|
||||||
|
"FEM",
|
||||||
|
(
|
||||||
|
"\n\nAs the wrong module was already loaded, a reload is not possible. "
|
||||||
|
"Restart FreeCAD to get the option for loading this module."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
message += translate(
|
||||||
|
"FEM", "\n\nNo matching module was found in the current python path."
|
||||||
|
)
|
||||||
|
|
||||||
|
# raise a dialog to the user
|
||||||
|
import FreeCADGui
|
||||||
|
|
||||||
|
button = QtGui.QMessageBox.critical(
|
||||||
|
FreeCADGui.getMainWindow(),
|
||||||
|
translate("FEM", "VTK module conflict"),
|
||||||
|
message,
|
||||||
|
buttons=buttons,
|
||||||
|
)
|
||||||
|
|
||||||
|
if button == QtGui.QMessageBox.Yes:
|
||||||
|
# try to reload the correct vtk module
|
||||||
|
_load_vtk_from(compatible_module)
|
||||||
|
|
||||||
|
|
||||||
|
# Returns if vtk python is incompatible and hence operations need to be aborted.
|
||||||
|
# If inform=True the user gets informed by dialog about incompatibilities
|
||||||
|
def vtk_compatibility_abort(inform=True):
|
||||||
|
|
||||||
|
if not vtk_module_compatible():
|
||||||
|
|
||||||
|
if inform:
|
||||||
|
# raise a dialog to the user that this functionality is not available
|
||||||
|
import FreeCAD
|
||||||
|
import FreeCADGui
|
||||||
|
from PySide import QtGui
|
||||||
|
|
||||||
|
translate = FreeCAD.Qt.translate
|
||||||
|
|
||||||
|
button = QtGui.QMessageBox.critical(
|
||||||
|
FreeCADGui.getMainWindow(),
|
||||||
|
translate("FEM", "VTK module conflict"),
|
||||||
|
translate(
|
||||||
|
"FEM", "This functionality is not available due to VTK python module conflict"
|
||||||
|
),
|
||||||
|
buttons=QtGui.QMessageBox.Discard,
|
||||||
|
)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
271
src/Mod/Fem/femobjects/post_glyphfilter.py
Normal file
271
src/Mod/Fem/femobjects/post_glyphfilter.py
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
# ***************************************************************************
|
||||||
|
# * 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 glyph filter"
|
||||||
|
__author__ = "Stefan Tröger"
|
||||||
|
__url__ = "https://www.freecad.org"
|
||||||
|
|
||||||
|
## @package post_glyphfilter
|
||||||
|
# \ingroup FEM
|
||||||
|
# \brief Post processing filter creating glyphs for vector fields
|
||||||
|
|
||||||
|
import FreeCAD
|
||||||
|
|
||||||
|
# check vtk version to potentially find missmatchs
|
||||||
|
from femguiutils.vtk_module_handling import vtk_module_handling
|
||||||
|
|
||||||
|
vtk_module_handling()
|
||||||
|
|
||||||
|
# IMPORTANT: Never import vtk directly. Often vtk is compiled with different QT
|
||||||
|
# version than FreeCAD, and "import vtk" crashes by importing qt components.
|
||||||
|
# Always import the filter and data modules only.
|
||||||
|
from vtkmodules.vtkFiltersCore import vtkMaskPoints
|
||||||
|
from vtkmodules.vtkFiltersCore import vtkGlyph3D
|
||||||
|
import vtkmodules.vtkFiltersSources as vtkSources
|
||||||
|
|
||||||
|
from . import base_fempythonobject
|
||||||
|
|
||||||
|
_PropHelper = base_fempythonobject._PropHelper
|
||||||
|
|
||||||
|
|
||||||
|
class PostGlyphFilter(base_fempythonobject.BaseFemPythonObject):
|
||||||
|
"""
|
||||||
|
A post processing filter adding glyphs
|
||||||
|
"""
|
||||||
|
|
||||||
|
Type = "Fem::PostFilterPython"
|
||||||
|
|
||||||
|
def __init__(self, obj):
|
||||||
|
super().__init__(obj)
|
||||||
|
|
||||||
|
for prop in self._get_properties():
|
||||||
|
prop.add_to_object(obj)
|
||||||
|
|
||||||
|
self.__setupFilterPipeline(obj)
|
||||||
|
|
||||||
|
def _get_properties(self):
|
||||||
|
|
||||||
|
prop = [
|
||||||
|
_PropHelper(
|
||||||
|
type="App::PropertyEnumeration",
|
||||||
|
name="Glyph",
|
||||||
|
group="Glyph",
|
||||||
|
doc="The form of the glyph",
|
||||||
|
value=["Arrow", "Cube"],
|
||||||
|
),
|
||||||
|
_PropHelper(
|
||||||
|
type="App::PropertyEnumeration",
|
||||||
|
name="OrientationData",
|
||||||
|
group="Glyph",
|
||||||
|
doc="Which vector field is used to orient the glyphs",
|
||||||
|
value=["None"],
|
||||||
|
),
|
||||||
|
_PropHelper(
|
||||||
|
type="App::PropertyEnumeration",
|
||||||
|
name="ScaleData",
|
||||||
|
group="Scale",
|
||||||
|
doc="Which data field is used to scale the glyphs",
|
||||||
|
value=["None"],
|
||||||
|
),
|
||||||
|
_PropHelper(
|
||||||
|
type="App::PropertyEnumeration",
|
||||||
|
name="VectorScaleMode",
|
||||||
|
group="Scale",
|
||||||
|
doc="If the scale data is a vector this property decides if the glyph is scaled by vector magnitude or by the individual components",
|
||||||
|
value=["Not a vector"],
|
||||||
|
),
|
||||||
|
_PropHelper(
|
||||||
|
type="App::PropertyFloatConstraint",
|
||||||
|
name="ScaleFactor",
|
||||||
|
group="Scale",
|
||||||
|
doc="A constant multiplier the glyphs are scaled with",
|
||||||
|
value=(1, 0, 1e12, 1e-12),
|
||||||
|
),
|
||||||
|
_PropHelper(
|
||||||
|
type="App::PropertyEnumeration",
|
||||||
|
name="MaskMode",
|
||||||
|
group="Masking",
|
||||||
|
doc="Which vertices are used as glyph locations",
|
||||||
|
value=["Use All", "Every Nth", "Uniform Samping"],
|
||||||
|
),
|
||||||
|
_PropHelper(
|
||||||
|
type="App::PropertyIntegerConstraint",
|
||||||
|
name="Stride",
|
||||||
|
group="Masking",
|
||||||
|
doc='Define the stride for "Every Nth" masking mode',
|
||||||
|
value=(2, 1, 999999999, 1),
|
||||||
|
),
|
||||||
|
_PropHelper(
|
||||||
|
type="App::PropertyIntegerConstraint",
|
||||||
|
name="MaxNumber",
|
||||||
|
group="Masking",
|
||||||
|
doc='Defines the maximal number of vertices used for "Uniform Sampling" masking mode',
|
||||||
|
value=(1000, 1, 999999999, 1),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
return prop
|
||||||
|
|
||||||
|
def __setupMaskingFilter(self, obj, masking):
|
||||||
|
|
||||||
|
if obj.MaskMode == "Use All":
|
||||||
|
masking.RandomModeOff()
|
||||||
|
masking.SetOnRatio(1)
|
||||||
|
masking.SetMaximumNumberOfPoints(int(1e10))
|
||||||
|
elif obj.MaskMode == "Every Nth":
|
||||||
|
masking.RandomModeOff()
|
||||||
|
masking.SetOnRatio(obj.Stride)
|
||||||
|
masking.SetMaximumNumberOfPoints(int(1e10))
|
||||||
|
else:
|
||||||
|
masking.SetOnRatio(1)
|
||||||
|
masking.SetMaximumNumberOfPoints(obj.MaxNumber)
|
||||||
|
masking.RandomModeOn()
|
||||||
|
|
||||||
|
def __setupGlyphFilter(self, obj, glyph):
|
||||||
|
|
||||||
|
# scaling
|
||||||
|
if obj.ScaleData != "None":
|
||||||
|
|
||||||
|
glyph.ScalingOn()
|
||||||
|
if obj.ScaleData in obj.getInputVectorFields():
|
||||||
|
|
||||||
|
# make sure the vector mode is set correctly
|
||||||
|
if obj.VectorScaleMode == "Not a vector":
|
||||||
|
obj.VectorScaleMode = ["Scale by magnitude", "Scale by components"]
|
||||||
|
obj.VectorScaleMode = "Scale by magnitude"
|
||||||
|
|
||||||
|
if obj.VectorScaleMode == "Scale by magnitude":
|
||||||
|
glyph.SetScaleModeToScaleByVector()
|
||||||
|
else:
|
||||||
|
glyph.SetScaleModeToScaleByVectorComponents()
|
||||||
|
|
||||||
|
glyph.SetInputArrayToProcess(2, 0, 0, 0, obj.ScaleData)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# scalar scaling mode
|
||||||
|
if obj.VectorScaleMode != "Not a vector":
|
||||||
|
obj.VectorScaleMode = ["Not a vector"]
|
||||||
|
|
||||||
|
glyph.SetInputArrayToProcess(2, 0, 0, 0, obj.ScaleData)
|
||||||
|
glyph.SetScaleModeToScaleByScalar()
|
||||||
|
else:
|
||||||
|
glyph.ScalingOff()
|
||||||
|
|
||||||
|
glyph.SetScaleFactor(obj.ScaleFactor)
|
||||||
|
|
||||||
|
# Orientation
|
||||||
|
if obj.OrientationData != "None":
|
||||||
|
glyph.OrientOn()
|
||||||
|
glyph.SetInputArrayToProcess(1, 0, 0, 0, obj.OrientationData)
|
||||||
|
else:
|
||||||
|
glyph.OrientOff()
|
||||||
|
|
||||||
|
def __setupFilterPipeline(self, obj):
|
||||||
|
|
||||||
|
# store of all algorithms for later access
|
||||||
|
# its map filter_name : [source, mask, glyph]
|
||||||
|
self._algorithms = {}
|
||||||
|
|
||||||
|
# create all vtkalgorithm combinations and set them as filter pipeline
|
||||||
|
sources = {"Arrow": vtkSources.vtkArrowSource, "Cube": vtkSources.vtkCubeSource}
|
||||||
|
|
||||||
|
for source_name in sources:
|
||||||
|
|
||||||
|
source = sources[source_name]()
|
||||||
|
|
||||||
|
masking = vtkMaskPoints()
|
||||||
|
self.__setupMaskingFilter(obj, masking)
|
||||||
|
|
||||||
|
glyph = vtkGlyph3D()
|
||||||
|
glyph.SetSourceConnection(source.GetOutputPort(0))
|
||||||
|
glyph.SetInputConnection(masking.GetOutputPort(0))
|
||||||
|
self.__setupGlyphFilter(obj, glyph)
|
||||||
|
|
||||||
|
self._algorithms[source_name] = [source, masking, glyph]
|
||||||
|
obj.addFilterPipeline(source_name, masking, glyph)
|
||||||
|
|
||||||
|
obj.setActiveFilterPipeline(obj.Glyph)
|
||||||
|
|
||||||
|
def onDocumentRestored(self, obj):
|
||||||
|
# resetup the pipeline
|
||||||
|
self.__setupFilterPipeline(obj)
|
||||||
|
|
||||||
|
def execute(self, obj):
|
||||||
|
# we check what new inputs
|
||||||
|
|
||||||
|
vector_fields = obj.getInputVectorFields()
|
||||||
|
all_fields = vector_fields + obj.getInputScalarFields()
|
||||||
|
|
||||||
|
vector_fields.sort()
|
||||||
|
all_fields.sort()
|
||||||
|
|
||||||
|
current_orient = obj.OrientationData
|
||||||
|
enumeration = ["None"] + vector_fields
|
||||||
|
obj.OrientationData = enumeration
|
||||||
|
if current_orient in enumeration:
|
||||||
|
obj.OrientationData = current_orient
|
||||||
|
|
||||||
|
current_scale = obj.ScaleData
|
||||||
|
enumeration = ["None"] + all_fields
|
||||||
|
obj.ScaleData = enumeration
|
||||||
|
if current_scale in enumeration:
|
||||||
|
obj.ScaleData = current_scale
|
||||||
|
|
||||||
|
# make sure parent class execute is called!
|
||||||
|
return False
|
||||||
|
|
||||||
|
def onChanged(self, obj, prop):
|
||||||
|
|
||||||
|
# check if we are setup already
|
||||||
|
if not hasattr(self, "_algorithms"):
|
||||||
|
return
|
||||||
|
|
||||||
|
if prop == "Glyph":
|
||||||
|
obj.setActiveFilterPipeline(obj.Glyph)
|
||||||
|
|
||||||
|
if prop == "MaskMode":
|
||||||
|
for filter in self._algorithms:
|
||||||
|
masking = self._algorithms[filter][1]
|
||||||
|
self.__setupMaskingFilter(obj, masking)
|
||||||
|
|
||||||
|
if prop == "Stride":
|
||||||
|
# if mode is use all stride setting needs to stay at one
|
||||||
|
if obj.MaskMode == "Every Nth":
|
||||||
|
for filter in self._algorithms:
|
||||||
|
masking = self._algorithms[filter][1]
|
||||||
|
masking.SetOnRatio(obj.Stride)
|
||||||
|
|
||||||
|
if prop == "MaxNumber":
|
||||||
|
if obj.MaskMode == "Uniform Sampling":
|
||||||
|
for filter in self._algorithms:
|
||||||
|
masking = self._algorithms[filter][1]
|
||||||
|
masking.SetMaximumNumberOfPoints(obj.MaxNumber)
|
||||||
|
|
||||||
|
if prop == "OrientationData" or prop == "ScaleData":
|
||||||
|
for filter in self._algorithms:
|
||||||
|
glyph = self._algorithms[filter][2]
|
||||||
|
self.__setupGlyphFilter(obj, glyph)
|
||||||
|
|
||||||
|
if prop == "ScaleFactor":
|
||||||
|
for filter in self._algorithms:
|
||||||
|
glyph = self._algorithms[filter][2]
|
||||||
|
glyph.SetScaleFactor(obj.ScaleFactor)
|
||||||
209
src/Mod/Fem/femtaskpanels/task_post_glyphfilter.py
Normal file
209
src/Mod/Fem/femtaskpanels/task_post_glyphfilter.py
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
# ***************************************************************************
|
||||||
|
# * 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 glyph filter task panel for the document object"
|
||||||
|
__author__ = "Stefan Tröger"
|
||||||
|
__url__ = "https://www.freecad.org"
|
||||||
|
|
||||||
|
## @package task_post_glyphfilter
|
||||||
|
# \ingroup FEM
|
||||||
|
# \brief task panel for post glyph filter object
|
||||||
|
|
||||||
|
from PySide import QtCore, QtGui
|
||||||
|
|
||||||
|
import FreeCAD
|
||||||
|
import FreeCADGui
|
||||||
|
|
||||||
|
from femguiutils import selection_widgets
|
||||||
|
from . import base_femtaskpanel
|
||||||
|
|
||||||
|
|
||||||
|
class _TaskPanel(base_femtaskpanel._BaseTaskPanel):
|
||||||
|
"""
|
||||||
|
The TaskPanel for editing properties of glyph filter
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, vobj):
|
||||||
|
super().__init__(vobj.Object)
|
||||||
|
|
||||||
|
# glyph parameter widget
|
||||||
|
self.widget = FreeCADGui.PySideUic.loadUi(
|
||||||
|
FreeCAD.getHomePath() + "Mod/Fem/Resources/ui/TaskPostGlyph.ui"
|
||||||
|
)
|
||||||
|
self.widget.setWindowIcon(FreeCADGui.getIcon(":/icons/FEM_PostFilterGlyph.svg"))
|
||||||
|
self.__init_widget()
|
||||||
|
|
||||||
|
# form made from param and selection widget
|
||||||
|
self.form = [self.widget, vobj.createDisplayTaskWidget()]
|
||||||
|
|
||||||
|
# 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()
|
||||||
|
|
||||||
|
def accept(self):
|
||||||
|
# self.obj.CharacteristicLength = self.elelen
|
||||||
|
# self.obj.References = self.selection_widget.references
|
||||||
|
# self.selection_widget.finish_selection()
|
||||||
|
return super().accept()
|
||||||
|
|
||||||
|
def reject(self):
|
||||||
|
# self.selection_widget.finish_selection()
|
||||||
|
return super().reject()
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
# Setup functions
|
||||||
|
# ###############
|
||||||
|
|
||||||
|
def __init_widget(self):
|
||||||
|
|
||||||
|
# set current values to ui
|
||||||
|
self._enumPropertyToCombobox(self.obj, "Glyph", self.widget.FormComboBox)
|
||||||
|
self._enumPropertyToCombobox(self.obj, "OrientationData", self.widget.OrientationComboBox)
|
||||||
|
self._enumPropertyToCombobox(self.obj, "ScaleData", self.widget.ScaleComboBox)
|
||||||
|
self._enumPropertyToCombobox(self.obj, "VectorScaleMode", self.widget.VectorModeComboBox)
|
||||||
|
self._enumPropertyToCombobox(self.obj, "MaskMode", self.widget.MaskModeComboBox)
|
||||||
|
|
||||||
|
self.widget.ScaleFactorBox.setValue(self.obj.ScaleFactor)
|
||||||
|
self.__slide_min = self.obj.ScaleFactor * 0.5
|
||||||
|
self.__slide_max = self.obj.ScaleFactor * 1.5
|
||||||
|
self.widget.ScaleSlider.setValue(50)
|
||||||
|
self.widget.StrideBox.setValue(self.obj.Stride)
|
||||||
|
self.widget.MaxBox.setValue(self.obj.MaxNumber)
|
||||||
|
self.__update_scaling_ui()
|
||||||
|
self.__update_masking_ui()
|
||||||
|
|
||||||
|
# connect all signals
|
||||||
|
self.widget.FormComboBox.currentTextChanged.connect(self._form_changed)
|
||||||
|
self.widget.OrientationComboBox.currentTextChanged.connect(self._orientation_changed)
|
||||||
|
self.widget.ScaleComboBox.currentTextChanged.connect(self._scale_data_changed)
|
||||||
|
self.widget.VectorModeComboBox.currentTextChanged.connect(self._scale_vector_mode_changed)
|
||||||
|
self.widget.ScaleFactorBox.valueChanged.connect(self._scale_factor_changed)
|
||||||
|
self.widget.ScaleSlider.valueChanged.connect(self._scale_slider_changed)
|
||||||
|
self.widget.MaskModeComboBox.currentTextChanged.connect(self._mask_mode_changed)
|
||||||
|
self.widget.StrideBox.valueChanged.connect(self._stride_changed)
|
||||||
|
self.widget.MaxBox.valueChanged.connect(self._max_number_changed)
|
||||||
|
|
||||||
|
def __update_scaling_ui(self):
|
||||||
|
enabled = self.widget.ScaleComboBox.currentIndex() != 0
|
||||||
|
self.widget.VectorModeComboBox.setEnabled(enabled)
|
||||||
|
self.widget.ScaleFactorBox.setEnabled(enabled)
|
||||||
|
self.widget.ScaleSlider.setEnabled(enabled)
|
||||||
|
|
||||||
|
def __update_masking_ui(self):
|
||||||
|
enabled = self.widget.MaskModeComboBox.currentIndex() != 0
|
||||||
|
self.widget.StrideBox.setEnabled(enabled)
|
||||||
|
self.widget.MaxBox.setEnabled(enabled)
|
||||||
|
|
||||||
|
# callbacks and logic
|
||||||
|
# ###################
|
||||||
|
|
||||||
|
def _form_changed(self, value):
|
||||||
|
self.obj.Glyph = value
|
||||||
|
self._recompute()
|
||||||
|
|
||||||
|
def _orientation_changed(self, value):
|
||||||
|
self.obj.OrientationData = value
|
||||||
|
self._recompute()
|
||||||
|
|
||||||
|
def _scale_data_changed(self, value):
|
||||||
|
self.obj.ScaleData = value
|
||||||
|
self._enumPropertyToCombobox(self.obj, "VectorScaleMode", self.widget.VectorModeComboBox)
|
||||||
|
self.__update_scaling_ui()
|
||||||
|
self._recompute()
|
||||||
|
|
||||||
|
def _scale_vector_mode_changed(self, value):
|
||||||
|
self.obj.VectorScaleMode = value
|
||||||
|
self._recompute()
|
||||||
|
|
||||||
|
def _scale_factor_changed(self, value):
|
||||||
|
|
||||||
|
# set slider
|
||||||
|
self.__slide_min = value * 0.5
|
||||||
|
self.__slide_max = value * 1.5
|
||||||
|
slider_value = (value - self.__slide_min) / (self.__slide_max - self.__slide_min) * 100.0
|
||||||
|
self.widget.ScaleSlider.blockSignals(True)
|
||||||
|
self.widget.ScaleSlider.setValue(slider_value)
|
||||||
|
self.widget.ScaleSlider.blockSignals(False)
|
||||||
|
|
||||||
|
self.obj.ScaleFactor = value
|
||||||
|
self._recompute()
|
||||||
|
|
||||||
|
def _scale_slider_changed(self, value):
|
||||||
|
|
||||||
|
# calculate value
|
||||||
|
# ( max - min )
|
||||||
|
# factor = min + ( slider_value x ------------- )
|
||||||
|
# 100
|
||||||
|
#
|
||||||
|
f = self.__slide_min + (value * (self.__slide_max - self.__slide_min) / 100)
|
||||||
|
|
||||||
|
# sync factor spin box
|
||||||
|
self.widget.ScaleFactorBox.blockSignals(True)
|
||||||
|
self.widget.ScaleFactorBox.setValue(f)
|
||||||
|
self.widget.ScaleFactorBox.blockSignals(False)
|
||||||
|
|
||||||
|
# set value
|
||||||
|
self.obj.ScaleFactor = f
|
||||||
|
self._recompute()
|
||||||
|
|
||||||
|
def _mask_mode_changed(self, value):
|
||||||
|
self.obj.MaskMode = value
|
||||||
|
self.__update_masking_ui()
|
||||||
|
self._recompute()
|
||||||
|
|
||||||
|
def _stride_changed(self, value):
|
||||||
|
self.obj.Stride = value
|
||||||
|
self._recompute()
|
||||||
|
|
||||||
|
def _max_number_changed(self, value):
|
||||||
|
self.obj.MaxNumber = value
|
||||||
|
self._recompute()
|
||||||
@@ -87,7 +87,7 @@ def get_defmake_count(fem_vtk_post=True):
|
|||||||
# we are not able to create VTK post objects
|
# we are not able to create VTK post objects
|
||||||
new_lines = []
|
new_lines = []
|
||||||
for li in lines_defmake:
|
for li in lines_defmake:
|
||||||
if "PostVtk" not in li:
|
if "Post" not in li:
|
||||||
new_lines.append(li)
|
new_lines.append(li)
|
||||||
lines_defmake = new_lines
|
lines_defmake = new_lines
|
||||||
return len(lines_defmake)
|
return len(lines_defmake)
|
||||||
|
|||||||
@@ -81,9 +81,13 @@ class TestObjectCreate(unittest.TestCase):
|
|||||||
# result children: mesh result --> 1
|
# result children: mesh result --> 1
|
||||||
# post pipeline children: region, scalar, cut, wrap --> 5
|
# post pipeline children: region, scalar, cut, wrap --> 5
|
||||||
# analysis itself is not in analysis group --> 1
|
# analysis itself is not in analysis group --> 1
|
||||||
# thus: -20
|
# vtk python post objects: glyph --> 1
|
||||||
|
|
||||||
self.assertEqual(len(doc.Analysis.Group), count_defmake - 20)
|
subtraction = 20
|
||||||
|
if "BUILD_FEM_VTK_PYTHON" in FreeCAD.__cmake__:
|
||||||
|
subtraction += 1
|
||||||
|
|
||||||
|
self.assertEqual(len(doc.Analysis.Group), count_defmake - subtraction)
|
||||||
self.assertEqual(len(doc.Objects), count_defmake)
|
self.assertEqual(len(doc.Objects), count_defmake)
|
||||||
|
|
||||||
fcc_print(
|
fcc_print(
|
||||||
@@ -1154,6 +1158,8 @@ def create_all_fem_objects_doc(doc):
|
|||||||
ObjectsFem.makePostVtkFilterCutFunction(doc, vres)
|
ObjectsFem.makePostVtkFilterCutFunction(doc, vres)
|
||||||
ObjectsFem.makePostVtkFilterWarp(doc, vres)
|
ObjectsFem.makePostVtkFilterWarp(doc, vres)
|
||||||
ObjectsFem.makePostVtkFilterContours(doc, vres)
|
ObjectsFem.makePostVtkFilterContours(doc, vres)
|
||||||
|
if "BUILD_FEM_VTK_PYTHON" in FreeCAD.__cmake__:
|
||||||
|
ObjectsFem.makePostFilterGlyph(doc, vres)
|
||||||
|
|
||||||
analysis.addObject(ObjectsFem.makeSolverCalculiXCcxTools(doc))
|
analysis.addObject(ObjectsFem.makeSolverCalculiXCcxTools(doc))
|
||||||
analysis.addObject(ObjectsFem.makeSolverCalculiX(doc))
|
analysis.addObject(ObjectsFem.makeSolverCalculiX(doc))
|
||||||
|
|||||||
86
src/Mod/Fem/femviewprovider/view_post_glyphfilter.py
Normal file
86
src/Mod/Fem/femviewprovider/view_post_glyphfilter.py
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
# ***************************************************************************
|
||||||
|
# * 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 glyph filter ViewProvider for the document object"
|
||||||
|
__author__ = "Stefan Tröger"
|
||||||
|
__url__ = "https://www.freecad.org"
|
||||||
|
|
||||||
|
## @package view_post_glyphfilter
|
||||||
|
# \ingroup FEM
|
||||||
|
# \brief view provider for post glyph filter object
|
||||||
|
|
||||||
|
import FreeCAD
|
||||||
|
import FreeCADGui
|
||||||
|
|
||||||
|
import FemGui
|
||||||
|
from PySide import QtGui
|
||||||
|
from femtaskpanels import task_post_glyphfilter
|
||||||
|
|
||||||
|
|
||||||
|
class VPPostGlyphFilter:
|
||||||
|
"""
|
||||||
|
A View Provider for the PostGlyphFilter object
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, vobj):
|
||||||
|
vobj.Proxy = self
|
||||||
|
|
||||||
|
def getIcon(self):
|
||||||
|
return ":/icons/FEM_PostFilterGlyph.svg"
|
||||||
|
|
||||||
|
def getDisplayModes(self, obj):
|
||||||
|
# Mandatory, as the ViewProviderPostFilterPython does not add any
|
||||||
|
# display modes. We can choose here any that is supported by it:
|
||||||
|
# "Outline", "Nodes", "Surface", "Surface with Edges",
|
||||||
|
# "Wireframe", "Wireframe (surface only)", "Nodes (surface only)"
|
||||||
|
|
||||||
|
# only surface makes sense for the glyphs
|
||||||
|
return ["Surface"]
|
||||||
|
|
||||||
|
def setDisplayMode(self, mode):
|
||||||
|
# the post object viewprovider implements the different display modes
|
||||||
|
# via vtk filter, not via masking modes. Hence we need to make sure
|
||||||
|
# to always stay in the "Default" masking mode, no matter the display mode
|
||||||
|
return "Default"
|
||||||
|
|
||||||
|
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