Merge pull request #20549 from davesrocketshop/external_manager_pr
Material: Interface with an external module
This commit is contained in:
@@ -6,6 +6,10 @@ endif(MSVC)
|
||||
|
||||
add_definitions(-DYAML_CPP_STATIC_DEFINE)
|
||||
|
||||
if(BUILD_MATERIAL_EXTERNAL)
|
||||
add_definitions(-DBUILD_MATERIAL_EXTERNAL)
|
||||
endif(BUILD_MATERIAL_EXTERNAL)
|
||||
|
||||
include_directories(
|
||||
${CMAKE_BINARY_DIR}
|
||||
${CMAKE_BINARY_DIR}/src
|
||||
@@ -55,6 +59,11 @@ generate_from_py(MaterialProperty)
|
||||
generate_from_py(Model)
|
||||
generate_from_py(UUIDs)
|
||||
|
||||
SET(MaterialsAPI_Files
|
||||
MaterialAPI/__init__.py
|
||||
MaterialAPI/MaterialManagerExternal.py
|
||||
)
|
||||
|
||||
SET(Python_SRCS
|
||||
Exceptions.h
|
||||
Array2D.pyi
|
||||
@@ -126,6 +135,12 @@ SET(Materials_SRCS
|
||||
PyVariants.h
|
||||
trim.h
|
||||
)
|
||||
if(BUILD_MATERIAL_EXTERNAL)
|
||||
list(APPEND Materials_SRCS
|
||||
ExternalManager.cpp
|
||||
ExternalManager.h
|
||||
)
|
||||
endif(BUILD_MATERIAL_EXTERNAL)
|
||||
|
||||
if(FREECAD_USE_PCH)
|
||||
add_definitions(-D_PreComp_)
|
||||
@@ -143,3 +158,14 @@ SET_BIN_DIR(Materials Materials /Mod/Material)
|
||||
SET_PYTHON_PREFIX_SUFFIX(Materials)
|
||||
|
||||
INSTALL(TARGETS Materials DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
ADD_CUSTOM_TARGET(MaterialsAPILib ALL
|
||||
SOURCES ${MaterialsAPI_Files} ${Material_QRC_SRCS}
|
||||
)
|
||||
|
||||
fc_target_copy_resource(MaterialsAPILib
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_BINARY_DIR}/Mod/Material
|
||||
${MaterialsAPI_Files})
|
||||
|
||||
INSTALL(FILES ${MaterialsAPI_Files} DESTINATION Mod/Material/MaterialAPI)
|
||||
|
||||
786
src/Mod/Material/App/ExternalManager.cpp
Normal file
786
src/Mod/Material/App/ExternalManager.cpp
Normal file
@@ -0,0 +1,786 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2024 David Carter <dcarter@david.carter.ca> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD 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 *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
**************************************************************************/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
#ifndef _PreComp_
|
||||
#endif
|
||||
|
||||
#include <Python.h>
|
||||
#include <QMutex>
|
||||
#include <QMutexLocker>
|
||||
|
||||
#include <App/Application.h>
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Interpreter.h>
|
||||
#include <CXX/Objects.hxx>
|
||||
|
||||
#include "Exceptions.h"
|
||||
#include "ExternalManager.h"
|
||||
#include "MaterialLibrary.h"
|
||||
#include "MaterialLibraryPy.h"
|
||||
#include "MaterialPy.h"
|
||||
#include "ModelLibrary.h"
|
||||
#include "ModelPy.h"
|
||||
#include "MaterialFilterPy.h"
|
||||
#include "MaterialFilterOptionsPy.h"
|
||||
|
||||
|
||||
using namespace Materials;
|
||||
|
||||
/* TRANSLATOR Material::Materials */
|
||||
|
||||
ExternalManager* ExternalManager::_manager = nullptr;
|
||||
QMutex ExternalManager::_mutex;
|
||||
|
||||
ExternalManager::ExternalManager()
|
||||
: _instantiated(false)
|
||||
{
|
||||
_hGrp = App::GetApplication().GetParameterGroupByPath(
|
||||
"User parameter:BaseApp/Preferences/Mod/Material/ExternalInterface");
|
||||
_hGrp->Attach(this);
|
||||
|
||||
getConfiguration();
|
||||
}
|
||||
|
||||
ExternalManager::~ExternalManager()
|
||||
{
|
||||
_hGrp->Detach(this);
|
||||
}
|
||||
|
||||
void ExternalManager::OnChange(ParameterGrp::SubjectType& /*rCaller*/, ParameterGrp::MessageType Reason)
|
||||
{
|
||||
if (std::strncmp(Reason, "Current", 7) == 0) {
|
||||
if (_instantiated) {
|
||||
// The old manager object will be deleted when reconnecting
|
||||
_instantiated = false;
|
||||
}
|
||||
getConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalManager::getConfiguration()
|
||||
{
|
||||
// _hGrp = App::GetApplication().GetParameterGroupByPath(
|
||||
// "User parameter:BaseApp/Preferences/Mod/Material/ExternalInterface");
|
||||
auto current = _hGrp->GetASCII("Current", "None");
|
||||
if (current == "None") {
|
||||
_moduleName = "";
|
||||
_className = "";
|
||||
}
|
||||
else {
|
||||
auto groupName =
|
||||
"User parameter:BaseApp/Preferences/Mod/Material/ExternalInterface/Interfaces/"
|
||||
+ current;
|
||||
auto hGrp = App::GetApplication().GetParameterGroupByPath(groupName.c_str());
|
||||
_moduleName = hGrp->GetASCII("Module", "");
|
||||
_className = hGrp->GetASCII("Class", "");
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalManager::instantiate()
|
||||
{
|
||||
_instantiated = false;
|
||||
Base::Console().Log("Loading external manager...\n");
|
||||
|
||||
if (_moduleName.empty() || _className.empty()) {
|
||||
Base::Console().Log("External module not defined\n");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Base::PyGILStateLocker lock;
|
||||
Py::Module mod(PyImport_ImportModule(_moduleName.c_str()), true);
|
||||
|
||||
if (mod.isNull()) {
|
||||
Base::Console().Log(" failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Py::Callable managerClass(mod.getAttr(_className));
|
||||
_managerObject = managerClass.apply();
|
||||
if (_managerObject.hasAttr("APIVersion")) {
|
||||
_instantiated = true;
|
||||
}
|
||||
|
||||
if (_instantiated) {
|
||||
Base::Console().Log("done\n");
|
||||
}
|
||||
else {
|
||||
Base::Console().Log("failed\n");
|
||||
}
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
Base::Console().Log("failed\n");
|
||||
e.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalManager::connect()
|
||||
{
|
||||
if (!_instantiated) {
|
||||
instantiate();
|
||||
|
||||
if (!_instantiated) {
|
||||
throw ConnectionError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalManager::initManager()
|
||||
{
|
||||
QMutexLocker locker(&_mutex);
|
||||
|
||||
if (!_manager) {
|
||||
_manager = new ExternalManager();
|
||||
}
|
||||
}
|
||||
|
||||
ExternalManager* ExternalManager::getManager()
|
||||
{
|
||||
initManager();
|
||||
|
||||
return _manager;
|
||||
}
|
||||
|
||||
//=====
|
||||
//
|
||||
// Library management
|
||||
//
|
||||
//=====
|
||||
|
||||
bool ExternalManager::checkMaterialLibraryType(const Py::Object& entry)
|
||||
{
|
||||
return entry.hasAttr("name") && entry.hasAttr("icon") && entry.hasAttr("readOnly")
|
||||
&& entry.hasAttr("timestamp");
|
||||
}
|
||||
|
||||
std::shared_ptr<Library>
|
||||
ExternalManager::libraryFromObject(const Py::Object& entry)
|
||||
{
|
||||
if (!checkMaterialLibraryType(entry)) {
|
||||
throw InvalidLibrary();
|
||||
}
|
||||
|
||||
Py::String pyName(entry.getAttr("name"));
|
||||
Py::Bytes pyIcon(entry.getAttr("icon"));
|
||||
Py::Boolean pyReadOnly(entry.getAttr("readOnly"));
|
||||
Py::String pyTimestamp(entry.getAttr("timestamp"));
|
||||
|
||||
QString libraryName;
|
||||
if (!pyName.isNone()) {
|
||||
libraryName = QString::fromStdString(pyName.as_string());
|
||||
}
|
||||
|
||||
QString icon;
|
||||
if (!pyIcon.isNone()) {
|
||||
icon = QString::fromStdString(pyIcon.as_string());
|
||||
}
|
||||
|
||||
bool readOnly = pyReadOnly.as_bool();
|
||||
|
||||
QString timestamp;
|
||||
if (!pyTimestamp.isNone()) {
|
||||
timestamp = QString::fromStdString(pyTimestamp.as_string());
|
||||
}
|
||||
|
||||
auto library = std::make_shared<Library>(libraryName, icon, readOnly, timestamp);
|
||||
return library;
|
||||
}
|
||||
|
||||
bool ExternalManager::checkMaterialObjectType(const Py::Object& entry)
|
||||
{
|
||||
return entry.hasAttr("UUID") && entry.hasAttr("path") && entry.hasAttr("name");
|
||||
}
|
||||
|
||||
std::tuple<QString, QString, QString>
|
||||
ExternalManager::materialObjectTypeFromObject(const Py::Object& entry)
|
||||
{
|
||||
QString uuid;
|
||||
auto pyUUID = entry.getAttr("UUID");
|
||||
if (!pyUUID.isNone()) {
|
||||
uuid = QString::fromStdString(pyUUID.as_string());
|
||||
}
|
||||
|
||||
QString path;
|
||||
auto pyPath = entry.getAttr("path");
|
||||
if (!pyPath.isNone()) {
|
||||
path = QString::fromStdString(pyPath.as_string());
|
||||
}
|
||||
|
||||
QString name;
|
||||
auto pyName = entry.getAttr("name");
|
||||
if (!pyName.isNone()) {
|
||||
name = QString::fromStdString(pyName.as_string());
|
||||
}
|
||||
|
||||
return std::tuple<QString, QString, QString>(uuid, path, name);
|
||||
}
|
||||
|
||||
std::shared_ptr<std::vector<std::shared_ptr<Library>>>
|
||||
ExternalManager::libraries()
|
||||
{
|
||||
auto libList = std::make_shared<std::vector<std::shared_ptr<Library>>>();
|
||||
|
||||
connect();
|
||||
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (_managerObject.hasAttr("libraries")) {
|
||||
Py::Callable libraries(_managerObject.getAttr("libraries"));
|
||||
Py::List list(libraries.apply());
|
||||
for (auto lib : list) {
|
||||
auto library = libraryFromObject(Py::Object(lib));
|
||||
libList->push_back(library);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Base::Console().Log("\tlibraries() not found\n");
|
||||
throw ConnectionError();
|
||||
}
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
Base::PyException e1; // extract the Python error text
|
||||
throw LibraryNotFound(e1.what());
|
||||
}
|
||||
|
||||
return libList;
|
||||
}
|
||||
|
||||
std::shared_ptr<std::vector<std::shared_ptr<Library>>> ExternalManager::modelLibraries()
|
||||
{
|
||||
auto libList = std::make_shared<std::vector<std::shared_ptr<Library>>>();
|
||||
|
||||
connect();
|
||||
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (_managerObject.hasAttr("modelLibraries")) {
|
||||
Py::Callable libraries(_managerObject.getAttr("modelLibraries"));
|
||||
Py::List list(libraries.apply());
|
||||
for (auto lib : list) {
|
||||
auto library = libraryFromObject(Py::Tuple(lib));
|
||||
libList->push_back(library);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Base::Console().Log("\tmodelLibraries() not found\n");
|
||||
throw ConnectionError();
|
||||
}
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
Base::PyException e1; // extract the Python error text
|
||||
throw LibraryNotFound(e1.what());
|
||||
}
|
||||
|
||||
return libList;
|
||||
}
|
||||
|
||||
std::shared_ptr<std::vector<std::shared_ptr<Library>>> ExternalManager::materialLibraries()
|
||||
{
|
||||
auto libList = std::make_shared<std::vector<std::shared_ptr<Library>>>();
|
||||
|
||||
connect();
|
||||
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (_managerObject.hasAttr("materialLibraries")) {
|
||||
Py::Callable libraries(_managerObject.getAttr("materialLibraries"));
|
||||
Py::List list(libraries.apply());
|
||||
for (auto lib : list) {
|
||||
auto library = libraryFromObject(Py::Tuple(lib));
|
||||
libList->push_back(library);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Base::Console().Log("\tmaterialLibraries() not found\n");
|
||||
throw ConnectionError();
|
||||
}
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
Base::PyException e1; // extract the Python error text
|
||||
throw LibraryNotFound(e1.what());
|
||||
}
|
||||
|
||||
return libList;
|
||||
}
|
||||
|
||||
std::shared_ptr<Library> ExternalManager::getLibrary(const QString& name)
|
||||
{
|
||||
// throw LibraryNotFound("Not yet implemented");
|
||||
connect();
|
||||
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (_managerObject.hasAttr("getLibrary")) {
|
||||
Py::Callable libraries(_managerObject.getAttr("getLibrary"));
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::String(name.toStdString()));
|
||||
Py::Tuple result(libraries.apply(args));
|
||||
|
||||
Py::Object libObject = result.getItem(0);
|
||||
auto lib = libraryFromObject(Py::Tuple(libObject));
|
||||
return std::make_shared<Library>(*lib);
|
||||
}
|
||||
else {
|
||||
Base::Console().Log("\tgetLibrary() not found\n");
|
||||
throw ConnectionError();
|
||||
}
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
Base::PyException e1; // extract the Python error text
|
||||
throw CreationError(e1.what());
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalManager::createLibrary(const QString& libraryName, const QString& icon, bool readOnly)
|
||||
{
|
||||
connect();
|
||||
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (_managerObject.hasAttr("createLibrary")) {
|
||||
Py::Callable libraries(_managerObject.getAttr("createLibrary"));
|
||||
Py::Tuple args(3);
|
||||
args.setItem(0, Py::String(libraryName.toStdString()));
|
||||
args.setItem(1, Py::String(icon.toStdString()));
|
||||
args.setItem(2, Py::Boolean(readOnly));
|
||||
libraries.apply(args); // No return expected
|
||||
}
|
||||
else {
|
||||
Base::Console().Log("\tcreateLibrary() not found\n");
|
||||
throw ConnectionError();
|
||||
}
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
Base::PyException e1; // extract the Python error text
|
||||
throw CreationError(e1.what());
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalManager::renameLibrary(const QString& libraryName, const QString& newName)
|
||||
{
|
||||
connect();
|
||||
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (_managerObject.hasAttr("renameLibrary")) {
|
||||
Py::Callable libraries(_managerObject.getAttr("renameLibrary"));
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::String(libraryName.toStdString()));
|
||||
args.setItem(1, Py::String(newName.toStdString()));
|
||||
libraries.apply(args); // No return expected
|
||||
}
|
||||
else {
|
||||
Base::Console().Log("\trenameLibrary() not found\n");
|
||||
throw ConnectionError();
|
||||
}
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
Base::PyException e1; // extract the Python error text
|
||||
throw RenameError(e1.what());
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalManager::changeIcon(const QString& libraryName, const QString& icon)
|
||||
{
|
||||
connect();
|
||||
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (_managerObject.hasAttr("changeIcon")) {
|
||||
Py::Callable libraries(_managerObject.getAttr("changeIcon"));
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::String(libraryName.toStdString()));
|
||||
args.setItem(1, Py::String(icon.toStdString()));
|
||||
libraries.apply(args); // No return expected
|
||||
}
|
||||
else {
|
||||
Base::Console().Log("\tchangeIcon() not found\n");
|
||||
throw ConnectionError();
|
||||
}
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
Base::PyException e1; // extract the Python error text
|
||||
throw ReplacementError(e1.what());
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalManager::removeLibrary(const QString& libraryName)
|
||||
{
|
||||
connect();
|
||||
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (_managerObject.hasAttr("removeLibrary")) {
|
||||
Py::Callable libraries(_managerObject.getAttr("removeLibrary"));
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::String(libraryName.toStdString()));
|
||||
libraries.apply(args); // No return expected
|
||||
}
|
||||
else {
|
||||
Base::Console().Log("\tremoveLibrary() not found\n");
|
||||
throw ConnectionError();
|
||||
}
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
Base::PyException e1; // extract the Python error text
|
||||
throw DeleteError(e1.what());
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<std::vector<std::tuple<QString, QString, QString>>>
|
||||
ExternalManager::libraryModels(const QString& libraryName)
|
||||
{
|
||||
auto modelList = std::make_shared<std::vector<std::tuple<QString, QString, QString>>>();
|
||||
|
||||
connect();
|
||||
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (_managerObject.hasAttr("libraryModels")) {
|
||||
Py::Callable libraries(_managerObject.getAttr("libraryModels"));
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::String(libraryName.toStdString()));
|
||||
Py::List list(libraries.apply(args));
|
||||
for (auto library : list) {
|
||||
auto entry = Py::Object(library);
|
||||
if (!checkMaterialObjectType(entry)) {
|
||||
throw InvalidModel();
|
||||
}
|
||||
|
||||
modelList->push_back(materialObjectTypeFromObject(entry));
|
||||
}
|
||||
}
|
||||
else {
|
||||
Base::Console().Log("\tlibraryModels() not found\n");
|
||||
throw ConnectionError();
|
||||
}
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
Base::PyException e1; // extract the Python error text
|
||||
throw LibraryNotFound(e1.what());
|
||||
}
|
||||
|
||||
return modelList;
|
||||
}
|
||||
|
||||
std::shared_ptr<std::vector<std::tuple<QString, QString, QString>>>
|
||||
ExternalManager::libraryMaterials(const QString& libraryName)
|
||||
{
|
||||
auto materialList = std::make_shared<std::vector<std::tuple<QString, QString, QString>>>();
|
||||
|
||||
connect();
|
||||
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (_managerObject.hasAttr("libraryMaterials")) {
|
||||
Py::Callable libraries(_managerObject.getAttr("libraryMaterials"));
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::String(libraryName.toStdString()));
|
||||
Py::List list(libraries.apply(args));
|
||||
for (auto library : list) {
|
||||
auto entry = Py::Object(library);
|
||||
if (!checkMaterialObjectType(entry)) {
|
||||
throw InvalidMaterial();
|
||||
}
|
||||
|
||||
materialList->push_back(materialObjectTypeFromObject(entry));
|
||||
}
|
||||
}
|
||||
else {
|
||||
Base::Console().Log("\tlibraryMaterials() not found\n");
|
||||
throw ConnectionError();
|
||||
}
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
Base::PyException e1; // extract the Python error text
|
||||
throw LibraryNotFound(e1.what());
|
||||
}
|
||||
|
||||
return materialList;
|
||||
}
|
||||
|
||||
std::shared_ptr<std::vector<std::tuple<QString, QString, QString>>>
|
||||
ExternalManager::libraryMaterials(const QString& libraryName,
|
||||
const std::shared_ptr<MaterialFilter>& filter,
|
||||
const MaterialFilterOptions& options)
|
||||
{
|
||||
auto materialList = std::make_shared<std::vector<std::tuple<QString, QString, QString>>>();
|
||||
|
||||
connect();
|
||||
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (_managerObject.hasAttr("libraryMaterials")) {
|
||||
Py::Callable libraries(_managerObject.getAttr("libraryMaterials"));
|
||||
Py::Tuple args(3);
|
||||
args.setItem(0, Py::String(libraryName.toStdString()));
|
||||
if (filter) {
|
||||
args.setItem(1,
|
||||
Py::Object(new MaterialFilterPy(new MaterialFilter(*filter)), true));
|
||||
}
|
||||
else {
|
||||
args.setItem(1, Py::None());
|
||||
}
|
||||
args.setItem(
|
||||
2,
|
||||
Py::Object(new MaterialFilterOptionsPy(new MaterialFilterOptions(options)), true));
|
||||
Py::List list(libraries.apply(args));
|
||||
for (auto library : list) {
|
||||
auto entry = Py::Object(library);
|
||||
if (!checkMaterialObjectType(entry)) {
|
||||
throw InvalidMaterial();
|
||||
}
|
||||
|
||||
materialList->push_back(materialObjectTypeFromObject(entry));
|
||||
}
|
||||
}
|
||||
else {
|
||||
Base::Console().Log("\tlibraryMaterials() not found\n");
|
||||
throw ConnectionError();
|
||||
}
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
Base::PyException e1; // extract the Python error text
|
||||
throw LibraryNotFound(e1.what());
|
||||
}
|
||||
|
||||
return materialList;
|
||||
}
|
||||
|
||||
//=====
|
||||
//
|
||||
// Model management
|
||||
//
|
||||
//=====
|
||||
|
||||
std::shared_ptr<Model> ExternalManager::getModel(const QString& uuid)
|
||||
{
|
||||
connect();
|
||||
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (_managerObject.hasAttr("getModel")) {
|
||||
Py::Callable libraries(_managerObject.getAttr("getModel"));
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::String(uuid.toStdString()));
|
||||
Py::Tuple result(libraries.apply(args)); // ignore return for now
|
||||
|
||||
Py::Object uuidObject = result.getItem(0);
|
||||
Py::Tuple libraryObject(result.getItem(1));
|
||||
Py::Object modelObject = result.getItem(2);
|
||||
|
||||
Py::Object pyName = libraryObject.getItem(0);
|
||||
Py::Object pyIcon = libraryObject.getItem(1);
|
||||
Py::Object readOnly = libraryObject.getItem(2);
|
||||
|
||||
QString name;
|
||||
if (!pyName.isNone()) {
|
||||
name = QString::fromStdString(pyName.as_string());
|
||||
}
|
||||
QString icon;
|
||||
if (!pyIcon.isNone()) {
|
||||
icon = QString::fromStdString(pyIcon.as_string());
|
||||
}
|
||||
auto library =
|
||||
std::make_shared<ModelLibrary>(name, QString(), icon, readOnly.as_bool());
|
||||
|
||||
Model* model = static_cast<ModelPy*>(*modelObject)->getModelPtr();
|
||||
model->setUUID(uuid);
|
||||
model->setLibrary(library);
|
||||
auto shared = std::make_shared<Model>(*model);
|
||||
|
||||
return shared;
|
||||
}
|
||||
else {
|
||||
Base::Console().Log("\tgetModel() not found\n");
|
||||
throw ConnectionError();
|
||||
}
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
Base::PyException e1; // extract the Python error text
|
||||
throw ModelNotFound(e1.what());
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalManager::addModel(const QString& libraryName,
|
||||
const QString& path,
|
||||
const std::shared_ptr<Model>& model)
|
||||
{
|
||||
connect();
|
||||
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (_managerObject.hasAttr("addModel")) {
|
||||
Py::Callable libraries(_managerObject.getAttr("addModel"));
|
||||
Py::Tuple args(3);
|
||||
args.setItem(0, Py::String(libraryName.toStdString()));
|
||||
args.setItem(1, Py::String(path.toStdString()));
|
||||
args.setItem(2, Py::Object(new ModelPy(new Model(*model)), true));
|
||||
libraries.apply(args); // No return expected
|
||||
}
|
||||
else {
|
||||
Base::Console().Log("\taddModel() not found\n");
|
||||
throw ConnectionError();
|
||||
}
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
Base::PyException e1; // extract the Python error text
|
||||
throw CreationError(e1.what());
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalManager::migrateModel(const QString& libraryName,
|
||||
const QString& path,
|
||||
const std::shared_ptr<Model>& model)
|
||||
{
|
||||
connect();
|
||||
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (_managerObject.hasAttr("migrateModel")) {
|
||||
Py::Callable libraries(_managerObject.getAttr("migrateModel"));
|
||||
Py::Tuple args(3);
|
||||
args.setItem(0, Py::String(libraryName.toStdString()));
|
||||
args.setItem(1, Py::String(path.toStdString()));
|
||||
args.setItem(2, Py::Object(new ModelPy(new Model(*model)), true));
|
||||
libraries.apply(args); // No return expected
|
||||
}
|
||||
else {
|
||||
Base::Console().Log("\tmigrateModel() not found\n");
|
||||
throw ConnectionError();
|
||||
}
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
Base::PyException e1; // extract the Python error text
|
||||
throw CreationError(e1.what());
|
||||
}
|
||||
}
|
||||
|
||||
//=====
|
||||
//
|
||||
// Material management
|
||||
//
|
||||
//=====
|
||||
|
||||
std::shared_ptr<Material> ExternalManager::getMaterial(const QString& uuid)
|
||||
{
|
||||
connect();
|
||||
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (_managerObject.hasAttr("getMaterial")) {
|
||||
Py::Callable libraries(_managerObject.getAttr("getMaterial"));
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::String(uuid.toStdString()));
|
||||
Py::Tuple result(libraries.apply(args));
|
||||
|
||||
Py::Object uuidObject = result.getItem(0);
|
||||
Py::Tuple libraryObject(result.getItem(1));
|
||||
Py::Object materialObject = result.getItem(2);
|
||||
|
||||
Py::Object pyName = libraryObject.getItem(0);
|
||||
Py::Object pyIcon = libraryObject.getItem(1);
|
||||
Py::Object readOnly = libraryObject.getItem(2);
|
||||
|
||||
QString name;
|
||||
if (!pyName.isNone()) {
|
||||
name = QString::fromStdString(pyName.as_string());
|
||||
}
|
||||
QString icon;
|
||||
if (!pyIcon.isNone()) {
|
||||
icon = QString::fromStdString(pyIcon.as_string());
|
||||
}
|
||||
auto library =
|
||||
std::make_shared<MaterialLibrary>(name, QString(), icon, readOnly.as_bool());
|
||||
|
||||
Material* material = static_cast<MaterialPy*>(*materialObject)->getMaterialPtr();
|
||||
material->setUUID(uuid);
|
||||
material->setLibrary(library);
|
||||
auto shared = std::make_shared<Material>(*material);
|
||||
|
||||
return shared;
|
||||
}
|
||||
else {
|
||||
Base::Console().Log("\tgetMaterial() not found\n");
|
||||
throw ConnectionError();
|
||||
}
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
Base::PyException e1; // extract the Python error text
|
||||
throw MaterialNotFound(e1.what());
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalManager::addMaterial(const QString& libraryName,
|
||||
const QString& path,
|
||||
const std::shared_ptr<Material>& material)
|
||||
{
|
||||
connect();
|
||||
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (_managerObject.hasAttr("addMaterial")) {
|
||||
Py::Callable libraries(_managerObject.getAttr("addMaterial"));
|
||||
Py::Tuple args(3);
|
||||
args.setItem(0, Py::String(libraryName.toStdString()));
|
||||
args.setItem(1, Py::String(path.toStdString()));
|
||||
args.setItem(2, Py::Object(new MaterialPy(new Material(*material)), true));
|
||||
libraries.apply(args); // No return expected
|
||||
}
|
||||
else {
|
||||
Base::Console().Log("\taddMaterial() not found\n");
|
||||
throw ConnectionError();
|
||||
}
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
Base::PyException e1; // extract the Python error text
|
||||
throw CreationError(e1.what());
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalManager::migrateMaterial(const QString& libraryName,
|
||||
const QString& path,
|
||||
const std::shared_ptr<Material>& material)
|
||||
{
|
||||
connect();
|
||||
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (_managerObject.hasAttr("migrateMaterial")) {
|
||||
Py::Callable libraries(_managerObject.getAttr("migrateMaterial"));
|
||||
Py::Tuple args(3);
|
||||
args.setItem(0, Py::String(libraryName.toStdString()));
|
||||
args.setItem(1, Py::String(path.toStdString()));
|
||||
auto mat = new Material(*material);
|
||||
args.setItem(2, Py::Object(new MaterialPy(mat), true));
|
||||
libraries.apply(args); // No return expected
|
||||
}
|
||||
else {
|
||||
Base::Console().Log("\tmigrateMaterial() not found\n");
|
||||
throw ConnectionError();
|
||||
}
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
Base::PyException e1; // extract the Python error text
|
||||
throw CreationError(e1.what());
|
||||
}
|
||||
}
|
||||
112
src/Mod/Material/App/ExternalManager.h
Normal file
112
src/Mod/Material/App/ExternalManager.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2024 David Carter <dcarter@david.carter.ca> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD 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 *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef MATERIAL_EXTERNALMANAGER_H
|
||||
#define MATERIAL_EXTERNALMANAGER_H
|
||||
|
||||
#include <Base/Parameter.h>
|
||||
#include <CXX/Objects.hxx>
|
||||
|
||||
#include <Mod/Material/MaterialGlobal.h>
|
||||
|
||||
class QMutex;
|
||||
class QString;
|
||||
|
||||
namespace Materials
|
||||
{
|
||||
|
||||
class Library;
|
||||
class Material;
|
||||
class Model;
|
||||
class MaterialFilter;
|
||||
class MaterialFilterOptions;
|
||||
|
||||
class MaterialsExport ExternalManager: public ParameterGrp::ObserverType
|
||||
{
|
||||
public:
|
||||
|
||||
static ExternalManager* getManager();
|
||||
|
||||
/// Observer message from the ParameterGrp
|
||||
void OnChange(ParameterGrp::SubjectType& rCaller, ParameterGrp::MessageType Reason) override;
|
||||
|
||||
// Library management
|
||||
std::shared_ptr<std::vector<std::shared_ptr<Library>>> libraries();
|
||||
std::shared_ptr<std::vector<std::shared_ptr<Library>>> modelLibraries();
|
||||
std::shared_ptr<std::vector<std::shared_ptr<Library>>> materialLibraries();
|
||||
std::shared_ptr<Library> getLibrary(const QString& name);
|
||||
void createLibrary(const QString& libraryName, const QString& icon, bool readOnly = true);
|
||||
void renameLibrary(const QString& libraryName, const QString& newName);
|
||||
void changeIcon(const QString& libraryName, const QString& icon);
|
||||
void removeLibrary(const QString& libraryName);
|
||||
std::shared_ptr<std::vector<std::tuple<QString, QString, QString>>>
|
||||
libraryModels(const QString& libraryName);
|
||||
std::shared_ptr<std::vector<std::tuple<QString, QString, QString>>>
|
||||
libraryMaterials(const QString& libraryName);
|
||||
std::shared_ptr<std::vector<std::tuple<QString, QString, QString>>>
|
||||
libraryMaterials(const QString& libraryName,
|
||||
const std::shared_ptr<MaterialFilter>& filter,
|
||||
const MaterialFilterOptions& options);
|
||||
|
||||
// Model management
|
||||
std::shared_ptr<Model> getModel(const QString& uuid);
|
||||
void
|
||||
addModel(const QString& libraryName, const QString& path, const std::shared_ptr<Model>& model);
|
||||
void
|
||||
migrateModel(const QString& libraryName, const QString& path, const std::shared_ptr<Model>& model);
|
||||
|
||||
// Material management
|
||||
std::shared_ptr<Material> getMaterial(const QString& uuid);
|
||||
void addMaterial(const QString& libraryName,
|
||||
const QString& path,
|
||||
const std::shared_ptr<Material>& material);
|
||||
void migrateMaterial(const QString& libraryName,
|
||||
const QString& path,
|
||||
const std::shared_ptr<Material>& material);
|
||||
|
||||
private:
|
||||
ExternalManager();
|
||||
~ExternalManager() override;
|
||||
|
||||
static void initManager();
|
||||
void getConfiguration();
|
||||
void instantiate();
|
||||
void connect();
|
||||
bool checkMaterialLibraryType(const Py::Object& entry);
|
||||
std::shared_ptr<Library> libraryFromObject(const Py::Object& entry);
|
||||
bool checkMaterialObjectType(const Py::Object& entry);
|
||||
std::tuple<QString, QString, QString> materialObjectTypeFromObject(const Py::Object& entry);
|
||||
|
||||
static ExternalManager* _manager;
|
||||
static QMutex _mutex;
|
||||
|
||||
// COnfiguration
|
||||
ParameterGrp::handle _hGrp;
|
||||
std::string _moduleName;
|
||||
std::string _className;
|
||||
bool _instantiated;
|
||||
|
||||
Py::Object _managerObject;
|
||||
};
|
||||
|
||||
} // namespace Materials
|
||||
|
||||
#endif // MATERIAL_EXTERNALMANAGER_H
|
||||
@@ -40,6 +40,16 @@ Library::Library(const QString& libraryName, const QString& icon, bool readOnly)
|
||||
, _readOnly(readOnly)
|
||||
{}
|
||||
|
||||
Library::Library(const QString& libraryName,
|
||||
const QString& icon,
|
||||
bool readOnly,
|
||||
const QString& timestamp)
|
||||
: _name(libraryName)
|
||||
, _iconPath(icon)
|
||||
, _readOnly(readOnly)
|
||||
, _timestamp(timestamp)
|
||||
{}
|
||||
|
||||
Library::Library(const QString& libraryName, const QString& dir, const QString& icon, bool readOnly)
|
||||
: _name(libraryName)
|
||||
, _directory(QDir::cleanPath(dir))
|
||||
|
||||
@@ -39,6 +39,10 @@ class MaterialsExport Library: public Base::BaseClass
|
||||
public:
|
||||
Library() = default;
|
||||
Library(const QString& libraryName, const QString& icon, bool readOnly = true);
|
||||
Library(const QString& libraryName,
|
||||
const QString& icon,
|
||||
bool readOnly,
|
||||
const QString& timestamp);
|
||||
Library(const QString& libraryName,
|
||||
const QString& dir,
|
||||
const QString& icon,
|
||||
@@ -53,7 +57,7 @@ public:
|
||||
{
|
||||
_name = newName;
|
||||
}
|
||||
bool sameName(const QString& name)
|
||||
bool isName(const QString& name)
|
||||
{
|
||||
return (_name == name);
|
||||
}
|
||||
@@ -87,6 +91,14 @@ public:
|
||||
{
|
||||
return QDir(_directory).absolutePath();
|
||||
}
|
||||
QString getTimestamp() const
|
||||
{
|
||||
return _timestamp;
|
||||
}
|
||||
void setTimestamp(const QString& timestamp)
|
||||
{
|
||||
_timestamp = timestamp;
|
||||
}
|
||||
|
||||
bool operator==(const Library& library) const;
|
||||
bool operator!=(const Library& library) const
|
||||
@@ -107,6 +119,7 @@ private:
|
||||
QString _directory;
|
||||
QString _iconPath;
|
||||
bool _readOnly;
|
||||
QString _timestamp;
|
||||
};
|
||||
|
||||
} // namespace Materials
|
||||
|
||||
249
src/Mod/Material/App/MaterialAPI/MaterialManagerExternal.py
Normal file
249
src/Mod/Material/App/MaterialAPI/MaterialManagerExternal.py
Normal file
@@ -0,0 +1,249 @@
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2024 David Carter <dcarter@davidcarter.ca> *
|
||||
# * *
|
||||
# * 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 *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
__author__ = "David Carter"
|
||||
__url__ = "https://www.davesrocketshop.com"
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass
|
||||
|
||||
import Materials
|
||||
|
||||
@dataclass
|
||||
class MaterialLibraryType:
|
||||
name: str
|
||||
icon: bytes
|
||||
readOnly: bool
|
||||
timestamp: str
|
||||
|
||||
@dataclass
|
||||
class MaterialLibraryObjectType:
|
||||
UUID: str
|
||||
path: str
|
||||
name: str
|
||||
|
||||
class MaterialManagerExternal(ABC):
|
||||
"""Abstract base class for all external material managers
|
||||
|
||||
Any external interface should be derivedfrom this base class."""
|
||||
|
||||
@classmethod
|
||||
def APIVersion(cls) -> tuple:
|
||||
"""Returns a tuple of 3 integers describing the API version
|
||||
|
||||
The version returned should be the latest supported version. This method
|
||||
allows the interface to use older modules."""
|
||||
return (1, 0, 0)
|
||||
|
||||
#
|
||||
# Library methods
|
||||
#
|
||||
|
||||
@abstractmethod
|
||||
def libraries(self) -> list[MaterialLibraryType]:
|
||||
"""Returns a list of libraries managed by this interface
|
||||
|
||||
The list contains a series of tuples describing all libraries managed by
|
||||
this module. Each tuple containes the library name, icon, a boolean to indicate
|
||||
if it is a read only library, and a timestamp that indicates when it was last
|
||||
modified."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def modelLibraries(self) -> list[MaterialLibraryType]:
|
||||
"""Returns a list of libraries managed by this interface
|
||||
|
||||
The list contains a series of tuples describing all libraries managed by
|
||||
this module. Each tuple containes the library name, icon, and a boolean to indicate
|
||||
if it is a read only library, and a timestamp that indicates when it was last
|
||||
modified.
|
||||
|
||||
This differs from the libraries() function in that it only returns libraries
|
||||
containing model objects."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def materialLibraries(self) -> list[MaterialLibraryType]:
|
||||
"""Returns a list of libraries managed by this interface
|
||||
|
||||
The list contains a series of tuples describing all libraries managed by
|
||||
this module. Each tuple containes the library name, icon, and a boolean to indicate
|
||||
if it is a read only library, and a timestamp that indicates when it was last
|
||||
modified.
|
||||
|
||||
This differs from the libraries() function in that it only returns libraries
|
||||
containing material objects."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def getLibrary(self, name: str) -> tuple:
|
||||
"""Get the library
|
||||
|
||||
Retrieve the library with the given name"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def createLibrary(self, name: str, icon: bytes, readOnly: bool) -> None:
|
||||
"""Create a new library
|
||||
|
||||
Create a new library with the given name"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def renameLibrary(self, oldName: str, newName: str) -> None:
|
||||
"""Rename an existing library
|
||||
|
||||
Change the name of an existing library"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def changeIcon(self, name: str, icon: bytes) -> None:
|
||||
"""Change the library icon
|
||||
|
||||
Change the library icon"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def removeLibrary(self, library: str) -> None:
|
||||
"""Delete a library and its contents
|
||||
|
||||
Deletes the library and any models or materials it contains"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def libraryModels(self, library: str) -> list[MaterialLibraryObjectType]:
|
||||
"""Returns a list of models managed by this library
|
||||
|
||||
Each list entry is a tuple containing the UUID, path, and name of the model"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def libraryMaterials(self, library: str,
|
||||
filter: Materials.MaterialFilter = None,
|
||||
options: Materials.MaterialFilterOptions = None) -> list[MaterialLibraryObjectType]:
|
||||
"""Returns a list of materials managed by this library
|
||||
|
||||
Each list entry is a tuple containing the UUID, path, and name of the material"""
|
||||
pass
|
||||
|
||||
#
|
||||
# Model methods
|
||||
#
|
||||
|
||||
@abstractmethod
|
||||
def getModel(self, uuid: str) -> Materials.Model:
|
||||
"""Retrieve a model given its UUID"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def addModel(self, library: str, path: str, model: Materials.Model) -> None:
|
||||
"""Add a model to a library in the given folder.
|
||||
|
||||
This will throw a DatabaseModelExistsError exception if the model already exists."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def migrateModel(self, library: str, path: str, model: Materials.Model) -> None:
|
||||
"""Add the model to the library.
|
||||
|
||||
If the model already exists, then no action is performed."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def updateModel(self, library: str, path: str, model: Materials.Model) -> None:
|
||||
"""Update the given model"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def setModelPath(self, library: str, path: str, model: Materials.Model) -> None:
|
||||
"""Change the model path within the library"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def renameModel(self, library: str, name: str, model: Materials.Model) -> None:
|
||||
"""Change the model name"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def moveModel(self, library: str, path: str, model: Materials.Model) -> None:
|
||||
"""Move a model across libraries
|
||||
|
||||
Move the model to the desired path in a different library. This should also
|
||||
remove the model from the old library if that library is managed by this
|
||||
interface"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def removeModel(self, model: Materials.Model) -> None:
|
||||
"""Remove the model from the library"""
|
||||
pass
|
||||
|
||||
#
|
||||
# Material methods
|
||||
#
|
||||
|
||||
@abstractmethod
|
||||
def getMaterial(self, uuid: str) -> Materials.Material:
|
||||
""" Retrieve a material given its UUID """
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def addMaterial(self, library: str, path: str, material: Materials.Material) -> None:
|
||||
"""Add a material to a library in the given folder.
|
||||
|
||||
This will throw a DatabaseMaterialExistsError exception if the model already exists."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def migrateMaterial(self, library: str, path: str, material: Materials.Material) -> None:
|
||||
"""Add the material to the library in the given folder.
|
||||
|
||||
If the material already exists, then no action is performed."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def updateMaterial(self, library: str, path: str, material: Materials.Material) -> None:
|
||||
"""Update the given material"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def setMaterialPath(self, library: str, path: str, material: Materials.Material) -> None:
|
||||
"""Change the material path within the library"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def renameMaterial(self, library: str, name: str, material: Materials.Material) -> None:
|
||||
"""Change the material name"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def moveMaterial(self, library: str, path: str, material: Materials.Material) -> None:
|
||||
"""Move a material across libraries
|
||||
|
||||
Move the material to the desired path in a different library. This should also
|
||||
remove the material from the old library if that library is managed by this
|
||||
interface"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def removeMaterial(self, material: Materials.Material) -> None:
|
||||
"""Remove the material from the library"""
|
||||
pass
|
||||
0
src/Mod/Material/App/MaterialAPI/__init__.py
Normal file
0
src/Mod/Material/App/MaterialAPI/__init__.py
Normal file
@@ -130,7 +130,7 @@ MaterialManagerLocal::getMaterialLibraries()
|
||||
std::shared_ptr<MaterialLibrary> MaterialManagerLocal::getLibrary(const QString& name) const
|
||||
{
|
||||
for (auto& library : *_libraryList) {
|
||||
if (library->isLocal() && library->sameName(name)) {
|
||||
if (library->isLocal() && library->isName(name)) {
|
||||
return library;
|
||||
}
|
||||
}
|
||||
@@ -160,7 +160,7 @@ void MaterialManagerLocal::createLibrary(const QString& libraryName,
|
||||
void MaterialManagerLocal::renameLibrary(const QString& libraryName, const QString& newName)
|
||||
{
|
||||
for (auto& library : *_libraryList) {
|
||||
if (library->isLocal() && library->sameName(libraryName)) {
|
||||
if (library->isLocal() && library->isName(libraryName)) {
|
||||
auto materialLibrary =
|
||||
reinterpret_cast<const std::shared_ptr<Materials::MaterialLibraryLocal>&>(library);
|
||||
materialLibrary->setName(newName);
|
||||
@@ -174,7 +174,7 @@ void MaterialManagerLocal::renameLibrary(const QString& libraryName, const QStri
|
||||
void MaterialManagerLocal::changeIcon(const QString& libraryName, const QString& icon)
|
||||
{
|
||||
for (auto& library : *_libraryList) {
|
||||
if (library->isLocal() && library->sameName(libraryName)) {
|
||||
if (library->isLocal() && library->isName(libraryName)) {
|
||||
auto materialLibrary =
|
||||
reinterpret_cast<const std::shared_ptr<Materials::MaterialLibraryLocal>&>(library);
|
||||
materialLibrary->setIconPath(icon);
|
||||
@@ -188,7 +188,7 @@ void MaterialManagerLocal::changeIcon(const QString& libraryName, const QString&
|
||||
void MaterialManagerLocal::removeLibrary(const QString& libraryName)
|
||||
{
|
||||
for (auto& library : *_libraryList) {
|
||||
if (library->isLocal() && library->sameName(libraryName)) {
|
||||
if (library->isLocal() && library->isName(libraryName)) {
|
||||
_libraryList->remove(library);
|
||||
|
||||
// At this point we should rebuild the material map
|
||||
@@ -207,7 +207,7 @@ MaterialManagerLocal::libraryMaterials(const QString& libraryName)
|
||||
for (auto& it : *_materialMap) {
|
||||
// This is needed to resolve cyclic dependencies
|
||||
auto library = it.second->getLibrary();
|
||||
if (library->sameName(libraryName)) {
|
||||
if (library->isName(libraryName)) {
|
||||
materials->push_back(std::tuple<QString, QString, QString>(it.first,
|
||||
it.second->getDirectory(),
|
||||
it.second->getName()));
|
||||
@@ -245,7 +245,7 @@ MaterialManagerLocal::libraryMaterials(const QString& libraryName,
|
||||
for (auto& it : *_materialMap) {
|
||||
// This is needed to resolve cyclic dependencies
|
||||
auto library = it.second->getLibrary();
|
||||
if (library->sameName(libraryName)) {
|
||||
if (library->isName(libraryName)) {
|
||||
if (passFilter(it.second, filter, options)) {
|
||||
materials->push_back(std::tuple<QString, QString, QString>(it.first,
|
||||
it.second->getDirectory(),
|
||||
|
||||
Reference in New Issue
Block a user