Expressions: Integrate into the property editor

- basic infrastructure for handling of expressions
- port the unit properties editor to support expressions
- port placement editor to support expressions
- expressions for double spinbox
- expressions in sketch constraints
This commit is contained in:
Stefan Tröger 2015-10-06 08:45:15 +02:00 committed by wmayer
parent 501fa80e4d
commit 4203a6f35b
11 changed files with 467 additions and 62 deletions

View File

@ -122,8 +122,10 @@ void PropertyExpressionEngine::Paste(const Property &from)
aboutToSetValue();
expressions.clear();
for (ExpressionMap::const_iterator it = fromee->expressions.begin(); it != fromee->expressions.end(); ++it)
for (ExpressionMap::const_iterator it = fromee->expressions.begin(); it != fromee->expressions.end(); ++it) {
expressions[it->first] = ExpressionInfo(it->second);
expressionChanged(it->first);
}
validator = fromee->validator;
@ -272,8 +274,10 @@ void PropertyExpressionEngine::slotObjectRenamed(const DocumentObject &obj)
aboutToSetValue();
for (ExpressionMap::iterator it = expressions.begin(); it != expressions.end(); ++it)
for (ExpressionMap::iterator it = expressions.begin(); it != expressions.end(); ++it) {
it->second.expression->visit(v);
expressionChanged(it->first);
}
hasSetValue();
}
@ -312,6 +316,10 @@ void PropertyExpressionEngine::setValue(const ObjectIdentifier & path, boost::sh
// Try to access value; it should trigger an exception if it is not supported, or if the path is invalid
prop->getPathValue(usePath);
// Check if the current expression equals the new one and do nothing if so to reduce unneeded computations
if(expressions.find(usePath) != expressions.end() && expr == expressions[usePath].expression)
return;
if (expr) {
std::string error = validateExpression(usePath, expr);
@ -320,11 +328,13 @@ void PropertyExpressionEngine::setValue(const ObjectIdentifier & path, boost::sh
aboutToSetValue();
expressions[usePath] = ExpressionInfo(expr, comment);
expressionChanged(usePath);
hasSetValue();
}
else {
aboutToSetValue();
expressions.erase(usePath);
expressionChanged(usePath);
hasSetValue();
}
}
@ -669,6 +679,9 @@ void PropertyExpressionEngine::renameExpressions(const std::map<ObjectIdentifier
aboutToSetValue();
expressions = newExpressions;
for (ExpressionMap::const_iterator i = expressions.begin(); i != expressions.end(); ++i)
expressionChanged(i->first);
hasSetValue();
}

View File

@ -25,6 +25,7 @@
#include <boost/unordered/unordered_map.hpp>
#include <boost/function.hpp>
#include <boost/signals.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/topological_sort.hpp>
#include <App/Property.h>
@ -49,7 +50,7 @@ class AppExport PropertyExpressionEngine : public App::Property
public:
typedef boost::function<std::string (const App::ObjectIdentifier & path, boost::shared_ptr<const App::Expression> expr)> ValidatorFunc;
/**
* @brief The ExpressionInfo struct encapsulates an expression and a comment.
*/
@ -117,6 +118,9 @@ public:
size_t numExpressions() const;
void slotObjectRenamed(const App::DocumentObject & obj);
///signal called when a expression was changed
boost::signal<void (const App::ObjectIdentifier &)> expressionChanged;
/* Python interface */
PyObject *getPyObject(void);

View File

@ -32,6 +32,7 @@
#include <Base/Tools.h>
#include <App/ObjectIdentifier.h>
#include <App/Document.h>
#include <boost/bind.hpp>
using namespace Gui;
using namespace App;
@ -39,6 +40,7 @@ using namespace App;
ExpressionBinding::ExpressionBinding()
: iconLabel(0)
, iconHeight(-1)
, m_autoApply(false)
{
}
@ -66,6 +68,9 @@ void Gui::ExpressionBinding::setExpression(boost::shared_ptr<Expression> expr)
lastExpression = getExpression();
docObj->ExpressionEngine.setValue(path, expr);
if(m_autoApply)
apply();
}
void ExpressionBinding::bind(const App::ObjectIdentifier &_path)
@ -75,6 +80,10 @@ void ExpressionBinding::bind(const App::ObjectIdentifier &_path)
Q_ASSERT(prop != 0);
path = prop->canonicalPath(_path);
//connect to be informed about changes
DocumentObject * docObj = path.getDocumentObject();
connection = docObj->ExpressionEngine.expressionChanged.connect(boost::bind(&ExpressionBinding::expressionChange, this, _1));
}
void ExpressionBinding::bind(const Property &prop)
@ -173,3 +182,9 @@ bool ExpressionBinding::apply()
return apply("App.ActiveDocument." + name + "." + std::string(prop->getName()));
}
void ExpressionBinding::expressionChange(const ObjectIdentifier& id) {
if(id==path)
onChange();
}

