Material: MaterialTreeWidget usability enhancements

Improves the MaterialTreeWidget beyond minimum viable product.

- Filters can now be filter lists to allow a variety of filtering
	options.
- User preferences allow the inclusion/exclusion of favorites and
	recents.
- Widget state such as expansion, tree expansions, etc are saved and
	restored.
- show current appearancee material when editing.
- implements a python interface

#fixes 13421: always opens full tree
This commit is contained in:
David Carter
2024-04-16 14:40:24 -04:00
committed by Chris Hennes
parent 9101454c4d
commit 9f43b0ff76
21 changed files with 1051 additions and 165 deletions

View File

@@ -27,9 +27,7 @@
#include <Base/Interpreter.h>
#include <Base/PyObjectBase.h>
// #include "Model.h"
#include "MaterialFilter.h"
#include "MaterialFilterPy.h"
#include "MaterialManagerPy.h"
#include "MaterialPy.h"
#include "ModelManagerPy.h"
@@ -68,6 +66,7 @@ PyMOD_INIT_FUNC(Materials)
Base::Console().Log("Loading Material module... done\n");
Base::Interpreter().addType(&Materials::MaterialManagerPy::Type, module, "MaterialManager");
Base::Interpreter().addType(&Materials::MaterialFilterPy::Type, module, "MaterialFilter");
Base::Interpreter().addType(&Materials::MaterialPy::Type, module, "Material");
Base::Interpreter().addType(&Materials::ModelManagerPy::Type, module, "ModelManager");
Base::Interpreter().addType(&Materials::ModelPropertyPy::Type, module, "ModelProperty");

View File

@@ -33,12 +33,31 @@
using namespace Materials;
MaterialFilterOptions::MaterialFilterOptions()
: _includeFavorites(true)
, _includeRecent(true)
, _includeFolders(true)
, _includeLibraries(true)
, _includeLegacy(false)
{}
MaterialFilterTreeWidgetOptions::MaterialFilterTreeWidgetOptions()
{
auto param = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Mod/Material/TreeWidget");
_includeFavorites = param->GetBool("ShowFavorites", true);
_includeRecent = param->GetBool("ShowRecent", true);
_includeFolders = param->GetBool("ShowEmptyFolders", false);
_includeLibraries = param->GetBool("ShowEmptyLibraries", true);
_includeLegacy = param->GetBool("ShowLegacy", false);
}
//===
TYPESYSTEM_SOURCE(Materials::MaterialFilter, Base::BaseClass)
MaterialFilter::MaterialFilter()
: _includeFolders(true)
, _includeLegacy(true)
, _required()
: _required()
, _requiredComplete()
{}

View File

