Materials: External interface refinements (#21524)

This commit is contained in:
David Carter
2025-05-24 06:25:44 +00:00
committed by GitHub
parent 77a11efa0b
commit 8ef8f64e9b
37 changed files with 1147 additions and 406 deletions

View File

@@ -300,6 +300,36 @@ public:
~ReplacementError() noexcept override = default;
};
class UpdateError: public Base::Exception
{
public:
UpdateError()
: Base::Exception("Unable to update object")
{}
explicit UpdateError(const char* msg)
: Base::Exception(msg)
{}
explicit UpdateError(const QString& msg)
: Base::Exception(msg.toStdString().c_str())
{}
~UpdateError() noexcept override = default;
};
class MoveError: public Base::Exception
{
public:
MoveError()
: Base::Exception("Unable to move object")
{}
explicit MoveError(const char* msg)
: Base::Exception(msg)
{}
explicit MoveError(const QString& msg)
: Base::Exception(msg.toStdString().c_str())
{}
~MoveError() noexcept override = default;
};
class ConnectionError: public Base::Exception
{
public:

View File

@@ -36,8 +36,10 @@
#include "ExternalManager.h"
#include "MaterialLibrary.h"
#include "MaterialLibraryPy.h"
#include "MaterialManager.h"
#include "MaterialPy.h"
#include "ModelLibrary.h"
#include "ModelManager.h"
#include "ModelPy.h"
#include "MaterialFilterPy.h"
#include "MaterialFilterOptionsPy.h"
@@ -98,10 +100,10 @@ void ExternalManager::getConfiguration()
void ExternalManager::instantiate()
{
_instantiated = false;
Base::Console().Log("Loading external manager...\n");
Base::Console().log("Loading external manager...\n");
if (_moduleName.empty() || _className.empty()) {
Base::Console().Log("External module not defined\n");
Base::Console().log("External module not defined\n");
return;
}
@@ -110,7 +112,7 @@ void ExternalManager::instantiate()
Py::Module mod(PyImport_ImportModule(_moduleName.c_str()), true);
if (mod.isNull()) {
Base::Console().Log(" failed\n");
Base::Console().log(" failed\n");
return;
}
@@ -121,14 +123,14 @@ void ExternalManager::instantiate()
}
if (_instantiated) {
Base::Console().Log("done\n");
Base::Console().log("done\n");
}
else {
Base::Console().Log("failed\n");
Base::Console().log("failed\n");
}
}
catch (Py::Exception& e) {
Base::Console().Log("failed\n");
Base::Console().log("failed\n");
e.clear();
}
}
@@ -168,8 +170,7 @@ ExternalManager* ExternalManager::getManager()
bool ExternalManager::checkMaterialLibraryType(const Py::Object& entry)
{
return entry.hasAttr("name") && entry.hasAttr("icon") && entry.hasAttr("readOnly")
&& entry.hasAttr("timestamp");
return entry.hasAttr("name") && entry.hasAttr("icon") && entry.hasAttr("readOnly");
}
std::shared_ptr<Library>
@@ -180,57 +181,53 @@ ExternalManager::libraryFromObject(const Py::Object& entry)
}
Py::String pyName(entry.getAttr("name"));
Py::Bytes pyIcon(entry.getAttr("icon"));
Py::Bytes pyIcon;
if (entry.getAttr("icon") != Py::None()) {
pyIcon = Py::Bytes(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;
QByteArray icon;
if (!pyIcon.isNone()) {
icon = QString::fromStdString(pyIcon.as_string());
icon = QByteArray(pyIcon.as_std_string().data(), pyIcon.size());
}
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);
auto library = std::make_shared<Library>(libraryName, icon, readOnly);
return library;
}
bool ExternalManager::checkMaterialObjectType(const Py::Object& entry)
bool ExternalManager::checkMaterialLibraryObjectType(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)
LibraryObject ExternalManager::materialLibraryObjectTypeFromObject(const Py::Object& entry)
{
QString uuid;
std::string uuid;
auto pyUUID = entry.getAttr("UUID");
if (!pyUUID.isNone()) {
uuid = QString::fromStdString(pyUUID.as_string());
uuid = pyUUID.as_string();
}
QString path;
std::string path;
auto pyPath = entry.getAttr("path");
if (!pyPath.isNone()) {
path = QString::fromStdString(pyPath.as_string());
path = pyPath.as_string();
}
QString name;
std::string name;
auto pyName = entry.getAttr("name");
if (!pyName.isNone()) {
name = QString::fromStdString(pyName.as_string());
name = pyName.as_string();
}
return std::tuple<QString, QString, QString>(uuid, path, name);
return LibraryObject(uuid, path, name);
}
std::shared_ptr<std::vector<std::shared_ptr<Library>>>
@@ -251,12 +248,13 @@ ExternalManager::libraries()
}
}
else {
Base::Console().Log("\tlibraries() not found\n");
Base::Console().log("\tlibraries() not found\n");
throw ConnectionError();
}
}
catch (Py::Exception& e) {
Base::PyException e1; // extract the Python error text
Base::Console().log("Library error %s", e1.what());
throw LibraryNotFound(e1.what());
}
@@ -280,7 +278,7 @@ std::shared_ptr<std::vector<std::shared_ptr<Library>>> ExternalManager::modelLib
}
}
else {
Base::Console().Log("\tmodelLibraries() not found\n");
Base::Console().log("\tmodelLibraries() not found\n");
throw ConnectionError();
}
}
@@ -309,7 +307,7 @@ std::shared_ptr<std::vector<std::shared_ptr<Library>>> ExternalManager::material
}
}
else {
Base::Console().Log("\tmaterialLibraries() not found\n");
Base::Console().log("\tmaterialLibraries() not found\n");
throw ConnectionError();
}
}
@@ -332,24 +330,26 @@ std::shared_ptr<Library> ExternalManager::getLibrary(const QString& name)
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 result(libraries.apply(args));
Py::Object libObject = result.getItem(0);
auto lib = libraryFromObject(Py::Tuple(libObject));
auto lib = libraryFromObject(result);
return std::make_shared<Library>(*lib);
}
else {
Base::Console().Log("\tgetLibrary() not found\n");
Base::Console().log("\tgetLibrary() not found\n");
throw ConnectionError();
}
}
catch (const InvalidLibrary&) {
throw LibraryNotFound();
}
catch (Py::Exception& e) {
Base::PyException e1; // extract the Python error text
throw CreationError(e1.what());
throw LibraryNotFound(e1.what());
}
}
void ExternalManager::createLibrary(const QString& libraryName, const QString& icon, bool readOnly)
void ExternalManager::createLibrary(const QString& libraryName, const QByteArray& icon, bool readOnly)
{
connect();
@@ -359,12 +359,12 @@ void ExternalManager::createLibrary(const QString& libraryName, const QString& i
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(1, Py::Bytes(icon.data(), icon.size()));
args.setItem(2, Py::Boolean(readOnly));
libraries.apply(args); // No return expected
}
else {
Base::Console().Log("\tcreateLibrary() not found\n");
Base::Console().log("\tcreateLibrary() not found\n");
throw ConnectionError();
}
}
@@ -388,7 +388,7 @@ void ExternalManager::renameLibrary(const QString& libraryName, const QString& n
libraries.apply(args); // No return expected
}
else {
Base::Console().Log("\trenameLibrary() not found\n");
Base::Console().log("\trenameLibrary() not found\n");
throw ConnectionError();
}
}
@@ -398,7 +398,7 @@ void ExternalManager::renameLibrary(const QString& libraryName, const QString& n
}
}
void ExternalManager::changeIcon(const QString& libraryName, const QString& icon)
void ExternalManager::changeIcon(const QString& libraryName, const QByteArray& icon)
{
connect();
@@ -408,11 +408,11 @@ void ExternalManager::changeIcon(const QString& libraryName, const QString& icon
Py::Callable libraries(_managerObject.getAttr("changeIcon"));
Py::Tuple args(2);
args.setItem(0, Py::String(libraryName.toStdString()));
args.setItem(1, Py::String(icon.toStdString()));
args.setItem(1, Py::Bytes(icon.data(), icon.size()));
libraries.apply(args); // No return expected
}
else {
Base::Console().Log("\tchangeIcon() not found\n");
Base::Console().log("\tchangeIcon() not found\n");
throw ConnectionError();
}
}
@@ -435,7 +435,7 @@ void ExternalManager::removeLibrary(const QString& libraryName)
libraries.apply(args); // No return expected
}
else {
Base::Console().Log("\tremoveLibrary() not found\n");
Base::Console().log("\tremoveLibrary() not found\n");
throw ConnectionError();
}
}
@@ -445,10 +445,10 @@ void ExternalManager::removeLibrary(const QString& libraryName)
}
}
std::shared_ptr<std::vector<std::tuple<QString, QString, QString>>>
std::shared_ptr<std::vector<LibraryObject>>
ExternalManager::libraryModels(const QString& libraryName)
{
auto modelList = std::make_shared<std::vector<std::tuple<QString, QString, QString>>>();
auto modelList = std::make_shared<std::vector<LibraryObject>>();
connect();
@@ -461,15 +461,15 @@ ExternalManager::libraryModels(const QString& libraryName)
Py::List list(libraries.apply(args));
for (auto library : list) {
auto entry = Py::Object(library);
if (!checkMaterialObjectType(entry)) {
if (!checkMaterialLibraryObjectType(entry)) {
throw InvalidModel();
}
modelList->push_back(materialObjectTypeFromObject(entry));
modelList->push_back(materialLibraryObjectTypeFromObject(entry));
}
}
else {
Base::Console().Log("\tlibraryModels() not found\n");
Base::Console().log("\tlibraryModels() not found\n");
throw ConnectionError();
}
}
@@ -481,10 +481,10 @@ ExternalManager::libraryModels(const QString& libraryName)
return modelList;
}
std::shared_ptr<std::vector<std::tuple<QString, QString, QString>>>
std::shared_ptr<std::vector<LibraryObject>>
ExternalManager::libraryMaterials(const QString& libraryName)
{
auto materialList = std::make_shared<std::vector<std::tuple<QString, QString, QString>>>();
auto materialList = std::make_shared<std::vector<LibraryObject>>();
connect();
@@ -497,15 +497,15 @@ ExternalManager::libraryMaterials(const QString& libraryName)
Py::List list(libraries.apply(args));
for (auto library : list) {
auto entry = Py::Object(library);
if (!checkMaterialObjectType(entry)) {
if (!checkMaterialLibraryObjectType(entry)) {
throw InvalidMaterial();
}
materialList->push_back(materialObjectTypeFromObject(entry));
materialList->push_back(materialLibraryObjectTypeFromObject(entry));
}
}
else {
Base::Console().Log("\tlibraryMaterials() not found\n");
Base::Console().log("\tlibraryMaterials() not found\n");
throw ConnectionError();
}
}
@@ -517,12 +517,12 @@ ExternalManager::libraryMaterials(const QString& libraryName)
return materialList;
}
std::shared_ptr<std::vector<std::tuple<QString, QString, QString>>>
std::shared_ptr<std::vector<LibraryObject>>
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>>>();
auto materialList = std::make_shared<std::vector<LibraryObject>>();
connect();
@@ -545,15 +545,15 @@ ExternalManager::libraryMaterials(const QString& libraryName,
Py::List list(libraries.apply(args));
for (auto library : list) {
auto entry = Py::Object(library);
if (!checkMaterialObjectType(entry)) {
if (!checkMaterialLibraryObjectType(entry)) {
throw InvalidMaterial();
}
materialList->push_back(materialObjectTypeFromObject(entry));
materialList->push_back(materialLibraryObjectTypeFromObject(entry));
}
}
else {
Base::Console().Log("\tlibraryMaterials() not found\n");
Base::Console().log("\tlibraryMaterials() not found\n");
throw ConnectionError();
}
}
@@ -565,12 +565,162 @@ ExternalManager::libraryMaterials(const QString& libraryName,
return materialList;
}
std::shared_ptr<std::vector<QString>> ExternalManager::libraryFolders(const QString& libraryName)
{
auto folderList = std::make_shared<std::vector<QString>>();
connect();
Base::PyGILStateLocker lock;
try {
if (_managerObject.hasAttr("libraryFolders")) {
Py::Callable folders(_managerObject.getAttr("libraryFolders"));
Py::Tuple args(1);
args.setItem(0, Py::String(libraryName.toStdString()));
Py::List list(folders.apply(args));
for (auto folder : list) {
auto entry = Py::Object(folder);
Py::String pyName(entry.getAttr("name"));
QString folderName;
if (!pyName.isNone()) {
folderName = QString::fromStdString(pyName.as_string());
}
folderList->push_back(folderName);
}
}
else {
Base::Console().log("\tlibraryFolders() not found\n");
throw ConnectionError();
}
}
catch (Py::Exception& e) {
Base::PyException e1; // extract the Python error text
throw LibraryNotFound(e1.what());
}
return folderList;
}
//=====
//
// Folder management
//
//=====
void ExternalManager::createFolder(const QString& libraryName, const QString& path)
{
connect();
Base::PyGILStateLocker lock;
try {
if (_managerObject.hasAttr("createFolder")) {
Py::Callable libraries(_managerObject.getAttr("createFolder"));
Py::Tuple args(2);
args.setItem(0, Py::String(libraryName.toStdString()));
args.setItem(1, Py::String(path.toStdString()));
Py::Object result(libraries.apply(args));
}
else {
Base::Console().log("\tcreateFolder() not found\n");
throw ConnectionError();
}
}
catch (Py::Exception& e) {
Base::PyException e1; // extract the Python error text
throw CreationError(e1.what());
}
}
void ExternalManager::renameFolder(const QString& libraryName,
const QString& oldPath,
const QString& newPath)
{
connect();
Base::PyGILStateLocker lock;
try {
if (_managerObject.hasAttr("renameFolder")) {
Py::Callable libraries(_managerObject.getAttr("renameFolder"));
Py::Tuple args(3);
args.setItem(0, Py::String(libraryName.toStdString()));
args.setItem(1, Py::String(oldPath.toStdString()));
args.setItem(2, Py::String(newPath.toStdString()));
Py::Object result(libraries.apply(args));
}
else {
Base::Console().log("\trenameFolder() not found\n");
throw ConnectionError();
}
}
catch (Py::Exception& e) {
Base::PyException e1; // extract the Python error text
throw RenameError(e1.what());
}
}
void ExternalManager::deleteRecursive(const QString& libraryName, const QString& path)
{
connect();
Base::PyGILStateLocker lock;
try {
if (_managerObject.hasAttr("deleteRecursive")) {
Py::Callable libraries(_managerObject.getAttr("deleteRecursive"));
Py::Tuple args(2);
args.setItem(0, Py::String(libraryName.toStdString()));
args.setItem(1, Py::String(path.toStdString()));
Py::Object result(libraries.apply(args));
}
else {
Base::Console().log("\tdeleteRecursive() not found\n");
throw ConnectionError();
}
}
catch (Py::Exception& e) {
Base::PyException e1; // extract the Python error text
throw DeleteError(e1.what());
}
}
//=====
//
// Model management
//
//=====
bool ExternalManager::checkModelObjectType(const Py::Object& entry)
{
return entry.hasAttr("libraryName") && entry.hasAttr("model");
}
std::shared_ptr<Model> ExternalManager::modelFromObject(const Py::Object& entry,
const QString& uuid)
{
if (!checkModelObjectType(entry)) {
throw InvalidModel();
}
Py::String pyName(entry.getAttr("libraryName"));
Py::Object modelObject(entry.getAttr("model"));
QString libraryName;
if (!pyName.isNone()) {
libraryName = QString::fromStdString(pyName.as_string());
}
// Using this call will use caching, whereas using our class function will not
auto library = ModelManager::getManager().getLibrary(libraryName);
Model* model = static_cast<ModelPy*>(*modelObject)->getModelPtr();
model->setUUID(uuid);
model->setLibrary(library);
auto shared = std::make_shared<Model>(*model);
return shared;
}
std::shared_ptr<Model> ExternalManager::getModel(const QString& uuid)
{
connect();
@@ -581,36 +731,14 @@ std::shared_ptr<Model> ExternalManager::getModel(const QString& uuid)
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 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);
auto shared = modelFromObject(result, uuid);
return shared;
}
else {
Base::Console().Log("\tgetModel() not found\n");
Base::Console().log("\tgetModel() not found\n");
throw ConnectionError();
}
}
@@ -637,7 +765,7 @@ void ExternalManager::addModel(const QString& libraryName,
libraries.apply(args); // No return expected
}
else {
Base::Console().Log("\taddModel() not found\n");
Base::Console().log("\taddModel() not found\n");
throw ConnectionError();
}
}
@@ -664,7 +792,7 @@ void ExternalManager::migrateModel(const QString& libraryName,
libraries.apply(args); // No return expected
}
else {
Base::Console().Log("\tmigrateModel() not found\n");
Base::Console().log("\tmigrateModel() not found\n");
throw ConnectionError();
}
}
@@ -674,12 +802,174 @@ void ExternalManager::migrateModel(const QString& libraryName,
}
}
void ExternalManager::updateModel(const QString& libraryName,
const QString& path,
const std::shared_ptr<Model>& model)
{
connect();
Base::PyGILStateLocker lock;
try {
if (_managerObject.hasAttr("updateModel")) {
Py::Callable libraries(_managerObject.getAttr("updateModel"));
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("\tupdateModel() not found\n");
throw ConnectionError();
}
}
catch (Py::Exception& e) {
Base::PyException e1; // extract the Python error text
throw UpdateError(e1.what());
}
}
void ExternalManager::setModelPath(const QString& libraryName,
const QString& path,
const QString& uuid)
{
connect();
Base::PyGILStateLocker lock;
try {
if (_managerObject.hasAttr("setModelPath")) {
Py::Callable libraries(_managerObject.getAttr("setModelPath"));
Py::Tuple args(3);
args.setItem(0, Py::String(libraryName.toStdString()));
args.setItem(1, Py::String(path.toStdString()));
args.setItem(2, Py::String(uuid.toStdString()));
libraries.apply(args); // No return expected
}
else {
Base::Console().log("\tsetModelPath() not found\n");
throw ConnectionError();
}
}
catch (Py::Exception& e) {
Base::PyException e1; // extract the Python error text
throw UpdateError(e1.what());
}
}
void ExternalManager::renameModel(const QString& libraryName,
const QString& name,
const QString& uuid)
{
connect();
Base::PyGILStateLocker lock;
try {
if (_managerObject.hasAttr("renameModel")) {
Py::Callable libraries(_managerObject.getAttr("renameModel"));
Py::Tuple args(3);
args.setItem(0, Py::String(libraryName.toStdString()));
args.setItem(1, Py::String(name.toStdString()));
args.setItem(2, Py::String(uuid.toStdString()));
libraries.apply(args); // No return expected
}
else {
Base::Console().log("\trenameModel() not found\n");
throw ConnectionError();
}
}
catch (Py::Exception& e) {
Base::PyException e1; // extract the Python error text
throw RenameError(e1.what());
}
}
void ExternalManager::moveModel(const QString& libraryName,
const QString& path,
const QString& uuid)
{
connect();
Base::PyGILStateLocker lock;
try {
if (_managerObject.hasAttr("moveModel")) {
Py::Callable libraries(_managerObject.getAttr("moveModel"));
Py::Tuple args(3);
args.setItem(0, Py::String(libraryName.toStdString()));
args.setItem(1, Py::String(path.toStdString()));
args.setItem(2, Py::String(uuid.toStdString()));
libraries.apply(args); // No return expected
}
else {
Base::Console().log("\tmoveModel() not found\n");
throw ConnectionError();
}
}
catch (Py::Exception& e) {
Base::PyException e1; // extract the Python error text
throw MoveError(e1.what());
}
}
void ExternalManager::removeModel(const QString& uuid)
{
connect();
Base::PyGILStateLocker lock;
try {
if (_managerObject.hasAttr("removeModel")) {
Py::Callable libraries(_managerObject.getAttr("removeModel"));
Py::Tuple args(1);
args.setItem(0, Py::String(uuid.toStdString()));
libraries.apply(args); // No return expected
}
else {
Base::Console().log("\tremoveModel() not found\n");
throw ConnectionError();
}
}
catch (Py::Exception& e) {
Base::PyException e1; // extract the Python error text
throw DeleteError(e1.what());
}
}
//=====
//
// Material management
//
//=====
bool ExternalManager::checkMaterialObjectType(const Py::Object& entry)
{
return entry.hasAttr("libraryName") && entry.hasAttr("material");
}
std::shared_ptr<Material> ExternalManager::materialFromObject(const Py::Object& entry,
const QString& uuid)
{
if (!checkMaterialObjectType(entry)) {
throw InvalidMaterial();
}
Py::String pyName(entry.getAttr("libraryName"));
Py::Object materialObject(entry.getAttr("material"));
QString libraryName;
if (!pyName.isNone()) {
libraryName = QString::fromStdString(pyName.as_string());
}
// Using this call will use caching, whereas using our class function will not
auto library = MaterialManager::getManager().getLibrary(libraryName);
Material* material = static_cast<MaterialPy*>(*materialObject)->getMaterialPtr();
material->setUUID(uuid);
material->setLibrary(library);
auto shared = std::make_shared<Material>(*material);
return shared;
}
std::shared_ptr<Material> ExternalManager::getMaterial(const QString& uuid)
{
connect();
@@ -690,36 +980,14 @@ std::shared_ptr<Material> ExternalManager::getMaterial(const QString& uuid)
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 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);
auto shared = materialFromObject(result, uuid);
return shared;
}
else {
Base::Console().Log("\tgetMaterial() not found\n");
Base::Console().log("\tgetMaterial() not found\n");
throw ConnectionError();
}
}
@@ -746,7 +1014,7 @@ void ExternalManager::addMaterial(const QString& libraryName,
libraries.apply(args); // No return expected
}
else {
Base::Console().Log("\taddMaterial() not found\n");
Base::Console().log("\taddMaterial() not found\n");
throw ConnectionError();
}
}
@@ -774,7 +1042,7 @@ void ExternalManager::migrateMaterial(const QString& libraryName,
libraries.apply(args); // No return expected
}
else {
Base::Console().Log("\tmigrateMaterial() not found\n");
Base::Console().log("\tmigrateMaterial() not found\n");
throw ConnectionError();
}
}
@@ -783,3 +1051,134 @@ void ExternalManager::migrateMaterial(const QString& libraryName,
throw CreationError(e1.what());
}
}
void ExternalManager::updateMaterial(const QString& libraryName,
const QString& path,
const std::shared_ptr<Material>& material)
{
connect();
Base::PyGILStateLocker lock;
try {
if (_managerObject.hasAttr("updateMaterial")) {
Py::Callable libraries(_managerObject.getAttr("updateMaterial"));
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("\tupdateMaterial() not found\n");
throw ConnectionError();
}
}
catch (Py::Exception& e) {
Base::PyException e1; // extract the Python error text
throw UpdateError(e1.what());
}
}
void ExternalManager::setMaterialPath(const QString& libraryName,
const QString& path,
const QString& uuid)
{
connect();
Base::PyGILStateLocker lock;
try {
if (_managerObject.hasAttr("setMaterialPath")) {
Py::Callable libraries(_managerObject.getAttr("setMaterialPath"));
Py::Tuple args(3);
args.setItem(0, Py::String(libraryName.toStdString()));
args.setItem(1, Py::String(path.toStdString()));
args.setItem(2, Py::String(uuid.toStdString()));
libraries.apply(args); // No return expected
}
else {
Base::Console().log("\tsetMaterialPath() not found\n");
throw ConnectionError();
}
}
catch (Py::Exception& e) {
Base::PyException e1; // extract the Python error text
throw UpdateError(e1.what());
}
}
void ExternalManager::renameMaterial(const QString& libraryName,
const QString& name,
const QString& uuid)
{
connect();
Base::PyGILStateLocker lock;
try {
if (_managerObject.hasAttr("renameMaterial")) {
Py::Callable libraries(_managerObject.getAttr("renameMaterial"));
Py::Tuple args(3);
args.setItem(0, Py::String(libraryName.toStdString()));
args.setItem(1, Py::String(name.toStdString()));
args.setItem(2, Py::String(uuid.toStdString()));
libraries.apply(args); // No return expected
}
else {
Base::Console().log("\trenameMaterial() not found\n");
throw ConnectionError();
}
}
catch (Py::Exception& e) {
Base::PyException e1; // extract the Python error text
throw RenameError(e1.what());
}
}
void ExternalManager::moveMaterial(const QString& libraryName,
const QString& path,
const QString& uuid)
{
connect();
Base::PyGILStateLocker lock;
try {
if (_managerObject.hasAttr("moveMaterial")) {
Py::Callable libraries(_managerObject.getAttr("moveMaterial"));
Py::Tuple args(3);
args.setItem(0, Py::String(libraryName.toStdString()));
args.setItem(1, Py::String(path.toStdString()));
args.setItem(2, Py::String(uuid.toStdString()));
libraries.apply(args); // No return expected
}
else {
Base::Console().log("\tmoveMaterial() not found\n");
throw ConnectionError();
}
}
catch (Py::Exception& e) {
Base::PyException e1; // extract the Python error text
throw MoveError(e1.what());
}
}
void ExternalManager::removeMaterial(const QString& uuid)
{
connect();
Base::PyGILStateLocker lock;
try {
if (_managerObject.hasAttr("removeMaterial")) {
Py::Callable libraries(_managerObject.getAttr("removeMaterial"));
Py::Tuple args(1);
args.setItem(0, Py::String(uuid.toStdString()));
libraries.apply(args); // No return expected
}
else {
Base::Console().log("\tremoveMaterial() not found\n");
throw ConnectionError();
}
}
catch (Py::Exception& e) {
Base::PyException e1; // extract the Python error text
throw DeleteError(e1.what());
}
}

