diff --git a/src/Mod/Material/App/AppMaterial.cpp b/src/Mod/Material/App/AppMaterial.cpp index ddc2e16a80..266cf370db 100644 --- a/src/Mod/Material/App/AppMaterial.cpp +++ b/src/Mod/Material/App/AppMaterial.cpp @@ -33,6 +33,10 @@ #include "MaterialManagerLocal.h" #include "ModelManagerLocal.h" #include "PropertyMaterial.h" +#if defined(BUILD_MATERIAL_EXTERNAL) +#include "ModelManagerExternal.h" +#include "MaterialManagerExternal.h" +#endif #include "Array2DPy.h" #include "Array3DPy.h" @@ -107,6 +111,10 @@ PyMOD_INIT_FUNC(Materials) Materials::MaterialManagerLocal ::init(); Materials::Model ::init(); Materials::ModelManager ::init(); +#if defined(BUILD_MATERIAL_EXTERNAL) + Materials::MaterialManagerExternal ::init(); + Materials::ModelManagerExternal ::init(); +#endif Materials::ModelManagerLocal ::init(); Materials::ModelUUIDs ::init(); diff --git a/src/Mod/Material/App/CMakeLists.txt b/src/Mod/Material/App/CMakeLists.txt index ce78d35f07..bbdd563801 100644 --- a/src/Mod/Material/App/CMakeLists.txt +++ b/src/Mod/Material/App/CMakeLists.txt @@ -22,6 +22,13 @@ include_directories( SYSTEM ${YAML_CPP_INCLUDE_DIR} ) + +if (BUILD_MATERIAL_EXTERNAL) +include_directories( + ${CMAKE_SOURCE_DIR}/src/3rdParty/lru-cache/include +) +endif(BUILD_MATERIAL_EXTERNAL) + link_directories(${YAML_CPP_LIBRARY_DIR}) if(BUILD_MATERIAL_EXTERNAL) @@ -70,6 +77,11 @@ SET(MaterialsAPI_Files MaterialAPI/MaterialManagerExternal.py ) +SET(MaterialsAPI_Files + MaterialAPI/__init__.py + MaterialAPI/MaterialManagerExternal.py +) + SET(Python_SRCS Exceptions.h Array2D.pyi @@ -145,6 +157,8 @@ if(BUILD_MATERIAL_EXTERNAL) list(APPEND Materials_SRCS ExternalManager.cpp ExternalManager.h + MaterialManagerExternal.cpp + MaterialManagerExternal.h ModelManagerExternal.cpp ModelManagerExternal.h ) diff --git a/src/Mod/Material/App/ExternalManager.cpp b/src/Mod/Material/App/ExternalManager.cpp index 595e512e42..0e478454b5 100644 --- a/src/Mod/Material/App/ExternalManager.cpp +++ b/src/Mod/Material/App/ExternalManager.cpp @@ -188,7 +188,6 @@ ExternalManager::libraryFromObject(const Py::Object& entry) if (!pyName.isNone()) { libraryName = QString::fromStdString(pyName.as_string()); } - QString icon; if (!pyIcon.isNone()) { icon = QString::fromStdString(pyIcon.as_string()); diff --git a/src/Mod/Material/App/MaterialLibrary.cpp b/src/Mod/Material/App/MaterialLibrary.cpp index 76f46a5e50..f0af06492b 100644 --- a/src/Mod/Material/App/MaterialLibrary.cpp +++ b/src/Mod/Material/App/MaterialLibrary.cpp @@ -56,6 +56,11 @@ MaterialLibrary::MaterialLibrary(const QString& libraryName, , _local(false) {} +MaterialLibrary::MaterialLibrary(const Library& library) + : Library(library) + , _local(false) +{} + bool MaterialLibrary::isLocal() const { return _local; diff --git a/src/Mod/Material/App/MaterialLibrary.h b/src/Mod/Material/App/MaterialLibrary.h index b1f4fc428c..00d5b51cad 100644 --- a/src/Mod/Material/App/MaterialLibrary.h +++ b/src/Mod/Material/App/MaterialLibrary.h @@ -57,6 +57,7 @@ public: const QString& dir, const QString& icon, bool readOnly = true); + MaterialLibrary(const Library& library); MaterialLibrary(const MaterialLibrary&) = delete; ~MaterialLibrary() override = default; diff --git a/src/Mod/Material/App/MaterialManager.cpp b/src/Mod/Material/App/MaterialManager.cpp index 8b37ff8d9b..67fb0d9b14 100644 --- a/src/Mod/Material/App/MaterialManager.cpp +++ b/src/Mod/Material/App/MaterialManager.cpp @@ -35,6 +35,9 @@ #include "MaterialConfigLoader.h" #include "MaterialLoader.h" #include "MaterialManager.h" +#if defined(BUILD_MATERIAL_EXTERNAL) +#include "MaterialManagerExternal.h" +#endif #include "MaterialManagerLocal.h" #include "ModelManager.h" #include "ModelUuids.h" @@ -49,14 +52,25 @@ using namespace Materials; TYPESYSTEM_SOURCE(Materials::MaterialManager, Base::BaseClass) QMutex MaterialManager::_mutex; +bool MaterialManager::_useExternal = false; MaterialManager* MaterialManager::_manager = nullptr; std::unique_ptr MaterialManager::_localManager; +#if defined(BUILD_MATERIAL_EXTERNAL) +std::unique_ptr MaterialManager::_externalManager; +#endif MaterialManager::MaterialManager() -{} +{ + _hGrp = App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/Mod/Material/ExternalInterface"); + _useExternal = _hGrp->GetBool("UseExternal", false); + _hGrp->Attach(this); +} MaterialManager::~MaterialManager() -{} +{ + _hGrp->Detach(this); +} MaterialManager& MaterialManager::getManager() { @@ -77,6 +91,22 @@ void MaterialManager::initManagers() if (!_localManager) { _localManager = std::make_unique(); } + +#if defined(BUILD_MATERIAL_EXTERNAL) + if (!_externalManager) { + _externalManager = std::make_unique(); + } +#endif +} + +void MaterialManager::OnChange(ParameterGrp::SubjectType& rCaller, ParameterGrp::MessageType Reason) +{ + const ParameterGrp& rGrp = static_cast(rCaller); + if (strcmp(Reason, "UseExternal") == 0) { + Base::Console().Log("Use external changed\n"); + _useExternal = rGrp.GetBool("UseExternal", false); + // _dbManager->refresh(); + } } void MaterialManager::cleanup() @@ -84,6 +114,11 @@ void MaterialManager::cleanup() if (_localManager) { _localManager->cleanup(); } +#if defined(BUILD_MATERIAL_EXTERNAL) + if (_externalManager) { + _externalManager->cleanup(); + } +#endif } void MaterialManager::refresh() @@ -183,22 +218,52 @@ QString MaterialManager::defaultMaterialUUID() std::shared_ptr>> MaterialManager::getLibraries() { - auto libraries = std::make_shared>>(); + // External libraries take precedence over local libraries + auto libMap = std::map>(); +#if defined(BUILD_MATERIAL_EXTERNAL) + if (_useExternal) { + auto remoteLibraries = _externalManager->getLibraries(); + for (auto& remote : *remoteLibraries) { + libMap.try_emplace(remote->getName(), remote); + } + } +#endif auto localLibraries = _localManager->getLibraries(); for (auto& local : *localLibraries) { - libraries->push_back(local); + libMap.try_emplace(local->getName(), local); + } + + // Consolidate into a single list + auto libraries = std::make_shared>>(); + for (auto libEntry : libMap) { + libraries->push_back(libEntry.second); } return libraries; } -std::shared_ptr>> MaterialManager::getLocalLibraries() +std::shared_ptr>> +MaterialManager::getLocalLibraries() { return _localManager->getLibraries(); } std::shared_ptr MaterialManager::getLibrary(const QString& name) const { +#if defined(BUILD_MATERIAL_EXTERNAL) + if (_useExternal) { + try + { + auto lib = _externalManager->getLibrary(name); + if (lib) { + return lib; + } + } + catch (const LibraryNotFound& e) { + } + } +#endif + // We really want to return the local library if not found, such as for User folder models return _localManager->getLibrary(name); } @@ -233,6 +298,18 @@ void MaterialManager::removeLibrary(const QString& libraryName) std::shared_ptr>> MaterialManager::libraryMaterials(const QString& libraryName) { +#if defined(BUILD_MATERIAL_EXTERNAL) + if (_useExternal) { + try { + auto materials = _externalManager->libraryMaterials(libraryName); + if (materials) { + return materials; + } + } + catch (const LibraryNotFound& e) { + } + } +#endif return _localManager->libraryMaterials(libraryName); } @@ -241,13 +318,42 @@ MaterialManager::libraryMaterials(const QString& libraryName, const std::shared_ptr& filter, const MaterialFilterOptions& options) { +#if defined(BUILD_MATERIAL_EXTERNAL) + if (_useExternal) { + try { + auto materials = _externalManager->libraryMaterials(libraryName, filter, options); + if (materials) { + return materials; + } + } + catch (const LibraryNotFound& e) { + } + } +#endif return _localManager->libraryMaterials(libraryName, filter, options); } +#if defined(BUILD_MATERIAL_EXTERNAL) +bool MaterialManager::isLocalLibrary(const QString& libraryName) +{ + if (_useExternal) { + try { + auto lib = _externalManager->getLibrary(libraryName); + if (lib) { + return false; + } + } + catch (const LibraryNotFound& e) { + } + } + return true; +} +#else bool MaterialManager::isLocalLibrary(const QString& /*libraryName*/) { return true; } +#endif //===== // @@ -346,6 +452,15 @@ MaterialManager::getLocalMaterials() const std::shared_ptr MaterialManager::getMaterial(const QString& uuid) const { +#if defined(BUILD_MATERIAL_EXTERNAL) + if (_useExternal) { + auto material = _externalManager->getMaterial(uuid); + if (material) { + return material; + } + } +#endif + // We really want to return the local material if not found, such as for User folder models return _localManager->getMaterial(uuid); } @@ -443,3 +558,55 @@ void MaterialManager::dereference(std::shared_ptr material) const { _localManager->dereference(material); } + +#if defined(BUILD_MATERIAL_EXTERNAL) +void MaterialManager::migrateToExternal(const std::shared_ptr& library) +{ + _externalManager->createLibrary(library->getName(), + library->getIconPath(), + library->isReadOnly()); + + auto materials = _localManager->libraryMaterials(library->getName()); + for (auto& tuple : *materials) { + auto uuid = std::get<0>(tuple); + auto path = std::get<1>(tuple); + auto name = std::get<2>(tuple); + Base::Console().Log("\t('%s', '%s', '%s')\n", + uuid.toStdString().c_str(), + path.toStdString().c_str(), + name.toStdString().c_str()); + + auto material = _localManager->getMaterial(uuid); + if (!material->isOldFormat()) { + _externalManager->migrateMaterial(library->getName(), path, material); + } + } +} + +void MaterialManager::validateMigration(const std::shared_ptr& library) +{ + auto materials = _localManager->libraryMaterials(library->getName()); + for (auto& tuple : *materials) { + auto uuid = std::get<0>(tuple); + auto path = std::get<1>(tuple); + auto name = std::get<2>(tuple); + Base::Console().Log("\t('%s', '%s', '%s')\n", + uuid.toStdString().c_str(), + path.toStdString().c_str(), + name.toStdString().c_str()); + + auto material = _localManager->getMaterial(uuid); + if (!material->isOldFormat()) { + auto externalMaterial = _externalManager->getMaterial(uuid); + material->validate(externalMaterial); + } + } +} + +// Cache stats +double MaterialManager::materialHitRate() +{ + initManagers(); + return _externalManager->materialHitRate(); +} +#endif diff --git a/src/Mod/Material/App/MaterialManager.h b/src/Mod/Material/App/MaterialManager.h index c3ec6bb517..eb8ca61995 100644 --- a/src/Mod/Material/App/MaterialManager.h +++ b/src/Mod/Material/App/MaterialManager.h @@ -51,7 +51,7 @@ class MaterialManagerLocal; class MaterialFilter; class MaterialFilterOptions; -class MaterialsExport MaterialManager: public Base::BaseClass +class MaterialsExport MaterialManager: public Base::BaseClass, ParameterGrp::ObserverType { TYPESYSTEM_HEADER_WITH_OVERRIDE(); @@ -136,14 +136,31 @@ public: void dereference(std::shared_ptr material) const; void dereference() const; + /// Observer message from the ParameterGrp + void OnChange(ParameterGrp::SubjectType& rCaller, ParameterGrp::MessageType Reason) override; + +#if defined(BUILD_MATERIAL_EXTERNAL) + void migrateToExternal(const std::shared_ptr& library); + void validateMigration(const std::shared_ptr& library); + + // Cache functions + static double materialHitRate(); +#endif + private: MaterialManager(); static void initManagers(); static MaterialManager* _manager; +#if defined(BUILD_MATERIAL_EXTERNAL) + static std::unique_ptr _externalManager; +#endif static std::unique_ptr _localManager; static QMutex _mutex; + static bool _useExternal; + + ParameterGrp::handle _hGrp; }; } // namespace Materials diff --git a/src/Mod/Material/App/MaterialManagerExternal.cpp b/src/Mod/Material/App/MaterialManagerExternal.cpp new file mode 100644 index 0000000000..de90466e26 --- /dev/null +++ b/src/Mod/Material/App/MaterialManagerExternal.cpp @@ -0,0 +1,210 @@ +/*************************************************************************** + * Copyright (c) 2023 David Carter * + * * + * 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 * + * . * + * * + **************************************************************************/ + +#include "PreCompiled.h" +#ifndef _PreComp_ +#endif + +#include + +#include + +#include "Exceptions.h" +#include "ExternalManager.h" +#include "MaterialLibrary.h" +#include "MaterialManagerExternal.h" + + +using namespace Materials; + +/* TRANSLATOR Material::Materials */ + +QMutex MaterialManagerExternal::_mutex; +LRU::Cache> + MaterialManagerExternal::_cache(DEFAULT_CACHE_SIZE); + +TYPESYSTEM_SOURCE(Materials::MaterialManagerExternal, Base::BaseClass) + +MaterialManagerExternal::MaterialManagerExternal() +{ + initCache(); +} + +void MaterialManagerExternal::initCache() +{ + QMutexLocker locker(&_mutex); + + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/Mod/Material/ExternalInterface"); + auto cacheSize = hGrp->GetInt("MaterialCacheSize", DEFAULT_CACHE_SIZE); + _cache.capacity(cacheSize); + + _cache.monitor(); +} + +void MaterialManagerExternal::cleanup() +{} + +void MaterialManagerExternal::refresh() +{ + resetCache(); +} + +//===== +// +// Library management +// +//===== + +std::shared_ptr>> MaterialManagerExternal::getLibraries() +{ + auto libraryList = std::make_shared>>(); + try { + auto externalLibraries = ExternalManager::getManager()->libraries(); + for (auto& entry : *externalLibraries) { + auto library = std::make_shared(*entry); + libraryList->push_back(library); + } + } + catch (const LibraryNotFound& e) { + } + catch (const ConnectionError& e) { + } + + return libraryList; +} + +std::shared_ptr>> +MaterialManagerExternal::getMaterialLibraries() +{ + auto libraryList = std::make_shared>>(); + try { + auto externalLibraries = ExternalManager::getManager()->materialLibraries(); + for (auto& entry : *externalLibraries) { + auto library = std::make_shared(*entry); + libraryList->push_back(library); + } + } + catch (const LibraryNotFound& e) { + } + catch (const ConnectionError& e) { + } + + return libraryList; +} + +std::shared_ptr MaterialManagerExternal::getLibrary(const QString& name) const +{ + try { + auto lib = ExternalManager::getManager()->getLibrary(name); + auto library = std::make_shared(*lib); + return library; + } + catch (const LibraryNotFound& e) { + throw LibraryNotFound(e); + } + catch (const ConnectionError& e) { + throw LibraryNotFound(e.what()); + } +} + +void MaterialManagerExternal::createLibrary(const QString& libraryName, + const QString& icon, + bool readOnly) +{ + ExternalManager::getManager()->createLibrary(libraryName, icon, readOnly); +} + +std::shared_ptr>> +MaterialManagerExternal::libraryMaterials(const QString& libraryName) +{ + return ExternalManager::getManager()->libraryMaterials(libraryName); +} + +std::shared_ptr>> +MaterialManagerExternal::libraryMaterials(const QString& libraryName, + const std::shared_ptr& filter, + const MaterialFilterOptions& options) +{ + return ExternalManager::getManager()->libraryMaterials(libraryName, filter, options); +} + +//===== +// +// Material management +// +//===== + +std::shared_ptr MaterialManagerExternal::getMaterial(const QString& uuid) const +{ + if (_cache.contains(uuid.toStdString())) { + return _cache.lookup(uuid.toStdString()); + } + try { + auto material = ExternalManager::getManager()->getMaterial(uuid); + _cache.emplace(uuid.toStdString(), material); + return material; + } + catch (const MaterialNotFound& e) { + _cache.emplace(uuid.toStdString(), nullptr); + return nullptr; + } + catch (const ConnectionError& e) { + _cache.emplace(uuid.toStdString(), nullptr); + return nullptr; + } +} + +void MaterialManagerExternal::addMaterial(const QString& libraryName, + const QString& path, + const std::shared_ptr& material) +{ + _cache.erase(material->getUUID().toStdString()); + ExternalManager::getManager()->addMaterial(libraryName, path, material); +} + +void MaterialManagerExternal::migrateMaterial(const QString& libraryName, + const QString& path, + const std::shared_ptr& material) +{ + _cache.erase(material->getUUID().toStdString()); + ExternalManager::getManager()->migrateMaterial(libraryName, path, material); +} + +//===== +// +// Cache management +// +//===== + +void MaterialManagerExternal::resetCache() +{ + _cache.clear(); +} + +double MaterialManagerExternal::materialHitRate() +{ + auto hitRate = _cache.stats().hit_rate(); + if (std::isnan(hitRate)) { + return 0; + } + return hitRate; +} diff --git a/src/Mod/Material/App/MaterialManagerExternal.h b/src/Mod/Material/App/MaterialManagerExternal.h new file mode 100644 index 0000000000..54fc4c9799 --- /dev/null +++ b/src/Mod/Material/App/MaterialManagerExternal.h @@ -0,0 +1,102 @@ +/*************************************************************************** + * Copyright (c) 2024 David Carter * + * * + * 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 * + * . * + * * + **************************************************************************/ + +#ifndef MATERIAL_MATERIALMANAGEREXTERNAl_H +#define MATERIAL_MATERIALMANAGEREXTERNAl_H + +#include +#include + +#include + +#include + +#include "FolderTree.h" +#include "Materials.h" + +class QMutex; + +namespace App +{ +class Material; +} + +namespace Materials +{ + +class MaterialLibrary; +class MaterialLibraryExternal; +class MaterialFilter; +class MaterialFilterOptions; + +class MaterialsExport MaterialManagerExternal: public Base::BaseClass +{ + TYPESYSTEM_HEADER_WITH_OVERRIDE(); + +public: + MaterialManagerExternal(); + ~MaterialManagerExternal() override = default; + + static void cleanup(); + void refresh(); + + static const int DEFAULT_CACHE_SIZE = 100; + + // Library management + std::shared_ptr>> getLibraries(); + std::shared_ptr>> getMaterialLibraries(); + std::shared_ptr getLibrary(const QString& name) const; + void createLibrary(const QString& libraryName, const QString& icon, bool readOnly = true); + std::shared_ptr>> + libraryMaterials(const QString& libraryName); + std::shared_ptr>> + libraryMaterials(const QString& libraryName, + const std::shared_ptr& filter, + const MaterialFilterOptions& options); + + // Folder management + + // Material management + std::shared_ptr getMaterial(const QString& uuid) const; + void addMaterial(const QString& libraryName, + const QString& path, + const std::shared_ptr& material); + void migrateMaterial(const QString& libraryName, + const QString& path, + const std::shared_ptr& material); + + // Cache functions + void resetCache(); + double materialHitRate(); + +private: + static void initCache(); + + static QMutex _mutex; + + // Older platforms (Ubuntu 20.04) can't use QString as the index + // due to a lack of a move constructor + static LRU::Cache> _cache; +}; + +} // namespace Materials + +#endif // MATERIAL_MATERIALMANAGEREXTERNAl_H \ No newline at end of file diff --git a/src/Mod/Material/App/MaterialPyImp.cpp b/src/Mod/Material/App/MaterialPyImp.cpp index 5a267d29ed..cc33e4040b 100644 --- a/src/Mod/Material/App/MaterialPyImp.cpp +++ b/src/Mod/Material/App/MaterialPyImp.cpp @@ -551,6 +551,7 @@ PyObject* MaterialPy::setAppearanceValue(PyObject* args) Py_INCREF(Py_None); return Py_None; } + PyObject* MaterialPy::setValue(PyObject* args) { char* name; @@ -595,7 +596,8 @@ PyObject* MaterialPy::setValue(PyObject* args) Py_Return; } - PyErr_SetString(PyExc_TypeError, "Either a string, a list, or an array are expected"); + PyErr_SetString(PyExc_TypeError, + "Either a string, a list, or an array are expected"); return nullptr; } @@ -669,4 +671,4 @@ PyObject* MaterialPy::mapping_subscript(PyObject* self, PyObject* key) { Py::Dict dict = static_cast(self)->getProperties(); return Py::new_reference_to(dict.getItem(Py::Object(key))); -} \ No newline at end of file +} diff --git a/src/Mod/Material/App/MaterialValue.h b/src/Mod/Material/App/MaterialValue.h index ffe88e537e..a84b64f4bb 100644 --- a/src/Mod/Material/App/MaterialValue.h +++ b/src/Mod/Material/App/MaterialValue.h @@ -112,7 +112,7 @@ public: // The precision is based on the value from the original materials editor static const int PRECISION = 6; - + void validate(const MaterialValue& other) const; protected: diff --git a/src/Mod/Material/App/ModelLibrary.h b/src/Mod/Material/App/ModelLibrary.h index 4264623084..e23e30484c 100644 --- a/src/Mod/Material/App/ModelLibrary.h +++ b/src/Mod/Material/App/ModelLibrary.h @@ -46,7 +46,7 @@ class MaterialsExport ModelLibrary: public Library, public: ModelLibrary(); - ModelLibrary(const Library& other); + ModelLibrary(const Library& library); ModelLibrary(const QString& libraryName, const QString& dir, const QString& icon, diff --git a/src/Mod/Material/App/ModelManagerLocal.cpp b/src/Mod/Material/App/ModelManagerLocal.cpp index cbb4178d1b..0890c333f2 100644 --- a/src/Mod/Material/App/ModelManagerLocal.cpp +++ b/src/Mod/Material/App/ModelManagerLocal.cpp @@ -121,7 +121,7 @@ void ModelManagerLocal::createLibrary(const QString& libraryName, void ModelManagerLocal::renameLibrary(const QString& libraryName, const QString& newName) { for (auto& library : *_libraryList) { - if (library->getName() == libraryName) { + if (library->isName(libraryName)) { library->setName(newName); return; } @@ -133,7 +133,7 @@ void ModelManagerLocal::renameLibrary(const QString& libraryName, const QString& void ModelManagerLocal::changeIcon(const QString& libraryName, const QString& icon) { for (auto& library : *_libraryList) { - if (library->getName() == libraryName) { + if (library->isName(libraryName)) { library->setIconPath(icon); return; } @@ -145,7 +145,7 @@ void ModelManagerLocal::changeIcon(const QString& libraryName, const QString& ic void ModelManagerLocal::removeLibrary(const QString& libraryName) { for (auto& library : *_libraryList) { - if (library->getName() == libraryName) { + if (library->isName(libraryName)) { _libraryList->remove(library); // At this point we should rebuild the model map @@ -163,7 +163,7 @@ ModelManagerLocal::libraryModels(const QString& libraryName) for (auto& it : *_modelMap) { // This is needed to resolve cyclic dependencies - if (it.second->getLibrary()->getName() == libraryName) { + if (it.second->getLibrary()->isName(libraryName)) { models->push_back( std::tuple(it.first, it.second->getDirectory(), it.second->getName())); } @@ -217,7 +217,7 @@ std::shared_ptr ModelManagerLocal::getModelByPath(const QString& path, std::shared_ptr ModelManagerLocal::getLibrary(const QString& name) const { for (auto& library : *_libraryList) { - if (library->getName() == name) { + if (library->isName(name)) { return library; } } diff --git a/src/Mod/Material/Gui/AppMatGui.cpp b/src/Mod/Material/Gui/AppMatGui.cpp index 68973a0084..8d6626d3c4 100644 --- a/src/Mod/Material/Gui/AppMatGui.cpp +++ b/src/Mod/Material/Gui/AppMatGui.cpp @@ -38,6 +38,10 @@ #include "MaterialTreeWidget.h" #include "MaterialTreeWidgetPy.h" +#if defined(BUILD_MATERIAL_EXTERNAL) +#include "DlgSettingsExternal.h" +#endif + // use a different name to CreateCommand() void CreateMaterialCommands(); @@ -108,6 +112,10 @@ PyMOD_INIT_FUNC(MatGui) QT_TRANSLATE_NOOP("QObject", "Material")); new Gui::PrefPageProducer( QT_TRANSLATE_NOOP("QObject", "Material")); +#if defined(BUILD_MATERIAL_EXTERNAL) + new Gui::PrefPageProducer( + QT_TRANSLATE_NOOP("QObject", "Material")); +#endif // add resources and reloads the translators loadMaterialResource(); diff --git a/src/Mod/Material/Gui/CMakeLists.txt b/src/Mod/Material/Gui/CMakeLists.txt index b867b1a11b..891a32a99c 100644 --- a/src/Mod/Material/Gui/CMakeLists.txt +++ b/src/Mod/Material/Gui/CMakeLists.txt @@ -4,6 +4,10 @@ else(MSVC) add_definitions(-DHAVE_LIMITS_H -DHAVE_CONFIG_H) endif(MSVC) +if(BUILD_MATERIAL_EXTERNAL) + add_definitions(-DBUILD_MATERIAL_EXTERNAL) +endif(BUILD_MATERIAL_EXTERNAL) + include_directories( ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src @@ -54,6 +58,12 @@ set(MatGui_UIC_SRCS ModelSelect.ui TextEdit.ui ) +if(BUILD_MATERIAL_EXTERNAL) + list(APPEND MatGui_UIC_SRCS + DlgSettingsExternal.ui + TaskMigrateExternal.ui + ) +endif(BUILD_MATERIAL_EXTERNAL) SET(MatGui_SRCS ${Python_SRCS} @@ -126,6 +136,16 @@ SET(MatGui_SRCS WorkbenchManipulator.cpp WorkbenchManipulator.h ) +if(BUILD_MATERIAL_EXTERNAL) + list(APPEND MatGui_SRCS + DlgSettingsExternal.cpp + DlgSettingsExternal.h + DlgSettingsExternal.ui + TaskMigrateExternal.cpp + TaskMigrateExternal.h + TaskMigrateExternal.ui + ) +endif(BUILD_MATERIAL_EXTERNAL) if(FREECAD_USE_PCH) add_definitions(-D_PreComp_) diff --git a/src/Mod/Material/Gui/Command.cpp b/src/Mod/Material/Gui/Command.cpp index 37d1059cc7..ae1ddcda95 100644 --- a/src/Mod/Material/Gui/Command.cpp +++ b/src/Mod/Material/Gui/Command.cpp @@ -36,6 +36,7 @@ #include "MaterialSave.h" #include "MaterialsEditor.h" #include "ModelSelect.h" +#include "TaskMigrateExternal.h" //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -187,6 +188,37 @@ bool CmdInspectMaterial::isActive() return (Gui::Control().activeDialog() == nullptr); } +//=========================================================================== +// Materials_MigrateToDatabase +//=========================================================================== + +#if defined(BUILD_MATERIAL_EXTERNAL) +DEF_STD_CMD_A(CmdMigrateToExternal) + +CmdMigrateToExternal::CmdMigrateToExternal() + : Command("Materials_MigrateToExternal") +{ + sGroup = "Standard-View"; + sMenuText = QT_TR_NOOP("Migrate..."); + sToolTipText = QT_TR_NOOP("Migrate the materials to the external materials manager"); + sWhatsThis = "Materials_MigrateToDatabase"; + sStatusTip = QT_TR_NOOP("Migrate existing materials to the external materials manager"); + // sPixmap = "Materials_Edit"; +} + +void CmdMigrateToExternal::activated(int iMsg) +{ + Q_UNUSED(iMsg); + MatGui::TaskMigrateExternal* dlg = new MatGui::TaskMigrateExternal(); + Gui::Control().showDialog(dlg); +} + +bool CmdMigrateToExternal::isActive() +{ + return true; +} +#endif + //--------------------------------------------------------------- void CreateMaterialCommands() @@ -198,4 +230,7 @@ void CreateMaterialCommands() rcCmdMgr.addCommand(new StdCmdSetMaterial()); rcCmdMgr.addCommand(new CmdInspectAppearance()); rcCmdMgr.addCommand(new CmdInspectMaterial()); +#if defined(BUILD_MATERIAL_EXTERNAL) + rcCmdMgr.addCommand(new CmdMigrateToExternal()); +#endif } diff --git a/src/Mod/Material/Gui/DlgSettingsExternal.cpp b/src/Mod/Material/Gui/DlgSettingsExternal.cpp new file mode 100644 index 0000000000..ecb4423e46 --- /dev/null +++ b/src/Mod/Material/Gui/DlgSettingsExternal.cpp @@ -0,0 +1,133 @@ +/*************************************************************************** + * Copyright (c) 2023 David Carter * + * * + * 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 * + * . * + * * + **************************************************************************/ + +#include "PreCompiled.h" +#ifndef _PreComp_ +#endif + +#include + +#include +#include + +#include "DlgSettingsExternal.h" +#include "ui_DlgSettingsExternal.h" + + +using namespace MatGui; + +DlgSettingsExternal::DlgSettingsExternal(QWidget* parent) + : PreferencePage(parent) + , ui(new Ui_DlgSettingsExternal) +{ + ui->setupUi(this); +} + +DlgSettingsExternal::~DlgSettingsExternal() +{} + +void DlgSettingsExternal::saveSettings() +{ + ParameterGrp::handle hGrp = + App::GetApplication().GetParameterGroupByPath(getPreferences().c_str()); + + ui->spinModelCacheSize->onSave(); + ui->spinMaterialCacheSize->onSave(); + + bool useExternal = ui->groupExternal->isChecked(); + hGrp->SetBool("UseExternal", useExternal); + hGrp->SetASCII("Current", ui->comboInterface->currentText().toStdString()); +} + +QString DlgSettingsExternal::toPerCent(double value) const +{ + QString pcString; + pcString.setNum(int(value * 100.0)); + pcString += QLatin1String("%"); + + return pcString; +} + +void DlgSettingsExternal::loadSettings() +{ + ParameterGrp::handle hGrp = + App::GetApplication().GetParameterGroupByPath(getPreferences().c_str()); + + loadInterfaces(); + + bool useExternal = hGrp->GetBool("UseExternal", false); + ui->groupExternal->setChecked(useExternal); + + auto cacheSize = hGrp->GetInt("ModelCacheSize", 100); + ui->spinModelCacheSize->setValue(cacheSize); + cacheSize = hGrp->GetInt("MaterialCacheSize", 100); + ui->spinMaterialCacheSize->setValue(cacheSize); + + // Cache stats + auto hitRate = Materials::ModelManager::modelHitRate(); + ui->inputModelCacheHitRate->setText(toPerCent(hitRate)); + hitRate = Materials::MaterialManager::materialHitRate(); + ui->inputMaterialCacheHitRate->setText(toPerCent(hitRate)); +} + +void DlgSettingsExternal::loadInterfaces() +{ + ParameterGrp::handle hGrp = + App::GetApplication().GetParameterGroupByPath(getPreferencesInterfaces().c_str()); + + ui->comboInterface->clear(); + ui->comboInterface->addItem(tr("None")); + + for (auto& group : hGrp->GetGroups()) { + auto moduleName = QString::fromStdString(group->GetGroupName()); + + ui->comboInterface->addItem(moduleName); + } + + hGrp = App::GetApplication().GetParameterGroupByPath(getPreferences().c_str()); + auto current = hGrp->GetASCII("Current", "None"); + ui->comboInterface->setCurrentText(QString::fromStdString(current)); +} + +std::string DlgSettingsExternal::getPreferences() const +{ + return "User parameter:BaseApp/Preferences/Mod/Material/ExternalInterface"; +} + +std::string DlgSettingsExternal::getPreferencesInterfaces() const +{ + return "User parameter:BaseApp/Preferences/Mod/Material/ExternalInterface/Interfaces"; +} + +/** + * Sets the strings of the subwidgets using the current language. + */ +void DlgSettingsExternal::changeEvent(QEvent* e) +{ + if (e->type() == QEvent::LanguageChange) { + ui->retranslateUi(this); + } + else { + QWidget::changeEvent(e); + } +} + +#include "moc_DlgSettingsExternal.cpp" diff --git a/src/Mod/Material/Gui/DlgSettingsExternal.h b/src/Mod/Material/Gui/DlgSettingsExternal.h new file mode 100644 index 0000000000..04aad01461 --- /dev/null +++ b/src/Mod/Material/Gui/DlgSettingsExternal.h @@ -0,0 +1,58 @@ +/*************************************************************************** + * Copyright (c) 2023 David Carter * + * * + * 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 * + * . * + * * + **************************************************************************/ + +#ifndef MATGUI_DLGSETTINGSEXTERNAL_H +#define MATGUI_DLGSETTINGSEXTERNAL_H + +#include +#include + + +namespace MatGui +{ +class Ui_DlgSettingsExternal; + +class DlgSettingsExternal: public Gui::Dialog::PreferencePage +{ + Q_OBJECT + +public: + explicit DlgSettingsExternal(QWidget* parent = nullptr); + ~DlgSettingsExternal() override; + +protected: + void saveSettings() override; + void loadSettings() override; + void loadInterfaces(); + void changeEvent(QEvent* e) override; + + std::string getPreferences() const; + std::string getPreferencesInterfaces() const; + +private: + QString toPerCent(double value) const; + + std::unique_ptr ui; +}; + +} // namespace MatGui + +#endif // MATGUI_DLGSETTINGSDATABASE_H diff --git a/src/Mod/Material/Gui/DlgSettingsExternal.ui b/src/Mod/Material/Gui/DlgSettingsExternal.ui new file mode 100644 index 0000000000..e714904929 --- /dev/null +++ b/src/Mod/Material/Gui/DlgSettingsExternal.ui @@ -0,0 +1,183 @@ + + + MatGui::DlgSettingsExternal + + + + 0 + 0 + 400 + 291 + + + + External Interface + + + + + + Use External Interface + + + true + + + + + + External Interface + + + + + + + + + + Current + + + Mod/Material/ExternalInterface + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Cache + + + + + + true + + + + + + + 4096 + + + MaterialCacheSize + + + Mod/Material/ExternalInterface + + + + + + + Model Cache Size + + + + + + + Hit Rate + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 4096 + + + ModelCacheSize + + + Mod/Material/ExternalInterface + + + + + + + + + + Hit Rate + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Material Cache Size + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Gui::PrefSpinBox + QSpinBox +
Gui/PrefWidgets.h
+
+ + Gui::PrefComboBox + QComboBox +
Gui/PrefWidgets.h
+
+
+ + +
diff --git a/src/Mod/Material/Gui/TaskMigrateExternal.cpp b/src/Mod/Material/Gui/TaskMigrateExternal.cpp new file mode 100644 index 0000000000..d9be8f0af1 --- /dev/null +++ b/src/Mod/Material/Gui/TaskMigrateExternal.cpp @@ -0,0 +1,209 @@ +/*************************************************************************** + * Copyright (c) 2024 David Carter * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "PreCompiled.h" +#ifndef _PreComp_ +#endif + +#include + +#include +#include + +#include "TaskMigrateExternal.h" +#include "ui_TaskMigrateExternal.h" + + +using namespace MatGui; + +/* TRANSLATOR MatGui::DlgMigrateExternal */ + +DlgMigrateExternal::DlgMigrateExternal(QWidget* parent) + : QWidget(parent) + , ui(new Ui_TaskMigrateExternal) +{ + ui->setupUi(this); + + showLibraries(); +} + +void DlgMigrateExternal::showLibraries() +{ + auto materialLibraries = Materials::MaterialManager::getManager().getLocalLibraries(); + for (auto library : *materialLibraries) { + if (library->getName() != QLatin1String("User")) { + auto item = new QListWidgetItem(library->getName()); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setCheckState(Qt::Checked); + item->setData(Qt::UserRole, QVariant::fromValue(library)); + ui->listMaterialLibraries->addItem(item); + } + } + + auto modelLibraries = Materials::ModelManager::getManager().getLocalLibraries(); + for (auto library : *modelLibraries) { + if (library->getName() != QLatin1String("User")) { + auto item = new QListWidgetItem(library->getName()); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setCheckState(Qt::Checked); + item->setData(Qt::UserRole, QVariant::fromValue(library)); + ui->listModelLibraries->addItem(item); + } + } +} + +void DlgMigrateExternal::migrate() +{ + try { + statusUpdate(tr("Migrating Models...")); + for (int row = 0; row < ui->listModelLibraries->count(); row++) { + auto item = ui->listModelLibraries->item(row); + if (item->checkState() == Qt::Checked) { + auto library = + item->data(Qt::UserRole).value>(); + statusUpdate(tr(" Library: ") + library->getName()); + Materials::ModelManager::getManager().migrateToExternal(library); + } + } + statusUpdate(tr("done")); + + statusUpdate(tr("Validating Models...")); + for (int row = 0; row < ui->listModelLibraries->count(); row++) { + auto item = ui->listModelLibraries->item(row); + if (item->checkState() == Qt::Checked) { + auto library = + item->data(Qt::UserRole).value>(); + statusUpdate(tr(" Library: ") + library->getName()); + Materials::ModelManager::getManager().validateMigration(library); + } + } + statusUpdate(tr("done")); + + statusUpdate(tr("Migrating Materials...")); + for (int row = 0; row < ui->listMaterialLibraries->count(); row++) { + auto item = ui->listMaterialLibraries->item(row); + if (item->checkState() == Qt::Checked) { + auto library = + item->data(Qt::UserRole).value>(); + statusUpdate(tr(" Library: ") + library->getName()); + Materials::MaterialManager::getManager().migrateToExternal(library); + } + } + statusUpdate(tr("done")); + + statusUpdate(tr("Validating Materials...")); + for (int row = 0; row < ui->listMaterialLibraries->count(); row++) { + auto item = ui->listMaterialLibraries->item(row); + if (item->checkState() == Qt::Checked) { + auto library = + item->data(Qt::UserRole).value>(); + statusUpdate(tr(" Library: ") + library->getName()); + Materials::MaterialManager::getManager().validateMigration(library); + } + } + statusUpdate(tr("done")); + } + catch (const Materials::ConnectionError& e) { + statusUpdate(QString::fromStdString(e.what())); + statusUpdate(tr("Aborted")); + } + catch (const Materials::CreationError& e) { + statusUpdate(QString::fromStdString(e.what())); + statusUpdate(tr("Aborted")); + } + catch (const Materials::InvalidModel& e) { + statusUpdate(QString::fromStdString(e.what())); + statusUpdate(tr("Aborted")); + } + catch (const Materials::InvalidMaterial& e) { + statusUpdate(QString::fromStdString(e.what())); + statusUpdate(tr("Aborted")); + } + catch (const Materials::InvalidProperty& e) { + statusUpdate(QString::fromStdString(e.what())); + statusUpdate(tr("Aborted")); + } + catch (const Base::Exception& e) { + statusUpdate(QString::fromStdString(e.what())); + statusUpdate(tr("Unknown exception - Aborted")); + } +} + +void DlgMigrateExternal::statusUpdate(const QString& status) +{ + Base::Console().Log("%s\n", status.toStdString().c_str()); + ui->textStatus->append(status); + + // This is required to update in real time + QCoreApplication::processEvents(); +} + +/* TRANSLATOR MatGui::TaskMigrateExternal */ + +TaskMigrateExternal::TaskMigrateExternal() +{ + _widget = new DlgMigrateExternal(); + addTaskBox(_widget); +} + +QDialogButtonBox::StandardButtons TaskMigrateExternal::getStandardButtons() const +{ + return QDialogButtonBox::Close | QDialogButtonBox::Ok; +} + +void TaskMigrateExternal::modifyStandardButtons(QDialogButtonBox* box) +{ + _migrateButton = box->button(QDialogButtonBox::Ok); + _closeButton = box->button(QDialogButtonBox::Close); + _migrateButton->setText(QApplication::translate("MatGui::TaskMigrateExternal", "&Migrate")); + // connect(btn, &QPushButton::clicked, this, &TaskMigrateExternal::onMigrate); +} + +void TaskMigrateExternal::onMigrate(bool checked) +{ + Q_UNUSED(checked) + + Gui::WaitCursor wc; + QCoreApplication::processEvents(); + + // Disable the buttons during migration + _migrateButton->setEnabled(false); + _closeButton->setEnabled(false); + + _widget->migrate(); + + _migrateButton->setEnabled(true); + _closeButton->setEnabled(true); +} + +bool TaskMigrateExternal::accept() +{ + _widget->migrate(); + return false; +} + +bool TaskMigrateExternal::reject() +{ + return true; +} + +#include "moc_TaskMigrateExternal.cpp" diff --git a/src/Mod/Material/Gui/TaskMigrateExternal.h b/src/Mod/Material/Gui/TaskMigrateExternal.h new file mode 100644 index 0000000000..3a74b6d997 --- /dev/null +++ b/src/Mod/Material/Gui/TaskMigrateExternal.h @@ -0,0 +1,78 @@ +/*************************************************************************** + * Copyright (c) 2024 David Carter * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef MATGUI_TASKMIGRATEEXTERNAL_H +#define MATGUI_TASKMIGRATEEXTERNAL_H + +#include + +#include + +#include + +#include +#include + +namespace MatGui { + +class Ui_TaskMigrateExternal; + +class DlgMigrateExternal: public QWidget +{ + Q_OBJECT + +public: + explicit DlgMigrateExternal(QWidget* parent = nullptr); + ~DlgMigrateExternal() override = default; + void migrate(); + void statusUpdate(const QString& status); + +private: + void showLibraries(); + + std::shared_ptr ui; +}; + +class TaskMigrateExternal: public Gui::TaskView::TaskDialog +{ + Q_OBJECT + +public: + TaskMigrateExternal(); + +public: + bool accept() override; + bool reject() override; + QDialogButtonBox::StandardButtons getStandardButtons() const override; + void modifyStandardButtons(QDialogButtonBox*) override; + void onMigrate(bool checked); + +private: + DlgMigrateExternal* _widget; + QPushButton* _migrateButton; + QPushButton* _closeButton; +}; + +} // namespace MatGui + +#endif // MATGUI_TASKMIGRATEEXTERNAL_H diff --git a/src/Mod/Material/Gui/TaskMigrateExternal.ui b/src/Mod/Material/Gui/TaskMigrateExternal.ui new file mode 100644 index 0000000000..65c82761b3 --- /dev/null +++ b/src/Mod/Material/Gui/TaskMigrateExternal.ui @@ -0,0 +1,63 @@ + + + MatGui::TaskMigrateExternal + + + + 0 + 0 + 400 + 300 + + + + Materials Migration + + + + + + Select Material Libraries + + + + + + + Select material libraries to migrate. Existing materials will not be overwritten. + + + + + + + Select Model Libraries + + + + + + + Select model libraries to migrate. Existing models will not be overwritten. + + + + + + + Status + + + + + + + QTextEdit::NoWrap + + + + + + + + diff --git a/src/Mod/Material/materialtests/TestMaterialFilter.py b/src/Mod/Material/materialtests/TestMaterialFilter.py index 7005ed867e..ca8a95934c 100644 --- a/src/Mod/Material/materialtests/TestMaterialFilter.py +++ b/src/Mod/Material/materialtests/TestMaterialFilter.py @@ -57,6 +57,9 @@ class MaterialFilterTestCases(unittest.TestCase): self.useUserDir = param.GetBool("UseMaterialsFromConfigDir", True) self.useCustomDir = param.GetBool("UseMaterialsFromCustomDir", False) + paramExternal = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Material/ExternalInterface") + self.useExternal = paramExternal.GetBool("UseExternal", False) + filePath = os.path.dirname(__file__) + os.sep testPath = filePath + "Materials" param.SetString("CustomMaterialsDir", testPath) @@ -65,6 +68,8 @@ class MaterialFilterTestCases(unittest.TestCase): param.SetBool("UseMaterialsFromConfigDir", False) param.SetBool("UseMaterialsFromCustomDir", True) + paramExternal.SetBool("UseExternal", False) + self.MaterialManager.refresh() def tearDown(self): @@ -77,6 +82,9 @@ class MaterialFilterTestCases(unittest.TestCase): param.SetBool("UseMaterialsFromConfigDir", self.useUserDir) param.SetBool("UseMaterialsFromCustomDir", self.useCustomDir) + paramExternal = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Material/ExternalInterface") + paramExternal.SetBool("UseExternal", self.useExternal) + self.MaterialManager.refresh() def testFilter(self):