add min max def to properties
This commit is contained in:
@@ -7,6 +7,10 @@
|
||||
#include "Vtk/uLibVtkInterface.h"
|
||||
#include "Math/Units.h"
|
||||
#include "Math/Dense.h"
|
||||
#include <QPushButton>
|
||||
#include <QColorDialog>
|
||||
#include <QFrame>
|
||||
#include <QSlider>
|
||||
#include "Settings.h"
|
||||
|
||||
namespace uLib {
|
||||
@@ -19,7 +23,7 @@ PropertyWidgetBase::PropertyWidgetBase(PropertyBase* prop, QWidget* parent)
|
||||
|
||||
std::string unit = prop->GetUnits();
|
||||
QString labelText = QString::fromStdString(prop->GetName());
|
||||
if (!unit.empty()) {
|
||||
if (!unit.empty() && unit != "color") {
|
||||
auto dim = Settings::Instance().IdentifyDimension(unit);
|
||||
std::string pref = Settings::Instance().GetPreferredUnit(dim);
|
||||
if (!pref.empty()) {
|
||||
@@ -203,6 +207,76 @@ BoolPropertyWidget::BoolPropertyWidget(Property<bool>* prop, QWidget* parent)
|
||||
}
|
||||
BoolPropertyWidget::~BoolPropertyWidget() {}
|
||||
|
||||
RangePropertyWidget::RangePropertyWidget(Property<double>* prop, QWidget* parent)
|
||||
: PropertyWidgetBase(prop, parent), m_Prop(prop) {
|
||||
m_Slider = new QSlider(::Qt::Horizontal, this);
|
||||
m_Slider->setRange(0, 100);
|
||||
m_Slider->setMinimumWidth(80);
|
||||
|
||||
m_Edit = new UnitLineEdit(this);
|
||||
m_Edit->setFixedWidth(50);
|
||||
|
||||
m_Layout->addWidget(m_Slider, 1);
|
||||
m_Layout->addWidget(m_Edit, 0);
|
||||
|
||||
connect(m_Slider, &QSlider::valueChanged, this, &RangePropertyWidget::onSliderChanged);
|
||||
connect(m_Edit, &UnitLineEdit::valueManualChanged, [this](double val){ m_Prop->Set(val); });
|
||||
|
||||
m_Connection = uLib::Object::connect(m_Prop, &Property<double>::PropertyChanged, [this](){
|
||||
this->updateUi();
|
||||
});
|
||||
updateUi();
|
||||
}
|
||||
RangePropertyWidget::~RangePropertyWidget() { m_Connection.disconnect(); }
|
||||
|
||||
void RangePropertyWidget::updateUi() {
|
||||
double val = m_Prop->Get();
|
||||
m_Edit->setValue(val);
|
||||
if (m_Prop->GetMax() != m_Prop->GetMin()) {
|
||||
int sliderVal = (int)((val - m_Prop->GetMin()) / (m_Prop->GetMax() - m_Prop->GetMin()) * 100.0);
|
||||
QSignalBlocker blocker(m_Slider);
|
||||
m_Slider->setValue(sliderVal);
|
||||
}
|
||||
}
|
||||
|
||||
void RangePropertyWidget::onSliderChanged(int val) {
|
||||
double realVal = m_Prop->GetMin() + (val / 100.0) * (m_Prop->GetMax() - m_Prop->GetMin());
|
||||
m_Prop->Set(realVal);
|
||||
}
|
||||
|
||||
ColorPropertyWidget::ColorPropertyWidget(Property<Vector3d>* prop, QWidget* parent)
|
||||
: PropertyWidgetBase(prop, parent), m_Prop(prop) {
|
||||
m_Button = new QPushButton(this);
|
||||
m_Button->setFixedWidth(60);
|
||||
this->updateButtonColor();
|
||||
m_Layout->addWidget(m_Button, 0, ::Qt::AlignRight);
|
||||
|
||||
connect(m_Button, &QPushButton::clicked, this, &ColorPropertyWidget::onClicked);
|
||||
m_Connection = uLib::Object::connect(m_Prop, &Property<Vector3d>::PropertyChanged, [this](){
|
||||
this->updateButtonColor();
|
||||
});
|
||||
}
|
||||
ColorPropertyWidget::~ColorPropertyWidget() {}
|
||||
|
||||
void ColorPropertyWidget::updateButtonColor() {
|
||||
Vector3d c = m_Prop->Get();
|
||||
QColor color = QColor::fromRgbF(std::max(0.0, std::min(1.0, c.x())),
|
||||
std::max(0.0, std::min(1.0, c.y())),
|
||||
std::max(0.0, std::min(1.0, c.z())));
|
||||
m_Button->setStyleSheet(QString("background-color: %1; border: 1px solid #555; height: 18px;").arg(color.name()));
|
||||
}
|
||||
|
||||
void ColorPropertyWidget::onClicked() {
|
||||
Vector3d c = m_Prop->Get();
|
||||
QColor current = QColor::fromRgbF(std::max(0.0, std::min(1.0, c.x())),
|
||||
std::max(0.0, std::min(1.0, c.y())),
|
||||
std::max(0.0, std::min(1.0, c.z())));
|
||||
QColor selected = QColorDialog::getColor(current, this, "Select Color");
|
||||
if (selected.isValid()) {
|
||||
m_Prop->Set(Vector3d(selected.redF(), selected.greenF(), selected.blueF()));
|
||||
}
|
||||
}
|
||||
|
||||
StringPropertyWidget::StringPropertyWidget(Property<std::string>* prop, QWidget* parent)
|
||||
: PropertyWidgetBase(prop, parent), m_Prop(prop) {
|
||||
m_LineEdit = new QLineEdit(this);
|
||||
@@ -358,6 +432,18 @@ void PropertyEditor::setObject(::uLib::Object* obj, bool displayOnly) {
|
||||
// Priority 1: Check if it provides enum labels
|
||||
if (!prop->GetEnumLabels().empty()) {
|
||||
widget = new EnumPropertyWidget(prop, m_Container);
|
||||
} else if (prop->GetUnits() == "color") {
|
||||
// Color Picker for Vector3d
|
||||
if (auto* pvec = dynamic_cast<Property<Vector3d>*>(prop)) {
|
||||
widget = new ColorPropertyWidget(pvec, m_Container);
|
||||
}
|
||||
} else if (prop->HasRange()) {
|
||||
// Slider for ranged doubles
|
||||
if (auto* pdbl = dynamic_cast<Property<double>*>(prop)) {
|
||||
widget = new RangePropertyWidget(pdbl, m_Container);
|
||||
} else if (auto* pflt = dynamic_cast<Property<float>*>(prop)) {
|
||||
// widget = new RangePropertyWidget<float>(pflt, m_Container);
|
||||
}
|
||||
} else {
|
||||
// Priority 2: Standard factory lookup
|
||||
auto it = m_Factories.find(prop->GetTypeIndex());
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#define PROPERTY_WIDGETS_H
|
||||
|
||||
#include <QWidget>
|
||||
class QPushButton;
|
||||
class QSlider;
|
||||
#include <QLabel>
|
||||
#include <QHBoxLayout>
|
||||
#include <QVBoxLayout>
|
||||
@@ -141,6 +143,20 @@ private:
|
||||
UnitLineEdit* m_Edits[Size];
|
||||
};
|
||||
|
||||
class RangePropertyWidget : public PropertyWidgetBase {
|
||||
Q_OBJECT
|
||||
public:
|
||||
RangePropertyWidget(Property<double>* prop, QWidget* parent = nullptr);
|
||||
virtual ~RangePropertyWidget();
|
||||
private slots:
|
||||
void onSliderChanged(int val);
|
||||
private:
|
||||
void updateUi();
|
||||
Property<double>* m_Prop;
|
||||
QSlider* m_Slider;
|
||||
UnitLineEdit* m_Edit;
|
||||
};
|
||||
|
||||
class BoolPropertyWidget : public PropertyWidgetBase {
|
||||
Q_OBJECT
|
||||
public:
|
||||
@@ -151,6 +167,19 @@ private:
|
||||
QCheckBox* m_CheckBox;
|
||||
};
|
||||
|
||||
class ColorPropertyWidget : public PropertyWidgetBase {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ColorPropertyWidget(Property<Vector3d>* prop, QWidget* parent = nullptr);
|
||||
virtual ~ColorPropertyWidget();
|
||||
private slots:
|
||||
void onClicked();
|
||||
private:
|
||||
void updateButtonColor();
|
||||
Property<Vector3d>* m_Prop;
|
||||
QPushButton* m_Button;
|
||||
};
|
||||
|
||||
class StringPropertyWidget : public PropertyWidgetBase {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
@@ -36,6 +36,13 @@ public:
|
||||
}
|
||||
virtual const std::string& GetGroup() const = 0;
|
||||
virtual void SetGroup(const std::string& group) = 0;
|
||||
|
||||
virtual bool HasRange() const { return false; }
|
||||
virtual double GetMin() const { return 0; }
|
||||
virtual double GetMax() const { return 0; }
|
||||
virtual bool HasDefault() const { return false; }
|
||||
virtual std::string GetDefaultValueAsString() const { return ""; }
|
||||
|
||||
std::string GetQualifiedName() const {
|
||||
if (GetGroup().empty()) return GetName();
|
||||
return GetGroup() + "." + GetName();
|
||||
@@ -63,7 +70,8 @@ class Property : public PropertyBase {
|
||||
public:
|
||||
// PROXY: Use an existing variable as back-end storage
|
||||
Property(Object* owner, const std::string& name, T* valuePtr, const std::string& units = "", const std::string& group = "")
|
||||
: m_owner(owner), m_name(name), m_units(units), m_group(group), m_value(valuePtr), m_own(false) {
|
||||
: m_owner(owner), m_name(name), m_units(units), m_group(group), m_value(valuePtr), m_own(false),
|
||||
m_HasRange(false), m_HasDefault(false) {
|
||||
if (m_owner) {
|
||||
m_owner->RegisterProperty(this);
|
||||
}
|
||||
@@ -71,7 +79,8 @@ public:
|
||||
|
||||
// MANAGED: Create and own internal storage
|
||||
Property(Object* owner, const std::string& name, const T& defaultValue = T(), const std::string& units = "", const std::string& group = "")
|
||||
: m_owner(owner), m_name(name), m_units(units), m_group(group), m_value(new T(defaultValue)), m_own(true) {
|
||||
: m_owner(owner), m_name(name), m_units(units), m_group(group), m_value(new T(defaultValue)), m_own(true),
|
||||
m_HasRange(false), m_HasDefault(true), m_Default(defaultValue) {
|
||||
if (m_owner) {
|
||||
m_owner->RegisterProperty(this);
|
||||
}
|
||||
@@ -103,15 +112,61 @@ public:
|
||||
|
||||
// Accessors
|
||||
const T& Get() const { return *m_value; }
|
||||
template<typename U = T>
|
||||
typename std::enable_if<std::is_arithmetic<U>::value, void>::type
|
||||
ValidateT(T& val) {
|
||||
if (m_HasRange) {
|
||||
if (val < m_Min) val = m_Min;
|
||||
if (val > m_Max) val = m_Max;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename U = T>
|
||||
typename std::enable_if<!std::is_arithmetic<U>::value, void>::type
|
||||
ValidateT(T& val) {
|
||||
}
|
||||
|
||||
void Set(const T& value) {
|
||||
if (*m_value != value) {
|
||||
*m_value = value;
|
||||
T val = value;
|
||||
ValidateT<T>(val);
|
||||
if (*m_value != val) {
|
||||
*m_value = val;
|
||||
ULIB_SIGNAL_EMIT(Property<T>::PropertyChanged);
|
||||
this->Updated();
|
||||
if (m_owner) m_owner->Updated();
|
||||
}
|
||||
}
|
||||
|
||||
void SetRange(const T& min, const T& max) { m_Min = min; m_Max = max; m_HasRange = true; }
|
||||
void SetDefault(const T& def) { m_Default = def; m_HasDefault = true; }
|
||||
|
||||
virtual bool HasRange() const override { return m_HasRange; }
|
||||
|
||||
template<typename U = T>
|
||||
typename std::enable_if<std::is_arithmetic<U>::value, double>::type
|
||||
GetMinT() const { return (double)m_Min; }
|
||||
|
||||
template<typename U = T>
|
||||
typename std::enable_if<!std::is_arithmetic<U>::value, double>::type
|
||||
GetMinT() const { return 0.0; }
|
||||
|
||||
template<typename U = T>
|
||||
typename std::enable_if<std::is_arithmetic<U>::value, double>::type
|
||||
GetMaxT() const { return (double)m_Max; }
|
||||
|
||||
template<typename U = T>
|
||||
typename std::enable_if<!std::is_arithmetic<U>::value, double>::type
|
||||
GetMaxT() const { return 0.0; }
|
||||
|
||||
virtual double GetMin() const override { return GetMinT<T>(); }
|
||||
virtual double GetMax() const override { return GetMaxT<T>(); }
|
||||
|
||||
virtual bool HasDefault() const override { return m_HasDefault; }
|
||||
virtual std::string GetDefaultValueAsString() const override {
|
||||
try { return boost::lexical_cast<std::string>(m_Default); }
|
||||
catch (...) { return ""; }
|
||||
}
|
||||
|
||||
// Operators for seamless usage
|
||||
operator const T&() const { return *m_value; }
|
||||
Property& operator=(const T& value) {
|
||||
@@ -149,6 +204,11 @@ private:
|
||||
T* m_value;
|
||||
bool m_own;
|
||||
Object* m_owner;
|
||||
bool m_HasRange;
|
||||
T m_Min;
|
||||
T m_Max;
|
||||
bool m_HasDefault;
|
||||
T m_Default;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -242,6 +302,8 @@ public:
|
||||
void save_override(const boost::serialization::hrp<T> &t) {
|
||||
if (m_Object) {
|
||||
Property<T>* p = new Property<T>(m_Object, t.name(), &const_cast<boost::serialization::hrp<T>&>(t).value(), t.units() ? t.units() : "", GetCurrentGroup());
|
||||
if (t.has_range()) p->SetRange(t.min_val(), t.max_val());
|
||||
if (t.has_default()) p->SetDefault(t.default_val());
|
||||
m_Object->RegisterDynamicProperty(p);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,15 +77,30 @@ class hrp : public boost::serialization::wrapper_traits<hrp<T>> {
|
||||
const char *m_name;
|
||||
const char *m_units;
|
||||
T &m_value;
|
||||
bool m_has_range;
|
||||
T m_min;
|
||||
T m_max;
|
||||
bool m_has_default;
|
||||
T m_default;
|
||||
|
||||
public:
|
||||
explicit hrp(const char *name_, T &t, const char* units_ = nullptr) : m_name(name_), m_units(units_), m_value(t) {}
|
||||
explicit hrp(const char *name_, T &t, const char* units_ = nullptr)
|
||||
: m_name(name_), m_units(units_), m_value(t), m_has_range(false), m_has_default(false) {}
|
||||
|
||||
hrp& range(const T& min_val, const T& max_val) { m_min = min_val; m_max = max_val; m_has_range = true; return *this; }
|
||||
hrp& set_default(const T& def_val) { m_default = def_val; m_has_default = true; return *this; }
|
||||
|
||||
const char *name() const { return this->m_name; }
|
||||
const char *units() const { return this->m_units; }
|
||||
T &value() { return this->m_value; }
|
||||
const T &const_value() const { return this->m_value; }
|
||||
|
||||
bool has_range() const { return m_has_range; }
|
||||
const T& min_val() const { return m_min; }
|
||||
const T& max_val() const { return m_max; }
|
||||
bool has_default() const { return m_has_default; }
|
||||
const T& default_val() const { return m_default; }
|
||||
|
||||
BOOST_SERIALIZATION_SPLIT_MEMBER()
|
||||
|
||||
template <class Archivex>
|
||||
@@ -110,16 +125,23 @@ class hrp_enum : public boost::serialization::wrapper_traits<hrp_enum<T>> {
|
||||
const char *m_units;
|
||||
T &m_value;
|
||||
std::vector<std::string> m_labels;
|
||||
bool m_has_default;
|
||||
T m_default;
|
||||
|
||||
public:
|
||||
explicit hrp_enum(const char *name_, T &t, const std::vector<std::string>& labels, const char* units_ = nullptr)
|
||||
: m_name(name_), m_units(units_), m_value(t), m_labels(labels) {}
|
||||
: m_name(name_), m_units(units_), m_value(t), m_labels(labels), m_has_default(false) {}
|
||||
|
||||
hrp_enum& set_default(const T& def_val) { m_default = def_val; m_has_default = true; return *this; }
|
||||
|
||||
const char *name() const { return this->m_name; }
|
||||
const char *units() const { return this->m_units; }
|
||||
T &value() { return this->m_value; }
|
||||
const std::vector<std::string>& labels() const { return m_labels; }
|
||||
|
||||
bool has_default() const { return m_has_default; }
|
||||
const T& default_val() const { return m_default; }
|
||||
|
||||
BOOST_SERIALIZATION_SPLIT_MEMBER()
|
||||
|
||||
template <class Archivex>
|
||||
|
||||
@@ -86,13 +86,13 @@ public:
|
||||
m_ShowBoundingBox(false),
|
||||
m_ShowScaleMeasures(false),
|
||||
m_Representation(Puppet::Surface),
|
||||
m_Opacity(-1.0),
|
||||
m_Opacity(1.0),
|
||||
m_Selectable(true),
|
||||
m_Selected(false),
|
||||
m_Visibility(true),
|
||||
m_Dragable(true)
|
||||
{
|
||||
m_Color[0] = m_Color[1] = m_Color[2] = -1.0;
|
||||
m_Color = Vector3d(-1, -1, -1);
|
||||
}
|
||||
|
||||
~PuppetData() {
|
||||
@@ -111,7 +111,7 @@ public:
|
||||
bool m_ShowBoundingBox;
|
||||
bool m_ShowScaleMeasures;
|
||||
int m_Representation;
|
||||
double m_Color[3];
|
||||
Vector3d m_Color;
|
||||
double m_Opacity;
|
||||
|
||||
bool m_Selectable;
|
||||
@@ -137,8 +137,9 @@ public:
|
||||
actor->GetProperty()->SetEdgeVisibility(0);
|
||||
}
|
||||
}
|
||||
if (m_Color[0] != -1.0) {
|
||||
actor->GetProperty()->SetColor(m_Color);
|
||||
if (m_Color.x() != -1.0) {
|
||||
double c[3] = {m_Color.x(), m_Color.y(), m_Color.z()};
|
||||
actor->GetProperty()->SetColor(c);
|
||||
}
|
||||
|
||||
if (m_Opacity != -1.0) {
|
||||
@@ -280,8 +281,11 @@ void Puppet::SetProp(vtkProp *prop)
|
||||
pd->m_Representation = vp->GetRepresentation();
|
||||
if (pd->m_Opacity < 0)
|
||||
pd->m_Opacity = vp->GetOpacity();
|
||||
if (pd->m_Color[0] < 0)
|
||||
vp->GetColor(pd->m_Color);
|
||||
if (pd->m_Color.x() < 0) {
|
||||
double c[3];
|
||||
vp->GetColor(c);
|
||||
pd->m_Color = Vector3d(c[0], c[1], c[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -622,10 +626,8 @@ struct AppearanceProxy {
|
||||
PuppetData* pd;
|
||||
template<class Archive>
|
||||
void serialize(Archive & ar, const unsigned int version) {
|
||||
ar & boost::serialization::make_hrp("ColorR", pd->m_Color[0]);
|
||||
ar & boost::serialization::make_hrp("ColorG", pd->m_Color[1]);
|
||||
ar & boost::serialization::make_hrp("ColorB", pd->m_Color[2]);
|
||||
ar & boost::serialization::make_hrp("Opacity", pd->m_Opacity);
|
||||
ar & boost::serialization::make_hrp("Color", pd->m_Color, "color");
|
||||
ar & boost::serialization::make_hrp("Opacity", pd->m_Opacity).range(0.0, 1.0).set_default(1.0);
|
||||
ar & boost::serialization::make_hrp_enum("Representation", pd->m_Representation, {"Points", "Wireframe", "Surface", "SurfaceWithEdges", "Volume", "Outline", "Slice"});
|
||||
ar & boost::serialization::make_hrp("Visibility", pd->m_Visibility);
|
||||
ar & boost::serialization::make_hrp("Pickable", pd->m_Selectable);
|
||||
|
||||
@@ -177,6 +177,9 @@ public:
|
||||
void save_override(const boost::serialization::hrp<T> &t) {
|
||||
if (m_Puppet) {
|
||||
uLib::Property<T>* p = new uLib::Property<T>(m_Puppet, t.name(), &const_cast<boost::serialization::hrp<T>&>(t).value(), t.units() ? t.units() : "", GetCurrentGroup());
|
||||
if (t.has_range()) p->SetRange(t.min_val(), t.max_val());
|
||||
if (t.has_default()) p->SetDefault(t.default_val());
|
||||
|
||||
m_Puppet->RegisterDisplayProperty(p);
|
||||
Vtk::Puppet* puppet = m_Puppet;
|
||||
uLib::Object::connect(p, &uLib::PropertyBase::Updated, [puppet](){ puppet->Update(); });
|
||||
|
||||
Reference in New Issue
Block a user