View File

@ -27,6 +27,7 @@
#include <App/ObjectIdentifier.h>
#include <boost/shared_ptr.hpp>
#include <QLabel>
#include <boost/signals.hpp>
namespace App {
class Expression;
@ -48,21 +49,34 @@ public:
bool hasExpression() const;
QPixmap getIcon(const char *name, const QSize &size) const;
//auto apply means that the python code is issues not only on aplly() but
//also on setExpression
bool autoApply() {return m_autoApply;};
void setAutoApply(bool value) {m_autoApply = value;};
protected:
const App::ObjectIdentifier & getPath() const { return path; }
boost::shared_ptr<App::Expression> getExpression() const;
std::string getExpressionString() const;
std::string getEscapedExpressionString() const;
virtual void setExpression(boost::shared_ptr<App::Expression> expr);
//gets called when the bound expression is changed, either by this binding or any external action
virtual void onChange() {};
private:
App::ObjectIdentifier path;
boost::shared_ptr<App::Expression> lastExpression;
protected:
QLabel* iconLabel;
QPalette defaultPalette;
int iconHeight;
void expressionChange(const App::ObjectIdentifier& id);
boost::signals::scoped_connection connection;
bool m_autoApply;
};
}

View File

@ -276,31 +276,6 @@ void Gui::QuantitySpinBox::setExpression(boost::shared_ptr<Expression> expr)
try {
ExpressionBinding::setExpression(expr);
if (getExpression()) {
std::auto_ptr<Expression> result(getExpression()->eval());
NumberExpression * value = freecad_dynamic_cast<NumberExpression>(result.get());
if (value) {
updateText(value->getQuantity());
setReadOnly(true);
iconLabel->setPixmap(getIcon(":/icons/bound-expression.svg", QSize(iconHeight, iconHeight)));
QPalette p(lineEdit()->palette());
p.setColor(QPalette::Text, Qt::lightGray);
lineEdit()->setPalette(p);
}
setToolTip(Base::Tools::fromStdString(getExpression()->toString()));
}
else {
setReadOnly(false);
iconLabel->setPixmap(getIcon(":/icons/bound-expression-unset.svg", QSize(iconHeight, iconHeight)));
QPalette p(lineEdit()->palette());
p.setColor(QPalette::Active, QPalette::Text, defaultPalette.color(QPalette::Text));
lineEdit()->setPalette(p);
}
iconLabel->setToolTip(QString());
}
catch (const Base::Exception & e) {
setReadOnly(true);
@ -311,6 +286,46 @@ void Gui::QuantitySpinBox::setExpression(boost::shared_ptr<Expression> expr)
}
}
void Gui::QuantitySpinBox::onChange() {
Q_ASSERT(isBound());
if (getExpression()) {
std::auto_ptr<Expression> result(getExpression()->eval());
NumberExpression * value = freecad_dynamic_cast<NumberExpression>(result.get());
if (value) {
std::stringstream s;
s << value->getValue();
lineEdit()->setText(value->getQuantity().getUserString());
setReadOnly(true);
QPixmap pixmap = getIcon(":/icons/bound-expression.svg", QSize(iconHeight, iconHeight));
iconLabel->setPixmap(pixmap);
QPalette p(lineEdit()->palette());
p.setColor(QPalette::Text, Qt::lightGray);
lineEdit()->setPalette(p);
}
iconLabel->setToolTip(QString());
setToolTip(Base::Tools::fromStdString(getExpression()->toString()));
}
else {
setReadOnly(false);
QPixmap pixmap = getIcon(":/icons/bound-expression-unset.svg", QSize(iconHeight, iconHeight));
iconLabel->setPixmap(pixmap);
QPalette p(lineEdit()->palette());
p.setColor(QPalette::Active, QPalette::Text, defaultPalette.color(QPalette::Text));
lineEdit()->setPalette(p);
<<<<<<< 175351b02ea3a586e1dbe0dc5e993966714ea236
=======
iconLabel->setToolTip(QString());
>>>>>>> further expression integration for property editor
}
iconLabel->setToolTip(QString());
}
bool QuantitySpinBox::apply(const std::string & propName)
{
if (!ExpressionBinding::apply(propName)) {

View File

@ -114,6 +114,9 @@ protected Q_SLOTS:
void userInput(const QString & text);
void openFormulaDialog();
void finishFormulaDialog();
//get notified on expression change
virtual void onChange();
protected:
virtual StepEnabled stepEnabled() const;

View File

@ -266,31 +266,6 @@ void UIntSpinBox::setExpression(boost::shared_ptr<Expression> expr)
try {
ExpressionBinding::setExpression(expr);
if (getExpression()) {
std::auto_ptr<Expression> result(getExpression()->eval());
NumberExpression * value = freecad_dynamic_cast<NumberExpression>(result.get());
if (value) {
setValue(boost::math::round(value->getValue()));
setReadOnly(true);
iconLabel->setPixmap(getIcon(":/icons/bound-expression.svg", QSize(iconHeight, iconHeight)));
QPalette p(lineEdit()->palette());
p.setColor(QPalette::Text, Qt::lightGray);
lineEdit()->setPalette(p);
}
setToolTip(Base::Tools::fromStdString(getExpression()->toString()));
}
else {
setReadOnly(false);
iconLabel->setPixmap(getIcon(":/icons/bound-expression-unset.svg", QSize(iconHeight, iconHeight)));
QPalette p(lineEdit()->palette());
p.setColor(QPalette::Active, QPalette::Text, defaultPalette.color(QPalette::Text));
lineEdit()->setPalette(p);
}
iconLabel->setToolTip(QString());
}
catch (const Base::Exception & e) {
setReadOnly(true);
@ -301,6 +276,35 @@ void UIntSpinBox::setExpression(boost::shared_ptr<Expression> expr)
}
}
void UIntSpinBox::onChange() {
if (getExpression()) {
std::auto_ptr<Expression> result(getExpression()->eval());
NumberExpression * value = freecad_dynamic_cast<NumberExpression>(result.get());
if (value) {
setValue(boost::math::round(value->getValue()));
setReadOnly(true);
iconLabel->setPixmap(getIcon(":/icons/bound-expression.svg", QSize(iconHeight, iconHeight)));
QPalette p(lineEdit()->palette());
p.setColor(QPalette::Text, Qt::lightGray);
lineEdit()->setPalette(p);
}
setToolTip(Base::Tools::fromStdString(getExpression()->toString()));
}
else {
setReadOnly(false);
iconLabel->setPixmap(getIcon(":/icons/bound-expression-unset.svg", QSize(iconHeight, iconHeight)));
QPalette p(lineEdit()->palette());
p.setColor(QPalette::Active, QPalette::Text, defaultPalette.color(QPalette::Text));
lineEdit()->setPalette(p);
}
iconLabel->setToolTip(QString());
}
bool UIntSpinBox::apply(const std::string & propName)
{
if (!ExpressionBinding::apply(propName)) {
@ -402,4 +406,178 @@ void UIntSpinBox::keyPressEvent(QKeyEvent *event)
}
}
DoubleSpinBox::DoubleSpinBox(QWidget* parent): QDoubleSpinBox(parent) {
defaultPalette = lineEdit()->palette();
/* Icon for f(x) */
QFontMetrics fm(lineEdit()->font());
int frameWidth = style()->pixelMetric(QStyle::PM_SpinBoxFrameWidth);
iconHeight = fm.height() - frameWidth;
iconLabel = new ExpressionLabel(lineEdit());
iconLabel->setCursor(Qt::ArrowCursor);
QPixmap pixmap = getIcon(":/icons/bound-expression-unset.svg", QSize(iconHeight, iconHeight));
iconLabel->setPixmap(pixmap);
iconLabel->setStyleSheet(QString::fromAscii("QLabel { border: none; padding: 0px; padding-top: %2px; width: %1px; height: %1px }").arg(iconHeight).arg(frameWidth/2));
iconLabel->hide();
lineEdit()->setStyleSheet(QString::fromAscii("QLineEdit { padding-right: %1px } ").arg(iconHeight+frameWidth));
QObject::connect(iconLabel, SIGNAL(clicked()), this, SLOT(openFormulaDialog()));
}
DoubleSpinBox::~DoubleSpinBox() {
}
bool DoubleSpinBox::apply(const std::string& propName) {
if (!ExpressionBinding::apply(propName)) {
Gui::Command::doCommand(Gui::Command::Doc, "%s = %u", propName.c_str(), value());
return true;
}
else
return false;
}
void DoubleSpinBox::bind(const ObjectIdentifier& _path) {
ExpressionBinding::bind(_path);
int frameWidth = style()->pixelMetric(QStyle::PM_SpinBoxFrameWidth);
lineEdit()->setStyleSheet(QString::fromAscii("QLineEdit { padding-right: %1px } ").arg(iconLabel->sizeHint().width() + frameWidth + 1));
iconLabel->show();
}
void DoubleSpinBox::setExpression(boost::shared_ptr<Expression> expr)
{
Q_ASSERT(isBound());
try {
ExpressionBinding::setExpression(expr);
}
catch (const Base::Exception & e) {
setReadOnly(true);
QPalette p(lineEdit()->palette());
p.setColor(QPalette::Active, QPalette::Text, Qt::red);
lineEdit()->setPalette(p);
iconLabel->setToolTip(QString::fromAscii(e.what()));
}
}
void DoubleSpinBox::onChange() {
if (getExpression()) {
std::auto_ptr<Expression> result(getExpression()->eval());
NumberExpression * value = freecad_dynamic_cast<NumberExpression>(result.get());
if (value) {
setValue(boost::math::round(value->getValue()));
setReadOnly(true);
iconLabel->setPixmap(getIcon(":/icons/bound-expression.svg", QSize(iconHeight, iconHeight)));
QPalette p(lineEdit()->palette());
p.setColor(QPalette::Text, Qt::lightGray);
lineEdit()->setPalette(p);
}
setToolTip(Base::Tools::fromStdString(getExpression()->toString()));
}
else {
setReadOnly(false);
iconLabel->setPixmap(getIcon(":/icons/bound-expression-unset.svg", QSize(iconHeight, iconHeight)));
QPalette p(lineEdit()->palette());
p.setColor(QPalette::Active, QPalette::Text, defaultPalette.color(QPalette::Text));
lineEdit()->setPalette(p);
}
iconLabel->setToolTip(QString());
}
void DoubleSpinBox::resizeEvent(QResizeEvent * event)
{
QAbstractSpinBox::resizeEvent(event);
int frameWidth = style()->pixelMetric(QStyle::PM_SpinBoxFrameWidth);
QSize sz = iconLabel->sizeHint();
iconLabel->move(lineEdit()->rect().right() - frameWidth - sz.width(), 0);
try {
if (isBound() && getExpression()) {
std::auto_ptr<Expression> result(getExpression()->eval());
NumberExpression * value = freecad_dynamic_cast<NumberExpression>(result.get());
if (value) {
setReadOnly(true);
QPixmap pixmap = getIcon(":/icons/bound-expression.svg", QSize(iconHeight, iconHeight));
iconLabel->setPixmap(pixmap);
QPalette p(lineEdit()->palette());
p.setColor(QPalette::Text, Qt::lightGray);
lineEdit()->setPalette(p);
}
setToolTip(Base::Tools::fromStdString(getExpression()->toString()));
}
else {
setReadOnly(false);
QPixmap pixmap = getIcon(":/icons/bound-expression-unset.svg", QSize(iconHeight, iconHeight));
iconLabel->setPixmap(pixmap);
QPalette p(lineEdit()->palette());
p.setColor(QPalette::Active, QPalette::Text, defaultPalette.color(QPalette::Text));
lineEdit()->setPalette(p);
}
iconLabel->setToolTip(QString());
}
catch (const Base::Exception & e) {
setReadOnly(true);
QPalette p(lineEdit()->palette());
p.setColor(QPalette::Active, QPalette::Text, Qt::red);
lineEdit()->setPalette(p);
iconLabel->setToolTip(QString::fromAscii(e.what()));
}
}
void DoubleSpinBox::openFormulaDialog()
{
Q_ASSERT(isBound());
Gui::Dialog::DlgExpressionInput* box = new Gui::Dialog::DlgExpressionInput(getPath(), getExpression(), Unit(), this);
connect(box, SIGNAL(finished(int)), this, SLOT(finishFormulaDialog()));
box->show();
QPoint pos = mapToGlobal(QPoint(0,0));
box->move(pos-box->expressionPosition());
box->setExpressionInputSize(width(), height());
}
void DoubleSpinBox::finishFormulaDialog()
{
Gui::Dialog::DlgExpressionInput* box = qobject_cast<Gui::Dialog::DlgExpressionInput*>(sender());
if (!box) {
qWarning() << "Sender is not a Gui::Dialog::DlgExpressionInput";
return;
}
if (box->result() == QDialog::Accepted)
setExpression(box->getExpression());
else if (box->discardedFormula())
setExpression(boost::shared_ptr<Expression>());
box->deleteLater();
}
void DoubleSpinBox::keyPressEvent(QKeyEvent *event)
{
if (event->text() == QString::fromUtf8("=") && isBound())
openFormulaDialog();
else {
if (!hasExpression())
QAbstractSpinBox::keyPressEvent(event);
}
}
#include "moc_SpinBox.cpp"

View File

@ -105,12 +105,40 @@ private Q_SLOTS:
protected:
virtual QString textFromValue ( int v ) const;
virtual int valueFromText ( const QString & text ) const;
virtual void onChange();
private:
void updateValidator();
UIntSpinBoxPrivate * d;
};
class DoubleSpinBoxPrivate;
/**
* The DoubleSpinBox class does exactly the same as Qt's QDoubleSpinBox but has expression
* support
* @author Stefan Tröger
*/
class GuiExport DoubleSpinBox : public QDoubleSpinBox, public ExpressionBinding
{
Q_OBJECT
public:
DoubleSpinBox ( QWidget* parent=0 );
virtual ~DoubleSpinBox();
void setExpression(boost::shared_ptr<App::Expression> expr);
void bind(const App::ObjectIdentifier &_path);
bool apply(const std::string &propName);
void keyPressEvent(QKeyEvent *event);
void resizeEvent(QResizeEvent *event);
private Q_SLOTS:
void finishFormulaDialog();
void openFormulaDialog();
virtual void onChange();
};
} // namespace Gui
#endif // GUI_SPINBOX_H

