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:
parent
501fa80e4d
commit
4203a6f35b
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user