Skip to content

Commit

Permalink
Expressions: Integrate into the property editor
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
ickby authored and wwmayer committed Dec 9, 2015
1 parent 501fa80 commit 4203a6f
Show file tree
Hide file tree
Showing 11 changed files with 451 additions and 46 deletions.
17 changes: 15 additions & 2 deletions src/App/PropertyExpressionEngine.cpp
Expand Up @@ -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;

Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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);

Expand All @@ -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();
}
}
Expand Down Expand Up @@ -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();
}

Expand Down
6 changes: 5 additions & 1 deletion src/App/PropertyExpressionEngine.h
Expand Up @@ -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>
Expand All @@ -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.
*/
Expand Down Expand Up @@ -117,6 +118,9 @@ class AppExport PropertyExpressionEngine : public App::Property
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);
Expand Down
15 changes: 15 additions & 0 deletions src/Gui/ExpressionBinding.cpp
Expand Up @@ -32,13 +32,15 @@
#include <Base/Tools.h>
#include <App/ObjectIdentifier.h>
#include <App/Document.h>
#include <boost/bind.hpp>

using namespace Gui;
using namespace App;

ExpressionBinding::ExpressionBinding()
: iconLabel(0)
, iconHeight(-1)
, m_autoApply(false)
{
}

Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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();
}
16 changes: 15 additions & 1 deletion src/Gui/ExpressionBinding.h
Expand Up @@ -27,6 +27,7 @@
#include <App/ObjectIdentifier.h>
#include <boost/shared_ptr.hpp>
#include <QLabel>
#include <boost/signals.hpp>

namespace App {
class Expression;
Expand All @@ -48,21 +49,34 @@ class GuiExport ExpressionBinding
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;
};

}
Expand Down
61 changes: 38 additions & 23 deletions src/Gui/QuantitySpinBox.cpp
Expand Up @@ -276,41 +276,56 @@ void Gui::QuantitySpinBox::setExpression(boost::shared_ptr<Expression> expr)

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()));
}
}

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)));
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);
}
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));
p.setColor(QPalette::Text, Qt::lightGray);
lineEdit()->setPalette(p);

}
iconLabel->setToolTip(QString());
setToolTip(Base::Tools::fromStdString(getExpression()->toString()));
}
catch (const Base::Exception & e) {
setReadOnly(true);
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, Qt::red);
p.setColor(QPalette::Active, QPalette::Text, defaultPalette.color(QPalette::Text));
lineEdit()->setPalette(p);
iconLabel->setToolTip(QString::fromAscii(e.what()));
<<<<<<< 175351b02ea3a586e1dbe0dc5e993966714ea236
=======
iconLabel->setToolTip(QString());
>>>>>>> further expression integration for property editor
}
iconLabel->setToolTip(QString());
}


bool QuantitySpinBox::apply(const std::string & propName)
{
if (!ExpressionBinding::apply(propName)) {
Expand Down
3 changes: 3 additions & 0 deletions src/Gui/QuantitySpinBox.h
Expand Up @@ -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;
Expand Down

0 comments on commit 4203a6f

Please sign in to comment.