Files
create/src/Gui/ViewProviderPyImp.cpp
2025-11-11 13:49:01 +01:00

868 lines
24 KiB
C++

/***************************************************************************
* Copyright (c) 2007 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 <Inventor/SbRotation.h>
#include <Inventor/SoFullPath.h>
#include <Inventor/details/SoDetail.h>
#include <Inventor/nodes/SoSeparator.h>
#include <Inventor/nodes/SoSwitch.h>
#include <QByteArray>
#include <QDataStream>
#include <Base/BoundBoxPy.h>
#include <Base/PyWrapParseTupleAndKeywords.h>
#include "PythonWrapper.h"
#include "SoFCDB.h"
// inclusion of the generated files (generated out of ViewProviderPy.pyi)
#include <Gui/ViewProviderPy.h>
#include <Gui/ViewProviderPy.cpp>
#include <Gui/View3DPy.h>
#include <Gui/View3DInventor.h>
#include <Base/Interpreter.h>
#include <Base/Matrix.h>
#include <Base/MatrixPy.h>
#include <Base/Placement.h>
#include <Base/PlacementPy.h>
#include <App/Document.h>
#include <App/DocumentObject.h>
#include <App/DocumentObjectPy.h>
using namespace Gui;
// returns a string which represent the object e.g. when printed in python
std::string ViewProviderPy::representation() const
{
return "<View provider object>";
}
PyObject* ViewProviderPy::addProperty(PyObject* args, PyObject* kwd)
{
char *sType, *sName = nullptr, *sGroup = nullptr, *sDoc = nullptr;
short attr = 0;
PyObject *ro = Py_False, *hd = Py_False, *lk = Py_False;
PyObject* enumVals = nullptr;
const std::array<const char*, 10> kwlist {
"type",
"name",
"group",
"doc",
"attr",
"read_only",
"hidden",
"locked",
"enum_vals",
nullptr
};
if (!Base::Wrapped_ParseTupleAndKeywords(
args,
kwd,
"ss|sethO!O!O!O",
kwlist,
&sType,
&sName,
&sGroup,
"utf-8",
&sDoc,
&attr,
&PyBool_Type,
&ro,
&PyBool_Type,
&hd,
&PyBool_Type,
&lk,
&enumVals
)) {
return nullptr;
}
App::Property* prop = getViewProviderPtr()->addDynamicProperty(
sType,
sName,
sGroup,
sDoc,
attr,
Base::asBoolean(ro),
Base::asBoolean(hd)
);
prop->setStatus(App::Property::LockDynamic, Base::asBoolean(lk));
// enum support
auto* propEnum = freecad_cast<App::PropertyEnumeration*>(prop);
if (propEnum && enumVals) {
propEnum->setPyObject(enumVals);
}
return Py::new_reference_to(this);
}
PyObject* ViewProviderPy::removeProperty(PyObject* args)
{
char* sName;
if (!PyArg_ParseTuple(args, "s", &sName)) {
return nullptr;
}
try {
bool ok = getViewProviderPtr()->removeDynamicProperty(sName);
return Py_BuildValue("O", (ok ? Py_True : Py_False));
}
catch (const Base::Exception& e) {
throw Py::RuntimeError(e.what());
}
}
PyObject* ViewProviderPy::supportedProperties(PyObject* args)
{
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
}
std::vector<Base::Type> ary;
Base::Type::getAllDerivedFrom(App::Property::getClassTypeId(), ary);
Py::List res;
for (auto& it : ary) {
auto data = static_cast<Base::BaseClass*>(it.createInstance());
if (data) {
delete data;
res.append(Py::String(it.getName()));
}
}
return Py::new_reference_to(res);
}
PyObject* ViewProviderPy::show(PyObject* args)
{
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
}
PY_TRY
{
getViewProviderPtr()->show();
Py_Return;
}
PY_CATCH;
}
PyObject* ViewProviderPy::hide(PyObject* args)
{
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
}
PY_TRY
{
getViewProviderPtr()->hide();
Py_Return;
}
PY_CATCH;
}
PyObject* ViewProviderPy::isVisible(PyObject* args)
{
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
}
PY_TRY
{
return Py::new_reference_to(Py::Boolean(getViewProviderPtr()->isShow()));
}
PY_CATCH;
}
PyObject* ViewProviderPy::canDragObject(PyObject* args)
{
PyObject* obj = Py_None;
if (!PyArg_ParseTuple(args, "|O", &obj)) {
return nullptr;
}
PY_TRY
{
Base::PyTypeCheck(&obj, &App::DocumentObjectPy::Type);
bool ret;
if (!obj) {
ret = getViewProviderPtr()->canDragObjects();
}
else {
ret = getViewProviderPtr()->canDragObject(
static_cast<App::DocumentObjectPy*>(obj)->getDocumentObjectPtr()
);
}
return Py::new_reference_to(Py::Boolean(ret));
}
PY_CATCH;
}
PyObject* ViewProviderPy::canDropObject(PyObject* args, PyObject* kw)
{
PyObject* obj = Py_None;
PyObject* owner = Py_None;
PyObject* pyElements = Py_None;
const char* subname = nullptr;
static const std::array<const char*, 5> kwlist {"obj", "owner", "subname", "elem", nullptr};
if (!Base::Wrapped_ParseTupleAndKeywords(args, kw, "|OOsO", kwlist, &obj, &owner, &subname, &pyElements)) {
return nullptr;
}
PY_TRY
{
Base::PyTypeCheck(
&obj,
&App::DocumentObjectPy::Type,
"expecting 'obj' to be of type App.DocumentObject or None"
);
Base::PyTypeCheck(
&owner,
&App::DocumentObjectPy::Type,
"expecting 'owner' to be of type App.DocumentObject or None"
);
Base::PyTypeCheck(&pyElements, PySequence_Check, "expecting 'elem' to be sequence or None");
bool ret;
App::DocumentObject* pcObject;
App::DocumentObject* pcOwner = nullptr;
App::PropertyStringList elements;
if (!obj && (owner || pyElements || subname)) {
PyErr_SetString(
PyExc_ValueError,
"'obj' must be specified if 'owner', 'subname' or 'elem' is given"
);
return nullptr;
}
if (!obj) {
ret = getViewProviderPtr()->canDropObjects();
return Py::new_reference_to(Py::Boolean(ret));
}
pcObject = static_cast<App::DocumentObjectPy*>(obj)->getDocumentObjectPtr();
if (owner) {
pcOwner = static_cast<App::DocumentObjectPy*>(owner)->getDocumentObjectPtr();
}
if (pyElements) {
try {
elements.setPyObject(pyElements);
}
catch (...) {
PyErr_SetString(PyExc_TypeError, "'elem' must be a sequence of strings");
return nullptr;
}
}
ret = getViewProviderPtr()->canDropObjectEx(pcObject, pcOwner, subname, elements.getValues());
return Py::new_reference_to(Py::Boolean(ret));
}
PY_CATCH;
}
PyObject* ViewProviderPy::canDragAndDropObject(PyObject* args)
{
PyObject* obj;
if (!PyArg_ParseTuple(args, "O!", &App::DocumentObjectPy::Type, &obj)) {
return nullptr;
}
PY_TRY
{
bool ret = getViewProviderPtr()->canDragAndDropObject(
static_cast<App::DocumentObjectPy*>(obj)->getDocumentObjectPtr()
);
return Py::new_reference_to(Py::Boolean(ret));
}
PY_CATCH;
}
PyObject* ViewProviderPy::dropObject(PyObject* args, PyObject* kw)
{
PyObject* obj;
PyObject* owner = Py_None;
PyObject* pyElements = Py_None;
const char* subname = nullptr;
static const std::array<const char*, 5> kwlist {"obj", "owner", "subname", "elem", nullptr};
if (!Base::Wrapped_ParseTupleAndKeywords(
args,
kw,
"O!|OsO",
kwlist,
&App::DocumentObjectPy::Type,
&obj,
&owner,
&subname,
&pyElements
)) {
return nullptr;
}
PY_TRY
{
Base::PyTypeCheck(
&owner,
&App::DocumentObjectPy::Type,
"expecting 'owner' to be of type App.DocumentObject or None"
);
Base::PyTypeCheck(&pyElements, PySequence_Check, "expecting 'elem' to be sequence or None");
auto pcObject = static_cast<App::DocumentObjectPy*>(obj)->getDocumentObjectPtr();
App::DocumentObject* pcOwner = nullptr;
App::PropertyStringList elements;
if (owner) {
pcOwner = static_cast<App::DocumentObjectPy*>(owner)->getDocumentObjectPtr();
}
if (pyElements) {
try {
elements.setPyObject(pyElements);
}
catch (...) {
PyErr_SetString(PyExc_TypeError, "'elem' must be a sequence of strings");
return nullptr;
}
}
auto ret = getViewProviderPtr()->dropObjectEx(pcObject, pcOwner, subname, elements.getValues());
return Py::new_reference_to(Py::String(ret));
}
PY_CATCH;
}
PyObject* ViewProviderPy::dragObject(PyObject* args)
{
PyObject* obj;
if (!PyArg_ParseTuple(args, "O!", &App::DocumentObjectPy::Type, &obj)) {
return nullptr;
}
PY_TRY
{
getViewProviderPtr()->dragObject(
static_cast<App::DocumentObjectPy*>(obj)->getDocumentObjectPtr()
);
Py_Return;
}
PY_CATCH;
}
PyObject* ViewProviderPy::replaceObject(PyObject* args)
{
PyObject* oldObj;
PyObject* newObj;
if (!PyArg_ParseTuple(
args,
"O!O!",
&App::DocumentObjectPy::Type,
&oldObj,
&App::DocumentObjectPy::Type,
&newObj
)) {
return nullptr;
}
PY_TRY
{
int ret = getViewProviderPtr()->replaceObject(
static_cast<App::DocumentObjectPy*>(oldObj)->getDocumentObjectPtr(),
static_cast<App::DocumentObjectPy*>(newObj)->getDocumentObjectPtr()
);
return Py::new_reference_to(Py::Long(ret));
}
PY_CATCH;
}
PyObject* ViewProviderPy::addDisplayMode(PyObject* args)
{
char* mode;
PyObject* obj;
if (!PyArg_ParseTuple(args, "Os", &obj, &mode)) {
return nullptr;
}
void* ptr = nullptr;
try {
Base::Interpreter().convertSWIGPointerObj("pivy.coin", "_p_SoNode", obj, &ptr, 0);
if (!ptr) {
PyErr_SetString(PyExc_RuntimeError, "Conversion of coin.SoNode failed");
return nullptr;
}
}
catch (const Base::Exception& e) {
PyErr_SetString(PyExc_RuntimeError, e.what());
return nullptr;
}
PY_TRY
{
auto node = static_cast<SoNode*>(ptr);
getViewProviderPtr()->addDisplayMaskMode(node, mode);
Py_Return;
}
PY_CATCH;
}
PyObject* ViewProviderPy::listDisplayModes(PyObject* args)
{
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
}
PY_TRY
{
std::vector<std::string> modes = getViewProviderPtr()->getDisplayModes();
PyObject* pyList = PyList_New(modes.size());
int i = 0;
for (const auto& mode : modes) {
PyObject* str = PyUnicode_FromString(mode.c_str());
PyList_SetItem(pyList, i++, str);
}
return pyList;
}
PY_CATCH;
}
PyObject* ViewProviderPy::toString(PyObject* args)
{
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
}
PY_TRY
{
std::string buffer = getViewProviderPtr()->toString();
return Py::new_reference_to(Py::String(buffer));
}
PY_CATCH;
}
PyObject* ViewProviderPy::setTransformation(PyObject* args)
{
PyObject* p;
Base::Matrix4D mat;
if (PyArg_ParseTuple(args, "O!", &(Base::MatrixPy::Type), &p)) {
mat = *static_cast<Base::MatrixPy*>(p)->getMatrixPtr();
getViewProviderPtr()->setTransformation(mat);
Py_Return;
}
PyErr_Clear();
if (PyArg_ParseTuple(args, "O!", &(Base::PlacementPy::Type), &p)) {
auto plc = static_cast<Base::PlacementPy*>(p);
getViewProviderPtr()->setTransformation(plc->getPlacementPtr()->toMatrix());
Py_Return;
}
PyErr_SetString(PyExc_TypeError, "The transformation must be a Base.Matrix or a Base.Placement");
return nullptr;
}
PyObject* ViewProviderPy::claimChildren(PyObject* args) const
{
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
}
std::vector<App::DocumentObject*> children = this->getViewProviderPtr()->claimChildren();
Py::List ret;
for (auto* child : children) {
if (child) {
ret.append(Py::asObject(child->getPyObject()));
}
else {
ret.append(Py::None());
}
}
return Py::new_reference_to(ret);
}
PyObject* ViewProviderPy::claimChildrenRecursive(PyObject* args) const
{
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
}
std::vector<App::DocumentObject*> children = this->getViewProviderPtr()->claimChildrenRecursive();
Py::List ret;
for (auto* child : children) {
if (child) {
ret.append(Py::asObject(child->getPyObject()));
}
else {
ret.append(Py::None());
}
}
return Py::new_reference_to(ret);
}
PyObject* ViewProviderPy::partialRender(PyObject* args)
{
PyObject* value = Py_None;
PyObject* clear = Py_False;
if (!PyArg_ParseTuple(args, "|OO!", &value, &PyBool_Type, &clear)) {
return nullptr;
}
std::vector<std::string> values;
if (value != Py_None) {
std::vector<Py::Object> pylist;
if (PyList_Check(value) || PyTuple_Check(value)) {
Py::Sequence seq(value);
for (const auto& it : seq) {
pylist.emplace_back(it);
}
}
else {
pylist.emplace_back(value);
}
values.reserve(pylist.size());
for (const auto& it : pylist) {
if (it.isString()) {
values.push_back(Py::String(it));
}
else {
std::string error = std::string("type must be str");
error += " not, ";
error += it.ptr()->ob_type->tp_name;
throw Py::TypeError(error);
}
}
}
Py::Long ret(getViewProviderPtr()->partialRender(values, Base::asBoolean(clear)));
return Py::new_reference_to(ret);
}
PyObject* ViewProviderPy::getElementColors(PyObject* args)
{
const char* element = nullptr;
if (!PyArg_ParseTuple(args, "|s", &element)) {
return nullptr;
}
Py::Dict dict;
for (auto& v : getViewProviderPtr()->getElementColors(element)) {
auto& c = v.second;
dict.setItem(
Py::String(v.first),
Py::TupleN(Py::Float(c.r), Py::Float(c.g), Py::Float(c.b), Py::Float(c.a))
);
}
return Py::new_reference_to(dict);
}
PyObject* ViewProviderPy::setElementColors(PyObject* args)
{
PyObject* pyObj;
if (!PyArg_ParseTuple(args, "O", &pyObj)) {
return nullptr;
}
if (!PyDict_Check(pyObj)) {
throw Py::TypeError("Expect a dict");
}
std::map<std::string, Base::Color> colors;
Py::Dict dict(pyObj);
for (auto it = dict.begin(); it != dict.end(); ++it) {
const auto& value = *it;
if (!value.first.isString() || !value.second.isSequence()) {
throw Py::TypeError("Expect the dictionary to contain items of type elementName:(r,g,b,a)");
}
App::PropertyColor prop;
prop.setPyObject(value.second.ptr());
colors[value.first.as_string()] = prop.getValue();
}
getViewProviderPtr()->setElementColors(colors);
Py_Return;
}
PyObject* ViewProviderPy::getElementPicked(PyObject* args) const
{
PyObject* obj;
if (!PyArg_ParseTuple(args, "O", &obj)) {
return nullptr;
}
void* ptr = nullptr;
Base::Interpreter().convertSWIGPointerObj("pivy.coin", "_p_SoPickedPoint", obj, &ptr, 0);
auto pp = static_cast<SoPickedPoint*>(ptr);
if (!pp) {
throw Base::TypeError("type must be coin.SoPickedPoint");
}
std::string name;
if (!getViewProviderPtr()->getElementPicked(pp, name)) {
Py_Return;
}
return Py::new_reference_to(Py::String(name));
}
PyObject* ViewProviderPy::getDetailPath(PyObject* args) const
{
const char* sub;
PyObject* path;
PyObject* append = Py_True;
if (!PyArg_ParseTuple(args, "sO|O!", &sub, &path, &PyBool_Type, &append)) {
return nullptr;
}
void* ptr = nullptr;
Base::Interpreter().convertSWIGPointerObj("pivy.coin", "_p_SoPath", path, &ptr, 0);
auto pPath = static_cast<SoPath*>(ptr);
if (!pPath) {
throw Base::TypeError("'path' must be a coin.SoPath");
}
SoDetail* det = nullptr;
if (!getViewProviderPtr()->getDetailPath(sub, static_cast<SoFullPath*>(pPath), append, det)) {
delete det;
Py_Return;
}
if (!det) {
Py_Return;
}
return Base::Interpreter()
.createSWIGPointerObj("pivy.coin", "_p_SoDetail", static_cast<void*>(det), 0);
}
PyObject* ViewProviderPy::signalChangeIcon(PyObject* args) const
{
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
}
getViewProviderPtr()->signalChangeIcon();
Py_Return;
}
PyObject* ViewProviderPy::getBoundingBox(PyObject* args)
{
PyObject* transform = Py_True;
PyObject* pyView = nullptr;
const char* subname = nullptr;
if (!PyArg_ParseTuple(
args,
"|sO!O!",
&subname,
&PyBool_Type,
&transform,
View3DInventorPy::type_object(),
&pyView
)) {
return nullptr;
}
PY_TRY
{
View3DInventor* view = nullptr;
if (pyView) {
view = static_cast<View3DInventorPy*>(pyView)->getView3DInventorPtr();
}
auto bbox = getViewProviderPtr()->getBoundingBox(subname, Base::asBoolean(transform), view);
return new Base::BoundBoxPy(new Base::BoundBox3d(bbox));
}
PY_CATCH;
}
PyObject* ViewProviderPy::doubleClicked(PyObject* args)
{
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
}
PY_TRY
{
return Py::new_reference_to(Py::Boolean(getViewProviderPtr()->doubleClicked()));
}
PY_CATCH;
}
PyObject* ViewProviderPy::getCustomAttributes(const char* attr) const
{
// search for dynamic property
App::Property* prop = getViewProviderPtr()->getDynamicPropertyByName(attr);
if (prop) {
return prop->getPyObject();
}
else {
return nullptr;
}
}
int ViewProviderPy::setCustomAttributes(const char* attr, PyObject* value)
{
// search for dynamic property
try {
App::Property* prop = getViewProviderPtr()->getDynamicPropertyByName(attr);
if (prop) {
prop->setPyObject(value);
return 1;
}
return 0;
}
catch (Base::Exception& exc) {
PyErr_Format(PyExc_AttributeError, "Attribute (Name: %s) error: '%s' ", attr, exc.what());
return -1;
}
catch (...) {
PyErr_Format(PyExc_AttributeError, "Unknown error in attribute %s", attr);
return -1;
}
}
Py::Object ViewProviderPy::getAnnotation() const
{
try {
auto node = getViewProviderPtr()->getAnnotation();
PyObject* Ptr
= Base::Interpreter().createSWIGPointerObj("pivy.coin", "_p_SoSeparator", node, 1);
node->ref();
return Py::Object(Ptr, true);
}
catch (const Base::Exception& e) {
throw Py::RuntimeError(e.what());
}
}
void ViewProviderPy::setAnnotation(Py::Object)
{}
Py::Object ViewProviderPy::getRootNode() const
{
try {
SoSeparator* node = getViewProviderPtr()->getRoot();
PyObject* Ptr
= Base::Interpreter().createSWIGPointerObj("pivy.coin", "_p_SoSeparator", node, 1);
node->ref();
return Py::Object(Ptr, true);
}
catch (const Base::Exception& e) {
throw Py::RuntimeError(e.what());
}
}
void ViewProviderPy::setRootNode(Py::Object)
{}
Py::Object ViewProviderPy::getSwitchNode() const
{
try {
SoSwitch* node = getViewProviderPtr()->getModeSwitch();
PyObject* Ptr = Base::Interpreter().createSWIGPointerObj("pivy.coin", "_p_SoSwitch", node, 1);
node->ref();
return Py::Object(Ptr, true);
}
catch (const Base::Exception& e) {
throw Py::RuntimeError(e.what());
}
}
void ViewProviderPy::setSwitchNode(Py::Object)
{}
Py::String ViewProviderPy::getIV() const
{
std::string buf = Gui::SoFCDB::writeNodesToString(getViewProviderPtr()->getRoot());
return {buf};
}
Py::Object ViewProviderPy::getIcon() const
{
PythonWrapper wrap;
wrap.loadGuiModule();
wrap.loadWidgetsModule();
QIcon icon = getViewProviderPtr()->getIcon();
return wrap.fromQIcon(new QIcon(icon));
}
Py::Long ViewProviderPy::getDefaultMode() const
{
return Py::Long((long)getViewProviderPtr()->getDefaultMode());
}
void ViewProviderPy::setDefaultMode(Py::Long arg)
{
return getViewProviderPtr()->setDefaultMode(arg);
}
Py::Boolean ViewProviderPy::getCanRemoveChildrenFromRoot() const
{
return {getViewProviderPtr()->canRemoveChildrenFromRoot()};
}
Py::Boolean ViewProviderPy::getLinkVisibility() const
{
return {getViewProviderPtr()->isLinkVisible()};
}
void ViewProviderPy::setLinkVisibility(Py::Boolean arg)
{
getViewProviderPtr()->setLinkVisible(arg);
}
Py::String ViewProviderPy::getDropPrefix() const
{
return {getViewProviderPtr()->getDropPrefix()};
}
void ViewProviderPy::setToggleVisibility(Py::Object arg)
{
std::string val;
if (PyObject_HasAttrString(arg.ptr(), "value")) {
// we are dealing with the enum
val = Py::String(arg.getAttr("value"));
}
else {
// we are dealing with a string
val = Py::String(arg);
}
if (val == "CanToggleVisibility") {
getViewProviderPtr()->setToggleVisibility(
ViewProvider::ToggleVisibilityMode::CanToggleVisibility
);
}
else if (val == "NoToggleVisibility") {
getViewProviderPtr()->setToggleVisibility(
ViewProvider::ToggleVisibilityMode::NoToggleVisibility
);
}
else {
throw Py::ValueError(
"Invalid ToggleVisibility mode. Use 'CanToggleVisibility' or 'NoToggleVisibility'."
);
}
}
Py::Object ViewProviderPy::getToggleVisibility() const
{
bool canToggleVisibility = getViewProviderPtr()->canToggleVisibility();
return Py::String(canToggleVisibility ? "CanToggleVisibility" : "NoToggleVisibility");
}