@@ -24,6 +24,7 @@
#include <memory>
#include <QMetaType>
#include <QSet>
#include <QString>
@@ -36,6 +37,102 @@ namespace Materials
class Material;
/*
* This class is used to set options for a material tree search
*
*/
class MaterialsExport MaterialFilterOptions
{
public:
MaterialFilterOptions();
virtual ~MaterialFilterOptions() = default;
/* Indicates if we should show favourite materials
*
* Default is to show favourite materials
*/
bool includeFavorites() const
{
return _includeFavorites;
}
void setIncludeFavorites(bool value)
{
_includeFavorites = value;
}
/* Indicates if we should show recent materials
*
* Default is to show recent materials
*/
bool includeRecent() const
{
return _includeRecent;
}
void setIncludeRecent(bool value)
{
_includeRecent = value;
}
/* Indicates if we should include empty folders
*
* Default is not to include empty folders
*/
bool includeEmptyFolders() const
{
return _includeFolders;
}
void setIncludeEmptyFolders(bool value)
{
_includeFolders = value;
}
/* Indicates if we should include empty libraries
*
* Default is to include empty libraries
*/
bool includeEmptyLibraries() const
{
return _includeLibraries;
}
void setIncludeEmptyLibraries(bool value)
{
_includeLibraries = value;
}
/* Indicates if we should include materials in the older format
*
* Default is not to include legacy format materials
*/
bool includeLegacy() const
{
return _includeLegacy;
}
void setIncludeLegacy(bool legacy)
{
_includeLegacy = legacy;
}
protected:
bool _includeFavorites;
bool _includeRecent;
bool _includeFolders;
bool _includeLibraries;
bool _includeLegacy;
};
/*
* The same class initialized with preferences for the MaterialTreeWidget
*
*/
class MaterialsExport MaterialFilterTreeWidgetOptions: public MaterialFilterOptions
{
public:
MaterialFilterTreeWidgetOptions();
~MaterialFilterTreeWidgetOptions() override = default;
};
/*
* This class is used to filter materials during a material tree search
*
@@ -48,30 +145,17 @@ public:
MaterialFilter();
virtual ~MaterialFilter() = default;
/* Indicates if we should include empty folders
*
* Default is to include empty folders
/*
* Filter name when used in a list of filters. The name should be
* unique within the list.
*/
bool includeEmptyFolders() const
QString name() const
{
return _includeFolders;
return _name;
}
void setIncludeEmptyFolders(bool value)
void setName(const QString& name)
{
_includeFolders = value;
}
/* Indicates if we should include materials in the older format
*
* Default is to include legacy format materials
*/
bool includeLegacy() const
{
return _includeLegacy;
}
void setIncludeLegacy(bool legacy)
{
_includeLegacy = legacy;
_name = name;
}
/* Sets of model UUIDs that should be included. Optionally, we can
@@ -101,12 +185,13 @@ public:
}
private:
bool _includeFolders;
bool _includeLegacy;
QString _name;
QSet<QString> _required;
QSet<QString> _requiredComplete;
};
} // namespace Materials
Q_DECLARE_METATYPE(Materials::MaterialFilter)
#endif // MATERIAL_MATERIALFILTER_H

View File

@@ -15,17 +15,11 @@
<Author Licence="LGPL" Name="DavidCarter" EMail="dcarter@davidcarter.ca" />
<UserDocu>Material filters.</UserDocu>
</Documentation>
<Attribute Name="IncludeEmptyFolders" ReadOnly="false">
<Attribute Name="Name" ReadOnly="false">
<Documentation>
<UserDocu>Include empty folders in the material list.</UserDocu>
<UserDocu>Name of the filter used to select a filter in a list</UserDocu>
</Documentation>
<Parameter Name="IncludeEmptyFolders" Type="Boolean"/>
</Attribute>
<Attribute Name="IncludeLegacy" ReadOnly="false">
<Documentation>
<UserDocu>Include legacy materials in the material list.</UserDocu>
</Documentation>
<Parameter Name="IncludeLegacy" Type="Boolean"/>
<Parameter Name="Name" Type="String"/>
</Attribute>
<Attribute Name="RequiredModels" ReadOnly="false">
<Documentation>

View File

@@ -61,24 +61,15 @@ int MaterialFilterPy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/)
return 0;
}
Py::Boolean MaterialFilterPy::getIncludeEmptyFolders() const
Py::String MaterialFilterPy::getName() const
{
return {getMaterialFilterPtr()->includeEmptyFolders()};
auto filterName = getMaterialFilterPtr()->name();
return {filterName.toStdString()};
}
void MaterialFilterPy::setIncludeEmptyFolders(const Py::Boolean value)
void MaterialFilterPy::setName(const Py::String value)
{
getMaterialFilterPtr()->setIncludeEmptyFolders(value.isTrue());
}
Py::Boolean MaterialFilterPy::getIncludeLegacy() const
{
return {getMaterialFilterPtr()->includeLegacy()};
}
void MaterialFilterPy::setIncludeLegacy(const Py::Boolean value)
{
getMaterialFilterPtr()->setIncludeLegacy(value.isTrue());
getMaterialFilterPtr()->setName(QString::fromStdString(value));
}
Py::List MaterialFilterPy::getRequiredModels() const

View File

@@ -266,7 +266,8 @@ QString MaterialLibrary::getUUIDFromPath(const QString& path) const
}
bool MaterialLibrary::materialInTree(const std::shared_ptr<Material>& material,
const std::shared_ptr<Materials::MaterialFilter>& filter) const
const std::shared_ptr<Materials::MaterialFilter>& filter,
const Materials::MaterialFilterOptions& options) const
{
if (!filter) {
// If there's no filter we always include
@@ -274,7 +275,7 @@ bool MaterialLibrary::materialInTree(const std::shared_ptr<Material>& material,
}
// filter out old format files
if (material->isOldFormat() && !filter->includeLegacy()) {
if (material->isOldFormat() && !options.includeLegacy()) {
return false;
}
@@ -283,7 +284,8 @@ bool MaterialLibrary::materialInTree(const std::shared_ptr<Material>& material,
}
std::shared_ptr<std::map<QString, std::shared_ptr<MaterialTreeNode>>>
MaterialLibrary::getMaterialTree(const std::shared_ptr<Materials::MaterialFilter>& filter) const
MaterialLibrary::getMaterialTree(const std::shared_ptr<Materials::MaterialFilter>& filter,
const Materials::MaterialFilterOptions& options) const
{
std::shared_ptr<std::map<QString, std::shared_ptr<MaterialTreeNode>>> materialTree =
std::make_shared<std::map<QString, std::shared_ptr<MaterialTreeNode>>>();
@@ -292,7 +294,7 @@ MaterialLibrary::getMaterialTree(const std::shared_ptr<Materials::MaterialFilter
auto filename = it.first;
auto material = it.second;
if (materialInTree(material, filter)) {
if (materialInTree(material, filter, options)) {
QStringList list = filename.split(QString::fromStdString("/"));
// Start at the root
@@ -325,7 +327,7 @@ MaterialLibrary::getMaterialTree(const std::shared_ptr<Materials::MaterialFilter
// Empty folders aren't included in _materialPathMap, so we add them by looking at the file
// system
if (!filter || filter->includeEmptyFolders()) {
if (!filter || options.includeEmptyFolders()) {
auto folderList = MaterialLoader::getMaterialFolders(*this);
for (auto& folder : *folderList) {
QStringList list = folder.split(QString::fromStdString("/"));

View File

@@ -41,6 +41,7 @@ namespace Materials
class Material;
class MaterialManager;
class MaterialFilter;
class MaterialFilterOptions;
class MaterialsExport MaterialLibrary: public LibraryBase,
public std::enable_shared_from_this<MaterialLibrary>
@@ -79,7 +80,8 @@ public:
std::shared_ptr<Material> addMaterial(const std::shared_ptr<Material>& material,
const QString& path);
std::shared_ptr<std::map<QString, std::shared_ptr<MaterialTreeNode>>>
getMaterialTree(const std::shared_ptr<Materials::MaterialFilter>& filter) const;
getMaterialTree(const std::shared_ptr<Materials::MaterialFilter>& filter,
const Materials::MaterialFilterOptions& options) const;
bool isReadOnly() const
{
@@ -99,7 +101,8 @@ protected:
void updatePaths(const QString& oldPath, const QString& newPath);
QString getUUIDFromPath(const QString& path) const;
bool materialInTree(const std::shared_ptr<Material>& material,
const std::shared_ptr<Materials::MaterialFilter>& filter) const;
const std::shared_ptr<Materials::MaterialFilter>& filter,
const Materials::MaterialFilterOptions& options) const;
bool _readOnly;
std::unique_ptr<std::map<QString, std::shared_ptr<Material>>> _materialPathMap;

View File

@@ -24,6 +24,7 @@
#include <random>
#endif
#include <QMutex>
#include <QDirIterator>
#include <QMutexLocker>

View File

@@ -24,8 +24,6 @@
#include <memory>
#include <QMutex>
#include <boost/filesystem.hpp>
#include <Mod/Material/MaterialGlobal.h>
@@ -34,9 +32,12 @@
#include "Materials.h"
#include "MaterialLibrary.h"
#include "MaterialFilter.h"
namespace fs = boost::filesystem;
class QMutex;
namespace App
{
class Material;
@@ -45,8 +46,6 @@ class Material;
namespace Materials
{
class MaterialFilter;
class MaterialsExport MaterialManager: public Base::BaseClass
{
TYPESYSTEM_HEADER_WITH_OVERRIDE();
@@ -77,13 +76,22 @@ public:
getMaterialTree(const std::shared_ptr<MaterialLibrary>& library,
const std::shared_ptr<Materials::MaterialFilter>& filter) const
{
return library->getMaterialTree(filter);
MaterialFilterOptions options;
return library->getMaterialTree(filter, options);
}
std::shared_ptr<std::map<QString, std::shared_ptr<MaterialTreeNode>>>
getMaterialTree(const std::shared_ptr<MaterialLibrary>& library,
const std::shared_ptr<Materials::MaterialFilter>& filter,
const MaterialFilterOptions& options) const
{
return library->getMaterialTree(filter, options);
}
std::shared_ptr<std::map<QString, std::shared_ptr<MaterialTreeNode>>>
getMaterialTree(const std::shared_ptr<MaterialLibrary>& library) const
{
std::shared_ptr<Materials::MaterialFilter> filter;
return library->getMaterialTree(filter);
MaterialFilterOptions options;
return library->getMaterialTree(filter, options);
}
std::shared_ptr<std::list<QString>>
getMaterialFolders(const std::shared_ptr<MaterialLibrary>& library) const;

View File

@@ -71,7 +71,7 @@ PyObject* MaterialManagerPy::getMaterial(PyObject* args)
PyObject* MaterialManagerPy::getMaterialByPath(PyObject* args)
{
char* path;
char* path {};
const char* lib = "";
if (!PyArg_ParseTuple(args, "et|s", "utf-8", &path, &lib)) {
return nullptr;

View File

@@ -34,6 +34,8 @@
#include "DlgSettingsMaterial.h"
#include "Workbench.h"
#include "WorkbenchManipulator.h"
#include "MaterialTreeWidget.h"
#include "MaterialTreeWidgetPy.h"
// use a different name to CreateCommand()
void CreateMaterialCommands();
@@ -78,7 +80,7 @@ PyMOD_INIT_FUNC(MatGui)
// load needed modules
try {
Base::Interpreter().runString("import Material");
Base::Interpreter().runString("import Materials");
}
catch (const Base::Exception& e) {
PyErr_SetString(PyExc_ImportError, e.what());
@@ -107,5 +109,18 @@ PyMOD_INIT_FUNC(MatGui)
// add resources and reloads the translators
loadMaterialResource();
Base::Interpreter().addType(&MatGui::MaterialTreeWidgetPy::Type,
matGuiModule,
"MaterialTreeWidget");
// Initialize types
MatGui::MaterialTreeWidget::init();
// Add custom widgets
new Gui::WidgetProducer<MatGui::MaterialTreeWidget>;
PyMOD_Return(matGuiModule);
}

View File

@@ -36,6 +36,14 @@ qt_find_and_add_translation(QM_SRCS "Resources/translations/*_*.ts"
qt_create_resource_file(${Material_TR_QRC} ${QM_SRCS})
qt_add_resources(MatGui_QRC_SRCS Resources/Material.qrc ${Material_TR_QRC})
generate_from_xml(MaterialTreeWidgetPy)
SET(Python_SRCS
MaterialTreeWidgetPy.xml
MaterialTreeWidgetPyImpl.cpp
)
SOURCE_GROUP("Python" FILES ${Python_SRCS})
set(MatGui_UIC_SRCS
Array2D.ui
Array3D.ui
@@ -51,6 +59,7 @@ set(MatGui_UIC_SRCS
)
SET(MatGui_SRCS
${Python_SRCS}
${MatGui_QRC_SRCS}
${MatGui_UIC_HDRS}
AppearancePreview.h

View File

@@ -402,18 +402,6 @@
</item>
<item row="0" column="1">
<widget class="QPushButton" name="buttonUserDefinedMaterial">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>40</width>
<height>32767</height>
</size>
</property>
<property name="text">
<string notr="true">...</string>
</property>

View File

@@ -163,11 +163,7 @@ DlgDisplayPropertiesImp::DlgDisplayPropertiesImp(bool floating, QWidget* parent,
// Create a filter to only include current format materials
// that contain the basic render model.
auto filter = std::make_shared<Materials::MaterialFilter>();
filter->setIncludeEmptyFolders(false);
filter->setIncludeLegacy(false);
filter->addRequiredComplete(Materials::ModelUUIDs::ModelUUID_Rendering_Basic);
d->ui.widgetMaterial->setFilter(filter);
setupFilters();
std::vector<Gui::ViewProvider*> views = getSelection();
setDisplayModes(views);
@@ -209,6 +205,32 @@ DlgDisplayPropertiesImp::~DlgDisplayPropertiesImp()
Gui::Selection().Detach(this);
}
void DlgDisplayPropertiesImp::setupFilters()
{
// Create a filter to only include current format materials
// that contain the basic render model.
auto filterList = std::make_shared<std::list<std::shared_ptr<Materials::MaterialFilter>>>();
auto filter = std::make_shared<Materials::MaterialFilter>();
filter->setName(tr("Basic Appearance"));
filter->addRequiredComplete(Materials::ModelUUIDs::ModelUUID_Rendering_Basic);
filterList->push_back(filter);
filter = std::make_shared<Materials::MaterialFilter>();
filter->setName(tr("Texture Appearance"));
filter->addRequiredComplete(Materials::ModelUUIDs::ModelUUID_Rendering_Texture);
filterList->push_back(filter);
filter = std::make_shared<Materials::MaterialFilter>();
filter->setName(tr("All Materials"));
filterList->push_back(filter);
d->ui.widgetMaterial->setIncludeEmptyFolders(false);
d->ui.widgetMaterial->setIncludeLegacy(false);
d->ui.widgetMaterial->setFilter(filterList);
}
void DlgDisplayPropertiesImp::setupConnections()
{
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
@@ -337,6 +359,7 @@ void DlgDisplayPropertiesImp::slotChangedObject(const Gui::ViewProvider& obj,
else if (prop.isDerivedFrom<App::PropertyMaterialList>()) {
//auto& value = static_cast<const App::PropertyMaterialList&>(prop).getValue();
if (prop_name == "ShapeAppearance") {
// Base::Console().Log("slotChangeObject(ShapeAppearance)\n");
// bool blocked = d->ui.buttonColor->blockSignals(true);
// auto color = value.diffuseColor;
// d->ui.buttonColor->setColor(QColor((int)(255.0f * color.r),
@@ -565,17 +588,21 @@ void DlgDisplayPropertiesImp::setDisplayModes(const std::vector<Gui::ViewProvide
void DlgDisplayPropertiesImp::setMaterial(const std::vector<Gui::ViewProvider*>& views)
{
bool material = false;
//App::Material::MaterialType matType = App::Material::DEFAULT;
for (auto view : views) {
if (auto* prop =
dynamic_cast<App::PropertyMaterial*>(view->getPropertyByName("ShapeMaterial"))) {
material = true;
// matType = prop->getValue().getType();
break;
}
}
d->ui.buttonUserDefinedMaterial->setEnabled(material);
Q_UNUSED(views);
// bool material = false;
// App::Material mat = App::Material(App::Material::DEFAULT);
// for (auto view : views) {
// if (auto* prop =
// dynamic_cast<App::PropertyMaterial*>(view->getPropertyByName("ShapeMaterial"))) {
// mat = prop->getValue();
// material = mat.uuid.empty();
// if (!material) {
// d->ui.widgetMaterial->setMaterial(QString::fromStdString(mat.uuid));
// }
// break;
// }
// }
// d->ui.buttonUserDefinedMaterial->setEnabled(material);
}
void DlgDisplayPropertiesImp::setColorPlot(const std::vector<Gui::ViewProvider*>& views)
@@ -595,9 +622,21 @@ void DlgDisplayPropertiesImp::setColorPlot(const std::vector<Gui::ViewProvider*>
void DlgDisplayPropertiesImp::setShapeAppearance(const std::vector<Gui::ViewProvider*>& views)
{
Q_UNUSED(views)
// Private::setElementAppearance(views, "ShapeColor", d->ui.buttonColor);
bool material = false;
App::Material mat = App::Material(App::Material::DEFAULT);
for (auto view : views) {
if (auto* prop =
dynamic_cast<App::PropertyMaterialList*>(view->getPropertyByName("ShapeAppearance"))) {
mat = prop->getValues()[0];
material = mat.uuid.empty();
if (!material) {
d->ui.widgetMaterial->setMaterial(QString::fromStdString(mat.uuid));
}
break;
}
}
// d->ui.buttonUserDefinedMaterial->setEnabled(material);
d->ui.buttonUserDefinedMaterial->setEnabled(true);
}
void DlgDisplayPropertiesImp::setLineColor(const std::vector<Gui::ViewProvider*>& views)
@@ -668,6 +707,7 @@ void DlgDisplayPropertiesImp::onMaterialSelected(
material->getAppearanceProperty(QString::fromLatin1("Shininess"))->getFloat();
mat.transparency =
material->getAppearanceProperty(QString::fromLatin1("Transparency"))->getFloat();
mat.uuid = material->getUUID().toStdString();
prop->setValue(mat);
}
}

View File

@@ -84,6 +84,7 @@ protected:
private:
void setupConnections();
void setupFilters();
void slotChangedObject(const Gui::ViewProvider&, const App::Property& Prop);
void setDisplayModes(const std::vector<Gui::ViewProvider*>&);
void setMaterial(const std::vector<Gui::ViewProvider*>&);

View File

@@ -43,6 +43,11 @@ void DlgSettingsMaterial::saveSettings()
ui->fc_custom_mat_dir->onSave();
ui->cb_delete_duplicates->onSave();
ui->cb_sort_by_resources->onSave();
ui->cb_show_favorites->onSave();
ui->cb_show_recent->onSave();
ui->cb_show_empty_libraries->onSave();
ui->cb_show_empty_folders->onSave();
ui->cb_show_legacy->onSave();
// Temporary for testing
ui->cb_legacy_editor->onSave();
@@ -57,6 +62,11 @@ void DlgSettingsMaterial::loadSettings()
ui->fc_custom_mat_dir->onRestore();
ui->cb_delete_duplicates->onRestore();
ui->cb_sort_by_resources->onRestore();
ui->cb_show_favorites->onRestore();
ui->cb_show_recent->onRestore();
ui->cb_show_empty_libraries->onRestore();
ui->cb_show_empty_folders->onRestore();
ui->cb_show_legacy->onRestore();
// Temporary for testing
ui->cb_legacy_editor->onRestore();

View File

@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>434</width>
<height>341</height>
<height>553</height>
</rect>
</property>
<property name="windowTitle">
@@ -231,6 +231,89 @@ If unchecked, they will be sorted by their name.</string>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gbMaterialSelector">
<property name="title">
<string>Material Selector</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="Gui::PrefCheckBox" name="cb_show_favorites">
<property name="text">
<string>Show favorites</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>ShowFavorites</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Material/TreeWidget</cstring>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefCheckBox" name="cb_show_recent">
<property name="text">
<string>Show recent</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>ShowRecent</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Material/TreeWidget</cstring>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefCheckBox" name="cb_show_empty_libraries">
<property name="text">
<string>Show empty libraries</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>ShowEmptyLibraries</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Material/TreeWidget</cstring>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefCheckBox" name="cb_show_empty_folders">
<property name="text">
<string>Show empty folders</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>ShowEmptyFolders</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Material/TreeWidget</cstring>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefCheckBox" name="cb_show_legacy">
<property name="text">
<string>Show legacy files</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>ShowLegacy</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Material/TreeWidget</cstring>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">

View File

@@ -38,12 +38,14 @@
#include <Mod/Material/App/Exceptions.h>
#include <Mod/Material/App/MaterialFilter.h>
#include <Mod/Material/App/MaterialFilterPy.h>
#include <Mod/Material/App/ModelUuids.h>
#include "MaterialTreeWidget.h"
#include "MaterialsEditor.h"
#include "ui_MaterialsEditor.h"
Q_DECLARE_METATYPE(Materials::MaterialFilterPy*)
using Base::Console;
using namespace MatGui;
@@ -51,7 +53,9 @@ using namespace MatGui;
/** Constructs a Material tree widget.
*/
MaterialTreeWidget::MaterialTreeWidget(std::shared_ptr<Materials::MaterialFilter> filter,
TYPESYSTEM_SOURCE(MatGui::MaterialTreeWidget, Base::BaseClass)
MaterialTreeWidget::MaterialTreeWidget(const std::shared_ptr<Materials::MaterialFilter>& filter,
QWidget* parent)
: QWidget(parent)
, m_expanded(false)
@@ -60,10 +64,21 @@ MaterialTreeWidget::MaterialTreeWidget(std::shared_ptr<Materials::MaterialFilter
setup();
}
MaterialTreeWidget::MaterialTreeWidget(
const std::shared_ptr<std::list<std::shared_ptr<Materials::MaterialFilter>>>& filterList,
QWidget* parent)
: QWidget(parent)
, m_expanded(false)
, _filter(std::make_shared<Materials::MaterialFilter>())
, _filterList(filterList)
{
setup();
}
MaterialTreeWidget::MaterialTreeWidget(QWidget* parent)
: QWidget(parent)
, m_expanded(false)
, _filter(nullptr)
, _filter(std::make_shared<Materials::MaterialFilter>())
{
setup();
}
@@ -80,7 +95,12 @@ void MaterialTreeWidget::setup()
/**
* Destroys the widget and detaches it from its parameter group.
*/
MaterialTreeWidget::~MaterialTreeWidget() = default;
MaterialTreeWidget::~MaterialTreeWidget()
{
addRecent(m_uuid);
saveWidgetSettings();
saveMaterialTree();
}
void MaterialTreeWidget::createLayout()
{
@@ -88,9 +108,9 @@ void MaterialTreeWidget::createLayout()
m_expand = new QPushButton(this);
m_expand->setIcon(style()->standardIcon(QStyle::SP_TitleBarUnshadeButton));
m_materialTree = new QTreeView(this);
m_filterCombo = new QComboBox(this);
m_editor = new QPushButton(tr("Launch editor"), this);
// m_materialTree->setSelectionModel(QAbstractItemView::SingleSelection);
m_materialTree->setSelectionMode(QAbstractItemView::SingleSelection);
m_materialTree->setSelectionBehavior(QAbstractItemView::SelectItems);
@@ -102,6 +122,7 @@ void MaterialTreeWidget::createLayout()
treeLayout->addWidget(m_materialTree);
auto buttonLayout = new QHBoxLayout();
buttonLayout->addWidget(m_filterCombo);
buttonLayout->addItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Preferred));
buttonLayout->addWidget(m_editor);
@@ -112,18 +133,34 @@ void MaterialTreeWidget::createLayout()
layout->addItem(buttonLayout);
setLayout(layout);
// Start in an unexpanded state. Store the state?
openWidgetState(false);
// Set the filter if using a filter list
if (hasMultipleFilters()) {
_filter = _filterList->front();
}
fillFilterCombo();
// Start in the previous expanded state
auto param = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Mod/Material/TreeWidget");
auto expanded = param->GetBool("WidgetExpanded", false);
setExpanded(expanded);
connect(m_expand, &QPushButton::clicked, this, &MaterialTreeWidget::expandClicked);
connect(m_editor, &QPushButton::clicked, this, &MaterialTreeWidget::editorClicked);
connect(m_filterCombo,
&QComboBox::currentTextChanged,
this,
&MaterialTreeWidget::onFilter);
}
void MaterialTreeWidget::openWidgetState(bool open)
void MaterialTreeWidget::setExpanded(bool open)
{
m_materialTree->setVisible(open);
m_editor->setVisible(open);
setFilterVisible(open);
m_expanded = open;
if (open) {
@@ -134,12 +171,33 @@ void MaterialTreeWidget::openWidgetState(bool open)
}
}
void MaterialTreeWidget::setFilterVisible(bool open)
{
if (open && hasMultipleFilters()) {
m_filterCombo->setVisible(true);
}
else {
m_filterCombo->setVisible(false);
}
}
void MaterialTreeWidget::fillFilterCombo()
{
m_filterCombo->clear();
if (hasMultipleFilters()) {
for (auto const& filter : *_filterList) {
m_filterCombo->addItem(filter->name());
}
}
}
void MaterialTreeWidget::expandClicked(bool checked)
{
Q_UNUSED(checked)
// Toggle the open state
openWidgetState(!m_expanded);
setExpanded(!m_expanded);
}
void MaterialTreeWidget::editorClicked(bool checked)
@@ -160,7 +218,7 @@ void MaterialTreeWidget::editorClicked(bool checked)
// Gui::Application::Instance->commandManager().runCommandByName("Materials_Edit");
// Toggle the open state
// openWidgetState(!m_expanded);
// setExpanded(!m_expanded);
}
void MaterialTreeWidget::updateMaterial(const QString& uuid)
@@ -212,8 +270,14 @@ QModelIndex MaterialTreeWidget::findInTree(const QString& uuid)
auto root = model->invisibleRootItem();
QModelIndex index;
if (findInTree(*root, &index, uuid)) {
return index;
// Find the original item, not the reference in favourites or recents
for (int i = 0; i < root->rowCount(); i++) {
auto child = root->child(i);
if (child->text() != tr("Favorites") && child->text() != tr("Recent")) {
if (findInTree(*child, &index, uuid)) {
return index;
}
}
}
return {};
@@ -231,6 +295,7 @@ void MaterialTreeWidget::setMaterial(const QString& uuid)
if (index.isValid()) {
QItemSelectionModel* selectionModel = m_materialTree->selectionModel();
selectionModel->select(index, QItemSelectionModel::SelectCurrent);
m_materialTree->scrollTo(index);
}
}
@@ -239,14 +304,61 @@ QString MaterialTreeWidget::getMaterialUUID() const
return m_uuid;
}
void MaterialTreeWidget::setFilter(std::shared_ptr<Materials::MaterialFilter> filter)
void MaterialTreeWidget::setFilter(const std::shared_ptr<Materials::MaterialFilter>& filter)
{
_filter.reset();
if (_filter) {
_filter.reset();
}
if (_filterList) {
_filterList.reset();
}
_filter = filter;
fillFilterCombo();
setFilterVisible(m_expanded);
updateMaterialTree();
}
void MaterialTreeWidget::setFilter(
const std::shared_ptr<std::list<std::shared_ptr<Materials::MaterialFilter>>>& filterList)
{
_filter.reset();
if (_filterList) {
_filterList.reset();
}
_filterList = filterList;
if (hasMultipleFilters()) {
_filter = _filterList->front();
}
fillFilterCombo();
setFilterVisible(m_expanded);
updateMaterialTree();
}
void MaterialTreeWidget::setActiveFilter(const QString& name)
{
if (_filterList) {
for (auto const& filter : *_filterList) {
if (filter->name() == name) {
_filter.reset();
_filter = filter;
// Save the library/folder expansion state
saveMaterialTree();
updateMaterialTree();
return;
}
}
}
}
void MaterialTreeWidget::updateMaterialTree()
{
_favorites.clear();
@@ -293,6 +405,70 @@ void MaterialTreeWidget::getRecents()
}
}
void MaterialTreeWidget::saveRecents()
{
auto param = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Mod/Material/Recent");
// Clear out the existing favorites
int count = param->GetInt("Recent", 0);
for (int i = 0; static_cast<long>(i) < count; i++) {
QString key = QString::fromLatin1("MRU%1").arg(i);
param->RemoveASCII(key.toStdString().c_str());
}
// Add the current values
int size = _recents.size();
if (size > _recentMax) {
size = _recentMax;
}
param->SetInt("Recent", size);
int j = 0;
for (auto& recent : _recents) {
QString key = QString::fromLatin1("MRU%1").arg(j);
param->SetASCII(key.toStdString().c_str(), recent.toStdString());
j++;
if (j >= size) {
break;
}
}
}
void MaterialTreeWidget::addRecent(const QString& uuid)
{
// Ensure it is a material. New, unsaved materials will not be
try {
auto material = _materialManager.getMaterial(uuid);
Q_UNUSED(material)
}
catch (const Materials::MaterialNotFound&) {
return;
}
// Ensure no duplicates
if (isRecent(uuid)) {
_recents.remove(uuid);
}
_recents.push_front(uuid);
while (_recents.size() > static_cast<std::size_t>(_recentMax)) {
_recents.pop_back();
}
saveRecents();
}
bool MaterialTreeWidget::isRecent(const QString& uuid) const
{
for (auto& it : _recents) {
if (it == uuid) {
return true;
}
}
return false;
}
void MaterialTreeWidget::createMaterialTree()
{
auto model = new QStandardItemModel(this);
@@ -312,36 +488,44 @@ void MaterialTreeWidget::createMaterialTree()
void MaterialTreeWidget::fillMaterialTree()
{
auto param = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Mod/Material/TreeWidget/MaterialTree");
auto model = dynamic_cast<QStandardItemModel*>(m_materialTree->model());
auto lib = new QStandardItem(tr("Favorites"));
lib->setFlags(Qt::ItemIsEnabled);
addExpanded(model, lib);
addFavorites(lib);
if (_filterOptions.includeFavorites()) {
auto lib = new QStandardItem(tr("Favorites"));
lib->setFlags(Qt::ItemIsEnabled);
addExpanded(model, lib, param);
addFavorites(lib);
}
lib = new QStandardItem(tr("Recent"));
lib->setFlags(Qt::ItemIsEnabled);
addExpanded(model, lib);
addRecents(lib);
// // Create a filter to only include current format materials
// // that contain the basic render model.
// Materials::MaterialFilter filter;
// filter.setIncludeEmptyFolders(false);
// filter.setIncludeLegacy(false);
// filter.addRequired(Materials::ModelUUIDs::ModelUUID_Rendering_Basic);
if (_filterOptions.includeRecent()) {
auto lib = new QStandardItem(tr("Recent"));
lib->setFlags(Qt::ItemIsEnabled);
addExpanded(model, lib, param);
addRecents(lib);
}
auto libraries = _materialManager.getMaterialLibraries();
for (const auto& library : *libraries) {
lib = new QStandardItem(library->getName());
lib->setFlags(Qt::ItemIsEnabled);
addExpanded(model, lib);
auto modelTree = _materialManager.getMaterialTree(library, _filter, _filterOptions);
QIcon icon(library->getIconPath());
QIcon folderIcon(QString::fromStdString(":/icons/folder.svg"));
bool showLibraries = _filterOptions.includeEmptyLibraries();
if (!_filterOptions.includeEmptyLibraries() && modelTree->size() > 0) {
showLibraries = true;
}
auto modelTree = _materialManager.getMaterialTree(library, _filter);
addMaterials(*lib, modelTree, folderIcon, icon);
if (showLibraries) {
auto lib = new QStandardItem(library->getName());
lib->setFlags(Qt::ItemIsEnabled);
addExpanded(model, lib, param);
QIcon icon(library->getIconPath());
QIcon folderIcon(QString::fromStdString(":/icons/folder.svg"));
addMaterials(*lib, modelTree, folderIcon, icon, param);
}
}
}
@@ -351,12 +535,34 @@ void MaterialTreeWidget::addExpanded(QStandardItem* parent, QStandardItem* child
m_materialTree->setExpanded(child->index(), true);
}
void MaterialTreeWidget::addExpanded(QStandardItem* parent,
QStandardItem* child,
const Base::Reference<ParameterGrp>& param)
{
parent->appendRow(child);
// Restore to any previous expansion state
auto expand = param->GetBool(child->text().toStdString().c_str(), true);
m_materialTree->setExpanded(child->index(), expand);
}
void MaterialTreeWidget::addExpanded(QStandardItemModel* model, QStandardItem* child)
{
model->appendRow(child);
m_materialTree->setExpanded(child->index(), true);
}
void MaterialTreeWidget::addExpanded(QStandardItemModel* model,
QStandardItem* child,
const Base::Reference<ParameterGrp>& param)
{
model->appendRow(child);
// Restore to any previous expansion state
auto expand = param->GetBool(child->text().toStdString().c_str(), true);
m_materialTree->setExpanded(child->index(), expand);
}
void MaterialTreeWidget::addRecents(QStandardItem* parent)
{
for (auto& uuid : _recents) {
@@ -397,17 +603,16 @@ void MaterialTreeWidget::addMaterials(
const std::shared_ptr<std::map<QString, std::shared_ptr<Materials::MaterialTreeNode>>>&
modelTree,
const QIcon& folderIcon,
const QIcon& icon)
const QIcon& icon,
const Base::Reference<ParameterGrp>& param)
{
auto childParam = param->GetGroup(parent.text().toStdString().c_str());
for (auto& mat : *modelTree) {
auto nodePtr = mat.second;
if (nodePtr->getType() == Materials::MaterialTreeNode::DataNode) {
auto material = nodePtr->getData();
QString uuid = material->getUUID();
// Base::Console().Log("Material path '%s'\n",
// material->getDirectory().toStdString().c_str());
// auto card = new QStandardItem(icon, material->getName());
auto card = new QStandardItem(icon, mat.first);
card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
card->setData(QVariant(uuid), Qt::UserRole);
@@ -416,10 +621,10 @@ void MaterialTreeWidget::addMaterials(
}
else {
auto node = new QStandardItem(folderIcon, mat.first);
addExpanded(&parent, node);
addExpanded(&parent, node, childParam);
node->setFlags(Qt::ItemIsEnabled);
auto treeMap = nodePtr->getFolder();
addMaterials(*node, treeMap, folderIcon, icon);
addMaterials(*node, treeMap, folderIcon, icon, childParam);
}
}
}
@@ -446,6 +651,7 @@ void MaterialTreeWidget::onSelectMaterial(const QItemSelection& selected,
std::string _uuid = uuid.toStdString();
Q_EMIT materialSelected(getMaterialManager().getMaterial(uuid));
Q_EMIT onMaterial(uuid);
}
void MaterialTreeWidget::onDoubleClick(const QModelIndex& index)
@@ -458,3 +664,48 @@ void MaterialTreeWidget::onDoubleClick(const QModelIndex& index)
updateMaterial(uuid);
}
}
void MaterialTreeWidget::onFilter(const QString& text)
{
setActiveFilter(text);
}
void MaterialTreeWidget::saveWidgetSettings()
{
auto param = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Mod/Material/TreeWidget");
param->SetBool("WidgetExpanded", m_expanded);
}
void MaterialTreeWidget::saveMaterialTree()
{
auto param = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Mod/Material/TreeWidget/MaterialTree");
param->Clear();
auto tree = m_materialTree;
auto model = dynamic_cast<QStandardItemModel*>(tree->model());
auto root = model->invisibleRootItem();
for (int i = 0; i < root->rowCount(); i++) {
auto child = root->child(i);
saveMaterialTreeChildren(param, tree, model, child);
}
}
void MaterialTreeWidget::saveMaterialTreeChildren(const Base::Reference<ParameterGrp>& param,
QTreeView* tree,
QStandardItemModel* model,
QStandardItem* item)
{
if (item->hasChildren()) {
param->SetBool(item->text().toStdString().c_str(), tree->isExpanded(item->index()));
auto treeParam = param->GetGroup(item->text().toStdString().c_str());
for (int i = 0; i < item->rowCount(); i++) {
auto child = item->child(i);
saveMaterialTreeChildren(treeParam, tree, model, child);
}
}
}

View File

@@ -41,6 +41,7 @@
#include <Mod/Material/App/MaterialFilter.h>
#include <Mod/Material/App/MaterialFilterPy.h>
#include <Mod/Material/App/MaterialManager.h>
#include <Mod/Material/App/Materials.h>
@@ -48,6 +49,7 @@ namespace MatGui
{
class CommandManager;
class WidgetFactoryInst;
class MaterialTreeWidgetPy;
/** The Material Tree widget class
* This widget is intended for use wherever materials are used. It is a light weight
@@ -65,36 +67,21 @@ class WidgetFactoryInst;
*
* \author David Carter
*/
class MatGuiExport MaterialTreeWidget: public QWidget
class MatGuiExport MaterialTreeWidget: public QWidget, public Base::BaseClass
{
Q_OBJECT
TYPESYSTEM_HEADER_WITH_OVERRIDE();
public:
explicit MaterialTreeWidget(std::shared_ptr<Materials::MaterialFilter> filter,
explicit MaterialTreeWidget(const std::shared_ptr<Materials::MaterialFilter>& filter,
QWidget* parent = nullptr);
explicit MaterialTreeWidget(
const std::shared_ptr<std::list<std::shared_ptr<Materials::MaterialFilter>>>& filterList,
QWidget* parent = nullptr);
explicit MaterialTreeWidget(QWidget* parent = nullptr);
~MaterialTreeWidget() override;
// void setEntryName( const QByteArray& name );
// QByteArray entryName() const;
// /** Does the same as setEntryName().
// * This function is added for convenience because the ui compiler
// * will use this function if the attribute stdset isn't set to 0 in a .ui file.
// */
// void setPrefEntry(const QByteArray& name);
// void setParamGrpPath( const QByteArray& path );
// QByteArray paramGrpPath() const;
// /** Does the same as setParamGrpPath().
// * This function is added for convenience because the ui compiler
// * will use this function if the attribute stdset isn't set to 0 in a .ui file.
// */
// void setPrefPath(const QByteArray& name);
// void OnChange(Base::Subject<const char*> &rCaller, const char * sReason) override;
// void onSave();
// void onRestore();
/** Set the material by specifying its UUID
*/
void setMaterial(const QString& uuid);
@@ -103,17 +90,84 @@ public:
QString getMaterialUUID() const;
/** Set the material filter
*/
void setFilter(std::shared_ptr<Materials::MaterialFilter> filter);
void setFilter(const std::shared_ptr<Materials::MaterialFilter>& filter);
void setFilter(
const std::shared_ptr<std::list<std::shared_ptr<Materials::MaterialFilter>>>& filterList);
void setActiveFilter(const QString &name);
void setExpanded(bool open);
bool getExpanded()
{
return m_expanded;
}
/* Indicates if we should show favourite materials
*/
bool includeFavorites() const
{
return _filterOptions.includeFavorites();
}
void setIncludeFavorites(bool value)
{
_filterOptions.setIncludeFavorites(value);
}
/* Indicates if we should show recent materials
*/
bool includeRecent() const
{
return _filterOptions.includeRecent();
}
void setIncludeRecent(bool value)
{
_filterOptions.setIncludeRecent(value);
}
/* Indicates if we should include empty folders
*/
bool includeEmptyFolders() const
{
return _filterOptions.includeEmptyFolders();
}
void setIncludeEmptyFolders(bool value)
{
_filterOptions.setIncludeEmptyFolders(value);
}
/* Indicates if we should include empty libraries
*/
bool includeEmptyLibraries() const
{
return _filterOptions.includeEmptyLibraries();
}
void setIncludeEmptyLibraries(bool value)
{
Base::Console().Log("setIncludeEmptyLibraries(%s)\n", (value ? "true" : "false"));
_filterOptions.setIncludeEmptyLibraries(value);
}
/* Indicates if we should include materials in the older format
*/
bool includeLegacy() const
{
return _filterOptions.includeLegacy();
}
void setIncludeLegacy(bool legacy)
{
_filterOptions.setIncludeLegacy(legacy);
}
Q_SIGNALS:
/** Emits this signal when a material has been selected */
void materialSelected(const std::shared_ptr<Materials::Material>& material);
void onMaterial(const QString& uuid);
private Q_SLOTS:
void expandClicked(bool checked);
void editorClicked(bool checked);
void onSelectMaterial(const QItemSelection& selected, const QItemSelection& deselected);
void onDoubleClick(const QModelIndex& index);
void onFilter(const QString& text);
private:
void setup();
@@ -122,6 +176,7 @@ private:
QPushButton* m_expand;
QTreeView* m_materialTree;
QPushButton* m_editor;
QComboBox* m_filterCombo;
bool m_expanded;
QString m_materialDisplay;
@@ -130,7 +185,10 @@ private:
std::list<QString> _favorites;
std::list<QString> _recents;
std::shared_ptr<Materials::MaterialFilter> _filter;
Materials::MaterialFilterTreeWidgetOptions _filterOptions;
std::shared_ptr<std::list<std::shared_ptr<Materials::MaterialFilter>>> _filterList;
int _recentMax;
MaterialTreeWidgetPy* pyTreeWidget {nullptr};
Materials::MaterialManager _materialManager;
@@ -146,7 +204,17 @@ protected:
}
void getFavorites();
void getRecents();
void saveRecents();
void addRecent(const QString& uuid);
bool isRecent(const QString& uuid) const;
void saveWidgetSettings();
void saveMaterialTreeChildren(const Base::Reference<ParameterGrp>& param,
QTreeView* tree,
QStandardItemModel* model,
QStandardItem* item);
void saveMaterialTree();
/** Create the widgets UI objects
*/
@@ -159,7 +227,13 @@ protected:
void fillMaterialTree();
void updateMaterialTree();
void addExpanded(QStandardItem* parent, QStandardItem* child);
void addExpanded(QStandardItem* parent,
QStandardItem* child,
const Base::Reference<ParameterGrp>& param);
void addExpanded(QStandardItemModel* model, QStandardItem* child);
void addExpanded(QStandardItemModel* model,
QStandardItem* child,
const Base::Reference<ParameterGrp>& param);
void addRecents(QStandardItem* parent);
void addFavorites(QStandardItem* parent);
void addMaterials(
@@ -167,9 +241,13 @@ protected:
const std::shared_ptr<std::map<QString, std::shared_ptr<Materials::MaterialTreeNode>>>&
modelTree,
const QIcon& folderIcon,
const QIcon& icon);
void openWidgetState(bool open);
const QIcon& icon,
const Base::Reference<ParameterGrp>& param);
void setFilterVisible(bool open);
void fillFilterCombo();
bool hasMultipleFilters() const {
return (_filterList && _filterList->size() > 1);
}
};
} // namespace MatGui