View File

@@ -29,11 +29,13 @@
class QMutex;
class QString;
class QByteArray;
namespace Materials
{
class Library;
class LibraryObject;
class Material;
class Model;
class MaterialFilter;
@@ -53,18 +55,26 @@ public:
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 createLibrary(const QString& libraryName,
const QByteArray& icon,
bool readOnly = true);
void renameLibrary(const QString& libraryName, const QString& newName);
void changeIcon(const QString& libraryName, const QString& icon);
void changeIcon(const QString& libraryName, const QByteArray& 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>>>
std::shared_ptr<std::vector<LibraryObject>> libraryModels(const QString& libraryName);
std::shared_ptr<std::vector<LibraryObject>> libraryMaterials(const QString& libraryName);
std::shared_ptr<std::vector<LibraryObject>>
libraryMaterials(const QString& libraryName,
const std::shared_ptr<MaterialFilter>& filter,
const MaterialFilterOptions& options);
std::shared_ptr<std::vector<QString>> libraryFolders(const QString& libraryName);
// Folder management
void createFolder(const QString& libraryName, const QString& path);
void renameFolder(const QString& libraryName,
const QString& oldPath,
const QString& newPath);
void deleteRecursive(const QString& libraryName, const QString& path);
// Model management
std::shared_ptr<Model> getModel(const QString& uuid);
@@ -72,6 +82,13 @@ public:
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);
void updateModel(const QString& libraryName,
const QString& path,
const std::shared_ptr<Model>& model);
void setModelPath(const QString& libraryName, const QString& path, const QString& uuid);
void renameModel(const QString& libraryName, const QString& name, const QString& uuid);
void moveModel(const QString& libraryName, const QString& path, const QString& uuid);
void removeModel(const QString& uuid);
// Material management
std::shared_ptr<Material> getMaterial(const QString& uuid);
@@ -81,6 +98,13 @@ public:
void migrateMaterial(const QString& libraryName,
const QString& path,
const std::shared_ptr<Material>& material);
void updateMaterial(const QString& libraryName,
const QString& path,
const std::shared_ptr<Material>& material);
void setMaterialPath(const QString& libraryName, const QString& path, const QString& uuid);
void renameMaterial(const QString& libraryName, const QString& name, const QString& uuid);
void moveMaterial(const QString& libraryName, const QString& path, const QString& uuid);
void removeMaterial(const QString& uuid);
private:
ExternalManager();
@@ -92,8 +116,12 @@ private:
void connect();
bool checkMaterialLibraryType(const Py::Object& entry);
std::shared_ptr<Library> libraryFromObject(const Py::Object& entry);
bool checkMaterialLibraryObjectType(const Py::Object& entry);
LibraryObject materialLibraryObjectTypeFromObject(const Py::Object& entry);
bool checkModelObjectType(const Py::Object& entry);
std::shared_ptr<Model> modelFromObject(const Py::Object& entry, const QString& uuid);
bool checkMaterialObjectType(const Py::Object& entry);
std::tuple<QString, QString, QString> materialObjectTypeFromObject(const Py::Object& entry);
std::shared_ptr<Material> materialFromObject(const Py::Object& entry, const QString& uuid);
static ExternalManager* _manager;
static QMutex _mutex;

View File

@@ -43,6 +43,8 @@ public:
FolderTreeNode()
: _type(NodeType::UnknownNode)
, _oldFormat(false)
, _readOnly(false)
{}
virtual ~FolderTreeNode() = default;
@@ -76,6 +78,14 @@ public:
assert(_type == NodeType::DataNode);
return _uuid;
}
bool isOldFormat() const
{
return _oldFormat;
}
bool isReadOnly() const
{
return _readOnly;
}
void setFolder(std::shared_ptr<std::map<QString, std::shared_ptr<FolderTreeNode<T>>>> folder)
{
@@ -92,12 +102,22 @@ public:
setType(NodeType::DataNode);
_uuid = uuid;
}
void setOldFormat(bool oldFormat)
{
_oldFormat = oldFormat;
}
void setReadOnly(bool readOnly)
{
_readOnly = readOnly;
}
private:
NodeType _type;
std::shared_ptr<std::map<QString, std::shared_ptr<FolderTreeNode<T>>>> _folder;
QString _uuid;
std::shared_ptr<T> _data;
bool _oldFormat;
bool _readOnly;
};
} // namespace Materials

