1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
3 * ParametersPlugin_EvalListener.cpp
5 * Created on: Apr 28, 2015
11 #include <ParametersPlugin_EvalListener.h>
12 #include <ParametersPlugin_Parameter.h>
13 #include <ParametersPlugin_PyInterp.h>
15 #include <Events_InfoMessage.h>
17 #include <ModelAPI_AttributeDouble.h>
18 #include <ModelAPI_AttributeInteger.h>
19 #include <ModelAPI_AttributeRefList.h>
20 #include <ModelAPI_AttributeString.h>
21 #include <ModelAPI_AttributeValidator.h>
22 #include <ModelAPI_Document.h>
23 #include <ModelAPI_Events.h>
24 #include <ModelAPI_Session.h>
25 #include <ModelAPI_Tools.h>
27 #include <GeomDataAPI_Point.h>
28 #include <GeomDataAPI_Point2D.h>
30 #include <QMessageBox>
36 //------------------------------------------------------------------------------
39 std::string toStdString(double theValue)
41 std::ostringstream sstream;
43 size_t aPos = sstream.str().find(".");
44 std::string aPnt = "";
45 if (aPos == std::string::npos)
47 return sstream.str() + aPnt;
50 std::set<std::string> toSet(const std::list<std::string>& theContainer)
52 return std::set<std::string>(theContainer.begin(), theContainer.end());
55 //------------------------------------------------------------------------------
57 ParametersPlugin_EvalListener::ParametersPlugin_EvalListener()
59 Events_Loop* aLoop = Events_Loop::loop();
61 Events_ID anEvents_IDs[] = {
62 ModelAPI_AttributeEvalMessage::eventId(),
63 ModelAPI_ObjectRenamedMessage::eventId(),
64 ModelAPI_ReplaceParameterMessage::eventId()
67 for (int i = 0; i < sizeof(anEvents_IDs)/sizeof(anEvents_IDs[0]); ++i)
68 aLoop->registerListener(this, anEvents_IDs[i], NULL, true);
70 myInterp = std::shared_ptr<ParametersPlugin_PyInterp>(new ParametersPlugin_PyInterp());
71 myInterp->initialize();
74 ParametersPlugin_EvalListener::~ParametersPlugin_EvalListener()
78 void ParametersPlugin_EvalListener::processEvent(
79 const std::shared_ptr<Events_Message>& theMessage)
81 if (!theMessage.get())
84 const Events_ID kEvaluationEvent = ModelAPI_AttributeEvalMessage::eventId();
85 const Events_ID kObjectRenamedEvent = ModelAPI_ObjectRenamedMessage::eventId();
86 const Events_ID kReplaceParameterEvent = ModelAPI_ReplaceParameterMessage::eventId();
88 if (theMessage->eventID() == kEvaluationEvent) {
89 processEvaluationEvent(theMessage);
90 } else if (theMessage->eventID() == kObjectRenamedEvent) {
91 processObjectRenamedEvent(theMessage);
92 } else if (theMessage->eventID() == kReplaceParameterEvent) {
93 processReplaceParameterEvent(theMessage);
95 Events_InfoMessage("ParametersPlugin_EvalListener",
96 "ParametersPlugin python interpreter, unhandled message caught: ")
97 .arg(theMessage->eventID().eventText()).send();
101 double ParametersPlugin_EvalListener::evaluate(FeaturePtr theParameter,
102 const std::string& theExpression, std::string& theError)
104 std::list<std::string> anExprParams = myInterp->compile(theExpression);
105 // find expression's params in the model
106 std::list<std::string> aContext;
107 std::list<std::string>::iterator it = anExprParams.begin();
108 for ( ; it != anExprParams.end(); it++) {
110 ResultParameterPtr aParamRes;
111 // If variable does not exist python interpreter will generate an error. It is OK.
112 // But due to the issue 1479 it should not check the history position of parameters relatively
113 // to feature that contains expression
114 if (!ModelAPI_Tools::findVariable(/*theParameter*/ FeaturePtr(),
115 *it, aValue, aParamRes, theParameter->document()))
118 aContext.push_back(*it + "=" + toStdString(aValue));
120 myInterp->extendLocalContext(aContext);
121 double result = myInterp->evaluate(theExpression, theError);
122 myInterp->clearLocalContext();
126 void ParametersPlugin_EvalListener::processEvaluationEvent(
127 const std::shared_ptr<Events_Message>& theMessage)
129 std::shared_ptr<ModelAPI_AttributeEvalMessage> aMessage =
130 std::dynamic_pointer_cast<ModelAPI_AttributeEvalMessage>(theMessage);
132 FeaturePtr aParamFeature =
133 std::dynamic_pointer_cast<ModelAPI_Feature>(aMessage->attribute()->owner());
134 if (aMessage->attribute()->attributeType() == ModelAPI_AttributeInteger::typeId()) {
135 AttributeIntegerPtr anAttribute =
136 std::dynamic_pointer_cast<ModelAPI_AttributeInteger>(aMessage->attribute());
138 int aValue = (int)evaluate(aParamFeature, anAttribute->text(), anError);
139 bool isValid = anError.empty();
141 anAttribute->setCalculatedValue(aValue);
142 anAttribute->setUsedParameters(isValid ?
143 toSet(myInterp->compile(anAttribute->text())) : std::set<std::string>());
144 anAttribute->setExpressionInvalid(!isValid);
145 anAttribute->setExpressionError(anAttribute->text().empty() ? "" : anError);
147 if (aMessage->attribute()->attributeType() == ModelAPI_AttributeDouble::typeId()) {
148 AttributeDoublePtr anAttribute =
149 std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(aMessage->attribute());
151 double aValue = evaluate(aParamFeature, anAttribute->text(), anError);
152 bool isValid = anError.empty();
154 anAttribute->setCalculatedValue(aValue);
155 anAttribute->setUsedParameters(isValid ?
156 toSet(myInterp->compile(anAttribute->text())) : std::set<std::string>());
157 anAttribute->setExpressionInvalid(!isValid);
158 anAttribute->setExpressionError(anAttribute->text().empty() ? "" : anError);
160 if (aMessage->attribute()->attributeType() == GeomDataAPI_Point::typeId()) {
161 AttributePointPtr anAttribute =
162 std::dynamic_pointer_cast<GeomDataAPI_Point>(aMessage->attribute());
163 std::string aText[] = {
164 anAttribute->textX(),
165 anAttribute->textY(),
168 double aCalculatedValue[] = {
173 for (int i = 0; i < 3; ++i) {
175 double aValue = evaluate(aParamFeature, aText[i], anError);
176 bool isValid = anError.empty();
177 if (isValid) aCalculatedValue[i] = aValue;
178 anAttribute->setUsedParameters(i,
179 isValid ? toSet(myInterp->compile(aText[i])) : std::set<std::string>());
180 anAttribute->setExpressionInvalid(i, !isValid);
181 anAttribute->setExpressionError(i, aText[i].empty() ? "" : anError);
183 anAttribute->setCalculatedValue(aCalculatedValue[0],
185 aCalculatedValue[2]);
187 if (aMessage->attribute()->attributeType() == GeomDataAPI_Point2D::typeId()) {
188 AttributePoint2DPtr anAttribute =
189 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aMessage->attribute());
190 std::string aText[] = {
191 anAttribute->textX(),
194 double aCalculatedValue[] = {
198 for (int i = 0; i < 2; ++i) {
200 double aValue = evaluate(aParamFeature, aText[i], anError);
201 bool isValid = anError.empty();
202 if (isValid) aCalculatedValue[i] = aValue;
203 anAttribute->setUsedParameters(i,
204 isValid ? toSet(myInterp->compile(aText[i])) : std::set<std::string>());
205 anAttribute->setExpressionInvalid(i, !isValid);
206 anAttribute->setExpressionError(i, aText[i].empty() ? "" : anError);
208 anAttribute->setCalculatedValue(aCalculatedValue[0],
209 aCalculatedValue[1]);
213 std::string ParametersPlugin_EvalListener::renameInPythonExpression(
214 const std::string& theExpression,
215 const std::string& theOldName,
216 const std::string& theNewName)
218 std::string anExpressionString = theExpression;
220 std::list<std::pair<int, int> > aPositions =
221 myInterp->positions(anExpressionString, theOldName);
223 if (aPositions.empty())
224 return anExpressionString;
226 std::map<int, std::list<int> > aLines;
227 std::list<std::pair<int, int> >::const_iterator it = aPositions.begin();
228 for (; it != aPositions.end(); ++it)
229 aLines[it->first].push_back(it->second);
231 // Start renaming from the end to keep indexes if theNewName is longer then theOldName
232 std::map<int, std::list<int> >::const_reverse_iterator ritLine = aLines.rbegin();
233 for (; ritLine != aLines.rend(); ++ritLine) {
234 // Calculate the start of the line (find the aLineNo occurrence of "\n" )
235 int aLineNo = ritLine->first - 1;
236 size_t aLineStart = 0;
237 for (int i = 0; i < aLineNo; ++i)
238 aLineStart = anExpressionString.find("\n", aLineStart) + 1;
240 const std::list<int>& aColOffsets = ritLine->second;
241 std::list<int>::const_reverse_iterator ritOffset = aColOffsets.rbegin();
242 for (; ritOffset != aColOffsets.rend(); ++ritOffset) {
243 int anOffset = *ritOffset;
244 anExpressionString.replace(aLineStart + anOffset, theOldName.size(), theNewName);
248 return anExpressionString;
251 void ParametersPlugin_EvalListener::renameInParameter(
252 std::shared_ptr<ParametersPlugin_Parameter> theParameter,
253 const std::string& theOldName,
254 const std::string& theNewName)
256 std::shared_ptr<ModelAPI_AttributeString> anExpressionAttribute =
257 theParameter->string(ParametersPlugin_Parameter::EXPRESSION_ID());
259 std::string anExpressionString = anExpressionAttribute->value();
260 anExpressionString = renameInPythonExpression(anExpressionString,
263 // Issue #588. No need for reevaluating expression.
264 // Moreover, current history may not contain necessary parameters.
265 anExpressionAttribute->owner()->data()->blockSendAttributeUpdated(true);
266 anExpressionAttribute->setValue(anExpressionString);
267 anExpressionAttribute->owner()->data()->blockSendAttributeUpdated(false);
270 void ParametersPlugin_EvalListener::renameInAttribute(
271 std::shared_ptr<ModelAPI_Attribute> theAttribute,
272 const std::string& theOldName,
273 const std::string& theNewName)
275 if (theAttribute->attributeType() == ModelAPI_AttributeInteger::typeId()) {
276 AttributeIntegerPtr anAttribute =
277 std::dynamic_pointer_cast<ModelAPI_AttributeInteger>(theAttribute);
278 std::string anExpressionString = anAttribute->text();
279 anExpressionString = renameInPythonExpression(anExpressionString,
280 theOldName, theNewName);
281 anAttribute->setText(anExpressionString);
283 if (theAttribute->attributeType() == ModelAPI_AttributeDouble::typeId()) {
284 AttributeDoublePtr anAttribute =
285 std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theAttribute);
286 std::string anExpressionString = anAttribute->text();
287 anExpressionString = renameInPythonExpression(anExpressionString,
288 theOldName, theNewName);
289 anAttribute->setText(anExpressionString);
291 if (theAttribute->attributeType() == GeomDataAPI_Point::typeId()) {
292 AttributePointPtr anAttribute =
293 std::dynamic_pointer_cast<GeomDataAPI_Point>(theAttribute);
294 std::string anExpressionString[3] = {
295 anAttribute->textX(),
296 anAttribute->textY(),
299 for (int i = 0; i < 3; ++i)
300 anExpressionString[i] = renameInPythonExpression(anExpressionString[i],
301 theOldName, theNewName);
302 anAttribute->setText(anExpressionString[0],
303 anExpressionString[1],
304 anExpressionString[2]);
306 if (theAttribute->attributeType() == GeomDataAPI_Point2D::typeId()) {
307 AttributePoint2DPtr anAttribute =
308 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttribute);
309 std::string anExpressionString[2] = {
310 anAttribute->textX(),
313 for (int i = 0; i < 2; ++i)
314 anExpressionString[i] = renameInPythonExpression(anExpressionString[i],
315 theOldName, theNewName);
316 anAttribute->setText(anExpressionString[0],
317 anExpressionString[1]);
321 void ParametersPlugin_EvalListener::renameInDependents(
322 std::shared_ptr<ModelAPI_ResultParameter> theResultParameter,
323 const std::string& theOldName,
324 const std::string& theNewName)
326 std::set<std::shared_ptr<ModelAPI_Attribute> > anAttributes =
327 theResultParameter->data()->refsToMe();
328 std::set<std::shared_ptr<ModelAPI_Attribute> >::const_iterator anAttributeIt =
329 anAttributes.cbegin();
330 for (; anAttributeIt != anAttributes.cend(); ++anAttributeIt) {
331 const AttributePtr& anAttribute = *anAttributeIt;
332 if (anAttribute->attributeType() == ModelAPI_AttributeRefList::typeId()) {
333 std::shared_ptr<ParametersPlugin_Parameter> aParameter =
334 std::dynamic_pointer_cast<ParametersPlugin_Parameter>(
335 anAttribute->owner());
336 if (aParameter.get())
338 renameInParameter(aParameter, theOldName, theNewName);
341 renameInAttribute(anAttribute, theOldName, theNewName);
345 bool isValidAttribute(const AttributePtr& theAttribute)
347 std::string aValidator;
348 Events_InfoMessage anError;
349 return ModelAPI_Session::get()->validators()->validate(theAttribute, aValidator, anError);
352 void setParameterName(ResultParameterPtr theResultParameter, const std::string& theName)
354 theResultParameter->data()->blockSendAttributeUpdated(true);
355 theResultParameter->data()->setName(theName);
356 theResultParameter->data()->blockSendAttributeUpdated(false, false);
358 std::shared_ptr<ParametersPlugin_Parameter> aParameter =
359 std::dynamic_pointer_cast<ParametersPlugin_Parameter>(
360 ModelAPI_Feature::feature(theResultParameter));
362 aParameter->data()->blockSendAttributeUpdated(true);
363 aParameter->data()->setName(theName);
364 aParameter->string(ParametersPlugin_Parameter::VARIABLE_ID())->setValue(theName);
365 aParameter->data()->blockSendAttributeUpdated(false);
368 void ParametersPlugin_EvalListener::processObjectRenamedEvent(
369 const std::shared_ptr<Events_Message>& theMessage)
371 std::shared_ptr<ModelAPI_ObjectRenamedMessage> aMessage =
372 std::dynamic_pointer_cast<ModelAPI_ObjectRenamedMessage>(theMessage);
374 // Empty new name is not available too but it will be rejected by
375 // name validator in isValidAttribute.
376 if (!aMessage.get() || aMessage->oldName().empty())
379 // check if the renamed object is a result parameter
380 ResultParameterPtr aResultParameter =
381 std::dynamic_pointer_cast<ModelAPI_ResultParameter>(aMessage->object());
382 if (!aResultParameter.get())
385 // get parameter feature for the result
386 std::shared_ptr<ParametersPlugin_Parameter> aParameter =
387 std::dynamic_pointer_cast<ParametersPlugin_Parameter>(
388 ModelAPI_Feature::feature(aResultParameter));
389 if (!aParameter.get())
392 std::string aNotActivatedNames;
393 if (!ModelAPI_Tools::allDocumentsActivated(aNotActivatedNames)) {
394 QMessageBox::StandardButton aRes = QMessageBox::warning(0, QObject::tr("Warning"),
395 QObject::tr("Selected objects can be used in Part documents which are not loaded: \
396 %1. Whould you like to continue?").arg(aNotActivatedNames.c_str()),
397 QMessageBox::No | QMessageBox::Yes, QMessageBox::No);
398 if (aRes != QMessageBox::Yes) {
399 setParameterName(aResultParameter, aMessage->oldName());
404 // try to update the parameter feature according the new name
405 setParameterName(aResultParameter, aMessage->newName());
406 // TODO(spo): replace with
407 // ModelAPI_Session::get()->validators()->validate(aParameter,
408 // ParametersPlugin_Parameter::VARIABLE_ID())
409 // when ModelAPI_ValidatorsFactory::validate(const std::shared_ptr<ModelAPI_Feature>& theFeature,
410 // const std::string& theAttribute) const
412 if (!isValidAttribute(aParameter->string(ParametersPlugin_Parameter::VARIABLE_ID()))) {
413 setParameterName(aResultParameter, aMessage->oldName());
417 renameInDependents(aResultParameter, aMessage->oldName(), aMessage->newName());
420 void ParametersPlugin_EvalListener::processReplaceParameterEvent(
421 const std::shared_ptr<Events_Message>& theMessage)
423 std::shared_ptr<ModelAPI_ReplaceParameterMessage> aMessage =
424 std::dynamic_pointer_cast<ModelAPI_ReplaceParameterMessage>(theMessage);
426 // get parameter feature for the object
427 std::shared_ptr<ParametersPlugin_Parameter> aParameter =
428 std::dynamic_pointer_cast<ParametersPlugin_Parameter>(
429 ModelAPI_Feature::feature(aMessage->object()));
430 if (!aParameter.get())
433 ResultParameterPtr aResultParameter =
434 std::dynamic_pointer_cast<ModelAPI_ResultParameter>(
435 aParameter->firstResult());
436 if (!aResultParameter.get())
439 double aRealValue = aResultParameter->data()->real(ModelAPI_ResultParameter::VALUE())->value();
440 std::string aValue = toStdString(aRealValue);
442 renameInDependents(aResultParameter, aResultParameter->data()->name(), aValue);