View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
<PythonExport
Father="BaseClassPy"
Name="MaterialTreeWidgetPy"
Twin="MaterialTreeWidget"
TwinPointer="MaterialTreeWidget"
Include="Mod/Material/Gui/MaterialTreeWidget.h"
Namespace="MatGui"
FatherInclude="Base/BaseClassPy.h"
FatherNamespace="Base"
Constructor="true"
Delete="false">
<Documentation>
<Author Licence="LGPL" Name="DavidCarter" EMail="dcarter@davidcarter.ca" />
<UserDocu>Material tree widget.</UserDocu>
</Documentation>
<Attribute Name="UUID" ReadOnly="false">
<Documentation>
<UserDocu>Material UUID.</UserDocu>
</Documentation>
<Parameter Name="UUID" Type="String"/>
</Attribute>
<Attribute Name="expanded" ReadOnly="false">
<Documentation>
<UserDocu>Expand material tree.</UserDocu>
</Documentation>
<Parameter Name="expanded" Type="Boolean"/>
</Attribute>
<Attribute Name="IncludeFavorites" ReadOnly="false">
<Documentation>
<UserDocu>Include favorites in the material list.</UserDocu>
</Documentation>
<Parameter Name="IncludeFavorites" Type="Boolean"/>
</Attribute>
<Attribute Name="IncludeRecent" ReadOnly="false">
<Documentation>
<UserDocu>Include recently used materials in the material list.</UserDocu>
</Documentation>
<Parameter Name="IncludeRecent" Type="Boolean"/>
</Attribute>
<Attribute Name="IncludeEmptyFolders" ReadOnly="false">
<Documentation>
<UserDocu>Include empty folders in the material list.</UserDocu>
</Documentation>
<Parameter Name="IncludeEmptyFolders" Type="Boolean"/>
</Attribute>
<Attribute Name="IncludeEmptyLibraries" ReadOnly="false">
<Documentation>
<UserDocu>Include empty libraries in the material list.</UserDocu>
</Documentation>
<Parameter Name="IncludeEmptyLibraries" Type="Boolean"/>
</Attribute>
<Attribute Name="IncludeLegacy" ReadOnly="false">
<Documentation>
<UserDocu>Include legacy materials in the material list.</UserDocu>
</Documentation>
<Parameter Name="IncludeLegacy" Type="Boolean"/>
</Attribute>
<Methode Name="setFilter">
<Documentation>
<UserDocu>Set the material filter or list of filters.</UserDocu>
</Documentation>
</Methode>
<Methode Name="selectFilter">
<Documentation>
<UserDocu>Set the current material filter.</UserDocu>
</Documentation>
</Methode>
</PythonExport>
</GenerateModel>

