Added expression support to QuantitySpinBox and InputField classes.

This commit is contained in:
Eivind Kvedalen 2015-09-07 00:08:54 +02:00 committed by wmayer
parent 31a6fece52
commit 897f8b78b8
13 changed files with 1153 additions and 19 deletions

View File

@ -205,6 +205,7 @@ set(Gui_MOC_HDRS
DocumentRecovery.h
EditorView.h
ExpressionCompleter.h
DlgExpressionInput.h
FileDialog.h
Flag.h
GraphicsViewZoom.h
@ -228,6 +229,7 @@ set(Gui_MOC_HDRS
PythonEditor.h
QuantitySpinBox.h
QListWidgetCustom.h
QuantitySpinBox_p.h
ReportView.h
SceneInspector.h
SelectionView.h
@ -308,6 +310,7 @@ SET(Gui_UIC_SRCS
DocumentRecovery.ui
DownloadManager.ui
DownloadItem.ui
DlgExpressionInput.ui
MouseButtons.ui
SceneInspector.ui
InputVector.ui
@ -367,6 +370,7 @@ SET(Dialog_CPP_SRCS
DlgProjectUtility.cpp
DlgPropertyLink.cpp
DlgTipOfTheDayImp.cpp
DlgExpressionInput.cpp
TaskDlgRelocation.cpp
DlgUndoRedo.cpp
InputVector.cpp
@ -398,6 +402,7 @@ SET(Dialog_HPP_SRCS
DlgProjectUtility.h
DlgPropertyLink.h
DlgTipOfTheDayImp.h
DlgExpressionInput.h
TaskDlgRelocation.h
DlgUndoRedo.h
InputVector.h
@ -435,6 +440,7 @@ SET(Dialog_SRCS
DlgPropertyLink.ui
DlgTipOfTheDay.ui
DlgTreeWidget.ui
DlgExpressionInput.ui
DownloadManager.ui
DownloadItem.ui
DocumentRecovery.ui
@ -894,6 +900,7 @@ SET(Widget_HPP_SRCS
InputField.h
ProgressBar.h
QuantitySpinBox.h
QuantitySpinBox_p.h
SpinBox.h
Splashscreen.h
WidgetFactory.h
@ -967,6 +974,7 @@ SET(FreeCADGui_CPP_SRCS
Document.cpp
DocumentModel.cpp
DocumentPyImp.cpp
ExpressionBinding.cpp
GraphicsViewZoom.cpp
ExpressionCompleter.cpp
GuiApplicationNativeEventAware.cpp
@ -988,6 +996,7 @@ SET(FreeCADGui_SRCS
BitmapFactory.h
Document.h
DocumentModel.h
ExpressionBinding.cpp
ExpressionCompleter.h
FreeCADGuiInit.py
GraphicsViewZoom.h

View File

@ -0,0 +1,173 @@
/***************************************************************************
* Copyright (c) 2015 Eivind Kvedalen <eivind@kvedalen.name> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 51 Franklin Street, *
* Fifth Floor, Boston, MA 02110-1301, USA *
* *
***************************************************************************/
#include "PreCompiled.h"
#include <QPainter>
#include <QDesktopWidget>
#include "DlgExpressionInput.h"
#include "ui_DlgExpressionInput.h"
#include "ExpressionCompleter.h"
#include <Base/Tools.h>
#include <App/Expression.h>
#include <App/DocumentObject.h>
using namespace App;
using namespace Gui::Dialog;
const int DlgExpressionInput::h = 15;
const int DlgExpressionInput::l = 30;
const int DlgExpressionInput::r = 30;
const int DlgExpressionInput::d = 7;
DlgExpressionInput::DlgExpressionInput(const App::ObjectIdentifier & _path, boost::shared_ptr<const Expression> _expression, const Base::Unit & _impliedUnit, QWidget *parent) :
QDialog(parent),
ui(new Ui::DlgExpressionInput),
expression(_expression ? _expression->copy() : 0),
path(_path),
discarded(false),
impliedUnit(_impliedUnit)
{
assert(path.getDocumentObject() != 0);
// Setup UI
ui->setupUi(this);
if (expression) {
ui->expression->setText(Base::Tools::fromStdString(expression->toString()));
textChanged(Base::Tools::fromStdString(expression->toString()));
}
// Connect signal(s)
connect(ui->expression, SIGNAL(textChanged(QString)), this, SLOT(textChanged(QString)));
connect(ui->discardBtn, SIGNAL(clicked()), this, SLOT(setDiscarded()));
// Set document object on line edit to create auto completer
DocumentObject * docObj = path.getDocumentObject();
ui->expression->setDocumentObject(docObj);
setWindowFlags(Qt::Widget | Qt::FramelessWindowHint);
setAttribute(Qt::WA_NoSystemBackground, true);
setAttribute(Qt::WA_TranslucentBackground, true);
setParent(0);
ui->expression->setFocus();
QDesktopWidget widget;
setMinimumWidth(widget.availableGeometry(widget.primaryScreen()).width()/2);
#ifndef FC_DEBUG
ui->parsedExpr->setVisible(false);
#endif
}
DlgExpressionInput::~DlgExpressionInput()
{
delete ui;
}
QPoint DlgExpressionInput::tip() const
{
return QPoint(l - d, 0);
}
void DlgExpressionInput::paintEvent(QPaintEvent * event) {
QPainter painter(this);
QPainterPath path;
path.moveTo(0, h + r / 2);
path.arcTo(QRect(0, h, r, r), 180, -90);
path.lineTo(l, h);
path.lineTo(l - d, 0);
path.lineTo(l + d, h);
path.lineTo(width() - r - 1, h);
path.arcTo(QRect(width() - r - 1, h, r, r), 90, -90);
path.lineTo(width() - 1, height() - r);
path.arcTo(QRect(width() - r - 1, height() - r - 1, r, r), 0, -90);
path.lineTo(r, height() - 1);
path.arcTo(QRect(0, height() - r - 1, r, r), -90, -90);
path.lineTo(0, h + r/2);
QPen pen(Qt::black);
QBrush brush(QColor(250, 250, 180));
pen.setWidthF(2.0);
painter.setBrush(brush);
painter.setPen(pen);
painter.fillPath(path, brush);
painter.drawPath(path);
}
void DlgExpressionInput::textChanged(const QString &text)
{
try {
boost::shared_ptr<Expression> expr(ExpressionParser::parse(path.getDocumentObject(), text.toUtf8().constData()));
if (expr) {
std::string error = path.getDocumentObject()->ExpressionEngine.validateExpression(path, expr);
if (error.size() > 0)
throw Base::Exception(error.c_str());
#ifdef FC_DEBUG
ui->parsedExpr->setText(Base::Tools::fromStdString(expr->toString()));
#endif
std::auto_ptr<Expression> result(expr->eval());
expression = expr;
ui->okBtn->setEnabled(true);
ui->errorMsg->setText(QString::fromUtf8(""));
NumberExpression * n = Base::freecad_dynamic_cast<NumberExpression>(result.get());
if (n) {
Base::Quantity value = n->getQuantity();
if (!value.getUnit().isEmpty() && value.getUnit() != impliedUnit)
throw Base::Exception("Unit mismatch between result and required unit");
value.setUnit(impliedUnit);
ui->result->setText(value.getUserString());
}
else
ui->result->setText(Base::Tools::fromStdString(result->toString()));
}
}
catch (Base::Exception & e) {
ui->errorMsg->setText(QString::fromUtf8(e.what()));
QPalette p(ui->errorMsg->palette());
p.setColor(QPalette::WindowText, Qt::red);
ui->errorMsg->setPalette(p);
ui->okBtn->setDisabled(true);
ui->result->setText(QString::fromAscii("--"));
}
}
void DlgExpressionInput::setDiscarded()
{
discarded = true;
reject();
}
#include "moc_DlgExpressionInput.cpp"

View File

@ -0,0 +1,85 @@
/***************************************************************************
* Copyright (c) 2015 Eivind Kvedalen <eivind@kvedalen.name> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 51 Franklin Street, *
* Fifth Floor, Boston, MA 02110-1301, USA *
* *
***************************************************************************/
#ifndef GUI_DIALOG_DLGEXPRESSIONINPUT_H
#define GUI_DIALOG_DLGEXPRESSIONINPUT_H
#include <QDialog>
#include <Base/Unit.h>
#include <App/ObjectIdentifier.h>
#include <boost/shared_ptr.hpp>
namespace Ui {
class DlgExpressionInput;
}
namespace Base {
class Quantity;
}
namespace App {
class Path;
class Expression;
class DocumentObject;
}
namespace Gui {
namespace Dialog {
class GuiExport DlgExpressionInput : public QDialog
{
Q_OBJECT
public:
explicit DlgExpressionInput(const App::ObjectIdentifier & _path, boost::shared_ptr<const App::Expression> _expression, const Base::Unit &_impliedUnit, QWidget *parent = 0);
~DlgExpressionInput();
boost::shared_ptr<App::Expression> getExpression() const { return expression; }
bool discardedFormula() const { return discarded; }
void paintEvent(QPaintEvent *event);
QPoint tip() const;
private Q_SLOTS:
void textChanged(const QString & text);
void setDiscarded();
private:
::Ui::DlgExpressionInput *ui;
boost::shared_ptr<App::Expression> expression;
App::ObjectIdentifier path;
bool discarded;
const Base::Unit impliedUnit;
static const int h;
static const int l;
static const int r;
static const int d;
};
}
}
#endif // GUI_DIALOG_EXPRESSIONINPUT_H

View File

@ -0,0 +1,213 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DlgExpressionInput</class>
<widget class="QDialog" name="DlgExpressionInput">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>600</width>
<height>134</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>600</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Formula editor</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="topMargin">
<number>25</number>
</property>
<item row="3" column="0" colspan="4">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="discardBtn">
<property name="text">
<string>&amp;Discard formula</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
<property name="default">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cancelBtn">
<property name="text">
<string>Cancel</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="okBtn">
<property name="text">
<string>Ok</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="1" colspan="3">
<widget class="QLabel" name="errorMsg">
<property name="palette">
<palette>
<active>
<colorrole role="Text">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
</active>
<inactive>
<colorrole role="Text">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
</inactive>
<disabled>
<colorrole role="Text">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>190</red>
<green>190</green>
<blue>190</blue>
</color>
</brush>
</colorrole>
</disabled>
</palette>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label">
<property name="text">
<string>=&gt;</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="Gui::ExpressionLineEdit" name="expression"/>
</item>
<item row="0" column="3">
<widget class="QLabel" name="result">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="4" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="1">
<widget class="QLabel" name="parsedExpr">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Gui::ExpressionLineEdit</class>
<extends>QLineEdit</extends>
<header>ExpressionCompleter.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>cancelBtn</sender>
<signal>clicked()</signal>
<receiver>DlgExpressionInput</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>399</x>
<y>69</y>
</hint>
<hint type="destinationlabel">
<x>392</x>
<y>85</y>
</hint>
</hints>
</connection>
<connection>
<sender>okBtn</sender>
<signal>clicked()</signal>
<receiver>DlgExpressionInput</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>505</x>
<y>68</y>
</hint>
<hint type="destinationlabel">
<x>505</x>
<y>94</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,154 @@
/***************************************************************************
* Copyright (c) 2015 Eivind Kvedalen <eivind@kvedalen.name> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 51 Franklin Street, *
* Fifth Floor, Boston, MA 02110-1301, USA *
* *
***************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
#endif
#include "ExpressionBinding.h"
#include "Command.h"
#include <App/Expression.h>
#include <App/DocumentObject.h>
#include <Base/Tools.h>
#include <App/ObjectIdentifier.h>
#include <App/Document.h>
using namespace Gui;
using namespace App;
ExpressionBinding::ExpressionBinding()
{
}
ExpressionBinding::~ExpressionBinding()
{
}
bool ExpressionBinding::isBound() const
{
return path.getDocumentObject() != 0;
}
void Gui::ExpressionBinding::setExpression(boost::shared_ptr<Expression> expr)
{
DocumentObject * docObj = path.getDocumentObject();
if (expr) {
const std::string error = docObj->ExpressionEngine.validateExpression(path, expr);
if (error.size())
throw Base::Exception(error.c_str());
}
docObj->ExpressionEngine.setValue(path, expr);
}
void ExpressionBinding::bind(const App::ObjectIdentifier &_path)
{
const Property * prop = _path.getProperty();
Q_ASSERT(prop != 0);
path = prop->canonicalPath(_path);
}
void ExpressionBinding::bind(const Property &prop)
{
bind(App::ObjectIdentifier(prop));
}
bool ExpressionBinding::hasExpression() const
{
return isBound() && getExpression() != 0;
}
boost::shared_ptr<App::Expression> ExpressionBinding::getExpression() const
{
DocumentObject * docObj = path.getDocumentObject();
Q_ASSERT(isBound() && docObj != 0);
return docObj->getExpression(path).expression;
}
std::string ExpressionBinding::getExpressionString() const
{
if (!getExpression())
throw Base::Exception("No expression found.");
return getExpression()->toString();
}
std::string ExpressionBinding::getEscapedExpressionString() const
{
return Base::Tools::escapedUnicodeFromUtf8(getExpressionString().c_str());
}
bool ExpressionBinding::apply(const std::string & propName)
{
if (hasExpression()) {
DocumentObject * docObj = path.getDocumentObject();
if (!docObj)
throw Base::Exception("Document object not found.");
Gui::Command::doCommand(Gui::Command::Doc,"App.getDocument('%s').%s.setExpression('%s', '%s')",
docObj->getDocument()->getName(),
docObj->getNameInDocument(),
path.toEscapedString().c_str(),
getEscapedExpressionString().c_str());
return true;
}
else {
if (isBound()) {
DocumentObject * docObj = path.getDocumentObject();
if (!docObj)
throw Base::Exception("Document object not found.");
Gui::Command::doCommand(Gui::Command::Doc,"App.getDocument('%s').%s.setExpression('%s', None)",
docObj->getDocument()->getName(),
docObj->getNameInDocument(),
path.toEscapedString().c_str());
return true;
}
return false;
}
}
bool ExpressionBinding::apply()
{
Property * prop(path.getProperty());
assert(prop != 0);
DocumentObject * docObj(path.getDocumentObject());
if (!docObj)
throw Base::Exception("Document object not found.");
std::string name = docObj->getNameInDocument();
return apply("App.ActiveDocument." + name + "." + std::string(prop->getName()));
}

View File

@ -0,0 +1,63 @@
/***************************************************************************
* Copyright (c) 2015 Eivind Kvedalen <eivind@kvedalen.name> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 51 Franklin Street, *
* Fifth Floor, Boston, MA 02110-1301, USA *
* *
***************************************************************************/
#ifndef EXPRESSIONBINDING_H
#define EXPRESSIONBINDING_H
#include <string>
#include <App/ObjectIdentifier.h>
#include <boost/shared_ptr.hpp>
namespace App {
class Expression;
}
namespace Gui {
class GuiExport ExpressionBinding
{
public:
ExpressionBinding();
~ExpressionBinding();
virtual void bind(const App::ObjectIdentifier & _path);
virtual void bind(const App::Property & prop);
bool isBound() const;
virtual bool apply(const std::string &propName);
virtual bool apply();
bool hasExpression() const;
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);
private:
App::ObjectIdentifier path;
//boost::shared_ptr<const App::Expression> expression;
};
}
#endif // EXPRESSIONBINDING_H

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg4069"
version="1.1"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="New document 7">
<defs
id="defs4071" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.5"
inkscape:cx="32"
inkscape:cy="32"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="967"
inkscape:window-height="814"
inkscape:window-x="589"
inkscape:window-y="86"
inkscape:window-maximized="0" />
<metadata
id="metadata4074">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<path
sodipodi:type="arc"
style="fill:#0000ff;fill-opacity:1;stroke:#0000ff;stroke-width:4.36290264;stroke-linejoin:round;stroke-miterlimit:4.19999981;stroke-opacity:1;stroke-dasharray:none"
id="path2985"
sodipodi:cx="325"
sodipodi:cy="503.43362"
sodipodi:rx="68.571426"
sodipodi:ry="61.785713"
d="m 393.57143,503.43362 a 68.571426,61.785713 0 1 1 -137.14286,0 68.571426,61.785713 0 1 1 137.14286,0 z"
transform="matrix(0.44478506,0,0,0.49741082,-112.73696,-218.43771)" />
<text
xml:space="preserve"
style="font-size:35.82092285px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.89552319;stroke-opacity:1;font-family:Arial;-inkscape-font-specification:Arial"
x="32.030895"
y="41.949329"
id="text3755"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3757"
x="32.030895"
y="41.949329"
style="font-size:35.82092285px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;stroke-width:0.89552319;font-family:Arial;-inkscape-font-specification:Arial Italic">f(x)</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -41,6 +41,7 @@
<file>ClassBrowser/type_enum.svg</file>
<file>ClassBrowser/type_module.svg</file>
<file>Std_ViewScreenShot.svg</file>
<file>bound-expression.svg</file>
<file>breakpoint.svg</file>
<file>debug-marker.svg</file>
<file>debug-start.svg</file>

View File

@ -31,12 +31,17 @@
#include <Base/Console.h>
#include <Base/Quantity.h>
#include <Base/Exception.h>
#include <Base/Tools.h>
#include <App/Application.h>
#include <App/PropertyUnits.h>
#include <App/DocumentObject.h>
#include "ExpressionCompleter.h"
#include "Command.h"
#include "InputField.h"
#include "BitmapFactory.h"
using namespace Gui;
using namespace App;
using namespace Base;
// --------------------------------------------------------------------
@ -59,7 +64,8 @@ private:
// --------------------------------------------------------------------
InputField::InputField(QWidget * parent)
: QLineEdit(parent),
: ExpressionLineEdit(parent),
ExpressionBinding(),
validInput(true),
actUnitValue(0),
Maximum(DOUBLE_MAX),
@ -92,6 +98,43 @@ InputField::~InputField()
{
}
void InputField::bind(const App::ObjectIdentifier &_path)
{
ExpressionBinding::bind(_path);
PropertyQuantity * prop = freecad_dynamic_cast<PropertyQuantity>(getPath().getProperty());
if (prop)
actQuantity = prop->getValue();
DocumentObject * docObj = getPath().getDocumentObject();
if (docObj) {
boost::shared_ptr<const Expression> expr(docObj->getExpression(getPath()).expression);
if (expr)
newInput(Tools::fromStdString(expr->toString()));
}
// Create document object, to initialize completer
setDocumentObject(docObj);
}
bool InputField::apply(const std::string &propName)
{
if (!ExpressionBinding::apply(propName)) {
Gui::Command::doCommand(Gui::Command::Doc,"%s = %f", propName.c_str(), getQuantity().getValue());
return true;
}
else
return false;
}
bool InputField::apply()
{
return ExpressionBinding::apply();
}
QPixmap InputField::getValidationIcon(const char* name, const QSize& size) const
{
QString key = QString::fromAscii("%1_%2x%3")
@ -110,6 +153,15 @@ QPixmap InputField::getValidationIcon(const char* name, const QSize& size) const
void InputField::updateText(const Base::Quantity& quant)
{
if (isBound()) {
boost::shared_ptr<const Expression> e(getPath().getDocumentObject()->getExpression(getPath()).expression);
if (e) {
setText(Tools::fromStdString(e->toString()));
return;
}
}
double dFactor;
QString txt = quant.getUserString(dFactor,actUnitStr);
actUnitValue = quant.getValue()/dFactor;
@ -182,7 +234,22 @@ void InputField::newInput(const QString & text)
try {
QString input = text;
fixup(input);
res = Quantity::parse(input);
if (isBound()) {
boost::shared_ptr<Expression> e(ExpressionParser::parse(getPath().getDocumentObject(), input.toUtf8()));
setExpression(e);
std::auto_ptr<Expression> evalRes(getExpression()->eval());
NumberExpression * value = freecad_dynamic_cast<NumberExpression>(evalRes.get());
if (value) {
res.setValue(value->getValue());
res.setUnit(value->getUnit());
}
}
else
res = Quantity::parse(input);
}
catch(Base::Exception &e){
ErrorText = e.what();
@ -485,14 +552,14 @@ void InputField::showEvent(QShowEvent * event)
void InputField::focusInEvent(QFocusEvent * event)
{
if (event->reason() == Qt::TabFocusReason ||
event->reason() == Qt::BacktabFocusReason ||
event->reason() == Qt::ShortcutFocusReason) {
if (!this->hasSelectedText())
selectNumber();
}
QLineEdit::focusInEvent(event);
if (event->reason() == Qt::TabFocusReason ||
event->reason() == Qt::BacktabFocusReason ||
event->reason() == Qt::ShortcutFocusReason) {
if (!this->hasSelectedText())
selectNumber();
}
QLineEdit::focusInEvent(event);
}
void InputField::keyPressEvent(QKeyEvent *event)

View File

@ -25,17 +25,27 @@
#define GUI_INPUTFIELD_H
#include <QValidator>
#include <App/Property.h>
#include <App/Expression.h>
#include <Base/Parameter.h>
#include <Base/Quantity.h>
#include "Widgets.h"
#include "Window.h"
#include "SpinBox.h"
#include "FileDialog.h"
#include "ExpressionBinding.h"
#include "ExpressionCompleter.h"
#ifdef Q_MOC_RUN
Q_DECLARE_METATYPE(Base::Quantity)
#endif
namespace App {
class DocumentObject;
class ObjectIdentifier;
class Expression;
}
namespace Gui {
@ -46,9 +56,9 @@ namespace Gui {
* and managing default and history values.
* Although it's derived from a QLineEdit widget, it supports most of the properties and signals
* of a spin box.
* \author Jürgen Riegel
* \author Jürgen Riegel
*/
class GuiExport InputField : public QLineEdit
class GuiExport InputField : public ExpressionLineEdit, public ExpressionBinding
{
Q_OBJECT
@ -62,7 +72,7 @@ class GuiExport InputField : public QLineEdit
public:
InputField ( QWidget * parent = 0 );
InputField (QWidget * parent = 0);
virtual ~InputField();
/// set the field with a quantity
@ -132,6 +142,10 @@ public:
std::vector<QString> getSavedValues(void);
//@}
void bind(const App::ObjectIdentifier &_path);
bool apply(const std::string &propName);
bool apply();
Q_SIGNALS:
/** gets emitted if the user has entered a VALID input
* Valid means the user inputted string obeys all restrictions
@ -187,6 +201,8 @@ private:
double StepSize;
int HistorySize;
int SaveSize;
QPalette defaultPalette;
};
} // namespace Gui

View File

@ -25,14 +25,31 @@
#ifndef _PreComp_
# include <QLineEdit>
# include <QFocusEvent>
# include <QHBoxLayout>
# include <QLabel>
# include <QStyle>
# include <QPixmapCache>
# include <QMouseEvent>
# include <QToolTip>
# include <QApplication>
#endif
#include "QuantitySpinBox.h"
#include "QuantitySpinBox_p.h"
#include "DlgExpressionInput.h"
#include "BitmapFactory.h"
#include "Command.h"
#include <Base/Tools.h>
#include <Base/Exception.h>
#include <App/Expression.h>
#include <sstream>
using namespace Gui;
using namespace App;
using namespace Base;
namespace Gui {
class QuantitySpinBoxPrivate
{
public:
@ -210,23 +227,183 @@ end:
double maximum;
double minimum;
double singleStep;
QPalette defaultPalette;
QLabel* iconLabel;
};
}
QuantitySpinBox::QuantitySpinBox(QWidget *parent)
: QAbstractSpinBox(parent), d_ptr(new QuantitySpinBoxPrivate())
: QAbstractSpinBox(parent),
ExpressionBinding(),
d_ptr(new QuantitySpinBoxPrivate())
{
d_ptr->locale = locale();
this->setContextMenuPolicy(Qt::DefaultContextMenu);
QObject::connect(lineEdit(), SIGNAL(textChanged(QString)),
this, SLOT(userInput(QString)));
d_ptr->defaultPalette = lineEdit()->palette();
/* Icon for f(x) */
d_ptr->iconLabel = new ExpressionLabel(lineEdit());
d_ptr->iconLabel->setCursor(Qt::ArrowCursor);
QPixmap pixmap = getIcon(":/icons/bound-expression.svg", QSize(lineEdit()->sizeHint().height(), lineEdit()->sizeHint().height()));
d_ptr->iconLabel->setPixmap(pixmap);
d_ptr->iconLabel->setStyleSheet(QString::fromAscii("QLabel { border: none; padding: 0px; }"));
d_ptr->iconLabel->hide();
int frameWidth = style()->pixelMetric(QStyle::PM_SpinBoxFrameWidth);
lineEdit()->setStyleSheet(QString::fromAscii("QLineEdit { padding-right: %1px } ").arg(d_ptr->iconLabel->sizeHint().width() + frameWidth + 1));
QObject::connect(d_ptr->iconLabel, SIGNAL(clicked()), this, SLOT(openFormulaDialog()));
}
QuantitySpinBox::~QuantitySpinBox()
{
}
void QuantitySpinBox::updateText(const Base::Quantity& quant)
QPixmap QuantitySpinBox::getIcon(const char* name, const QSize& size) const
{
QString key = QString::fromAscii("%1_%2x%3")
.arg(QString::fromAscii(name))
.arg(size.width())
.arg(size.height());
QPixmap icon;
if (QPixmapCache::find(key, icon))
return icon;
icon = BitmapFactory().pixmapFromSvg(name, size);
if (!icon.isNull())
QPixmapCache::insert(key, icon);
return icon;
}
void QuantitySpinBox::bind(const App::ObjectIdentifier &_path)
{
Q_D(QuantitySpinBox);
ExpressionBinding::bind(_path);
d->iconLabel->show();
}
void Gui::QuantitySpinBox::setExpression(boost::shared_ptr<Expression> expr)
{
Q_D(QuantitySpinBox);
Q_ASSERT(isBound());
try {
ExpressionBinding::setExpression(expr);
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);
QPalette p(lineEdit()->palette());
p.setColor(QPalette::Text, Qt::lightGray);
lineEdit()->setPalette(p);
}
setToolTip(Base::Tools::fromStdString(getExpression()->toString()));
}
else {
setReadOnly(false);
QPalette p(lineEdit()->palette());
p.setColor(QPalette::Active, QPalette::Text, d->defaultPalette.color(QPalette::Text));
lineEdit()->setPalette(p);
}
d->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);
d->iconLabel->setToolTip(QString::fromAscii(e.what()));
}
}
bool QuantitySpinBox::apply(const std::string & propName)
{
if (!ExpressionBinding::apply(propName)) {
Gui::Command::doCommand(Gui::Command::Doc, "%s = %f", propName.c_str(), value().getValue());
return true;
}
else
return false;
}
bool QuantitySpinBox::apply()
{
return ExpressionBinding::apply();
}
void QuantitySpinBox::resizeEvent(QResizeEvent * event)
{
Q_D(const QuantitySpinBox);
QAbstractSpinBox::resizeEvent(event);
int frameWidth = style()->pixelMetric(QStyle::PM_SpinBoxFrameWidth);
QPixmap pixmap = getIcon(":/icons/bound-expression.svg", QSize(lineEdit()->rect().height() - frameWidth * 2,
lineEdit()->rect().height() - frameWidth * 2));
d->iconLabel->setPixmap(pixmap);
QSize sz = d->iconLabel->sizeHint();
d->iconLabel->move(lineEdit()->rect().right() - frameWidth - sz.width(),
lineEdit()->rect().top() - frameWidth);
try {
if (isBound() && getExpression()) {
std::auto_ptr<Expression> result(getExpression()->eval());
NumberExpression * value = freecad_dynamic_cast<NumberExpression>(result.get());
if (value) {
setReadOnly(true);
QPalette p(lineEdit()->palette());
p.setColor(QPalette::Text, Qt::lightGray);
lineEdit()->setPalette(p);
}
setToolTip(Base::Tools::fromStdString(getExpression()->toString()));
}
else {
setReadOnly(false);
QPalette p(lineEdit()->palette());
p.setColor(QPalette::Active, QPalette::Text, d->defaultPalette.color(QPalette::Text));
lineEdit()->setPalette(p);
}
d->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);
d->iconLabel->setToolTip(QString::fromAscii(e.what()));
}
}
void Gui::QuantitySpinBox::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Equal && isBound())
openFormulaDialog();
else {
if (!hasExpression())
QAbstractSpinBox::keyPressEvent(event);
}
}
void QuantitySpinBox::updateText(const Quantity &quant)
{
Q_D(QuantitySpinBox);
@ -312,6 +489,21 @@ void QuantitySpinBox::userInput(const QString & text)
valueChanged(res.getValue());
}
void QuantitySpinBox::openFormulaDialog()
{
Q_ASSERT(isBound());
Q_D(const QuantitySpinBox);
Gui::Dialog::DlgExpressionInput box(getPath(), getExpression(), d->unit, this);
QPoint pos = mapToGlobal(QPoint(width() / 2, height() / 2));
box.setGeometry(pos.x() - box.tip().x(), pos.y() - box.tip().y(), width(), height());
if (box.exec() == QDialog::Accepted)
setExpression(box.getExpression());
else if (box.discardedFormula())
setExpression(boost::shared_ptr<Expression>());
}
Base::Unit QuantitySpinBox::unit() const
{
Q_D(const QuantitySpinBox);
@ -430,6 +622,23 @@ void QuantitySpinBox::showEvent(QShowEvent * event)
selectNumber();
}
bool QuantitySpinBox::event(QEvent * event)
{
if (event->type() == QEvent::ToolTip) {
if (isBound() && getExpression() && lineEdit()->isReadOnly()) {
QHelpEvent * helpEvent = static_cast<QHelpEvent*>(event);
QToolTip::showText( helpEvent->globalPos(), Base::Tools::fromStdString(getExpression()->toString()), this);
event->accept();
return true;
}
else
return QAbstractSpinBox::event(event);
}
else
return QAbstractSpinBox::event(event);
}
void QuantitySpinBox::focusInEvent(QFocusEvent * event)
{
bool hasSel = lineEdit()->hasSelectedText();
@ -438,8 +647,16 @@ void QuantitySpinBox::focusInEvent(QFocusEvent * event)
if (event->reason() == Qt::TabFocusReason ||
event->reason() == Qt::BacktabFocusReason ||
event->reason() == Qt::ShortcutFocusReason) {
if (!hasSel)
selectNumber();
if (isBound() && getExpression() && lineEdit()->isReadOnly()) {
QHelpEvent * helpEvent = new QHelpEvent(QEvent::ToolTip, QPoint( 0, rect().height() ), mapToGlobal( QPoint( 0, rect().height() ) ));
QApplication::postEvent(this, helpEvent);
lineEdit()->setSelection(0, 0);
}
else {
if (!hasSel)
selectNumber();
}
}
}
@ -454,6 +671,7 @@ void QuantitySpinBox::focusOutEvent(QFocusEvent * event)
if (state != QValidator::Acceptable) {
lineEdit()->setText(d->validStr);
}
QToolTip::hideText();
QAbstractSpinBox::focusOutEvent(event);
}
@ -530,3 +748,4 @@ void QuantitySpinBox::fixup(QString &input) const
#include "moc_QuantitySpinBox.cpp"
#include "moc_QuantitySpinBox_p.cpp"

View File

@ -26,6 +26,7 @@
#include <QAbstractSpinBox>
#include <Base/Quantity.h>
#include "ExpressionBinding.h"
#ifdef Q_MOC_RUN
Q_DECLARE_METATYPE(Base::Quantity)
@ -34,7 +35,7 @@ Q_DECLARE_METATYPE(Base::Quantity)
namespace Gui {
class QuantitySpinBoxPrivate;
class GuiExport QuantitySpinBox : public QAbstractSpinBox
class GuiExport QuantitySpinBox : public QAbstractSpinBox, public ExpressionBinding
{
Q_OBJECT
@ -95,6 +96,13 @@ public:
virtual QValidator::State validate(QString &input, int &pos) const;
virtual void fixup(QString &str) const;
bool event(QEvent *event);
void setExpression(boost::shared_ptr<App::Expression> expr);
void bind(const App::ObjectIdentifier &_path);
bool apply(const std::string &propName);
bool apply();
public Q_SLOTS:
/// Sets the field with a quantity
void setValue(const Base::Quantity& val);
@ -103,14 +111,19 @@ public Q_SLOTS:
protected Q_SLOTS:
void userInput(const QString & text);
void openFormulaDialog();
protected:
virtual StepEnabled stepEnabled() const;
virtual void showEvent(QShowEvent * event);
virtual void focusInEvent(QFocusEvent * event);
virtual void focusOutEvent(QFocusEvent * event);
virtual void keyPressEvent(QKeyEvent *event);
virtual void resizeEvent(QResizeEvent *event);
private:
QPixmap getIcon(const char *name, const QSize &size) const;
void updateText(const Base::Quantity&);
Q_SIGNALS:

View File

@ -0,0 +1,43 @@
/***************************************************************************
* Copyright (c) 2015 Eivind Kvedalen <eivind@kvedalen.name> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 51 Franklin Street, *
* Fifth Floor, Boston, MA 02110-1301, USA *
* *
***************************************************************************/
#ifndef QUANTITYSPINBOX_P_H
#define QUANTITYSPINBOX_P_H
#include <QLabel>
class ExpressionLabel : public QLabel
{
Q_OBJECT
public:
ExpressionLabel(QWidget * parent) : QLabel(parent) { }
protected:
void mouseReleaseEvent(QMouseEvent * event) {
if (rect().contains(event->pos()))
Q_EMIT clicked();
}
Q_SIGNALS:
void clicked();
};
#endif // QUANTITYSPINBOX_P_H