View File

@@ -34,28 +34,60 @@ using namespace Materials;
TYPESYSTEM_SOURCE(Materials::Library, Base::BaseClass)
Library::Library(const QString& libraryName, const QString& icon, bool readOnly)
Library::Library(const QString& libraryName, const QString& iconPath, bool readOnly)
: _name(libraryName)
, _iconPath(icon)
, _readOnly(readOnly)
, _local(false)
{
setIcon(iconPath);
}
Library::Library(const QString& libraryName, const QByteArray& icon, bool readOnly)
: _name(libraryName)
, _icon(icon)
, _readOnly(readOnly)
, _local(false)
{}
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)
const QString& dir,
const QString& iconPath,
bool readOnly)
: _name(libraryName)
, _directory(QDir::cleanPath(dir))
, _iconPath(icon)
, _readOnly(readOnly)
{}
, _local(false)
{
setIcon(iconPath);
}
QByteArray Library::getIcon(const QString& iconPath)
{
QFile file(iconPath);
if (!file.open(QIODevice::ReadOnly)) {
Base::Console().log("Failed to open icon file '%s'\n", iconPath.toStdString().c_str());
return QByteArray(); // Return an empty QByteArray if file opening fails
}
QByteArray data = file.readAll();
file.close();
return data;
}
void Library::setIcon(const QString& iconPath)
{
_icon = getIcon(iconPath);
}
bool Library::isLocal() const
{
return _local;
}
void Library::setLocal(bool local)
{
_local = local;
}
bool Library::operator==(const Library& library) const
{
@@ -67,10 +99,8 @@ void Library::validate(const Library& remote) const
if (getName() != remote.getName()) {
throw InvalidLibrary("Library names don't match");
}
if (getIconPath() != remote.getIconPath()) {
Base::Console().log("Icon path 1 '%s'\n", getIconPath().toStdString().c_str());
Base::Console().log("Icon path 2 '%s'\n", remote.getIconPath().toStdString().c_str());
throw InvalidLibrary("Library icon paths don't match");
if (getIcon() != remote.getIcon()) {
throw InvalidLibrary("Library icons don't match");
}
// Local and remote paths will differ

View File

@@ -23,6 +23,7 @@
#define MATERIAL_LIBRARY_H
#include <QDir>
#include <QByteArray>
#include <QString>
#include <Base/BaseClass.h>
@@ -40,16 +41,16 @@ public:
Library() = default;
Library(const Library &other) = 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 QByteArray& icon, bool readOnly);
Library(const QString& libraryName,
const QString& dir,
const QString& icon,
const QString& iconPath,
bool readOnly = true);
~Library() override = default;
bool isLocal() const;
void setLocal(bool local);
QString getName() const
{
return _name;
@@ -63,13 +64,19 @@ public:
return (_name == name);
}
QString getIconPath() const
QByteArray getIcon() const
{
return _iconPath;
return _icon;
}
void setIconPath(const QString& icon)
static QByteArray getIcon(const QString& iconPath);
void setIcon(const QByteArray& icon)
{
_iconPath = icon;
_icon = icon;
}
void setIcon(const QString& iconPath);
bool hasIcon() const
{
return !_icon.isEmpty();
}
bool isReadOnly() const
{
@@ -92,14 +99,6 @@ 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
@@ -118,9 +117,72 @@ public:
private:
QString _name;
QString _directory;
QString _iconPath;
QByteArray _icon;
bool _readOnly;
QString _timestamp;
bool _local;
QByteArray loadByteArrayFromFile(const QString& filePath) const;
};
class MaterialsExport LibraryObject
{
public:
LibraryObject(const QString& uuid, const QString& path, const QString& name)
: _uuid(uuid)
, _path(path)
, _name(name)
{}
LibraryObject(const std::string& uuid, const std::string& path, const std::string& name)
: _uuid(QString::fromStdString(uuid))
, _path(QString::fromStdString(path))
, _name(QString::fromStdString(name))
{}
~LibraryObject() = default;
void setUUID(const QString& uuid)
{
_uuid = uuid;
}
void setUUID(const std::string& uuid)
{
_uuid = QString::fromStdString(uuid);
}
QString getUUID() const
{
return _uuid;
}
void setPath(const QString& path)
{
_path = path;
}
void setPath(const std::string& path)
{
_path = QString::fromStdString(path);
}
QString getPath() const
{
return _path;
}
void setName(const QString& name)
{
_name = name;
}
void setName(const std::string& name)
{
_name = QString::fromStdString(name);
}
QString getName() const
{
return _name;
}
private:
QString _uuid;
QString _path;
QString _name;
};
} // namespace Materials