View File

@@ -0,0 +1,238 @@
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#include "PreCompiled.h"
#include <Gui/PythonWrapper.h>
#include "MaterialTreeWidget.h"
#include "MaterialTreeWidgetPy.h"
#include "MaterialTreeWidgetPy.cpp"
using namespace MatGui;
// returns a string which represents the object e.g. when printed in python
std::string MaterialTreeWidgetPy::representation() const
{
std::ostringstream str;
str << "<MaterialTreeWidget at " << getMaterialTreeWidgetPtr() << ">";
return str.str();
}
PyObject* MaterialTreeWidgetPy::PyMake(struct _typeobject*, PyObject*, PyObject*) // Python wrapper
{
// never create such objects with the constructor
return new MaterialTreeWidgetPy(new MaterialTreeWidget());
}
// constructor method
int MaterialTreeWidgetPy::PyInit(PyObject* args, PyObject* /*kwd*/)
{
PyObject* obj {};
if (PyArg_ParseTuple(args, "")) {
return 0;
}
PyErr_Clear();
if (PyArg_ParseTuple(args, "O!", &(MatGui::MaterialTreeWidgetPy::Type), &obj)) {
auto widget = static_cast<MatGui::MaterialTreeWidgetPy*>(obj)->getMaterialTreeWidgetPtr();
_pcTwinPointer = widget;
return 0;
}
// PyErr_Clear();
// if (PyArg_ParseTuple(args, "O!", &(QWidget::Type), &obj)) {
// auto widget = static_cast<MatGui::MaterialTreeWidget*>(obj);
// _pcTwinPointer = widget;
// return 0;
// }
PyErr_Clear();
if (PyArg_ParseTuple(args, "O", &obj)) {
if (QLatin1String(obj->ob_type->tp_name) == QLatin1String("PySide2.QtWidgets.QWidget")) {
Gui::PythonWrapper wrap;
wrap.loadWidgetsModule();
auto qObject = wrap.toQObject(Py::Object(obj));
auto widget = static_cast<MatGui::MaterialTreeWidget*>(qObject);
_pcTwinPointer = widget;
return 0;
}
else {
PyErr_Format(PyExc_TypeError,
"empty parameter list, or MaterialTreeWidget expected not '%s'",
obj->ob_type->tp_name);
return -1;
}
}
PyErr_SetString(PyExc_TypeError, "empty parameter list, or MaterialTreeWidget expected");
// PyErr_Format(PyExc_TypeError,
// "empty parameter list, or MaterialTreeWidget expected not '%s'",
// obj->ob_type->tp_name);
return -1;
}
Py::String MaterialTreeWidgetPy::getUUID() const
{
return Py::String(getMaterialTreeWidgetPtr()->getMaterialUUID().toStdString());
}
void MaterialTreeWidgetPy::setUUID(const Py::String value)
{
getMaterialTreeWidgetPtr()->setMaterial(QString::fromStdString(value));
}
Py::Boolean MaterialTreeWidgetPy::getexpanded() const
{
return {getMaterialTreeWidgetPtr()->getExpanded()};
}
void MaterialTreeWidgetPy::setexpanded(const Py::Boolean value)
{
getMaterialTreeWidgetPtr()->setExpanded(value.isTrue());
}
Py::Boolean MaterialTreeWidgetPy::getIncludeFavorites() const
{
return {getMaterialTreeWidgetPtr()->includeFavorites()};
}
void MaterialTreeWidgetPy::setIncludeFavorites(const Py::Boolean value)
{
getMaterialTreeWidgetPtr()->setIncludeFavorites(value.isTrue());
}
Py::Boolean MaterialTreeWidgetPy::getIncludeRecent() const
{
return {getMaterialTreeWidgetPtr()->includeRecent()};
}
void MaterialTreeWidgetPy::setIncludeRecent(const Py::Boolean value)
{
getMaterialTreeWidgetPtr()->setIncludeRecent(value.isTrue());
}
Py::Boolean MaterialTreeWidgetPy::getIncludeEmptyFolders() const
{
return {getMaterialTreeWidgetPtr()->includeEmptyFolders()};
}
void MaterialTreeWidgetPy::setIncludeEmptyFolders(const Py::Boolean value)
{
getMaterialTreeWidgetPtr()->setIncludeEmptyFolders(value.isTrue());
}
Py::Boolean MaterialTreeWidgetPy::getIncludeEmptyLibraries() const
{
return {getMaterialTreeWidgetPtr()->includeEmptyLibraries()};
}
void MaterialTreeWidgetPy::setIncludeEmptyLibraries(const Py::Boolean value)
{
getMaterialTreeWidgetPtr()->setIncludeEmptyLibraries(value.isTrue());
}
Py::Boolean MaterialTreeWidgetPy::getIncludeLegacy() const
{
return {getMaterialTreeWidgetPtr()->includeLegacy()};
}
void MaterialTreeWidgetPy::setIncludeLegacy(const Py::Boolean value)
{
getMaterialTreeWidgetPtr()->setIncludeLegacy(value.isTrue());
}
PyObject* MaterialTreeWidgetPy::setFilter(PyObject* args)
{
PyObject* obj;
if (!PyArg_ParseTuple(args, "O", &obj)) {
return nullptr;
}
if (PyObject_TypeCheck(obj, &(Materials::MaterialFilterPy::Type))) {
auto filter = static_cast<Materials::MaterialFilterPy*>(obj)->getMaterialFilterPtr();
Base::Console().Log("Filter '%s'\n", filter->name().toStdString().c_str());
auto filterPtr = std::make_shared<Materials::MaterialFilter>(*filter);
getMaterialTreeWidgetPtr()->setFilter(filterPtr);
}
else if (PyList_Check(obj)) {
// The argument is a list of filters
Base::Console().Log("Filter List\n");
Py_ssize_t n = PyList_Size(obj);
Base::Console().Log("n = %d\n", n);
if (n < 0) {
Py_Return;
}
PyObject* item;
auto filterList = std::make_shared<std::list<std::shared_ptr<Materials::MaterialFilter>>>();
for (int i = 0; i < n; i++) {
item = PyList_GetItem(obj, i);
if (PyObject_TypeCheck(item, &(Materials::MaterialFilterPy::Type))) {
auto filter =
static_cast<Materials::MaterialFilterPy*>(item)->getMaterialFilterPtr();
Base::Console().Log("\tFilter '%s'\n",
filter->name().toStdString().c_str()); auto filterPtr =
std::make_shared<Materials::MaterialFilter>(*filter);
filterList->push_back(filterPtr);
// getMaterialTreeWidgetPtr()->setFilter(
//
// *static_cast<Materials::MaterialFilterPy*>(obj)->getMaterialFilterPtr());
}
else {
PyErr_Format(PyExc_TypeError,
"List entry must be of type 'MaterialFilter' not '%s'",
obj->ob_type->tp_name);
return nullptr;
}
}
getMaterialTreeWidgetPtr()->setFilter(filterList);
}
else {
PyErr_Format(PyExc_TypeError,
"Type must be 'MaterialFilter' or list of 'MaterialFilter' not '%s'",
obj->ob_type->tp_name);
return nullptr;
}
Py_Return;
}
PyObject* MaterialTreeWidgetPy::selectFilter(PyObject* args)
{
char* name;
if (!PyArg_ParseTuple(args, "s", &name)) {
return nullptr;
}
Base::Console().Log("selectFilter(%s)\n", name);
Py_Return;
}
PyObject* MaterialTreeWidgetPy::getCustomAttributes(const char* /*attr*/) const
{
return nullptr;
}
int MaterialTreeWidgetPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
{
return 0;
}