View File

@ -36,6 +36,7 @@
#endif
#include <Base/Tools.h>
#include <Base/Console.h>
#include <App/Application.h>
#include <App/Document.h>
#include <App/DocumentObject.h>
@ -53,12 +54,13 @@
#include <Gui/QuantitySpinBox.h>
#include "PropertyItem.h"
#include <SpinBox.h>
using namespace Gui::PropertyEditor;
TYPESYSTEM_SOURCE(Gui::PropertyEditor::PropertyItem, Base::BaseClass);
PropertyItem::PropertyItem() : parentItem(0), readonly(false)
PropertyItem::PropertyItem() : parentItem(0), readonly(false), cleared(false)
{
precision = Base::UnitsApi::getDecimals();
}
@ -80,6 +82,26 @@ void PropertyItem::reset()
void PropertyItem::setPropertyData(const std::vector<App::Property*>& items)
{
//if we have a single property we can bind it for expression handling
if(items.size() == 1) {
const App::Property& p = *items.front();
if(!(p.getContainer()->getPropertyType(&p) & App::Prop_ReadOnly)) {
App::ObjectIdentifier id(p);
std::vector<App::ObjectIdentifier> paths;
p.getPaths(paths);
//there may be no paths available in this property (for example an empty constraint list)
if(id.getProperty() && !paths.empty())
bind(id);
}
else
setReadOnly(true);
}
propertyItems = items;
updateData();
this->initialize();
@ -346,15 +368,24 @@ QVariant PropertyItem::data(int column, int role) const
bool PropertyItem::setData (const QVariant& value)
{
//check if we have an expression set. If so we do nothing, as than the editor is responsible
//for issuing the relevant python code
if(hasExpression())
return true;
cleared = false;
// This is the basic mechanism to set the value to
// a property and if no property is set for this item
// it delegates it to its parent which sets then the
// property or delegates again to its parent...
if (propertyItems.empty()) {
PropertyItem* parent = this->parent();
if (!parent || !parent->parent())
return false;
parent->setProperty(qPrintable(objectName()),value);
return true;
}
else {
@ -363,6 +394,7 @@ bool PropertyItem::setData (const QVariant& value)
}
}
Qt::ItemFlags PropertyItem::flags(int column) const
{
Qt::ItemFlags basicFlags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
@ -380,6 +412,17 @@ int PropertyItem::row() const
return 0;
}
void PropertyItem::bind(const App::ObjectIdentifier& _path) {
Gui::ExpressionBinding::bind(_path);
propertyBound();
}
void PropertyItem::bind(const App::Property& prop) {
Gui::ExpressionBinding::bind(prop);
propertyBound();
}
// --------------------------------------------------------------------
TYPESYSTEM_SOURCE(Gui::PropertyEditor::PropertyStringItem, Gui::PropertyEditor::PropertyItem);
@ -409,6 +452,7 @@ QWidget* PropertyStringItem::createEditor(QWidget* parent, const QObject* receiv
{
QLineEdit *le = new QLineEdit(parent);
le->setFrame(false);
le->setReadOnly(isReadOnly());
QObject::connect(le, SIGNAL(textChanged(const QString&)), receiver, method);
return le;
}
@ -454,6 +498,7 @@ QWidget* PropertyFontItem::createEditor(QWidget* parent, const QObject* receiver
{
QComboBox *cb = new QComboBox(parent);
cb->setFrame(false);
cb->setDisabled(isReadOnly());
QObject::connect(cb, SIGNAL(activated(const QString&)), receiver, method);
return cb;
}
@ -512,6 +557,7 @@ QWidget* PropertyIntegerItem::createEditor(QWidget* parent, const QObject* recei
{
QSpinBox *sb = new QSpinBox(parent);
sb->setFrame(false);
sb->setReadOnly(isReadOnly());
QObject::connect(sb, SIGNAL(valueChanged(int)), receiver, method);
return sb;
}
@ -558,6 +604,7 @@ QWidget* PropertyIntegerConstraintItem::createEditor(QWidget* parent, const QObj
{
QSpinBox *sb = new QSpinBox(parent);
sb->setFrame(false);
sb->setReadOnly(isReadOnly());
QObject::connect(sb, SIGNAL(valueChanged(int)), receiver, method);
return sb;
}
@ -599,6 +646,10 @@ QVariant PropertyFloatItem::toString(const QVariant& prop) const
{
double value = prop.toDouble();
QString data = QLocale::system().toString(value, 'f', decimals());
if(hasExpression())
data += QString::fromAscii(" ( %1 )").arg(QString::fromStdString(getExpressionString()));
return QVariant(data);
}
@ -621,10 +672,18 @@ void PropertyFloatItem::setValue(const QVariant& value)
QWidget* PropertyFloatItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const
{
QDoubleSpinBox *sb = new QDoubleSpinBox(parent);
Gui::DoubleSpinBox *sb = new Gui::DoubleSpinBox(parent);
sb->setFrame(false);
sb->setDecimals(decimals());
sb->setReadOnly(isReadOnly());
QObject::connect(sb, SIGNAL(valueChanged(double)), receiver, method);
if(isBound()) {
sb->bind(getPath());
sb->setAutoApply(true);
}
return sb;
}
@ -653,7 +712,11 @@ PropertyUnitItem::PropertyUnitItem()
QVariant PropertyUnitItem::toString(const QVariant& prop) const
{
const Base::Quantity& unit = prop.value<Base::Quantity>();
return QVariant(unit.getUserString());
QString string = unit.getUserString();
if(hasExpression())
string += QString::fromAscii(" ( %1 )").arg(QString::fromStdString(getExpressionString()));
return QVariant(string);
}
QVariant PropertyUnitItem::value(const App::Property* prop) const
@ -679,6 +742,14 @@ QWidget* PropertyUnitItem::createEditor(QWidget* parent, const QObject* receiver
Gui::QuantitySpinBox *infield = new Gui::QuantitySpinBox(parent);
infield->setFrame(false);
infield->setMinimumHeight(0);
infield->setReadOnly(isReadOnly());
//if we are bound to an expression we need to bind it to the input field
if(isBound()) {
infield->bind(getPath());
infield->setAutoApply(true);
}
QObject::connect(infield, SIGNAL(valueChanged(double)), receiver, method);
return infield;
}
@ -767,10 +838,17 @@ void PropertyFloatConstraintItem::setValue(const QVariant& value)
QWidget* PropertyFloatConstraintItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const
{
QDoubleSpinBox *sb = new QDoubleSpinBox(parent);
Gui::DoubleSpinBox *sb = new Gui::DoubleSpinBox(parent);
sb->setDecimals(decimals());
sb->setFrame(false);
sb->setReadOnly(isReadOnly());
QObject::connect(sb, SIGNAL(valueChanged(double)), receiver, method);
if(isBound()) {
sb->bind(getPath());
sb->setAutoApply(true);
}
return sb;
}
@ -873,6 +951,7 @@ QWidget* PropertyBoolItem::createEditor(QWidget* parent, const QObject* receiver
cb->setFrame(false);
cb->addItem(QLatin1String("false"));
cb->addItem(QLatin1String("true"));
cb->setDisabled(isReadOnly());
QObject::connect(cb, SIGNAL(activated(int)), receiver, method);
return cb;
}
@ -994,6 +1073,13 @@ void PropertyVectorItem::setZ(double z)
setData(QVariant::fromValue(Base::Vector3d(x(), y(), z)));
}
void PropertyVectorItem::propertyBound() {
m_x->bind(App::ObjectIdentifier(getPath())<<App::ObjectIdentifier::String("x"));
m_y->bind(App::ObjectIdentifier(getPath())<<App::ObjectIdentifier::String("y"));
m_z->bind(App::ObjectIdentifier(getPath())<<App::ObjectIdentifier::String("z"));
}
// ---------------------------------------------------------------
@ -1100,6 +1186,15 @@ void PropertyVectorDistanceItem::setZ(Base::Quantity z)
setData(QVariant::fromValue(Base::Vector3d(x().getValue(), y().getValue(), z.getValue())));
}
void PropertyVectorDistanceItem::propertyBound() {
if(isBound()) {
m_x->bind(App::ObjectIdentifier(getPath())<<App::ObjectIdentifier::String("x"));
m_y->bind(App::ObjectIdentifier(getPath())<<App::ObjectIdentifier::String("y"));
m_z->bind(App::ObjectIdentifier(getPath())<<App::ObjectIdentifier::String("z"));
};
}
// ---------------------------------------------------------------
@ -1708,9 +1803,10 @@ void PropertyPlacementItem::setValue(const QVariant& value)
}
QWidget* PropertyPlacementItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const
{
{
PlacementEditor *pe = new PlacementEditor(this->propertyName(), parent);
QObject::connect(pe, SIGNAL(valueChanged(const QVariant &)), receiver, method);
pe->setDisabled(isReadOnly());
return pe;
}
@ -1726,6 +1822,20 @@ QVariant PropertyPlacementItem::editorData(QWidget *editor) const
return pe->value();
}
void PropertyPlacementItem::propertyBound() {
if(isBound()) {
m_a->bind(App::ObjectIdentifier(getPath())<<App::ObjectIdentifier::String("Rotation")
<<App::ObjectIdentifier::String("Angle"));
m_d->bind(App::ObjectIdentifier(getPath())<<App::ObjectIdentifier::String("Rotation")
<<App::ObjectIdentifier::String("Axis"));
m_p->bind(App::ObjectIdentifier(getPath())<<App::ObjectIdentifier::String("Base"));
}
}
// ---------------------------------------------------------------
TYPESYSTEM_SOURCE(Gui::PropertyEditor::PropertyEnumItem, Gui::PropertyEditor::PropertyItem);
@ -1763,6 +1873,7 @@ QWidget* PropertyEnumItem::createEditor(QWidget* parent, const QObject* receiver
{
QComboBox *cb = new QComboBox(parent);
cb->setFrame(false);
cb->setDisabled(isReadOnly());
QObject::connect(cb, SIGNAL(activated(int)), receiver, method);
return cb;
}
@ -1822,6 +1933,7 @@ QWidget* PropertyStringListItem::createEditor(QWidget* parent, const QObject* re
{
Gui::LabelEditor* le = new Gui::LabelEditor(parent);
le->setAutoFillBackground(true);
le->setDisabled(isReadOnly());
QObject::connect(le, SIGNAL(textChanged(const QString&)), receiver, method);
return le;
}
@ -1890,6 +2002,7 @@ QWidget* PropertyFloatListItem::createEditor(QWidget* parent, const QObject* rec
Gui::LabelEditor* le = new Gui::LabelEditor(parent);
le->setAutoFillBackground(true);
le->setInputType(Gui::LabelEditor::Float);
le->setDisabled(isReadOnly());
QObject::connect(le, SIGNAL(textChanged(const QString&)), receiver, method);
return le;
}
@ -1958,6 +2071,7 @@ QWidget* PropertyIntegerListItem::createEditor(QWidget* parent, const QObject* r
Gui::LabelEditor* le = new Gui::LabelEditor(parent);
le->setAutoFillBackground(true);
le->setInputType(Gui::LabelEditor::Integer);
le->setDisabled(isReadOnly());
QObject::connect(le, SIGNAL(textChanged(const QString&)), receiver, method);
return le;
}
@ -2068,6 +2182,7 @@ void PropertyColorItem::setValue(const QVariant& value)
QWidget* PropertyColorItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const
{
Gui::ColorButton* cb = new Gui::ColorButton( parent );
cb->setDisabled(isReadOnly());
QObject::connect(cb, SIGNAL(changed()), receiver, method);
return cb;
}
@ -2121,6 +2236,7 @@ QWidget* PropertyFileItem::createEditor(QWidget* parent, const QObject* receiver
{
Gui::FileChooser *fc = new Gui::FileChooser(parent);
fc->setAutoFillBackground(true);
fc->setDisabled(isReadOnly());
QObject::connect(fc, SIGNAL(fileNameSelected(const QString&)), receiver, method);
return fc;
}
@ -2172,6 +2288,7 @@ QWidget* PropertyPathItem::createEditor(QWidget* parent, const QObject* receiver
Gui::FileChooser *fc = new Gui::FileChooser(parent);
fc->setMode(FileChooser::Directory);
fc->setAutoFillBackground(true);
fc->setDisabled(isReadOnly());
QObject::connect(fc, SIGNAL(fileNameSelected(const QString&)), receiver, method);
return fc;
}
@ -2222,6 +2339,7 @@ QWidget* PropertyTransientFileItem::createEditor(QWidget* parent, const QObject*
{
Gui::FileChooser *fc = new Gui::FileChooser(parent);
fc->setAutoFillBackground(true);
fc->setDisabled(isReadOnly());
QObject::connect(fc, SIGNAL(fileNameSelected(const QString&)), receiver, method);
return fc;
}
@ -2381,6 +2499,7 @@ QWidget* PropertyLinkItem::createEditor(QWidget* parent, const QObject* receiver
{
LinkLabel *ll = new LinkLabel(parent);
ll->setAutoFillBackground(true);
ll->setDisabled(isReadOnly());
QObject::connect(ll, SIGNAL(linkChanged(const QStringList&)), receiver, method);
return ll;
}

View File

@ -37,6 +37,7 @@
#include <Base/UnitsApi.h>
#include <App/PropertyStandard.h>
#include <Gui/Widgets.h>
#include <Gui/ExpressionBinding.h>
Q_DECLARE_METATYPE(Base::Vector3f)
Q_DECLARE_METATYPE(Base::Vector3d)
@ -49,7 +50,7 @@ namespace Gui {
namespace Dialog { class TaskPlacement; }
namespace PropertyEditor {
class GuiExport PropertyItem : virtual public QObject, public Base::BaseClass
class GuiExport PropertyItem : virtual public QObject, public Base::BaseClass, public ExpressionBinding
{
Q_OBJECT
@ -73,6 +74,12 @@ public:
virtual QVariant editorData(QWidget *editor) const;
virtual bool isSeparator() const { return false; }
/**override the bind functions to ensure we issue the propertyBound() call, which is then overloaded by
childs which like to be informed of a binding*/
virtual void bind(const App::Property& prop);
virtual void bind(const App::ObjectIdentifier& _path);
virtual void propertyBound() {};
void setParent(PropertyItem* parent);
PropertyItem *parent() const;
void appendChild(PropertyItem *child);
@ -114,6 +121,7 @@ private:
QList<PropertyItem*> childItems;
bool readonly;
int precision;
bool cleared;
};
/**
@ -351,6 +359,7 @@ protected:
protected:
PropertyVectorItem();
virtual void propertyBound();
private:
PropertyFloatItem* m_x;
@ -375,6 +384,8 @@ class GuiExport PropertyVectorDistanceItem: public PropertyItem
virtual void setEditorData(QWidget *editor, const QVariant& data) const;
virtual QVariant editorData(QWidget *editor) const;
virtual void propertyBound();
Base::Quantity x() const;
void setX(Base::Quantity x);
Base::Quantity y() const;
@ -517,6 +528,8 @@ class GuiExport PropertyPlacementItem: public PropertyItem
virtual void setEditorData(QWidget *editor, const QVariant& data) const;
virtual QVariant editorData(QWidget *editor) const;
virtual void propertyBound();
Base::Quantity getAngle() const;
void setAngle(Base::Quantity);
Base::Vector3d getAxis() const;

View File

@ -60,8 +60,8 @@ QVariant PropertyConstraintListItem::toString(const QVariant& prop) const
void PropertyConstraintListItem::initialize()
{
const Sketcher::PropertyConstraintList* item = static_cast<const Sketcher::PropertyConstraintList*>(getPropertyData()[0]);
const std::vector< Sketcher::Constraint * > &vals = item->getValues();
const Sketcher::PropertyConstraintList* list = static_cast<const Sketcher::PropertyConstraintList*>(getPropertyData()[0]);
const std::vector< Sketcher::Constraint * > &vals = list->getValues();
int id = 1;
int iNamed = 0;
@ -101,6 +101,9 @@ void PropertyConstraintListItem::initialize()
item->setObjectName(internalName);
this->appendChild(item);
}
item->bind(list->createPath(id-1));
item->setAutoApply(true);
}
}