View File

@@ -19,22 +19,22 @@ class Material(BaseClass):
"""
LibraryName: Final[str] = ...
"""Model library name."""
"""Material library name."""
LibraryRoot: Final[str] = ...
"""Model library path."""
"""Material library path."""
LibraryIcon: Final[str] = ...
"""Model icon path."""
LibraryIcon: Final[bytes] = ...
"""Material icon."""
Name: str = ...
"""Model name."""
"""Material name."""
Directory: str = ...
"""Model directory relative to the library root."""
"""Material directory relative to the library root."""
UUID: Final[str] = ...
"""Unique model identifier. This is only valid after the material is saved."""
"""Unique material identifier. This is only valid after the material is saved."""
Description: str = ...
"""Description of the material."""

View File

@@ -32,7 +32,6 @@ class MaterialLibraryType:
name: str
icon: bytes
readOnly: bool
timestamp: str
@dataclass
class MaterialLibraryObjectType:
@@ -40,6 +39,16 @@ class MaterialLibraryObjectType:
path: str
name: str
@dataclass
class ModelObjectType:
libraryName: str
model: Materials.Model
@dataclass
class MaterialObjectType:
libraryName: str
material: Materials.Material
class MaterialManagerExternal(ABC):
"""Abstract base class for all external material managers
@@ -63,9 +72,7 @@ class MaterialManagerExternal(ABC):
The list contains a series of tuples describing all libraries managed by
this module. Each tuple contains 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
if it is a read only library."""
@abstractmethod
def modelLibraries(self) -> list[MaterialLibraryType]:
@@ -73,12 +80,10 @@ class MaterialManagerExternal(ABC):
The list contains a series of tuples describing all libraries managed by
this module. Each tuple contains 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.
if it is a read only library.
This differs from the libraries() function in that it only returns libraries
containing model objects."""
pass
@abstractmethod
def materialLibraries(self) -> list[MaterialLibraryType]:
@@ -86,164 +91,165 @@ class MaterialManagerExternal(ABC):
The list contains a series of tuples describing all libraries managed by
this module. Each tuple contains 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.
if it is a read only library.
This differs from the libraries() function in that it only returns libraries
containing material objects."""
pass
@abstractmethod
def getLibrary(self, name: str) -> tuple:
def getLibrary(self, libraryName: str) -> MaterialLibraryType:
"""Get the library
Retrieve the library with the given name"""
pass
@abstractmethod
def createLibrary(self, name: str, icon: bytes, readOnly: bool) -> None:
def createLibrary(self, libraryName: str, icon: bytes, readOnly: bool) -> None:
"""Create a new library
Create a new library with the given name"""
pass
Create a new library with the given name."""
@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:
def changeIcon(self, libraryName: str, icon: bytes) -> None:
"""Change the library icon
Change the library icon"""
pass
@abstractmethod
def removeLibrary(self, library: str) -> None:
def removeLibrary(self, libraryName: 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]:
def libraryModels(self, libraryName: 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]:
def libraryMaterials(self, libraryName: str,
materialFilter: 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
@abstractmethod
def libraryFolders(self, libraryName: str) -> list[str]:
"""Returns a list of folders managed by this library
This will return a list of all folders in the library including empty folders"""
#
# Folder methods
#
@abstractmethod
def createFolder(self, libraryName: str, path: str) -> None:
"""Create a new folder in the given library"""
@abstractmethod
def renameFolder(self, libraryName: str, oldPath: str, newPath: str) -> None:
"""Rename the folder"""
@abstractmethod
def deleteRecursive(self, libraryName: str, path: str) -> None:
"""Delete the folder and all of its contents"""
#
# Model methods
#
@abstractmethod
def getModel(self, uuid: str) -> Materials.Model:
def getModel(self, uuid: str) -> ModelObjectType:
"""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.
def addModel(self, libraryName: str, path: str, model: Materials.Model) -> None:
"""Add a model to a library in the given folder. The folder path is relative to
the library and will be created if it doesn't already exist.
This will throw a DatabaseModelExistsError exception if the model already exists."""
pass
@abstractmethod
def migrateModel(self, library: str, path: str, model: Materials.Model) -> None:
def migrateModel(self, libraryName: 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:
def updateModel(self, libraryName: str, path: str, model: Materials.Model) -> None:
"""Update the given model"""
pass
@abstractmethod
def setModelPath(self, library: str, path: str, model: Materials.Model) -> None:
def setModelPath(self, libraryName: str, path: str, uuid: str) -> None:
"""Change the model path within the library"""
pass
@abstractmethod
def renameModel(self, library: str, name: str, model: Materials.Model) -> None:
def renameModel(self, libraryName: str, name: str, uuid: str) -> None:
"""Change the model name"""
pass
@abstractmethod
def moveModel(self, library: str, path: str, model: Materials.Model) -> None:
def moveModel(self, libraryName: str, path: str, uuid: str) -> 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:
def removeModel(self, uuid: str) -> None:
"""Remove the model from the library"""
pass
#
# Material methods
#
@abstractmethod
def getMaterial(self, uuid: str) -> Materials.Material:
def getMaterial(self, uuid: str) -> MaterialObjectType:
""" 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.
def addMaterial(self, libraryName: str, path: str, material: Materials.Material) -> None:
"""Add a material to a library in the given folder. The folder path is relative to
the library and will be created if it doesn't already exist.
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.
def migrateMaterial(self, libraryName: str, path: str, material: Materials.Material) -> None:
"""Add the material to the library in the given folder. The folder path is relative to
the library and will be created if it doesn't already exist.
If the material already exists, then no action is performed."""
pass
@abstractmethod
def updateMaterial(self, library: str, path: str, material: Materials.Material) -> None:
def updateMaterial(self, libraryName: str, path: str, material: Materials.Material) -> None:
"""Update the given material"""
pass
@abstractmethod
def setMaterialPath(self, library: str, path: str, material: Materials.Material) -> None:
def setMaterialPath(self, libraryName: str, path: str, uuid: str) -> None:
"""Change the material path within the library"""
pass
@abstractmethod
def renameMaterial(self, library: str, name: str, material: Materials.Material) -> None:
def renameMaterial(self, libraryName: str, name: str, uuid: str) -> None:
"""Change the material name"""
pass
@abstractmethod
def moveMaterial(self, library: str, path: str, material: Materials.Material) -> None:
def moveMaterial(self, libraryName: str, path: str, uuid: str) -> 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:
def removeMaterial(self, uuid: str) -> None:
"""Remove the material from the library"""
pass

View File

@@ -45,7 +45,6 @@ TYPESYSTEM_SOURCE(Materials::MaterialLibrary, Base::BaseClass)
MaterialLibrary::MaterialLibrary(const QString& libraryName, const QString& icon, bool readOnly)
: Library(libraryName, icon, readOnly)
, _local(false)
{}
MaterialLibrary::MaterialLibrary(const QString& libraryName,
@@ -53,24 +52,12 @@ MaterialLibrary::MaterialLibrary(const QString& libraryName,
const QString& icon,
bool readOnly)
: Library(libraryName, dir, icon, readOnly)
, _local(false)
{}
MaterialLibrary::MaterialLibrary(const Library& library)
: Library(library)
, _local(false)
{}
bool MaterialLibrary::isLocal() const
{
return _local;
}
void MaterialLibrary::setLocal(bool local)
{
_local = local;
}
std::shared_ptr<std::map<QString, std::shared_ptr<MaterialTreeNode>>>
MaterialLibrary::getMaterialTree(const std::shared_ptr<Materials::MaterialFilter>& filter,
const Materials::MaterialFilterOptions& options) const
@@ -78,11 +65,11 @@ MaterialLibrary::getMaterialTree(const std::shared_ptr<Materials::MaterialFilter
std::shared_ptr<std::map<QString, std::shared_ptr<MaterialTreeNode>>> materialTree =
std::make_shared<std::map<QString, std::shared_ptr<MaterialTreeNode>>>();
auto materials = MaterialManager::getManager().libraryMaterials(getName(), filter, options);
auto materials = MaterialManager::getManager().libraryMaterials(getName(), filter, options, isLocal());
for (auto& it : *materials) {
auto uuid = std::get<0>(it);
auto path = std::get<1>(it);
auto filename = std::get<2>(it);
auto uuid = it.getUUID();
auto path = it.getPath();
auto filename = it.getName();
QStringList list = path.split(QStringLiteral("/"));
@@ -98,6 +85,7 @@ MaterialLibrary::getMaterialTree(const std::shared_ptr<Materials::MaterialFilter
std::shared_ptr<MaterialTreeNode> child =
std::make_shared<MaterialTreeNode>();
child->setFolder(mapPtr);
child->setReadOnly(isReadOnly());
(*node)[itp] = child;
node = mapPtr;
}
@@ -108,6 +96,11 @@ MaterialLibrary::getMaterialTree(const std::shared_ptr<Materials::MaterialFilter
}
std::shared_ptr<MaterialTreeNode> child = std::make_shared<MaterialTreeNode>();
child->setUUID(uuid);
child->setReadOnly(isReadOnly());
if (isLocal()) {
auto material = MaterialManager::getManager().getMaterial(uuid);
child->setOldFormat(material->isOldFormat());
}
(*node)[filename] = child;
}

View File

@@ -55,15 +55,12 @@ public:
MaterialLibrary(const QString& libraryName, const QString& icon, bool readOnly = true);
MaterialLibrary(const QString& libraryName,
const QString& dir,
const QString& icon,
const QString& iconPath,
bool readOnly = true);
MaterialLibrary(const Library& library);
MaterialLibrary(const MaterialLibrary&) = delete;
~MaterialLibrary() override = default;
bool isLocal() const;
void setLocal(bool local);
virtual std::shared_ptr<std::map<QString, std::shared_ptr<MaterialTreeNode>>>
getMaterialTree(const std::shared_ptr<Materials::MaterialFilter>& filter,
const Materials::MaterialFilterOptions& options) const;
@@ -73,9 +70,6 @@ public:
{
return shared_from_this();
}
protected:
bool _local;
};
class MaterialsExport MaterialLibraryLocal: public MaterialLibrary
@@ -86,7 +80,7 @@ public:
MaterialLibraryLocal() = default;
MaterialLibraryLocal(const QString& libraryName,
const QString& dir,
const QString& icon,
const QString& iconPath,
bool readOnly = true);
~MaterialLibraryLocal() override = default;

View File

@@ -19,8 +19,8 @@ class MaterialLibrary(BaseClass):
Name: str = ...
"""Name of the library"""
Icon: str = ...
"""String value of the icon."""
Icon: bytes = ...
"""Icon as an array of bytes."""
Directory: str = ...
"""Local directory where the library is located. For non-local libraries this will be empty"""

View File

@@ -72,15 +72,22 @@ void MaterialLibraryPy::setName(const Py::String value)
getMaterialLibraryPtr()->setName(QString::fromStdString(value));
}
Py::String MaterialLibraryPy::getIcon() const
Py::Object MaterialLibraryPy::getIcon() const
{
auto path = getMaterialLibraryPtr()->getIconPath();
return {path.toStdString()};
auto icon = getMaterialLibraryPtr()->getIcon();
return Py::Bytes(icon.data(), icon.size());
}
void MaterialLibraryPy::setIcon(const Py::String value)
void MaterialLibraryPy::setIcon(const Py::Object value)
{
getMaterialLibraryPtr()->setIconPath(QString::fromStdString(value));
if (value.isNone()) {
getMaterialLibraryPtr()->setIcon(QByteArray());
}
else {
auto pyBytes = Py::Bytes(value);
getMaterialLibraryPtr()->setIcon(
QByteArray(pyBytes.as_std_string().data(), pyBytes.size()));
}
}
Py::String MaterialLibraryPy::getDirectory() const

View File

@@ -61,15 +61,21 @@ std::unique_ptr<MaterialManagerExternal> MaterialManager::_externalManager;
MaterialManager::MaterialManager()
{
#if defined(BUILD_MATERIAL_EXTERNAL)
_hGrp = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Mod/Material/ExternalInterface");
_useExternal = _hGrp->GetBool("UseExternal", false);
_hGrp->Attach(this);
#else
_useExternal = false;
#endif
}
MaterialManager::~MaterialManager()
{
#if defined(BUILD_MATERIAL_EXTERNAL)
_hGrp->Detach(this);
#endif
}
MaterialManager& MaterialManager::getManager()
@@ -267,26 +273,49 @@ std::shared_ptr<MaterialLibrary> MaterialManager::getLibrary(const QString& name
return _localManager->getLibrary(name);
}
void MaterialManager::createLibrary(const QString& /*libraryName*/, const QString& /*icon*/, bool /*readOnly*/)
void MaterialManager::createLibrary(const QString& libraryName,
const QString& iconPath,
bool readOnly)
{
#if defined(BUILD_MATERIAL_EXTERNAL)
if (_useExternal) {
auto icon = Materials::Library::getIcon(iconPath);
_externalManager->createLibrary(libraryName, icon, readOnly);
return;
}
#endif
throw CreationError("Local library requires a path");
}
void MaterialManager::createLocalLibrary(const QString& libraryName,
const QString& directory,
const QString& icon,
const QString& iconPath,
bool readOnly)
{
_localManager->createLibrary(libraryName, directory, icon, readOnly);
_localManager->createLibrary(libraryName, directory, iconPath, readOnly);
}
void MaterialManager::renameLibrary(const QString& libraryName, const QString& newName)
{
_localManager->renameLibrary(libraryName, newName);
auto library = getLibrary(libraryName);
if (library) {
#if defined(BUILD_MATERIAL_EXTERNAL)
if (!library->isLocal()) {
if (_useExternal) {
_externalManager->renameLibrary(libraryName, newName);
return;
}
throw Materials::RenameError();
}
#endif
_localManager->renameLibrary(libraryName, newName);
}
}
void MaterialManager::changeIcon(const QString& libraryName, const QString& icon)
void MaterialManager::changeIcon(const QString& libraryName, const QString& iconPath)
{
auto icon = Materials::Library::getIcon(iconPath);
_localManager->changeIcon(libraryName, icon);
}
@@ -295,11 +324,11 @@ void MaterialManager::removeLibrary(const QString& libraryName)
_localManager->removeLibrary(libraryName);
}
std::shared_ptr<std::vector<std::tuple<QString, QString, QString>>>
MaterialManager::libraryMaterials(const QString& libraryName)
std::shared_ptr<std::vector<LibraryObject>>
MaterialManager::libraryMaterials(const QString& libraryName, bool local)
{
#if defined(BUILD_MATERIAL_EXTERNAL)
if (_useExternal) {
if (_useExternal && !local) {
try {
auto materials = _externalManager->libraryMaterials(libraryName);
if (materials) {
@@ -313,13 +342,14 @@ MaterialManager::libraryMaterials(const QString& libraryName)
return _localManager->libraryMaterials(libraryName);
}
std::shared_ptr<std::vector<std::tuple<QString, QString, QString>>>
std::shared_ptr<std::vector<LibraryObject>>
MaterialManager::libraryMaterials(const QString& libraryName,
const std::shared_ptr<MaterialFilter>& filter,
const MaterialFilterOptions& options)
const MaterialFilterOptions& options,
bool local)
{
#if defined(BUILD_MATERIAL_EXTERNAL)
if (_useExternal) {
if (_useExternal && !local) {
try {
auto materials = _externalManager->libraryMaterials(libraryName, filter, options);
if (materials) {
@@ -383,6 +413,14 @@ void MaterialManager::createFolder(const std::shared_ptr<MaterialLibrary>& libra
_localManager->createFolder(materialLibrary, path);
}
#if defined(BUILD_MATERIAL_EXTERNAL)
else if (_useExternal) {
_externalManager->createFolder(library, path);
}
else {
throw Materials::CreationError("External materials are not enabled");
}
#endif
}
void MaterialManager::renameFolder(const std::shared_ptr<MaterialLibrary>& library,
@@ -395,6 +433,14 @@ void MaterialManager::renameFolder(const std::shared_ptr<MaterialLibrary>& libra
_localManager->renameFolder(materialLibrary, oldPath, newPath);
}
#if defined(BUILD_MATERIAL_EXTERNAL)
else if (_useExternal) {
_externalManager->renameFolder(library, oldPath, newPath);
}
else {
throw Materials::RenameError("External materials are not enabled");
}
#endif
}
void MaterialManager::deleteRecursive(const std::shared_ptr<MaterialLibrary>& library,
@@ -406,6 +452,14 @@ void MaterialManager::deleteRecursive(const std::shared_ptr<MaterialLibrary>& li
_localManager->deleteRecursive(materialLibrary, path);
}
#if defined(BUILD_MATERIAL_EXTERNAL)
else if (_useExternal) {
_externalManager->deleteRecursive(library, path);
}
else {
throw Materials::DeleteError("External materials are not enabled");
}
#endif
}
//=====
@@ -562,16 +616,22 @@ void MaterialManager::dereference(std::shared_ptr<Material> material) const
#if defined(BUILD_MATERIAL_EXTERNAL)
void MaterialManager::migrateToExternal(const std::shared_ptr<Materials::MaterialLibrary>& library)
{
_externalManager->createLibrary(library->getName(),
library->getIconPath(),
library->isReadOnly());
try {
_externalManager->createLibrary(library->getName(),
library->getIcon(),
library->isReadOnly());
}
catch (const CreationError&) {
}
catch (const ConnectionError&) {
}
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",
for (auto& it : *materials) {
auto uuid = it.getUUID();
auto path = it.getPath();
auto name = it.getName();
Base::Console().log("\t('%s', '%s', '%s')\n",
uuid.toStdString().c_str(),
path.toStdString().c_str(),
name.toStdString().c_str());
@@ -586,11 +646,12 @@ void MaterialManager::migrateToExternal(const std::shared_ptr<Materials::Materia
void MaterialManager::validateMigration(const std::shared_ptr<Materials::MaterialLibrary>& 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",
_externalManager->resetCache();
for (auto& it : *materials) {
auto uuid = it.getUUID();
auto path = it.getPath();
auto name = it.getName();
Base::Console().log("\t('%s', '%s', '%s')\n",
uuid.toStdString().c_str(),
path.toStdString().c_str(),
name.toStdString().c_str());

View File

@@ -69,23 +69,27 @@ public:
static QString defaultMaterialUUID();
// Library management
bool useExternal() const { return _useExternal; }
std::shared_ptr<std::list<std::shared_ptr<MaterialLibrary>>> getLibraries();
std::shared_ptr<std::list<std::shared_ptr<MaterialLibrary>>> getLocalLibraries();
std::shared_ptr<MaterialLibrary> getLibrary(const QString& name) const;
void createLibrary(const QString& libraryName, const QString& icon, bool readOnly = true);
void createLibrary(const QString& libraryName,
const QString& iconPath,
bool readOnly = true);
void createLocalLibrary(const QString& libraryName,
const QString& directory,
const QString& icon,
const QString& iconPath,
bool readOnly = true);
void renameLibrary(const QString& libraryName, const QString& newName);
void changeIcon(const QString& libraryName, const QString& icon);
void changeIcon(const QString& libraryName, const QString& iconPath);
void removeLibrary(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>>>
std::shared_ptr<std::vector<LibraryObject>>
libraryMaterials(const QString& libraryName, bool local = false);
std::shared_ptr<std::vector<LibraryObject>>
libraryMaterials(const QString& libraryName,
const std::shared_ptr<MaterialFilter>& filter,
const MaterialFilterOptions& options);
const MaterialFilterOptions& options,
bool local = false);
bool isLocalLibrary(const QString& libraryName);
// Folder management

View File

@@ -124,22 +124,40 @@ std::shared_ptr<MaterialLibrary> MaterialManagerExternal::getLibrary(const QStri
catch (const ConnectionError& e) {
throw LibraryNotFound(e.what());
}
catch (...) {
throw LibraryNotFound("Unknown exception");
}
}
void MaterialManagerExternal::createLibrary(const QString& libraryName,
const QString& icon,
const QByteArray& icon,
bool readOnly)
{
ExternalManager::getManager()->createLibrary(libraryName, icon, readOnly);
}
std::shared_ptr<std::vector<std::tuple<QString, QString, QString>>>
void MaterialManagerExternal::renameLibrary(const QString& libraryName, const QString& newName)
{
ExternalManager::getManager()->renameLibrary(libraryName, newName);
}
void MaterialManagerExternal::changeIcon(const QString& libraryName, const QByteArray& icon)
{
ExternalManager::getManager()->changeIcon(libraryName, icon);
}
void MaterialManagerExternal::removeLibrary(const QString& libraryName)
{
ExternalManager::getManager()->removeLibrary(libraryName);
}
std::shared_ptr<std::vector<LibraryObject>>
MaterialManagerExternal::libraryMaterials(const QString& libraryName)
{
return ExternalManager::getManager()->libraryMaterials(libraryName);
}
std::shared_ptr<std::vector<std::tuple<QString, QString, QString>>>
std::shared_ptr<std::vector<LibraryObject>>
MaterialManagerExternal::libraryMaterials(const QString& libraryName,
const std::shared_ptr<MaterialFilter>& filter,
const MaterialFilterOptions& options)
@@ -147,12 +165,44 @@ MaterialManagerExternal::libraryMaterials(const QString& libraryName,
return ExternalManager::getManager()->libraryMaterials(libraryName, filter, options);
}
//=====
//
// Folder management
//
//=====
void MaterialManagerExternal::createFolder(const std::shared_ptr<MaterialLibrary>& library,
const QString& path)
{
ExternalManager::getManager()->createFolder(library->getName(), path);
}
void MaterialManagerExternal::renameFolder(const std::shared_ptr<MaterialLibrary>& library,
const QString& oldPath,
const QString& newPath)
{
ExternalManager::getManager()->renameFolder(library->getName(), oldPath, newPath);
}
void MaterialManagerExternal::deleteRecursive(const std::shared_ptr<MaterialLibrary>& library,
const QString& path)
{
ExternalManager::getManager()->deleteRecursive(library->getName(), path);
}
//=====
//
// Material management
//
//=====
std::shared_ptr<Material> MaterialManagerExternal::materialNotFound(const QString& uuid) const
{
// Setting the cache value to nullptr prevents repeated lookups
_cache.emplace(uuid.toStdString(), nullptr);
return nullptr;
}
std::shared_ptr<Material> MaterialManagerExternal::getMaterial(const QString& uuid) const
{
if (_cache.contains(uuid.toStdString())) {
@@ -164,12 +214,13 @@ std::shared_ptr<Material> MaterialManagerExternal::getMaterial(const QString& uu
return material;
}
catch (const MaterialNotFound& e) {
_cache.emplace(uuid.toStdString(), nullptr);
return nullptr;
return materialNotFound(uuid);
}
catch (const ConnectionError& e) {
_cache.emplace(uuid.toStdString(), nullptr);
return nullptr;
return materialNotFound(uuid);
}
catch (...) {
return materialNotFound(uuid);
}
}

View File

@@ -42,6 +42,7 @@ class Material;
namespace Materials
{
class LibraryObject;
class MaterialLibrary;
class MaterialLibraryExternal;
class MaterialFilter;
@@ -64,15 +65,25 @@ public:
std::shared_ptr<std::list<std::shared_ptr<MaterialLibrary>>> getLibraries();
std::shared_ptr<std::list<std::shared_ptr<MaterialLibrary>>> getMaterialLibraries();
std::shared_ptr<MaterialLibrary> getLibrary(const QString& name) const;
void createLibrary(const QString& libraryName, const QString& icon, bool readOnly = true);
std::shared_ptr<std::vector<std::tuple<QString, QString, QString>>>
void createLibrary(const QString& libraryName,
const QByteArray& icon,
bool readOnly = true);
void renameLibrary(const QString& libraryName, const QString& newName);
void changeIcon(const QString& libraryName, const QByteArray& icon);
void removeLibrary(const QString& libraryName);
std::shared_ptr<std::vector<LibraryObject>>
libraryMaterials(const QString& libraryName);
std::shared_ptr<std::vector<std::tuple<QString, QString, QString>>>
std::shared_ptr<std::vector<LibraryObject>>
libraryMaterials(const QString& libraryName,
const std::shared_ptr<MaterialFilter>& filter,
const MaterialFilterOptions& options);
// Folder management
void createFolder(const std::shared_ptr<MaterialLibrary>& library, const QString& path);
void renameFolder(const std::shared_ptr<MaterialLibrary>& library,
const QString& oldPath,
const QString& newPath);
void deleteRecursive(const std::shared_ptr<MaterialLibrary>& library, const QString& path);
// Material management
std::shared_ptr<Material> getMaterial(const QString& uuid) const;
@@ -89,6 +100,7 @@ public:
private:
static void initCache();
std::shared_ptr<Material> materialNotFound(const QString& uuid) const;
static QMutex _mutex;

View File

@@ -140,7 +140,7 @@ std::shared_ptr<MaterialLibrary> MaterialManagerLocal::getLibrary(const QString&
void MaterialManagerLocal::createLibrary(const QString& libraryName,
const QString& directory,
const QString& icon,
const QString& iconPath,
bool readOnly)
{
QDir dir;
@@ -151,7 +151,7 @@ void MaterialManagerLocal::createLibrary(const QString& libraryName,
}
auto materialLibrary =
std::make_shared<MaterialLibraryLocal>(libraryName, directory, icon, readOnly);
std::make_shared<MaterialLibraryLocal>(libraryName, directory, iconPath, readOnly);
_libraryList->push_back(materialLibrary);
// This needs to be persisted somehow
@@ -171,13 +171,13 @@ void MaterialManagerLocal::renameLibrary(const QString& libraryName, const QStri
throw LibraryNotFound();
}
void MaterialManagerLocal::changeIcon(const QString& libraryName, const QString& icon)
void MaterialManagerLocal::changeIcon(const QString& libraryName, const QByteArray& icon)
{
for (auto& library : *_libraryList) {
if (library->isLocal() && library->isName(libraryName)) {
auto materialLibrary =
reinterpret_cast<const std::shared_ptr<Materials::MaterialLibraryLocal>&>(library);
materialLibrary->setIconPath(icon);
materialLibrary->setIcon(icon);
return;
}
}
@@ -199,18 +199,17 @@ void MaterialManagerLocal::removeLibrary(const QString& libraryName)
throw LibraryNotFound();
}
std::shared_ptr<std::vector<std::tuple<QString, QString, QString>>>
std::shared_ptr<std::vector<LibraryObject>>
MaterialManagerLocal::libraryMaterials(const QString& libraryName)
{
auto materials = std::make_shared<std::vector<std::tuple<QString, QString, QString>>>();
auto materials = std::make_shared<std::vector<LibraryObject>>();
for (auto& it : *_materialMap) {
// This is needed to resolve cyclic dependencies
auto library = it.second->getLibrary();
if (library->isName(libraryName)) {
materials->push_back(std::tuple<QString, QString, QString>(it.first,
it.second->getDirectory(),
it.second->getName()));
materials->push_back(
LibraryObject(it.first, it.second->getDirectory(), it.second->getName()));
}
}
@@ -235,21 +234,20 @@ bool MaterialManagerLocal::passFilter(const std::shared_ptr<Material>& material,
return filter->modelIncluded(material);
}
std::shared_ptr<std::vector<std::tuple<QString, QString, QString>>>
std::shared_ptr<std::vector<LibraryObject>>
MaterialManagerLocal::libraryMaterials(const QString& libraryName,
const std::shared_ptr<MaterialFilter>& filter,
const MaterialFilterOptions& options)
{
auto materials = std::make_shared<std::vector<std::tuple<QString, QString, QString>>>();
auto materials = std::make_shared<std::vector<LibraryObject>>();
for (auto& it : *_materialMap) {
// This is needed to resolve cyclic dependencies
auto library = it.second->getLibrary();
if (library->isName(libraryName)) {
if (passFilter(it.second, filter, options)) {
materials->push_back(std::tuple<QString, QString, QString>(it.first,
it.second->getDirectory(),
it.second->getName()));
materials->push_back(
LibraryObject(it.first, it.second->getDirectory(), it.second->getName()));
}
}
}

View File

@@ -43,6 +43,7 @@ class Material;
namespace Materials
{
class LibraryObject;
class MaterialLibrary;
class MaterialLibraryLocal;
class MaterialFilter;
@@ -65,14 +66,14 @@ public:
std::shared_ptr<MaterialLibrary> getLibrary(const QString& name) const;
void createLibrary(const QString& libraryName,
const QString& directory,
const QString& icon,
const QString& iconPath,
bool readOnly = true);
void renameLibrary(const QString& libraryName, const QString& newName);
void changeIcon(const QString& libraryName, const QString& icon);
void changeIcon(const QString& libraryName, const QByteArray& icon);
void removeLibrary(const QString& libraryName);
std::shared_ptr<std::vector<std::tuple<QString, QString, QString>>>
std::shared_ptr<std::vector<LibraryObject>>
libraryMaterials(const QString& libraryName);
std::shared_ptr<std::vector<std::tuple<QString, QString, QString>>>
std::shared_ptr<std::vector<LibraryObject>>
libraryMaterials(const QString& libraryName,
const std::shared_ptr<MaterialFilter>& filter,
const MaterialFilterOptions& options);

View File

@@ -147,13 +147,15 @@ Py::List MaterialManagerPy::getMaterialLibraries() const
reinterpret_cast<const std::shared_ptr<Materials::MaterialLibraryLocal>&>(lib);
libTuple.setItem(0, Py::String(materialLibrary->getName().toStdString()));
libTuple.setItem(1, Py::String(materialLibrary->getDirectoryPath().toStdString()));
libTuple.setItem(2, Py::String(materialLibrary->getIconPath().toStdString()));
libTuple.setItem(2,
Py::Bytes(Py::Bytes(materialLibrary->getIcon().data(),
materialLibrary->getIcon().size())));
}
else
{
libTuple.setItem(0, Py::String());
libTuple.setItem(1, Py::String());
libTuple.setItem(2, Py::String());
libTuple.setItem(2, Py::Bytes());
}
list.append(libTuple);

View File

@@ -87,15 +87,19 @@ Py::String MaterialPy::getLibraryRoot() const
return "";
}
Py::String MaterialPy::getLibraryIcon() const
Py::Object MaterialPy::getLibraryIcon() const
{
auto library = getMaterialPtr()->getLibrary();
if (library->isLocal()) {
auto materialLibrary =
reinterpret_cast<const std::shared_ptr<Materials::MaterialLibraryLocal>&>(library);
return {materialLibrary ? materialLibrary->getIconPath().toStdString() : ""};
auto icon = materialLibrary->getIcon();
if (icon.isNull()) {
return Py::Bytes();
}
return Py::Bytes(icon.data(), icon.size());
}
return "";
return Py::Bytes();
}
Py::String MaterialPy::getName() const

View File

@@ -22,8 +22,8 @@ class Model(BaseClass):
LibraryRoot: Final[str] = ""
"""Model library path."""
LibraryIcon: Final[str] = ""
"""Model icon path."""
LibraryIcon: Final[bytes] = ""
"""Model icon."""
Name: str = ""
"""Model name."""

View File

@@ -41,32 +41,19 @@ TYPESYSTEM_SOURCE(Materials::ModelLibrary, Materials::Library)
ModelLibrary::ModelLibrary(const Library& other)
: Library(other)
, _local(false)
{}
ModelLibrary::ModelLibrary(const QString& libraryName,
const QString& dir,
const QString& icon,
const QString& iconPath,
bool readOnly)
: Library(libraryName, dir, icon, readOnly)
, _local(false)
: Library(libraryName, dir, iconPath, readOnly)
{}
ModelLibrary::ModelLibrary()
: _local(false)
{
}
bool ModelLibrary::isLocal() const
{
return _local;
}
void ModelLibrary::setLocal(bool local)
{
_local = local;
}
std::shared_ptr<std::map<QString, std::shared_ptr<ModelTreeNode>>>
ModelLibrary::getModelTree(ModelFilter filter) const
{
@@ -75,9 +62,9 @@ ModelLibrary::getModelTree(ModelFilter filter) const
auto models = ModelManager::getManager().libraryModels(getName());
for (auto& it : *models) {
auto uuid = std::get<0>(it);
auto path = std::get<1>(it);
auto filename = std::get<2>(it);
auto uuid = it.getUUID();
auto path = it.getPath();
auto filename = it.getName();
auto model = ModelManager::getManager().getModel(getName(), uuid);
if (ModelManager::passFilter(filter, model->getType())) {
@@ -121,9 +108,9 @@ ModelLibraryLocal::ModelLibraryLocal(const Library& other)
ModelLibraryLocal::ModelLibraryLocal(const QString& libraryName,
const QString& dir,
const QString& icon,
const QString& iconPath,
bool readOnly)
: ModelLibrary(libraryName, dir, icon, readOnly)
: ModelLibrary(libraryName, dir, iconPath, readOnly)
{
setLocal(true);

View File

@@ -49,14 +49,11 @@ public:
ModelLibrary(const Library& library);
ModelLibrary(const QString& libraryName,
const QString& dir,
const QString& icon,
const QString& iconPath,
bool readOnly = true);
ModelLibrary(const ModelLibrary& other) = delete;
~ModelLibrary() override = default;
bool isLocal() const;
void setLocal(bool local);
std::shared_ptr<std::map<QString, std::shared_ptr<ModelTreeNode>>>
getModelTree(ModelFilter filter) const;
@@ -65,9 +62,6 @@ public:
{
return shared_from_this();
}
private:
bool _local;
};
class MaterialsExport ModelLibraryLocal: public ModelLibrary
@@ -78,9 +72,9 @@ public:
ModelLibraryLocal();
ModelLibraryLocal(const Library& other);
ModelLibraryLocal(const QString& libraryName,
const QString& dir,
const QString& icon,
bool readOnly = true);
const QString& dir,
const QString& iconPath,
bool readOnly = true);
ModelLibraryLocal(const ModelLibraryLocal& other) = delete;
~ModelLibraryLocal() override = default;

View File

@@ -158,10 +158,12 @@ std::shared_ptr<std::list<std::shared_ptr<ModelLibrary>>> ModelManager::getLocal
return _localManager->getLibraries();
}
void ModelManager::createLibrary([[maybe_unused]] const QString& libraryName, [[maybe_unused]] const QString& icon,
[[maybe_unused]] bool readOnly)
void ModelManager::createLibrary([[maybe_unused]] const QString& libraryName,
[[maybe_unused]] const QString& iconPath,
[[maybe_unused]] bool readOnly)
{
#if defined(BUILD_MATERIAL_EXTERNAL)
auto icon = Materials::Library::getIcon(iconPath);
_externalManager->createLibrary(libraryName, icon, readOnly);
#endif
}
@@ -202,7 +204,7 @@ void ModelManager::removeLibrary(const QString& libraryName)
_localManager->removeLibrary(libraryName);
}
std::shared_ptr<std::vector<std::tuple<QString, QString, QString>>>
std::shared_ptr<std::vector<LibraryObject>>
ModelManager::libraryModels(const QString& libraryName)
{
#if defined(BUILD_MATERIAL_EXTERNAL)
@@ -319,16 +321,22 @@ bool ModelManager::passFilter(ModelFilter filter, Model::ModelType modelType)
#if defined(BUILD_MATERIAL_EXTERNAL)
void ModelManager::migrateToExternal(const std::shared_ptr<Materials::ModelLibrary>& library)
{
_externalManager->createLibrary(library->getName(),
library->getIconPath(),
library->isReadOnly());
try {
_externalManager->createLibrary(library->getName(),
library->getIcon(),
library->isReadOnly());
}
catch (const CreationError&) {
}
catch (const ConnectionError&) {
}
auto models = _localManager->libraryModels(library->getName());
for (auto& tuple : *models) {
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",
for (auto& it : *models) {
auto uuid = it.getUUID();
auto path = it.getPath();
auto name = it.getName();
Base::Console().log("\t('%s', '%s', '%s')\n",
uuid.toStdString().c_str(),
path.toStdString().c_str(),
name.toStdString().c_str());
@@ -341,11 +349,11 @@ void ModelManager::migrateToExternal(const std::shared_ptr<Materials::ModelLibra
void ModelManager::validateMigration(const std::shared_ptr<Materials::ModelLibrary>& library)
{
auto models = _localManager->libraryModels(library->getName());
for (auto& tuple : *models) {
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",
for (auto& it : *models) {
auto uuid = it.getUUID();
auto path = it.getPath();
auto name = it.getName();
Base::Console().log("\t('%s', '%s', '%s')\n",
uuid.toStdString().c_str(),
path.toStdString().c_str(),
name.toStdString().c_str());

View File

@@ -55,7 +55,9 @@ public:
std::shared_ptr<std::list<std::shared_ptr<ModelLibrary>>> getLibraries();
std::shared_ptr<std::list<std::shared_ptr<ModelLibrary>>> getLocalLibraries();
std::shared_ptr<ModelLibrary> getLibrary(const QString& name) const;
void createLibrary(const QString& libraryName, const QString& icon, bool readOnly = true);
void createLibrary(const QString& libraryName,
const QString& iconPath,
bool readOnly = true);
void createLocalLibrary(const QString& libraryName,
const QString& directory,
const QString& icon,
@@ -63,7 +65,7 @@ public:
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>>>
std::shared_ptr<std::vector<LibraryObject>>
libraryModels(const QString& libraryName);
bool isLocalLibrary(const QString& libraryName);

View File

@@ -102,16 +102,19 @@ std::shared_ptr<ModelLibrary> ModelManagerExternal::getLibrary(const QString& na
catch (const ConnectionError& e) {
throw LibraryNotFound(e.what());
}
catch (...) {
throw LibraryNotFound("Unknown exception");
}
}
void ModelManagerExternal::createLibrary(const QString& libraryName,
const QString& icon,
bool readOnly)
const QByteArray& icon,
bool readOnly)
{
ExternalManager::getManager()->createLibrary(libraryName, icon, readOnly);
}
std::shared_ptr<std::vector<std::tuple<QString, QString, QString>>>
std::shared_ptr<std::vector<LibraryObject>>
ModelManagerExternal::libraryModels(const QString& libraryName)
{
return ExternalManager::getManager()->libraryModels(libraryName);
@@ -123,6 +126,13 @@ ModelManagerExternal::libraryModels(const QString& libraryName)
//
//=====
std::shared_ptr<Model> ModelManagerExternal::modelNotFound(const QString& uuid)
{
// Setting the cache value to nullptr prevents repeated lookups
_cache.emplace(uuid.toStdString(), nullptr);
return nullptr;
}
std::shared_ptr<Model> ModelManagerExternal::getModel(const QString& uuid)
{
if (_cache.contains(uuid.toStdString())) {
@@ -135,12 +145,13 @@ std::shared_ptr<Model> ModelManagerExternal::getModel(const QString& uuid)
return model;
}
catch (const ModelNotFound& e) {
_cache.emplace(uuid.toStdString(), nullptr);
return nullptr;
return modelNotFound(uuid);
}
catch (const ConnectionError& e) {
_cache.emplace(uuid.toStdString(), nullptr);
return nullptr;
return modelNotFound(uuid);
}
catch (...) {
return modelNotFound(uuid);
}
}

View File

@@ -54,9 +54,9 @@ public:
std::shared_ptr<std::list<std::shared_ptr<ModelLibrary>>> getLibraries();
std::shared_ptr<ModelLibrary> getLibrary(const QString& name) const;
void createLibrary(const QString& libraryName,
const QString& icon,
const QByteArray& icon,
bool readOnly = true);
std::shared_ptr<std::vector<std::tuple<QString, QString, QString>>>
std::shared_ptr<std::vector<LibraryObject>>
libraryModels(const QString& libraryName);
// Model management
@@ -73,6 +73,7 @@ public:
private:
static void initCache();
std::shared_ptr<Model> modelNotFound(const QString& uuid);
static QMutex _mutex;

View File

@@ -134,7 +134,7 @@ void ModelManagerLocal::changeIcon(const QString& libraryName, const QString& ic
{
for (auto& library : *_libraryList) {
if (library->isName(libraryName)) {
library->setIconPath(icon);
library->setIcon(icon);
return;
}
}
@@ -156,16 +156,16 @@ void ModelManagerLocal::removeLibrary(const QString& libraryName)
throw LibraryNotFound();
}
std::shared_ptr<std::vector<std::tuple<QString, QString, QString>>>
std::shared_ptr<std::vector<LibraryObject>>
ModelManagerLocal::libraryModels(const QString& libraryName)
{
auto models = std::make_shared<std::vector<std::tuple<QString, QString, QString>>>();
auto models = std::make_shared<std::vector<LibraryObject>>();
for (auto& it : *_modelMap) {
// This is needed to resolve cyclic dependencies
if (it.second->getLibrary()->isName(libraryName)) {
models->push_back(
std::tuple<QString, QString, QString>(it.first, it.second->getDirectory(), it.second->getName()));
LibraryObject(it.first, it.second->getDirectory(), it.second->getName()));
}
}

View File

@@ -55,7 +55,7 @@ public:
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>>>
std::shared_ptr<std::vector<LibraryObject>>
libraryModels(const QString& libraryName);
std::shared_ptr<std::map<QString, std::shared_ptr<Model>>> getModels()

View File

@@ -124,7 +124,7 @@ Py::List ModelManagerPy::getModelLibraries() const
Py::Tuple libTuple(3);
libTuple.setItem(0, Py::String(lib->getName().toStdString()));
libTuple.setItem(1, Py::String(lib->getDirectoryPath().toStdString()));
libTuple.setItem(2, Py::String(lib->getIconPath().toStdString()));
libTuple.setItem(2, Py::Bytes(lib->getIcon().data(), lib->getIcon().size()));
libTuple.setItem(3, Py::Boolean(lib->isReadOnly()));
list.append(libTuple);
@@ -143,7 +143,7 @@ Py::List ModelManagerPy::getLocalModelLibraries() const
Py::Tuple libTuple(3);
libTuple.setItem(0, Py::String(lib->getName().toStdString()));
libTuple.setItem(1, Py::String(lib->getDirectoryPath().toStdString()));
libTuple.setItem(2, Py::String(lib->getIconPath().toStdString()));
libTuple.setItem(2, Py::Bytes(lib->getIcon().data(), lib->getIcon().size()));
libTuple.setItem(3, Py::Boolean(lib->isReadOnly()));
list.append(libTuple);

View File

@@ -61,13 +61,16 @@ Py::String ModelPy::getLibraryName() const
Py::String ModelPy::getLibraryRoot() const
{
auto library = getModelPtr()->getLibrary();
if (!library->isLocal()) {
return "";
}
return Py::String(library ? library->getDirectoryPath().toStdString() : "");
}
Py::String ModelPy::getLibraryIcon() const
Py::Object ModelPy::getLibraryIcon() const
{
auto library = getModelPtr()->getLibrary();
return Py::String(library ? library->getIconPath().toStdString() : "");
return Py::Bytes(library->getIcon().data(), library->getIcon().size());
}
Py::String ModelPy::getName() const

View File

@@ -32,6 +32,7 @@
#include <Mod/Material/App/MaterialLibrary.h>
#include "MaterialsEditor.h"
#include "MaterialSave.h"
#include "ui_MaterialSave.h"
@@ -360,7 +361,7 @@ void MaterialSave::showSelectedTree()
if (ui->comboLibrary->count() > 0) {
auto variant = ui->comboLibrary->currentData();
auto library = variant.value<std::shared_ptr<Materials::MaterialLibrary>>();
QIcon icon(library->getIconPath());
auto icon = MaterialsEditor::getIcon(library);
QIcon folderIcon(QStringLiteral(":/icons/folder.svg"));
_libraryName = library->getName();
_selectedPath = QStringLiteral("/") + _libraryName;

View File

@@ -572,7 +572,7 @@ void MaterialTreeWidget::fillMaterialTree()
lib->setFlags(Qt::ItemIsEnabled);
addExpanded(model, lib, param);
QIcon icon(library->getIconPath());
auto icon = MaterialsEditor::getIcon(library);
QIcon folderIcon(QStringLiteral(":/icons/folder.svg"));
addMaterials(*lib, materialTree, folderIcon, icon, param);
@@ -619,7 +619,7 @@ void MaterialTreeWidget::addRecents(QStandardItem* parent)
for (auto& uuid : _recents) {
try {
auto material = getMaterialManager().getMaterial(uuid);
QIcon icon(material->getLibrary()->getIconPath());
auto icon = MaterialsEditor::getIcon(material->getLibrary());
auto card = new QStandardItem(icon, material->getName());
card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
card->setData(QVariant(uuid), Qt::UserRole);
@@ -636,7 +636,7 @@ void MaterialTreeWidget::addFavorites(QStandardItem* parent)
for (auto& uuid : _favorites) {
try {
auto material = getMaterialManager().getMaterial(uuid);
QIcon icon(material->getLibrary()->getIconPath());
auto icon = MaterialsEditor::getIcon(material->getLibrary());
auto card = new QStandardItem(icon, material->getName());
card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
card->setData(QVariant(uuid), Qt::UserRole);

View File

@@ -801,6 +801,33 @@ void MaterialsEditor::createAppearanceTree()
connect(delegate, &MaterialDelegate::propertyChange, this, &MaterialsEditor::propertyChange);
}
QIcon MaterialsEditor::getIcon(const std::shared_ptr<Materials::Library>& library)
{
// Load from the QByteArray if available
QIcon icon;
if (library->hasIcon()) {
QImage image;
if (!image.loadFromData(library->getIcon())) {
Base::Console().log("Unable to load icon image for library '%s'\n",
library->getName().toStdString().c_str());
return QIcon();
}
icon = QIcon(QPixmap::fromImage(image));
}
return icon;
}
QIcon MaterialsEditor::getIcon(const std::shared_ptr<Materials::ModelLibrary>& library)
{
return getIcon(std::static_pointer_cast<Materials::Library>(library));
}
QIcon MaterialsEditor::getIcon(const std::shared_ptr<Materials::MaterialLibrary>& library)
{
return getIcon(std::static_pointer_cast<Materials::Library>(library));
}
void MaterialsEditor::addRecents(QStandardItem* parent)
{
auto tree = ui->treeMaterials;
@@ -808,13 +835,13 @@ void MaterialsEditor::addRecents(QStandardItem* parent)
try {
auto material = getMaterialManager().getMaterial(uuid);
// if (material->getLibrary()->isLocal()) {
QIcon icon = QIcon(material->getLibrary()->getIconPath());
auto card = new QStandardItem(icon, libraryPath(material));
card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled
| Qt::ItemIsDropEnabled);
card->setData(QVariant(uuid), Qt::UserRole);
QIcon icon = getIcon(material->getLibrary());
auto card = new QStandardItem(icon, libraryPath(material));
card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled
| Qt::ItemIsDropEnabled);
card->setData(QVariant(uuid), Qt::UserRole);
addExpanded(tree, parent, card);
addExpanded(tree, parent, card);
// }
}
catch (const Materials::MaterialNotFound&) {
@@ -828,7 +855,7 @@ void MaterialsEditor::addFavorites(QStandardItem* parent)
for (auto& uuid : _favorites) {
try {
auto material = getMaterialManager().getMaterial(uuid);
QIcon icon = QIcon(material->getLibrary()->getIconPath());
QIcon icon = getIcon(material->getLibrary());
auto card = new QStandardItem(icon, libraryPath(material));
card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled
| Qt::ItemIsDropEnabled);
@@ -877,7 +904,7 @@ void MaterialsEditor::fillMaterialTree()
lib->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled);
addExpanded(tree, model, lib, param);
QIcon icon(library->getIconPath());
QIcon icon = getIcon(library);
QIcon folderIcon(QStringLiteral(":/icons/folder.svg"));
addMaterials(*lib, materialTree, folderIcon, icon, param);

View File

@@ -87,6 +87,10 @@ public:
static QString libraryPath(const std::shared_ptr<Materials::Material>& material);
static QIcon getIcon(const std::shared_ptr<Materials::MaterialLibrary>& library);
static QIcon getIcon(const std::shared_ptr<Materials::ModelLibrary>& library);
static QIcon getIcon(const std::shared_ptr<Materials::Library>& library);
void updateMaterialAppearance();
void updateMaterialProperties();
void updateMaterialGeneral();

View File

@@ -33,6 +33,7 @@
#include <Gui/Command.h>
#include <Gui/WaitCursor.h>
#include "MaterialsEditor.h"
#include "ModelSelect.h"
#include "ui_ModelSelect.h"
@@ -272,7 +273,7 @@ void ModelSelect::addRecents(QStandardItem* parent)
auto model = Materials::ModelManager::getManager().getModel(uuid);
if (Materials::ModelManager::getManager().passFilter(_filter, model->getType())) {
QIcon icon = QIcon(model->getLibrary()->getIconPath());
auto icon = MaterialsEditor::getIcon(model->getLibrary());
auto card = new QStandardItem(icon, model->getName());
card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled
| Qt::ItemIsDropEnabled);
@@ -294,7 +295,7 @@ void ModelSelect::addFavorites(QStandardItem* parent)
auto model = Materials::ModelManager::getManager().getModel(uuid);
if (Materials::ModelManager::getManager().passFilter(_filter, model->getType())) {
QIcon icon = QIcon(model->getLibrary()->getIconPath());
auto icon = MaterialsEditor::getIcon(model->getLibrary());
auto card = new QStandardItem(icon, model->getName());
card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled
| Qt::ItemIsDropEnabled);
@@ -350,7 +351,7 @@ void ModelSelect::fillTree()
addExpanded(tree, model, lib);
auto modelTree = Materials::ModelManager::getManager().getModelTree(library, _filter);
addModels(*lib, modelTree, QIcon(library->getIconPath()));
addModels(*lib, modelTree, MaterialsEditor::getIcon(library));
}
}

View File

@@ -153,7 +153,7 @@ void DlgMigrateExternal::migrate()
void DlgMigrateExternal::statusUpdate(const QString& status)
{
Base::Console().Log("%s\n", status.toStdString().c_str());
Base::Console().log("%s\n", status.toStdString().c_str());
ui->textStatus->append(status);
// This is required to update in real time