From 4203a6f35b08bad77872d1f05155a1bb3d9a9aea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Tr=C3=B6ger?= Date: Tue, 6 Oct 2015 08:45:15 +0200 Subject: [PATCH] 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 --- src/App/PropertyExpressionEngine.cpp | 17 +- src/App/PropertyExpressionEngine.h | 6 +- src/Gui/ExpressionBinding.cpp | 15 ++ src/Gui/ExpressionBinding.h | 16 +- src/Gui/QuantitySpinBox.cpp | 61 ++++-- src/Gui/QuantitySpinBox.h | 3 + src/Gui/SpinBox.cpp | 200 +++++++++++++++++- src/Gui/SpinBox.h | 28 +++ src/Gui/propertyeditor/PropertyItem.cpp | 129 ++++++++++- src/Gui/propertyeditor/PropertyItem.h | 15 +- .../Gui/PropertyConstraintListItem.cpp | 7 +- 11 files changed, 451 insertions(+), 46 deletions(-) diff --git a/src/App/PropertyExpressionEngine.cpp b/src/App/PropertyExpressionEngine.cpp index 634db77bc3de..ddcbed54891f 100644 --- a/src/App/PropertyExpressionEngine.cpp +++ b/src/App/PropertyExpressionEngine.cpp @@ -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::mapfirst); + hasSetValue(); } diff --git a/src/App/PropertyExpressionEngine.h b/src/App/PropertyExpressionEngine.h index 61ec9834f3b8..406b3a0ca594 100644 --- a/src/App/PropertyExpressionEngine.h +++ b/src/App/PropertyExpressionEngine.h @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -49,7 +50,7 @@ class AppExport PropertyExpressionEngine : public App::Property public: typedef boost::function expr)> ValidatorFunc; - + /** * @brief The ExpressionInfo struct encapsulates an expression and a comment. */ @@ -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 expressionChanged; /* Python interface */ PyObject *getPyObject(void); diff --git a/src/Gui/ExpressionBinding.cpp b/src/Gui/ExpressionBinding.cpp index 3af8053be0e9..e560f4960cfc 100644 --- a/src/Gui/ExpressionBinding.cpp +++ b/src/Gui/ExpressionBinding.cpp @@ -32,6 +32,7 @@ #include #include #include +#include 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 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(); +} diff --git a/src/Gui/ExpressionBinding.h b/src/Gui/ExpressionBinding.h index 83dc478cbb87..f89438ab518d 100644 --- a/src/Gui/ExpressionBinding.h +++ b/src/Gui/ExpressionBinding.h @@ -27,6 +27,7 @@ #include #include #include +#include namespace App { class Expression; @@ -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 getExpression() const; std::string getExpressionString() const; std::string getEscapedExpressionString() const; virtual void setExpression(boost::shared_ptr 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 lastExpression; + protected: QLabel* iconLabel; QPalette defaultPalette; int iconHeight; + + void expressionChange(const App::ObjectIdentifier& id); + boost::signals::scoped_connection connection; + bool m_autoApply; }; } diff --git a/src/Gui/QuantitySpinBox.cpp b/src/Gui/QuantitySpinBox.cpp index 95546ee80b82..0cf4806a702e 100644 --- a/src/Gui/QuantitySpinBox.cpp +++ b/src/Gui/QuantitySpinBox.cpp @@ -276,41 +276,56 @@ void Gui::QuantitySpinBox::setExpression(boost::shared_ptr 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 result(getExpression()->eval()); - NumberExpression * value = freecad_dynamic_cast(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 result(getExpression()->eval()); + NumberExpression * value = freecad_dynamic_cast(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)) { diff --git a/src/Gui/QuantitySpinBox.h b/src/Gui/QuantitySpinBox.h index 71e2dbffb547..3363d098119e 100644 --- a/src/Gui/QuantitySpinBox.h +++ b/src/Gui/QuantitySpinBox.h @@ -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; diff --git a/src/Gui/SpinBox.cpp b/src/Gui/SpinBox.cpp index 7ca51f945abf..d100ce9e61c8 100644 --- a/src/Gui/SpinBox.cpp +++ b/src/Gui/SpinBox.cpp @@ -266,15 +266,78 @@ void UIntSpinBox::setExpression(boost::shared_ptr 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())); + } +} + +void UIntSpinBox::onChange() { + + if (getExpression()) { + std::auto_ptr result(getExpression()->eval()); + NumberExpression * value = freecad_dynamic_cast(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)) { + Gui::Command::doCommand(Gui::Command::Doc, "%s = %u", propName.c_str(), value()); + return true; + } + else + return false; +} + +bool UIntSpinBox::apply() +{ + return ExpressionBinding::apply(); +} + +void UIntSpinBox::resizeEvent(QResizeEvent * event) +{ + QAbstractSpinBox::resizeEvent(event); + + int frameWidth = style()->pixelMetric(QStyle::PM_SpinBoxFrameWidth); - if (getExpression()) { + QSize sz = iconLabel->sizeHint(); + iconLabel->move(lineEdit()->rect().right() - frameWidth - sz.width(), 0); + + try { + if (isBound() && getExpression()) { std::auto_ptr result(getExpression()->eval()); NumberExpression * value = freecad_dynamic_cast(result.get()); if (value) { - setValue(boost::math::round(value->getValue())); setReadOnly(true); - iconLabel->setPixmap(getIcon(":/icons/bound-expression.svg", QSize(iconHeight, iconHeight))); + QPixmap pixmap = getIcon(":/icons/bound-expression.svg", QSize(iconHeight, iconHeight)); + iconLabel->setPixmap(pixmap); QPalette p(lineEdit()->palette()); p.setColor(QPalette::Text, Qt::lightGray); @@ -284,7 +347,9 @@ void UIntSpinBox::setExpression(boost::shared_ptr expr) } else { setReadOnly(false); - iconLabel->setPixmap(getIcon(":/icons/bound-expression-unset.svg", QSize(iconHeight, iconHeight))); + 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); @@ -299,10 +364,74 @@ void UIntSpinBox::setExpression(boost::shared_ptr expr) lineEdit()->setPalette(p); iconLabel->setToolTip(QString::fromAscii(e.what())); } + } -bool UIntSpinBox::apply(const std::string & propName) +void UIntSpinBox::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 UIntSpinBox::finishFormulaDialog() +{ + Gui::Dialog::DlgExpressionInput* box = qobject_cast(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()); + + box->deleteLater(); +} + +void UIntSpinBox::keyPressEvent(QKeyEvent *event) +{ + if (event->text() == QString::fromUtf8("=") && isBound()) + openFormulaDialog(); + else { + if (!hasExpression()) + QAbstractSpinBox::keyPressEvent(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; @@ -311,12 +440,61 @@ bool UIntSpinBox::apply(const std::string & propName) return false; } -bool UIntSpinBox::apply() +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 expr) { - return ExpressionBinding::apply(); + 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 UIntSpinBox::resizeEvent(QResizeEvent * event) +void DoubleSpinBox::onChange() { + + if (getExpression()) { + std::auto_ptr result(getExpression()->eval()); + NumberExpression * value = freecad_dynamic_cast(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); @@ -363,7 +541,7 @@ void UIntSpinBox::resizeEvent(QResizeEvent * event) } -void UIntSpinBox::openFormulaDialog() +void DoubleSpinBox::openFormulaDialog() { Q_ASSERT(isBound()); @@ -376,7 +554,7 @@ void UIntSpinBox::openFormulaDialog() box->setExpressionInputSize(width(), height()); } -void UIntSpinBox::finishFormulaDialog() +void DoubleSpinBox::finishFormulaDialog() { Gui::Dialog::DlgExpressionInput* box = qobject_cast(sender()); if (!box) { @@ -392,7 +570,7 @@ void UIntSpinBox::finishFormulaDialog() box->deleteLater(); } -void UIntSpinBox::keyPressEvent(QKeyEvent *event) +void DoubleSpinBox::keyPressEvent(QKeyEvent *event) { if (event->text() == QString::fromUtf8("=") && isBound()) openFormulaDialog(); diff --git a/src/Gui/SpinBox.h b/src/Gui/SpinBox.h index 7f7d6442f1a4..9f2ab474a940 100644 --- a/src/Gui/SpinBox.h +++ b/src/Gui/SpinBox.h @@ -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 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 diff --git a/src/Gui/propertyeditor/PropertyItem.cpp b/src/Gui/propertyeditor/PropertyItem.cpp index 1b9b0c8b06e2..ac86658cfd36 100644 --- a/src/Gui/propertyeditor/PropertyItem.cpp +++ b/src/Gui/propertyeditor/PropertyItem.cpp @@ -36,6 +36,7 @@ #endif #include +#include #include #include #include @@ -53,12 +54,13 @@ #include #include "PropertyItem.h" +#include 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& 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 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(); - 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())<bind(App::ObjectIdentifier(getPath())<bind(App::ObjectIdentifier(getPath())<bind(App::ObjectIdentifier(getPath())<bind(App::ObjectIdentifier(getPath())<bind(App::ObjectIdentifier(getPath())<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())<bind(App::ObjectIdentifier(getPath())<bind(App::ObjectIdentifier(getPath())<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; } diff --git a/src/Gui/propertyeditor/PropertyItem.h b/src/Gui/propertyeditor/PropertyItem.h index bc4be9f5d44e..82d664fc7d8d 100644 --- a/src/Gui/propertyeditor/PropertyItem.h +++ b/src/Gui/propertyeditor/PropertyItem.h @@ -37,6 +37,7 @@ #include #include #include +#include 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 @@ class GuiExport PropertyItem : virtual public QObject, public Base::BaseClass 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 @@ class GuiExport PropertyItem : virtual public QObject, public Base::BaseClass QList childItems; bool readonly; int precision; + bool cleared; }; /** @@ -351,6 +359,7 @@ class GuiExport PropertyVectorItem: public PropertyItem 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; diff --git a/src/Mod/Sketcher/Gui/PropertyConstraintListItem.cpp b/src/Mod/Sketcher/Gui/PropertyConstraintListItem.cpp index 1dcd339f22fd..1c848d53d34d 100644 --- a/src/Mod/Sketcher/Gui/PropertyConstraintListItem.cpp +++ b/src/Mod/Sketcher/Gui/PropertyConstraintListItem.cpp @@ -60,8 +60,8 @@ QVariant PropertyConstraintListItem::toString(const QVariant& prop) const void PropertyConstraintListItem::initialize() { - const Sketcher::PropertyConstraintList* item = static_cast(getPropertyData()[0]); - const std::vector< Sketcher::Constraint * > &vals = item->getValues(); + const Sketcher::PropertyConstraintList* list = static_cast(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); } }