FILE(TO_CMAKE_PATH "$ENV{PYTHON_LIB_DIR}/${PYTHON_LIBRARY_DLL}" PYTHON_LIBRARY)
FILE(TO_CMAKE_PATH $ENV{PYTHON_INC_DIR} PYTHON_INCLUDE_DIR)
-# TOOD: Clean a mess with python interpreter
-# FIND_PACKAGE(PythonInterp)
FIND_PACKAGE(PythonLibs)
INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_DIRS})
INCLUDE(Common)
INCLUDE(FindQt5)
+INCLUDE(FindPython)
ADD_SUBDIRECTORY (src/Event)
ADD_SUBDIRECTORY (src/Model)
ADD_SUBDIRECTORY (src/Config)
ADD_SUBDIRECTORY (src/PartSet)
ADD_SUBDIRECTORY (src/PartSetPlugin)
+ADD_SUBDIRECTORY (src/PyEvent)
+ADD_SUBDIRECTORY (src/PyInterp)
+ADD_SUBDIRECTORY (src/PyConsole)
ADD_SUBDIRECTORY (src/XGUI)
/*\r
* Class to pass a feature entry extracted from xml file.\r
* Example of the feature entry:\r
- * <feature id="new_part" text="Part" tooltip="Creates a new part" icon=":pictures/part_ico.png"/>\r
+ * <feature id="Part" text="New part" tooltip="Creates a new part" icon=":pictures/part_ico.png"/>\r
*/\r
class CONFIG_EXPORT Config_FeatureMessage: public Event_Message\r
{\r
<plugin>
<workbench id="Part">
<group id="Operations">
- <feature id="new_part" text="Part" tooltip="Creates a new part" icon=":pictures/part_ico.png"/>
+ <feature id="Part" text="New part" tooltip="Creates a new part" icon=":pictures/part_ico.png"/>
<feature id="duplicate" text="Duplicate" tooltip="Duplicate selected object" icon=":icons/duplicate.png"/>
<feature id="remove" text="Remove" tooltip="Remove selected object" icon=":icons/remove.png"/>
</group>
</workbench>
<workbench id="Construction">
<group id="Basic">
- <feature id="new_point" text="Point" tooltip="Create a new point" icon=":icons/point.png">
+ <feature id="Point" text="Point" tooltip="Create a new point" icon=":icons/point.png">
<value id="x" type="double" label="X:" min="0" max="" step="0.1" default="0"/>
<value id="y" type="double" label="Y:" min="0" max="" step="0.1" default="1"/>
<value id="z" type="double" label="Z:" min="0" max="10" step="0.1" default="2"/>
</feature>
- <feature id="new_axis" text="Axis" tooltip="Create a new axis" icon=":icons/axis.png" keysequence=""/>
- <feature id="new_plane" text="Plane" tooltip="Create a new plane" icon=":icons/plane.png" keysequence=""/>
+ <feature id="Axis" text="Axis" tooltip="Create a new axis" icon=":icons/axis.png" keysequence=""/>
+ <feature id="Plane" text="Plane" tooltip="Create a new plane" icon=":icons/plane.png" keysequence=""/>
</group>
</workbench>
-</plugin>
\ No newline at end of file
+</plugin>
INCLUDE(Common)
INCLUDE(FindCAS)
-INCLUDE(FindBoost)
SET(PROJECT_HEADERS
Model.h
+ Model_Application.h
Model_Document.h
Model_PluginManager.h
- Model_Feature.h
+ Model_Object.h
+ Model_Iterator.h
+ Model_AttributeDouble.h
+ Model_AttributeDocRef.h
)
SET(PROJECT_SOURCES
+ Model_Application.cxx
Model_Document.cxx
Model_PluginManager.cxx
- Model_Feature.cxx
+ Model_Object.cxx
+ Model_Iterator.cxx
+ Model_AttributeDouble.cxx
+ Model_AttributeDocRef.cxx
)
-ADD_DEFINITIONS(-DMODEL_EXPORTS ${CAS_DEFINITIONS} ${BOOST_DEFINITIONS})
+ADD_DEFINITIONS(-DMODEL_EXPORTS ${CAS_DEFINITIONS})
ADD_LIBRARY(Model SHARED ${PROJECT_SOURCES} ${PROJECT_HEADERS})
TARGET_LINK_LIBRARIES(Model ${PROJECT_LIBRARIES} ${CAS_OCAF} ModelAPI Event Config)
// Created: Fri Sep 2 2011
// Author: Mikhail PONIKAROV
-#include <Model_Application.hxx>
-#include <Model_Document.hxx>
+#include <Model_Application.h>
+#include <Model_Document.h>
IMPLEMENT_STANDARD_HANDLE(Model_Application, TDocStd_Application)
IMPLEMENT_STANDARD_RTTIEXT(Model_Application, TDocStd_Application)
//function : getApplication
//purpose :
//=======================================================================
-Handle_Model_Application Model_Application::GetApplication()
+Handle(Model_Application) Model_Application::getApplication()
{
return TheApplication;
}
//function : getDocument
//purpose :
//=======================================================================
-ModelAPI_Document* Model_Application::GetMainDocument()
+std::shared_ptr<Model_Document> Model_Application::getDocument(std::string theDocID)
{
+ if (myDocs.find(theDocID) != myDocs.end())
+ return myDocs[theDocID];
- if (myMainDoc.IsNull()) {
- myMainDoc = new Model_Document("BinOcaf");
- }
- return *myMainDoc;
+ std::shared_ptr<Model_Document> aNew(new Model_Document(theDocID));
+ myDocs[theDocID] = aNew;
+ return aNew;
}
//=======================================================================
#ifndef Model_Application_HeaderFile
#define Model_Application_HeaderFile
-#include <Model_Document.hxx>
-#include <ModelAPI_Application.hxx>
+#include <Model_Document.h>
#include <TDocStd_Application.hxx>
+#include <map>
// Define handle class
DEFINE_STANDARD_HANDLE(Model_Application, TDocStd_Application)
* Application supports the formats and document management. It is uses OCAF-lke
* architecture and just implements specific features of the module.
*/
-class Model_Application: public TDocStd_Application, public ModelAPI_Application
+class Model_Application: public TDocStd_Application
{
public:
// useful methods inside of the module
// CASCADE RTTI
- DEFINE_STANDARD_RTTI(Model_Application)
- ;
+ DEFINE_STANDARD_RTTI(Model_Application);
//! Retuns the application: one per process
- MODEL_EXPORT static Handle_Model_Application GetApplication();
- //! Returns the main document (on first call creates it)
- MODEL_EXPORT ModelAPI_Document* GetMainDocument();
+ MODEL_EXPORT static Handle_Model_Application getApplication();
+ //! Returns the main document (on first call creates it) by the string identifier
+ MODEL_EXPORT std::shared_ptr<Model_Document> getDocument(std::string theDocID);
public:
// Redefined OCAF methods
Model_Application();
private:
-
- Handle_Model_Document myMainDoc; ///< main document of an application
+ /// Map from string identifiers to created documents of an application
+ std::map<std::string, std::shared_ptr<Model_Document> > myDocs;
};
#endif
--- /dev/null
+// File: ModelAPI_AttributeDocRef.cxx
+// Created: 2 Apr 2014
+// Author: Mikhail PONIKAROV
+
+#include "Model_AttributeDocRef.h"
+#include "Model_Application.h"
+
+using namespace std;
+
+void Model_AttributeDocRef::setValue(std::shared_ptr<ModelAPI_Document> theDoc)
+{
+ myComment->Set(TCollection_ExtendedString(theDoc->id().c_str()));
+}
+
+std::shared_ptr<ModelAPI_Document> Model_AttributeDocRef::value()
+{
+ if (myComment->Get().Length())
+ return Model_Application::getApplication()->getDocument(
+ TCollection_AsciiString(myComment->Get()).ToCString());
+ // not initialized
+ return std::shared_ptr<ModelAPI_Document>();
+}
+
+Model_AttributeDocRef::Model_AttributeDocRef(TDF_Label& theLabel)
+{
+ // check the attribute could be already presented in this doc (after load document)
+ if (!theLabel.FindAttribute(TDataStd_Comment::GetID(), myComment)) {
+ // create attribute: not initialized by value yet, just empty string
+ myComment = TDataStd_Comment::Set(theLabel, "");
+ }
+}
--- /dev/null
+// File: Model_AttributeDocRef.h
+// Created: 2 Apr 2014
+// Author: Mikhail PONIKAROV
+
+#ifndef Model_AttributeDocRef_HeaderFile
+#define Model_AttributeDocRef_HeaderFile
+
+#include "Model.h"
+#include "ModelAPI_AttributeDocRef.h"
+#include <TDataStd_Comment.hxx>
+#include <TDF_Label.hxx>
+
+/**\class Model_AttributeDocRef
+ * \ingroup DataModel
+ * \brief Attribute that contains reference to another document.
+ */
+
+class MODEL_EXPORT Model_AttributeDocRef : public ModelAPI_AttributeDocRef
+{
+ Handle_TDataStd_Comment myComment; ///< reference to document is identified as string-id
+public:
+ /// Defines the document referenced from this attribute
+ virtual void setValue(std::shared_ptr<ModelAPI_Document> theDoc);
+
+ /// Returns document referenced from this attribute
+ virtual std::shared_ptr<ModelAPI_Document> value();
+
+protected:
+ /// Initializes attibutes
+ Model_AttributeDocRef(TDF_Label& theLabel);
+
+ friend class Model_Object;
+};
+
+#endif
--- /dev/null
+// File: ModelAPI_AttributeDouble.cxx
+// Created: 2 Apr 2014
+// Author: Mikhail PONIKAROV
+
+#include "Model_AttributeDouble.h"
+
+using namespace std;
+
+void Model_AttributeDouble::setValue(const double theValue)
+{
+ myReal->Set(theValue);
+}
+
+double Model_AttributeDouble::value()
+{
+ return myReal->Get();
+}
+
+Model_AttributeDouble::Model_AttributeDouble(TDF_Label& theLabel)
+{
+ // check the attribute could be already presented in this doc (after load document)
+ if (!theLabel.FindAttribute(TDataStd_Real::GetID(), myReal)) {
+ // create attribute: not initialized by value yet, just zero
+ myReal = TDataStd_Real::Set(theLabel, 0.);
+ }
+}
+
--- /dev/null
+// File: Model_AttributeDouble.h
+// Created: 2 Apr 2014
+// Author: Mikhail PONIKAROV
+
+#ifndef Model_AttributeDouble_HeaderFile
+#define Model_AttributeDouble_HeaderFile
+
+#include "Model.h"
+#include "ModelAPI_AttributeDouble.h"
+#include <TDataStd_Real.hxx>
+#include <TDF_Label.hxx>
+
+/**\class Model_AttributeDouble
+ * \ingroup DataModel
+ * \brief Attribute that contains real value with double precision.
+ */
+
+class MODEL_EXPORT Model_AttributeDouble : public ModelAPI_AttributeDouble
+{
+ Handle_TDataStd_Real myReal; ///< double is Real attribute
+public:
+ /// Defines the double value
+ virtual void setValue(const double theValue);
+
+ /// Returns the double value
+ virtual double value();
+
+protected:
+ /// Initializes attibutes
+ Model_AttributeDouble(TDF_Label& theLabel);
+
+ friend class Model_Object;
+};
+
+#endif
// File: Model_Document.cxx
-// Created: 28 Dec 2011
+// Created: 28 Feb 2014
// Author: Mikhail PONIKAROV
-// Copyright: CEA 2011
#include <Model_Document.h>
+#include <ModelAPI_Feature.h>
+#include <Model_Object.h>
+#include <Model_Application.h>
+#include <Model_PluginManager.h>
+#include <Model_Iterator.h>
#include <TDataStd_Integer.hxx>
+#include <TDataStd_Comment.hxx>
-IMPLEMENT_STANDARD_HANDLE(Model_Document, MMgt_TShared)
-IMPLEMENT_STANDARD_RTTIEXT(Model_Document, MMgt_TShared)
-
-static const int UNDO_LIMIT = 10; // number of possible undo operations in the module
+static const int UNDO_LIMIT = 10; // number of possible undo operations
static const int TAG_GENERAL = 1; // general properties tag
static const int TAG_OBJECTS = 2; // tag of the objects sub-tree (Root for Model_ObjectsMgr)
static const int TAG_HISTORY = 3; // tag of the history sub-tree (Root for Model_History)
-static const int TAG_ISOTOPES = 4; // tag of the isotopes sub-tree (Root for MaterialMC_Isotope)
using namespace std;
-bool Model_Document::Load(const char* theFileName)
+bool Model_Document::load(const char* theFileName)
{
bool myIsError = Standard_False;
/*
return !myIsError;
}
-bool Model_Document::Save(const char* theFileName)
+bool Model_Document::save(const char* theFileName)
{
bool myIsError = true;
/*
return !myIsError;
}
-void Model_Document::Close()
+void Model_Document::close()
{
- TDocStd_Document::Close();
+ myDoc->Close();
}
-void Model_Document::StartOperation()
+void Model_Document::startOperation()
{
- TDocStd_Document::NewCommand();
+ myDoc->NewCommand();
}
-void Model_Document::FinishOperation()
+void Model_Document::finishOperation()
{
- TDocStd_Document::CommitCommand();
+ myDoc->CommitCommand();
myTransactionsAfterSave++;
}
-void Model_Document::AbortOperation()
+void Model_Document::abortOperation()
{
- TDocStd_Document::AbortCommand();
+ myDoc->AbortCommand();
}
-bool Model_Document::IsOperation()
+bool Model_Document::isOperation()
{
- return TDocStd_Document::HasOpenCommand() == Standard_True ;
+ return myDoc->HasOpenCommand() == Standard_True ;
}
-bool Model_Document::IsModified()
+bool Model_Document::isModified()
{
return myTransactionsAfterSave != 0;
}
-bool Model_Document::CanUndo()
+bool Model_Document::canUndo()
{
- return TDocStd_Document::GetAvailableUndos() > 0;
+ return myDoc->GetAvailableUndos() > 0;
}
-void Model_Document::Undo()
+void Model_Document::undo()
{
- TDocStd_Document::Undo();
+ myDoc->Undo();
myTransactionsAfterSave--;
}
-bool Model_Document::CanRedo()
+bool Model_Document::canRedo()
{
- return TDocStd_Document::GetAvailableRedos() > 0;
+ return myDoc->GetAvailableRedos() > 0;
}
-void Model_Document::Redo()
+void Model_Document::redo()
{
- TDocStd_Document::Redo();
+ myDoc->Redo();
myTransactionsAfterSave++;
}
-Model_Document::Model_Document(const TCollection_ExtendedString& theStorageFormat)
- : TDocStd_Document(theStorageFormat)
+void Model_Document::addFeature(
+ std::shared_ptr<ModelAPI_Feature> theFeature, const std::string theGroupID)
+{
+ TDF_Label aGroupLab = groupLabel(theGroupID);
+ TDF_Label anObjLab = aGroupLab.NewChild();
+ std::shared_ptr<Model_Object> aData(new Model_Object);
+ aData->setLabel(anObjLab);
+ theFeature->setData(aData);
+ setUniqueName(theFeature, theGroupID);
+ theFeature->initAttributes();
+ TDataStd_Comment::Set(anObjLab, theFeature->getKind().c_str());
+}
+
+std::shared_ptr<ModelAPI_Feature> Model_Document::feature(TDF_Label& theLabel)
+{
+ Handle(TDataStd_Comment) aFeatureID;
+ if (theLabel.FindAttribute(TDataStd_Comment::GetID(), aFeatureID)) {
+ string anID(TCollection_AsciiString(aFeatureID->Get()).ToCString());
+ std::shared_ptr<ModelAPI_Feature> aResult = Model_PluginManager::get()->createFeature(anID);
+ std::shared_ptr<Model_Object> aData(new Model_Object);
+ aData->setLabel(theLabel);
+ aResult->setData(aData);
+ aResult->initAttributes();
+ return aResult;
+ }
+ return std::shared_ptr<ModelAPI_Feature>(); // not found
+}
+
+shared_ptr<ModelAPI_Document> Model_Document::subDocument(string theDocID)
+{
+ return Model_Application::getApplication()->getDocument(theDocID);
+}
+
+shared_ptr<ModelAPI_Iterator> Model_Document::featuresIterator(const string theGroup)
+{
+ shared_ptr<Model_Document> aThis(Model_Application::getApplication()->getDocument(myID));
+ return shared_ptr<ModelAPI_Iterator>(new Model_Iterator(aThis, groupLabel(theGroup)));
+}
+
+Model_Document::Model_Document(const std::string theID)
+ : myID(theID), myDoc(new TDocStd_Document("BinOcaf")) // binary OCAF format
{
- SetUndoLimit(UNDO_LIMIT);
+ myDoc->SetUndoLimit(UNDO_LIMIT);
myTransactionsAfterSave = 0;
}
-Model_Document::~Model_Document()
+TDF_Label Model_Document::groupLabel(const string theGroup)
+{
+ if (myGroups.find(theGroup) == myGroups.end()) {
+ myGroups[theGroup] = myDoc->Main().FindChild(TAG_OBJECTS).NewChild();
+ }
+ return myGroups[theGroup];
+}
+
+void Model_Document::setUniqueName(
+ shared_ptr<ModelAPI_Feature> theFeature, const string theGroupID)
{
+ // first count all objects of such kind to start with index = count + 1
+ int aNumObjects = 0;
+ shared_ptr<ModelAPI_Iterator> anIter = featuresIterator(theGroupID);
+ for(; anIter->more(); anIter->next()) {
+ if (anIter->currentKind() == theFeature->getKind())
+ aNumObjects++;
+ }
+ // generate candidate name
+ stringstream aNameStream;
+ aNameStream<<theFeature->getKind()<<"_"<<aNumObjects + 1;
+ string aName = aNameStream.str();
+ // check this is unique, if not, increase index by 1
+ for(anIter = featuresIterator(theGroupID); anIter->more();) {
+ if (anIter->currentName() == aName) {
+ aNumObjects++;
+ stringstream aNameStream;
+ aNameStream<<theFeature->getKind()<<"_"<<aNumObjects + 1;
+ // reinitialize iterator to make sure a new name is unique
+ anIter = featuresIterator(theGroupID);
+ } else anIter->next();
+ }
+
+ theFeature->data()->setName(aName);
}
-// File: Model_Document.hxx
-// Created: 28 Dec 2011
+// File: Model_Document.h
+// Created: 28 Feb 2014
// Author: Mikhail PONIKAROV
-// Copyright: CEA 2011
#ifndef Model_Document_HeaderFile
#define Model_Document_HeaderFile
#include <Model.h>
#include <ModelAPI_Document.h>
#include <TDocStd_Document.hxx>
+#include <map>
class Handle_Model_Document;
* to provide access to all stored data.
*/
-class Model_Document: public TDocStd_Document, public ModelAPI_Document
+class Model_Document: public ModelAPI_Document
{
public:
- DEFINE_STANDARD_RTTI(Model_Document)
- ;
-
- //! Creates new document by the format string of a storage
- Model_Document(const TCollection_ExtendedString& theStorageFormat);
- //! Deletes all high-level data, managed this document
- ~Model_Document();
-
//! Loads the OCAF document from the file.
//! \param theFileName full name of the file to load
//! \param theStudyID identifier of the SALOME study to associate with loaded file
//! \returns true if file was loaded successfully
- MODEL_EXPORT bool Load(const char* theFileName);
+ MODEL_EXPORT bool load(const char* theFileName);
//! Saves the OCAF document to the file.
//! \param theFileName full name of the file to store
//! \returns true if file was stored successfully
- MODEL_EXPORT bool Save(const char* theFileName);
+ MODEL_EXPORT bool save(const char* theFileName);
//! Removes document data
- MODEL_EXPORT void Close();
+ MODEL_EXPORT void close();
//! Starts a new operation (opens a tansaction)
- MODEL_EXPORT void StartOperation();
+ MODEL_EXPORT void startOperation();
//! Finishes the previously started operation (closes the transaction)
- MODEL_EXPORT void FinishOperation();
+ MODEL_EXPORT void finishOperation();
//! Aborts the operation
- MODEL_EXPORT void AbortOperation();
+ MODEL_EXPORT void abortOperation();
//! Returns true if operation has been started, but not yet finished or aborted
- MODEL_EXPORT bool IsOperation();
+ MODEL_EXPORT bool isOperation();
//! Returns true if document was modified (since creation/opening)
- MODEL_EXPORT bool IsModified();
+ MODEL_EXPORT bool isModified();
//! Returns True if there are available Undos
- MODEL_EXPORT bool CanUndo();
+ MODEL_EXPORT bool canUndo();
//! Undoes last operation
- MODEL_EXPORT void Undo();
+ MODEL_EXPORT void undo();
//! Returns True if there are available Redos
- MODEL_EXPORT bool CanRedo();
+ MODEL_EXPORT bool canRedo();
//! Redoes last operation
- MODEL_EXPORT void Redo();
+ MODEL_EXPORT void redo();
+
+ //! Adds to the document the new feature of the given group id
+ //! \param theFeature a feature object that will be connected to the document in this method
+ //! \param theGroupID identifier of the groups of objects (must be greater than zero)
+ MODEL_EXPORT virtual void addFeature(std::shared_ptr<ModelAPI_Feature> theFeature,
+ const std::string theGroupID);
+
+ //! Returns the existing feature by the label
+ //! \param theLabel base label of the feature
+ MODEL_EXPORT virtual std::shared_ptr<ModelAPI_Feature> feature(TDF_Label& theLabel);
+
+ //! Adds a new sub-document by the identifier, or returns existing one if it is already exist
+ MODEL_EXPORT virtual std::shared_ptr<ModelAPI_Document> subDocument(std::string theDocID);
+
+ //! Creates an iterator of the features by the specific groups
+ MODEL_EXPORT virtual std::shared_ptr<ModelAPI_Iterator> featuresIterator(
+ const std::string theGroup);
+
+ MODEL_EXPORT virtual const std::string& id() const {return myID;}
protected:
+ //! Returns (creates if needed) the group label
+ TDF_Label groupLabel(const std::string theGroup);
+
+ //! Initializes feature with a unique name in this group (unique name is generated as
+ //! feature type + "_" + index
+ void setUniqueName(
+ std::shared_ptr<ModelAPI_Feature> theFeature, const std::string theGroupID);
+
+ //! Creates new document with binary file format
+ Model_Document(const std::string theID);
+
+ friend class Model_Application;
+
private:
+ std::string myID; ///< identifier of the document in the application
+ Handle_TDocStd_Document myDoc; ///< OCAF document
int myTransactionsAfterSave; ///< number of transactions after the last "save" call, used for "IsModified" method
+ std::map<std::string, TDF_Label> myGroups; ///< root labels of the features groups identified by names
};
-// Define handle class
-DEFINE_STANDARD_HANDLE(Model_Document, TDocStd_Document)
-
#endif
+++ /dev/null
-// File: Model_Feature.hxx
-// Created: 21 Mar 2014
-// Author: Mikhail PONIKAROV
-
-#include <Model_Feature.h>
-
-using namespace std;
-
-Model_Feature::Model_Feature()
-{
-}
-
-string Model_Feature::GetKind()
-{
- return "Point";
-}
+++ /dev/null
-// File: Model_Feature.hxx
-// Created: 21 Mar 2014
-// Author: Mikhail PONIKAROV
-
-#ifndef Model_Feature_HeaderFile
-#define Model_Feature_HeaderFile
-
-#include "Model.h"
-#include <ModelAPI_Feature.h>
-
-/**\class Model_Feature
- * \ingroup DataModel
- * \brief General object of the application that allows
- * to get/set attributes from the document and compute result of an operation.
- */
-
-class Model_Feature: public ModelAPI_Feature
-{
- Model_Feature();
-
- friend class Model_PluginManager;
-public:
- /// Returns the kind of a feature (like "Point")
- virtual std::string GetKind();
-};
-
-#endif
--- /dev/null
+// File: Model_Iterator.hxx
+// Created: 1 Apr 2014
+// Author: Mikhail PONIKAROV
+
+#include "Model_Iterator.h"
+#include "Model_Document.h"
+#include <TDataStd_Comment.hxx>
+#include <TDataStd_Name.hxx>
+
+using namespace std;
+
+void Model_Iterator::next()
+{
+ return myIter.Next();
+}
+
+bool Model_Iterator::more()
+{
+ return myIter.More();
+}
+
+shared_ptr<ModelAPI_Feature> Model_Iterator::current()
+{
+ TDF_Label aLab = myIter.Value()->Label();
+ return myDoc->feature(aLab);
+}
+
+string Model_Iterator::currentKind()
+{
+ return string(TCollection_AsciiString(
+ Handle(TDataStd_Comment)::DownCast(myIter.Value())->Get()).ToCString());
+}
+
+string Model_Iterator::currentName()
+{
+ TDF_Label aLab = myIter.Value()->Label();
+ Handle(TDataStd_Name) aName;
+ if (aLab.FindAttribute(TDataStd_Name::GetID(), aName))
+ return string(TCollection_AsciiString(aName->Get()).ToCString());
+ return ""; // name is not found
+}
+
+int Model_Iterator::numIterationsLeft()
+{
+ int aResult = 0;
+ TDF_ChildIDIterator aTempIter(myIter);
+ for(; aTempIter.More(); aTempIter.Next())
+ aResult++;
+ return aResult;
+}
+
+Model_Iterator::Model_Iterator(std::shared_ptr<Model_Document> theDoc, TDF_Label theLab)
+ : myDoc(theDoc), myIter(theLab, TDataStd_Comment::GetID(), Standard_False)
+{}
--- /dev/null
+// File: Model_Iterator.h
+// Created: 1 Apr 2014
+// Author: Mikhail PONIKAROV
+
+#ifndef Model_Iterator_HeaderFile
+#define Model_Iterator_HeaderFile
+
+#include "Model.h"
+#include "ModelAPI_Iterator.h"
+#include <TDF_Label.hxx>
+#include <TDF_ChildIDIterator.hxx>
+
+class Model_Document;
+
+/**\class Model_Iterator
+ * \ingroup DataModel
+ * \brief Allows to iterate features of the document. Is created by the document
+ * (see method featuresIterator).
+ */
+
+class MODEL_EXPORT Model_Iterator : public ModelAPI_Iterator
+{
+ std::shared_ptr<Model_Document> myDoc; ///< the document of iterated objects
+ TDF_ChildIDIterator myIter; ///< iterator of the features-labels
+public:
+ /// Iterates to the next feature
+ virtual void next();
+ /// Returns true if the current iteration is valid and next iteration is possible
+ virtual bool more();
+ /// Returns the currently iterated feature
+ virtual std::shared_ptr<ModelAPI_Feature> current();
+ /// Returns the kind of the current feature (faster than Current()->getKind())
+ virtual std::string currentKind();
+ /// Returns the name of the current feature (faster than Current()->getName())
+ virtual std::string currentName();
+ /// Don't changes the current position of iterator. Not fast: iterates the left items.
+ /// \returns number of left iterations
+ virtual int numIterationsLeft();
+
+protected:
+ /// Initializes iterator
+ /// \param theDoc document where the iteration is performed
+ /// \param theLab label of the features group to iterate
+ Model_Iterator(std::shared_ptr<Model_Document> theDoc, TDF_Label theLab);
+
+ friend class Model_Document;
+};
+
+#endif
--- /dev/null
+// File: Model_Object.hxx
+// Created: 21 Mar 2014
+// Author: Mikhail PONIKAROV
+
+#include <Model_Object.h>
+#include <Model_AttributeDocRef.h>
+#include <Model_AttributeDouble.h>
+#include <TDataStd_Name.hxx>
+
+using namespace std;
+
+Model_Object::Model_Object()
+{
+}
+
+void Model_Object::setLabel(TDF_Label& theLab)
+{
+ myLab = theLab;
+}
+
+string Model_Object::getName()
+{
+ Handle(TDataStd_Name) aName;
+ if (myLab.FindAttribute(TDataStd_Name::GetID(), aName))
+ return string(TCollection_AsciiString(aName->Get()).ToCString());
+ return ""; // not defined
+}
+
+void Model_Object::setName(string theName)
+{
+ TDataStd_Name::Set(myLab, theName.c_str());
+}
+
+void Model_Object::addAttribute(string theID, string theAttrType)
+{
+ TDF_Label anAttrLab = myLab.FindChild(myAttrs.size() + 1);
+ ModelAPI_Attribute* anAttr = 0;
+ if (theAttrType == ModelAPI_AttributeDocRef::type())
+ anAttr = new Model_AttributeDocRef(anAttrLab);
+ else if (theAttrType == ModelAPI_AttributeDouble::type())
+ anAttr = new Model_AttributeDouble(anAttrLab);
+
+ if (anAttr)
+ myAttrs[theID] = std::shared_ptr<ModelAPI_Attribute>(anAttr);
+ else
+ ; // TODO: generate error on unknown attribute request and/or add mechanism for customization
+}
+
+shared_ptr<ModelAPI_AttributeDocRef> Model_Object::docRef(const string theID)
+{
+ map<string, shared_ptr<ModelAPI_Attribute> >::iterator aFound = myAttrs.find(theID);
+ if (aFound == myAttrs.end()) {
+ // TODO: generate error on unknown attribute request and/or add mechanism for customization
+ return std::shared_ptr<ModelAPI_AttributeDocRef>();
+ }
+ shared_ptr<ModelAPI_AttributeDocRef> aRes =
+ dynamic_pointer_cast<ModelAPI_AttributeDocRef>(aFound->second);
+ if (!aRes) {
+ // TODO: generate error on invalid attribute type request
+ }
+ return aRes;
+}
+
+shared_ptr<ModelAPI_AttributeDouble> Model_Object::real(const string theID)
+{
+ map<string, shared_ptr<ModelAPI_Attribute> >::iterator aFound = myAttrs.find(theID);
+ if (aFound == myAttrs.end()) {
+ // TODO: generate error on unknown attribute request and/or add mechanism for customization
+ return std::shared_ptr<ModelAPI_AttributeDouble>();
+ }
+ shared_ptr<ModelAPI_AttributeDouble> aRes =
+ dynamic_pointer_cast<ModelAPI_AttributeDouble>(aFound->second);
+ if (!aRes) {
+ // TODO: generate error on invalid attribute type request
+ }
+ return aRes;
+}
--- /dev/null
+// File: Model_Object.hxx
+// Created: 21 Mar 2014
+// Author: Mikhail PONIKAROV
+
+#ifndef Model_Object_HeaderFile
+#define Model_Object_HeaderFile
+
+#include "Model.h"
+#include <ModelAPI_Object.h>
+#include <TDF_Label.hxx>
+
+#include <map>
+
+class ModelAPI_Attribute;
+
+/**\class Model_Object
+ * \ingroup DataModel
+ * \brief General object of the application that allows
+ * to get/set attributes from the document and compute result of an operation.
+ */
+
+class Model_Object: public ModelAPI_Object
+{
+ TDF_Label myLab; ///< label of the feature in the document
+ /// All attributes of the object identified by the attribute ID
+ std::map<std::string, std::shared_ptr<ModelAPI_Attribute> > myAttrs;
+
+ Model_Object();
+
+ friend class Model_Document;
+
+public:
+ /// Returns the name of the feature visible by the user in the object browser
+ virtual std::string getName();
+ /// Defines the name of the feature visible by the user in the object browser
+ virtual void setName(std::string theName);
+ /// Returns the attribute that references to another document
+ virtual std::shared_ptr<ModelAPI_AttributeDocRef> docRef(const std::string theID);
+ /// Returns the attribute that contains real value with double precision
+ virtual std::shared_ptr<ModelAPI_AttributeDouble> real(const std::string theID);
+
+ /// Initializes object by the attributes: must be called just after the object is created
+ /// for each attribute of the object
+ /// \param theID identifier of the attribute that can be referenced by this ID later
+ /// \param theAttrType type of the created attribute (received from the type method)
+ virtual void addAttribute(std::string theID, std::string theAttrType);
+
+ /// Puts feature to the document data sub-structure
+ void setLabel(TDF_Label& theLab);
+};
+
+#endif
#include <Model_PluginManager.h>
#include <ModelAPI_Feature.h>
#include <ModelAPI_Plugin.h>
-#include <Model_Feature.h>
+#include <Model_Object.h>
+#include <Model_Document.h>
+#include <Model_Application.h>
#include <Event_Loop.h>
#include <Config_FeatureMessage.h>
#include <Config_ModuleReader.h>
static Model_PluginManager* myImpl = new Model_PluginManager();
-boost::shared_ptr<ModelAPI_Feature> Model_PluginManager::createFeature(string theFeatureID)
+std::shared_ptr<ModelAPI_Feature> Model_PluginManager::createFeature(string theFeatureID)
{
if (this != myImpl) return myImpl->createFeature(theFeatureID);
loadLibrary(myCurrentPluginName);
}
if (myPluginObjs.find(myCurrentPluginName) != myPluginObjs.end()) {
- return myPluginObjs[myCurrentPluginName]->createFeature(theFeatureID);
+ std::shared_ptr<ModelAPI_Feature> aCreated =
+ myPluginObjs[myCurrentPluginName]->createFeature(theFeatureID);
+ return aCreated;
}
}
- return boost::shared_ptr<ModelAPI_Feature>(); // return nothing
+ return std::shared_ptr<ModelAPI_Feature>(); // return nothing
+}
+
+std::shared_ptr<ModelAPI_Document> Model_PluginManager::rootDocument()
+{
+ return std::shared_ptr<ModelAPI_Document>(
+ Model_Application::getApplication()->getDocument("root"));
+}
+
+shared_ptr<ModelAPI_Document> Model_PluginManager::currentDocument()
+{
+ if (!myCurrentDoc)
+ myCurrentDoc = rootDocument();
+ return myCurrentDoc;
+}
+
+void Model_PluginManager::setCurrentDocument(shared_ptr<ModelAPI_Document> theDoc)
+{
+ myCurrentDoc = theDoc;
}
Model_PluginManager::Model_PluginManager()
//TODO(sbh): Implement static method to extract event id [SEID]
static Event_ID aFeatureEvent = Event_Loop::eventByName("FeatureEvent");
- ModelAPI_PluginManager::SetPluginManager(boost::shared_ptr<ModelAPI_PluginManager>(this));
+ ModelAPI_PluginManager::SetPluginManager(std::shared_ptr<ModelAPI_PluginManager>(this));
// register the configuration reading listener
Event_Loop* aLoop = Event_Loop::loop();
aLoop->registerListener(this, aFeatureEvent);
if (aMsg) {
// proccess the plugin info, load plugin
if (myPlugins.find(aMsg->id()) == myPlugins.end()) {
- myPlugins[aMsg->id()] = aMsg->pluginLibrary(); // TO DO: plugin name must be also imported from XMLs
+ myPlugins[aMsg->id()] = aMsg->pluginLibrary();
}
}
// plugins information was started to load, so, it will be loaded
#include <Event_Listener.h>
#include <map>
+class Model_Document;
+
/**\class Model_PluginManager
* \ingroup DataModel
* \brief Object that knows (from the initial XML file) which
* plugin contains which feature, loads and stores reference to loaded plugins by
* the feature functionality request.
*/
-
class Model_PluginManager : public ModelAPI_PluginManager, public Event_Listener
{
bool myPluginsInfoLoaded; ///< it true if plugins information is loaded
- /// map of feature IDs to plugin name and object
+ /// map of feature IDs to plugin name
std::map<std::string, std::string> myPlugins;
std::map<std::string, ModelAPI_Plugin*> myPluginObjs; ///< instances of the already plugins
std::string myCurrentPluginName; ///< name of the plugin that must be loaded currently
+ std::shared_ptr<ModelAPI_Document> myCurrentDoc; ///< current working document
public:
/// Creates the feature object using plugins functionality
- MODEL_EXPORT virtual boost::shared_ptr<ModelAPI_Feature> createFeature(std::string theFeatureID);
+ MODEL_EXPORT virtual std::shared_ptr<ModelAPI_Feature> createFeature(std::string theFeatureID);
+
+ /// Returns the root document of the application (that may contains sub-documents)
+ MODEL_EXPORT virtual std::shared_ptr<ModelAPI_Document> rootDocument();
+
+ /// Returns the current document that used for current work in the application
+ MODEL_EXPORT virtual std::shared_ptr<ModelAPI_Document> currentDocument();
+
+ /// Defines the current document that used for current work in the application
+ MODEL_EXPORT virtual void setCurrentDocument(std::shared_ptr<ModelAPI_Document> theDoc);
/// Registers the plugin that creates features.
/// It is obligatory for each plugin to call this function on loading to be found by
/// the plugin manager on call of the feature)
- virtual void registerPlugin(ModelAPI_Plugin* thePlugin);
+ MODEL_EXPORT virtual void registerPlugin(ModelAPI_Plugin* thePlugin);
/// Processes the configuration file reading
MODEL_EXPORT virtual void processEvent(const Event_Message* theMessage);
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.11)
-INCLUDE(Common)
FIND_PACKAGE(SWIG REQUIRED)
INCLUDE(${SWIG_USE_FILE})
-INCLUDE(FindPython)
-INCLUDE(FindBoost)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
SET(PROJECT_HEADERS
ModelAPI_PluginManager.h
ModelAPI_Plugin.h
ModelAPI_Feature.h
+ ModelAPI_Iterator.h
+ ModelAPI_Object.h
ModelAPI_Document.h
+ ModelAPI_Attribute.h
+ ModelAPI_AttributeDouble.h
+ ModelAPI_AttributeDocRef.h
)
SET(PROJECT_SOURCES
#include "ModelAPI_Document.h"
#include "ModelAPI_PluginManager.h"
#include "ModelAPI_Feature.h"
+ #include "ModelAPI_Object.h"
+ #include "ModelAPI_Attribute.h"
+ #include "ModelAPI_AttributeDocRef.h"
+ #include "ModelAPI_AttributeDouble.h"
%}
// to avoid error on this
// standard definitions
%include "typemaps.i"
%include "std_string.i"
+%include <std_shared_ptr.i>
// boost pointers
-%include <boost_shared_ptr.i>
+// %include <boost_shared_ptr.i>
+%shared_ptr(ModelAPI_Document)
%shared_ptr(ModelAPI_PluginManager)
%shared_ptr(ModelAPI_Feature)
+%shared_ptr(ModelAPI_Object)
+%shared_ptr(ModelAPI_Attribute)
+%shared_ptr(ModelAPI_AttributeDocRef)
+%shared_ptr(ModelAPI_AttributeDouble)
// all supported interfaces
%include "ModelAPI_Document.h"
%include "ModelAPI_PluginManager.h"
%include "ModelAPI_Feature.h"
+%include "ModelAPI_Object.h"
+%include "ModelAPI_Attribute.h"
+%include "ModelAPI_AttributeDocRef.h"
+%include "ModelAPI_AttributeDouble.h"
--- /dev/null
+// File: ModelAPI_Attribute.h
+// Created: 2 Apr 2014
+// Author: Mikhail PONIKAROV
+
+#ifndef ModelAPI_Attribute_HeaderFile
+#define ModelAPI_Attribute_HeaderFile
+
+#include "ModelAPI.h"
+#include <string>
+
+/**\class ModelAPI_Attribute
+ * \ingroup DataModel
+ * \brief Generic attribute of the Object.
+ */
+
+class MODELAPI_EXPORT ModelAPI_Attribute
+{
+public:
+
+ /// Returns the type of this class of attributes, not static method
+ virtual std::string attributeType() = 0;
+
+protected:
+ /// Objects are created for features automatically
+ ModelAPI_Attribute()
+ {}
+};
+
+#endif
--- /dev/null
+// File: ModelAPI_AttributeDocRef.h
+// Created: 2 Apr 2014
+// Author: Mikhail PONIKAROV
+
+#ifndef ModelAPI_AttributeDocRef_HeaderFile
+#define ModelAPI_AttributeDocRef_HeaderFile
+
+#include "ModelAPI_Attribute.h"
+#include "ModelAPI_Document.h"
+
+/**\class ModelAPI_AttributeDocRef
+ * \ingroup DataModel
+ * \brief Attribute that contains reference to another document.
+ */
+
+class MODELAPI_EXPORT ModelAPI_AttributeDocRef : public ModelAPI_Attribute
+{
+public:
+ /// Defines the document referenced from this attribute
+ virtual void setValue(std::shared_ptr<ModelAPI_Document> theDoc) = 0;
+
+ /// Returns document referenced from this attribute
+ virtual std::shared_ptr<ModelAPI_Document> value() = 0;
+
+ /// Returns the type of this class of attributes
+ static std::string type() {return "DocRef";}
+
+ /// Returns the type of this class of attributes, not static method
+ virtual std::string attributeType() {return type();}
+
+protected:
+ /// Objects are created for features automatically
+ ModelAPI_AttributeDocRef()
+ {}
+};
+
+#endif
--- /dev/null
+// File: ModelAPI_AttributeDouble.h
+// Created: 2 Apr 2014
+// Author: Mikhail PONIKAROV
+
+#ifndef ModelAPI_AttributeDouble_HeaderFile
+#define ModelAPI_AttributeDouble_HeaderFile
+
+#include "ModelAPI_Attribute.h"
+
+/**\class ModelAPI_AttributeDouble
+ * \ingroup DataModel
+ * \brief Attribute that contains real value with double precision.
+ */
+
+class MODELAPI_EXPORT ModelAPI_AttributeDouble : public ModelAPI_Attribute
+{
+public:
+ /// Defines the double value
+ virtual void setValue(double theValue) = 0;
+
+ /// Returns the double value
+ virtual double value() = 0;
+
+ /// Returns the type of this class of attributes
+ static std::string type() {return "Double";}
+
+ /// Returns the type of this class of attributes, not static method
+ virtual std::string attributeType() {return type();}
+
+protected:
+ /// Objects are created for features automatically
+ ModelAPI_AttributeDouble()
+ {}
+};
+
+#endif
-// File: Model_Document.hxx
-// Created: 28 Dec 2011
+// File: ModelAPI_Document.cxx
+// Created: 28 Feb 2014
// Author: Mikhail PONIKAROV
-// Copyright: CEA 2011
#ifndef ModelAPI_Document_HeaderFile
#define ModelAPI_Document_HeaderFile
#include <ModelAPI.h>
+#include <string>
+#include <memory>
+
+class ModelAPI_Feature;
+class ModelAPI_Iterator;
+
+/// Common groups identifiers
+/// Group of parameters
+static const std::string PARAMETERS_GROUP = "Parameters";
+/// Group of constructions
+static const std::string CONSTRUCTIONS_GROUP = "Construction";
+/// Group of parts
+static const std::string PARTS_GROUP = "Parts";
/**\class Model_Document
* \ingroup DataModel
* Also it provides acces to this data: open/save, transactions management etc.
* to provide access to all stored data.
*/
-
class ModelAPI_Document
{
public:
-
//! Loads the OCAF document from the file.
//! \param theFileName full name of the file to load
//! \param theStudyID identifier of the SALOME study to associate with loaded file
//! \returns true if file was loaded successfully
- MODELAPI_EXPORT virtual bool Load(const char* theFileName) = 0;
+ MODELAPI_EXPORT virtual bool load(const char* theFileName) = 0;
//! Saves the OCAF document to the file.
//! \param theFileName full name of the file to store
//! \returns true if file was stored successfully
- MODELAPI_EXPORT virtual bool Save(const char* theFileName) = 0;
+ MODELAPI_EXPORT virtual bool save(const char* theFileName) = 0;
//! Removes document data
- MODELAPI_EXPORT virtual void Close() = 0;
+ MODELAPI_EXPORT virtual void close() = 0;
//! Starts a new operation (opens a tansaction)
- MODELAPI_EXPORT virtual void StartOperation() = 0;
+ MODELAPI_EXPORT virtual void startOperation() = 0;
//! Finishes the previously started operation (closes the transaction)
- MODELAPI_EXPORT virtual void FinishOperation() = 0;
+ MODELAPI_EXPORT virtual void finishOperation() = 0;
//! Aborts the operation
- MODELAPI_EXPORT virtual void AbortOperation() = 0;
+ MODELAPI_EXPORT virtual void abortOperation() = 0;
//! Returns true if operation has been started, but not yet finished or aborted
- MODELAPI_EXPORT virtual bool IsOperation() = 0;
+ MODELAPI_EXPORT virtual bool isOperation() = 0;
//! Returns true if document was modified (since creation/opening)
- MODELAPI_EXPORT virtual bool IsModified() = 0;
+ MODELAPI_EXPORT virtual bool isModified() = 0;
//! Returns True if there are available Undos
- MODELAPI_EXPORT virtual bool CanUndo() = 0;
+ MODELAPI_EXPORT virtual bool canUndo() = 0;
//! Undoes last operation
- MODELAPI_EXPORT virtual void Undo() = 0;
+ MODELAPI_EXPORT virtual void undo() = 0;
//! Returns True if there are available Redos
- MODELAPI_EXPORT virtual bool CanRedo() = 0;
+ MODELAPI_EXPORT virtual bool canRedo() = 0;
//! Redoes last operation
- MODELAPI_EXPORT virtual void Redo() = 0;
+ MODELAPI_EXPORT virtual void redo() = 0;
+
+ //! Adds to the document the new feature of the given group id
+ //! \param theFeature a feature object that will be connected to the document in this method
+ //! \param theGroupID identifier of the groups of object
+ MODELAPI_EXPORT virtual void addFeature(std::shared_ptr<ModelAPI_Feature> theFeature,
+ const std::string theGroupID) = 0;
+
+ ///! Adds a new sub-document by the identifier, or returns existing one if it is already exist
+ MODELAPI_EXPORT virtual std::shared_ptr<ModelAPI_Document> subDocument(std::string theDocID) = 0;
+
+ ///! Creates an iterator of the features by the specific groups
+ MODELAPI_EXPORT virtual std::shared_ptr<ModelAPI_Iterator> featuresIterator(
+ const std::string theGroup) = 0;
+
+ MODELAPI_EXPORT virtual const std::string& id() const = 0;
protected:
/// Only for SWIG wrapping it is here
#include "ModelAPI.h"
#include <string>
+#include <memory>
-class ModelAPI_Feature;
+class ModelAPI_Object;
/**\class ModelAPI_Feature
* \ingroup DataModel
- * \brief General object of the application that allows
- * to get/set attributes from the document and compute result of an operation.
+ * \brief Functionality of the model object: to update result,
+ * to initialize attributes, etc.
*/
class MODELAPI_EXPORT ModelAPI_Feature
{
+ std::shared_ptr<ModelAPI_Object> myData; ///< manager of the data model of a feature
+
public:
/// Returns the kind of a feature (like "Point")
- virtual std::string GetKind() = 0;
+ virtual std::string getKind() = 0;
+
+ /// Request for initialization of data model of the feature: adding all attributes
+ virtual void initAttributes() = 0;
+
+ /// Computes or recomputes the result
+ virtual void execute() = 0;
+
+ /// Returns the data manager of this feature
+ std::shared_ptr<ModelAPI_Object> data() {return myData;}
protected:
/// Use plugin manager for features creation: this method is
/// defined here only for SWIG-wrapping
ModelAPI_Feature()
- {
- }
+ {}
+
+ /// Sets the data manager of an object (document does)
+ void setData(std::shared_ptr<ModelAPI_Object> theData) {myData = theData;}
+ friend class Model_Document;
};
#endif
--- /dev/null
+// File: ModelAPI_Iterator.hxx
+// Created: 1 Apr 2014
+// Author: Mikhail PONIKAROV
+
+#ifndef ModelAPI_Iterator_HeaderFile
+#define ModelAPI_Iterator_HeaderFile
+
+#include "ModelAPI.h"
+#include <string>
+#include <memory>
+
+class ModelAPI_Feature;
+class ModelAPI_Document;
+
+/**\class ModelAPI_Iterator
+ * \ingroup DataModel
+ * \brief Allows to iterate features of the document. Is created by the document
+ * (see method featuresIterator).
+ */
+
+class MODELAPI_EXPORT ModelAPI_Iterator
+{
+public:
+ /// Iterates to the next feature
+ virtual void next() = 0;
+ /// Returns true if the current iteration is valid and next iteration is possible
+ virtual bool more() = 0;
+ /// Returns the currently iterated feature
+ virtual std::shared_ptr<ModelAPI_Feature> current() = 0;
+ /// Returns the kind of the current feature (faster than Current()->getKind())
+ virtual std::string currentKind() = 0;
+ /// Returns the name of the current feature (faster than Current()->getName())
+ virtual std::string currentName() = 0;
+ /// Don't changes the current position of iterator. Not fast: iterates the left items.
+ /// \returns number of left iterations
+ virtual int numIterationsLeft() = 0;
+
+protected:
+ /// Use plugin manager for features creation: this method is
+ /// defined here only for SWIG-wrapping
+ ModelAPI_Iterator()
+ {}
+};
+
+#endif
--- /dev/null
+// File: ModelAPI_Object.hxx
+// Created: 21 Mar 2014
+// Author: Mikhail PONIKAROV
+
+#ifndef ModelAPI_Object_HeaderFile
+#define ModelAPI_Object_HeaderFile
+
+#include "ModelAPI.h"
+#include <string>
+#include <memory>
+
+class ModelAPI_AttributeDocRef;
+class ModelAPI_AttributeDouble;
+
+/**\class ModelAPI_Object
+ * \ingroup DataModel
+ * \brief General object of the application that allows
+ * to get/set attributes from the document and compute result of an operation.
+ */
+
+class MODELAPI_EXPORT ModelAPI_Object
+{
+public:
+
+ /// Returns the name of the feature visible by the user in the object browser
+ virtual std::string getName() = 0;
+
+ /// Defines the name of the feature visible by the user in the object browser
+ virtual void setName(std::string theName) = 0;
+
+ /// Returns the attribute that references to another document
+ virtual std::shared_ptr<ModelAPI_AttributeDocRef> docRef(const std::string theID) = 0;
+ /// Returns the attribute that contains real value with double precision
+ virtual std::shared_ptr<ModelAPI_AttributeDouble> real(const std::string theID) = 0;
+
+ /// Initializes object by the attributes: must be called just after the object is created
+ /// for each attribute of the object
+ /// \param theID identifier of the attribute that can be referenced by this ID later
+ /// \param theAttrType type of the created attribute (received from the type method)
+ virtual void addAttribute(std::string theID, std::string theAttrType) = 0;
+
+protected:
+ /// Objects are created for features automatically
+ ModelAPI_Object()
+ {}
+};
+
+#endif
#include "ModelAPI.h"
#include <string>
-#include <boost/shared_ptr.hpp>
+#include <memory>
class ModelAPI_Feature;
{
public:
/// Creates the feature object of this plugin by the feature string ID
- virtual boost::shared_ptr<ModelAPI_Feature> createFeature(std::string theFeatureID) = 0;
+ virtual std::shared_ptr<ModelAPI_Feature> createFeature(std::string theFeatureID) = 0;
protected:
/// Is needed for python wrapping by swig
#include <ModelAPI_Document.h>
// to avoid unresolved ModelAPI_Feature()
#include <ModelAPI_Feature.h>
+// to avoid unresolved ModelAPI_Object()
+#include <ModelAPI_Object.h>
// to avoid unresolved ModelAPI_Plugin()
#include <ModelAPI_Plugin.h>
+// to avoid unresolved ModelAPI_Iterator()
+#include <ModelAPI_Iterator.h>
+// to avoid unresolved ModelAPI_Iterator()
+#include <ModelAPI_Iterator.h>
+#include <ModelAPI_Attribute.h>
+#include <ModelAPI_AttributeDocRef.h>
+#include <ModelAPI_AttributeDouble.h>
#ifdef WIN32
#include <windows.h>
string library(const string& theLibName);
/// Manager that will be initialized from Model package, one per application
-boost::shared_ptr<ModelAPI_PluginManager> MY_MANAGER;
+std::shared_ptr<ModelAPI_PluginManager> MY_MANAGER;
ModelAPI_PluginManager::ModelAPI_PluginManager()
{
}
void ModelAPI_PluginManager::SetPluginManager(
- boost::shared_ptr<ModelAPI_PluginManager> theManager)
+ std::shared_ptr<ModelAPI_PluginManager> theManager)
{
MY_MANAGER = theManager;
}
-boost::shared_ptr<ModelAPI_PluginManager> ModelAPI_PluginManager::get()
+std::shared_ptr<ModelAPI_PluginManager> ModelAPI_PluginManager::get()
{
if (!MY_MANAGER) { // import Model library that implements this interface of ModelAPI
loadLibrary("Model");
return aLibName;
}
+// for debug purpose only (cerr), before the error management system is implemented
+#include <iostream>
void ModelAPI_PluginManager::loadLibrary(const string theLibName)
{
string aFileName = library(theLibName);
#include "ModelAPI.h"
#include <string>
-#include <boost/shared_ptr.hpp>
+#include <memory>
class ModelAPI_Feature;
class ModelAPI_Plugin;
+class ModelAPI_Document;
/**\class ModelAPI_PluginManager
* \ingroup DataModel
{
public:
/// Creates the feature object using plugins functionality
- virtual boost::shared_ptr<ModelAPI_Feature> createFeature(std::string theFeatureID) = 0;
+ virtual std::shared_ptr<ModelAPI_Feature> createFeature(std::string theFeatureID) = 0;
/// Returns the real implementation (the alone instance per application) of the plugin manager
- static boost::shared_ptr<ModelAPI_PluginManager> get();
+ static std::shared_ptr<ModelAPI_PluginManager> get();
/// Registers the plugin that creates features.
/// It is obligatory for each plugin to call this function on loading to be found by
/// the plugin manager on call of the feature)
virtual void registerPlugin(ModelAPI_Plugin* thePlugin) = 0;
+ /// Returns the root document of the application (that may contains sub-documents)
+ virtual std::shared_ptr<ModelAPI_Document> rootDocument() = 0;
+
+ /// Returns the current document that used for current work in the application
+ virtual std::shared_ptr<ModelAPI_Document> currentDocument() = 0;
+
+ /// Defines the current document that used for current work in the application
+ virtual void setCurrentDocument(std::shared_ptr<ModelAPI_Document> theDoc) = 0;
+
/// loads the library with specific name, appends "lib*.dll" or "*.so" depending on the platform
static void ModelAPI_PluginManager::loadLibrary(const std::string theLibName);
ModelAPI_PluginManager();
protected:
- static void SetPluginManager(boost::shared_ptr<ModelAPI_PluginManager> theManager);
+ static void SetPluginManager(std::shared_ptr<ModelAPI_PluginManager> theManager);
};
#endif
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.11)
INCLUDE(Common)
-INCLUDE(FindBoost)
SET(PROJECT_HEADERS
PartSetPlugin.h
PartSetPlugin_Plugin.h
- PartSetPlugin_NewPart.h
+ PartSetPlugin_Part.h
+ PartSetPlugin_Point.h
)
SET(PROJECT_SOURCES
PartSetPlugin_Plugin.cxx
- PartSetPlugin_NewPart.cxx
+ PartSetPlugin_Part.cxx
+ PartSetPlugin_Point.cxx
)
ADD_DEFINITIONS(-DPARTSETPLUGIN_EXPORTS ${BOOST_DEFINITIONS})
+++ /dev/null
-// File: PartSetPlugin_NewPart.cxx
-// Created: 27 Mar 2014
-// Author: Mikhail PONIKAROV
-
-#include "PartSetPlugin_NewPart.hxx"
-
-PartSetPlugin_NewPart::PartSetPlugin_NewPart()
-{
-}
+++ /dev/null
-// File: PartSetPlugin_NewPart.hxx
-// Created: 27 Mar 2014
-// Author: Mikhail PONIKAROV
-
-#ifndef PartSetPlugin_NewPart_HeaderFile
-#define PartSetPlugin_NewPart_HeaderFile
-
-#include "PartSetPlugin.h"
-#include <ModelAPI_Feature.h>
-
-/**\class PartSetPlugin_NewPart
- * \ingroup DataModel
- * \brief Feature for creation of the new part in PartSet.
- */
-
-class PartSetPlugin_NewPart: public ModelAPI_Feature
-{
-public:
- /// Returns the kind of a feature
- PARTSETPLUGIN_EXPORT virtual std::string GetKind() {return "new_part";}
-
- /// Use plugin manager for features creation
- PartSetPlugin_NewPart();
-};
-
-#endif
+++ /dev/null
-// File: PartSetPlugin_NewPart.hxx
-// Created: 27 Mar 2014
-// Author: Mikhail PONIKAROV
-
-#ifndef PartSetPlugin_NewPart_HeaderFile
-#define PartSetPlugin_NewPart_HeaderFile
-
-#include "PartSetPlugin.h"
-#include <ModelAPI_Feature.h>
-
-/**\class PartSetPlugin_NewPart
- * \ingroup DataModel
- * \brief Feature for creation of the new part in PartSet.
- */
-
-class PartSetPlugin_NewPart: public ModelAPI_Feature
-{
-public:
- /// Returns the kind of a feature
- PARTSETPLUGIN_EXPORT virtual std::string GetKind() {return "new_part";}
-
- /// Use plugin manager for features creation
- PartSetPlugin_NewPart();
-};
-
-#endif
--- /dev/null
+// File: PartSetPlugin_Part.cxx
+// Created: 27 Mar 2014
+// Author: Mikhail PONIKAROV
+
+#include "PartSetPlugin_Part.h"
+#include "ModelAPI_PluginManager.h"
+#include "ModelAPI_Document.h"
+#include "ModelAPI_Object.h"
+#include "ModelAPI_Iterator.h"
+#include "ModelAPI_AttributeDocRef.h"
+
+using namespace std;
+
+PartSetPlugin_Part::PartSetPlugin_Part()
+{
+}
+
+void PartSetPlugin_Part::initAttributes()
+{
+ data()->addAttribute(PART_ATTR_DOC_REF, ModelAPI_AttributeDocRef::type());
+}
+
+void PartSetPlugin_Part::execute()
+{
+ shared_ptr<ModelAPI_AttributeDocRef> aDocRef = data()->docRef(PART_ATTR_DOC_REF);
+ if (!aDocRef->value()) { // create a document if not yet created
+ shared_ptr<ModelAPI_Document> aPartSetDoc = ModelAPI_PluginManager::get()->rootDocument();
+ aDocRef->setValue(aPartSetDoc->subDocument(data()->getName()));
+ }
+}
--- /dev/null
+// File: PartSetPlugin_Part.h
+// Created: 27 Mar 2014
+// Author: Mikhail PONIKAROV
+
+#ifndef PartSetPlugin_Part_HeaderFile
+#define PartSetPlugin_Part_HeaderFile
+
+#include "PartSetPlugin.h"
+#include <ModelAPI_Feature.h>
+
+/// part reference attribute
+const std::string PART_ATTR_DOC_REF = "PartDocument";
+
+/**\class PartSetPlugin_Part
+ * \ingroup DataModel
+ * \brief Feature for creation of the new part in PartSet.
+ */
+class PartSetPlugin_Part: public ModelAPI_Feature
+{
+public:
+ /// Returns the kind of a feature
+ PARTSETPLUGIN_EXPORT virtual std::string getKind() {return "Part";}
+
+ /// Creates a new part document if needed
+ PARTSETPLUGIN_EXPORT virtual void execute();
+
+ /// Request for initialization of data model of the feature: adding all attributes
+ PARTSETPLUGIN_EXPORT virtual void initAttributes();
+
+ /// Use plugin manager for features creation
+ PartSetPlugin_Part();
+};
+
+#endif
#include "PartSetPlugin_Plugin.h"
-#include "PartSetPlugin_NewPart.h"
+#include "PartSetPlugin_Part.h"
+#include "PartSetPlugin_Point.h"
#include <ModelAPI_PluginManager.h>
+#include <ModelAPI_Document.h>
using namespace std;
ModelAPI_PluginManager::get()->registerPlugin(this);
}
-boost::shared_ptr<ModelAPI_Feature> PartSetPlugin_Plugin::createFeature(string theFeatureID)
+std::shared_ptr<ModelAPI_Feature> PartSetPlugin_Plugin::createFeature(string theFeatureID)
{
- if (theFeatureID == "new_part") {
- return boost::shared_ptr<ModelAPI_Feature>(new PartSetPlugin_NewPart());
+ std::shared_ptr<ModelAPI_Feature> aCreated;
+ bool isCurrent = true; // to create a feature in the current document
+ if (theFeatureID == "Part") {
+ aCreated = std::shared_ptr<ModelAPI_Feature>(new PartSetPlugin_Part);
+ isCurrent = false; // allways create in the root document
+ } else if (theFeatureID == "Point") {
+ aCreated = std::shared_ptr<ModelAPI_Feature>(new PartSetPlugin_Point);
+ }
+
+ // add to a root document for the current moment
+ if (aCreated) {
+ shared_ptr<ModelAPI_Document> aDoc = isCurrent ?
+ ModelAPI_PluginManager::get()->currentDocument() :
+ ModelAPI_PluginManager::get()->rootDocument();
+ aDoc->addFeature(aCreated, PARTS_GROUP);
}
// feature of such kind is not found
- return boost::shared_ptr<ModelAPI_Feature>();
+ return aCreated;
}
{
public:
/// Creates the feature object of this plugin by the feature string ID
- virtual boost::shared_ptr<ModelAPI_Feature> createFeature(std::string theFeatureID);
+ virtual std::shared_ptr<ModelAPI_Feature> createFeature(std::string theFeatureID);
public:
/// Is needed for python wrapping by swig
--- /dev/null
+// File: PartSetPlugin_Point.cxx
+// Created: 27 Mar 2014
+// Author: Mikhail PONIKAROV
+
+#include "PartSetPlugin_Point.h"
+#include "ModelAPI_PluginManager.h"
+#include "ModelAPI_Document.h"
+#include "ModelAPI_Object.h"
+#include "ModelAPI_AttributeDouble.h"
+
+using namespace std;
+
+PartSetPlugin_Point::PartSetPlugin_Point()
+{
+}
+
+void PartSetPlugin_Point::initAttributes()
+{
+ data()->addAttribute(POINT_ATTR_X, ModelAPI_AttributeDouble::type());
+ data()->addAttribute(POINT_ATTR_Y, ModelAPI_AttributeDouble::type());
+ data()->addAttribute(POINT_ATTR_Z, ModelAPI_AttributeDouble::type());
+}
+
+// this is for debug only
+#include <iostream>
+void PartSetPlugin_Point::execute()
+{
+ // TODO: create a real shape for the point using OCC layer
+ cout<<"X="<<data()->real(POINT_ATTR_X)->value()<<" Y="<<data()->real(POINT_ATTR_Y)->value()
+ <<" Z="<<data()->real(POINT_ATTR_Z)->value()<<endl;
+}
--- /dev/null
+// File: PartSetPlugin_Point.h
+// Created: 3 Apr 2014
+// Author: Mikhail PONIKAROV
+
+#ifndef PartSetPlugin_Point_HeaderFile
+#define PartSetPlugin_Point_HeaderFile
+
+#include "PartSetPlugin.h"
+#include <ModelAPI_Feature.h>
+
+/// attribute name for X coordinate
+const std::string POINT_ATTR_X = "x";
+/// attribute name for Y coordinate
+const std::string POINT_ATTR_Y = "y";
+/// attribute name for Z coordinate
+const std::string POINT_ATTR_Z = "z";
+
+/**\class PartSetPlugin_Point
+ * \ingroup DataModel
+ * \brief Feature for creation of the new part in PartSet.
+ */
+class PartSetPlugin_Point: public ModelAPI_Feature
+{
+public:
+ /// Returns the kind of a feature
+ PARTSETPLUGIN_EXPORT virtual std::string getKind() {return "Point";}
+
+ /// Creates a new part document if needed
+ PARTSETPLUGIN_EXPORT virtual void execute();
+
+ /// Request for initialization of data model of the feature: adding all attributes
+ PARTSETPLUGIN_EXPORT virtual void initAttributes();
+
+ /// Use plugin manager for features creation
+ PartSetPlugin_Point();
+};
+
+#endif
--- /dev/null
+
+SET(CMAKE_AUTOMOC ON)
+
+# header files
+SET(PROJECT_HEADERS
+ PyConsole.h
+ PyConsole_Console.h
+ PyConsole_Editor.h
+ PyConsole_EnhEditor.h
+ PyConsole_EnhInterp.h
+ PyConsole_Event.h
+ PyConsole_Interp.h
+ PyConsole_Request.h
+)
+
+SET(PROJECT_AUTOMOC
+ ${CMAKE_CURRENT_BINARY_DIR}/PyConsole_automoc.cpp
+)
+
+# resource files / to be processed by lrelease
+SET(TEXT_RESOURCES
+ resources/PyConsole_msg_en.ts
+ resources/PyConsole_msg_fr.ts
+ resources/PyConsole_msg_ja.ts
+)
+# sources / static
+SET(PROJECT_SOURCES
+ PyConsole_Console.cxx
+ PyConsole_Editor.cxx
+ PyConsole_EnhEditor.cxx
+ PyConsole_EnhInterp.cxx
+ PyConsole_Event.cxx
+ PyConsole_Interp.cxx
+ PyConsole_Request.cxx
+)
+
+SET(PROJECT_LIBRARIES
+ PyInterp
+ ${Qt5Widgets_LIBRARIES}
+)
+
+QT5_ADD_TRANSLATION(QM_RESOURCES ${TEXT_RESOURCES})
+
+SOURCE_GROUP ("Generated Files" FILES ${PROJECT_AUTOMOC} ${QM_RESOURCES})
+
+INCLUDE_DIRECTORIES(
+ ${PROJECT_SOURCE_DIR}/src/PyEvent
+ ${PROJECT_SOURCE_DIR}/src/PyInterp
+)
+
+ADD_DEFINITIONS(-DPYCONSOLE_EXPORTS -DHAVE_DEBUG_PYTHON)
+
+ADD_LIBRARY(PyConsole STATIC
+ ${PROJECT_HEADERS}
+ ${PROJECT_SOURCES}
+ ${TEXT_RESOURCES}
+ ${QM_RESOURCES}
+)
+
+TARGET_LINK_LIBRARIES(PyConsole ${PROJECT_LIBRARIES})
+
+#INSTALL(TARGETS PyConsole DESTINATION bin)
+
+
--- /dev/null
+
+#if !defined ( PYCONSOLE_H )
+#define PYCONSOLE_H
+
+// ========================================================
+// set dllexport type for Win platform
+#ifdef WIN32
+# if defined PYCONSOLE_EXPORTS || defined PyConsole_EXPORTS
+# define PYCONSOLE_EXPORT __declspec(dllexport)
+# else
+# define PYCONSOLE_EXPORT __declspec(dllimport)
+# endif
+#else // WIN32
+# define PYCONSOLE_EXPORT
+#endif // WIN32
+
+// ========================================================
+// avoid warning messages
+#ifdef WIN32
+#pragma warning (disable : 4786)
+#pragma warning (disable : 4251)
+#endif
+
+#endif // PYCONSOLE_H
--- /dev/null
+
+/*!
+ \class PyConsole_Console
+ \brief Python console widget.
+*/
+
+#include "PyConsole_Interp.h" /// !!! WARNING !!! THIS INCLUDE MUST BE VERY FIRST !!!
+#include "PyConsole_Console.h"
+#include "PyConsole_EnhEditor.h"
+#include "PyConsole_EnhInterp.h"
+
+//#include <Qtx.h>
+
+#include <QAction>
+#include <QApplication>
+#include <QClipboard>
+#include <QEvent>
+#include <QMenu>
+#include <QVBoxLayout>
+
+/*!
+ \brief Constructor.
+
+ Creates new python console widget.
+ \param parent parent widget
+ \param interp python interpreter
+*/
+PyConsole_Console::PyConsole_Console( QWidget* parent, PyConsole_Interp* interp )
+: QWidget( parent )
+{
+ // create python interpreter
+ myInterp = interp;
+ if ( !myInterp )
+ myInterp = new PyConsole_Interp();
+
+ // initialize Python interpretator
+ myInterp->initialize();
+
+ // create editor console
+ QVBoxLayout* lay = new QVBoxLayout( this );
+ lay->setMargin( 0 );
+ myEditor = new PyConsole_Editor( myInterp, this );
+ char* synchronous = getenv("PYTHON_CONSOLE_SYNC");
+ if (synchronous && atoi(synchronous))
+ {
+ myEditor->setIsSync(true);
+ }
+ myEditor->viewport()->installEventFilter( this );
+ lay->addWidget( myEditor );
+
+ createActions();
+}
+
+/**
+ * Protected constructor.
+ */
+PyConsole_Console::PyConsole_Console( QWidget* parent, PyConsole_Interp* i, PyConsole_Editor* e)
+ : QWidget (parent), myEditor(e), myInterp(i)
+{}
+
+/*!
+ \brief Destructor.
+
+ Does nothing for the moment.
+*/
+PyConsole_Console::~PyConsole_Console()
+{
+}
+
+/*!
+ \brief Execute python command in the interpreter.
+ \param command string with command and arguments
+*/
+void PyConsole_Console::exec( const QString& command )
+{
+ if ( myEditor )
+ myEditor->exec( command );
+}
+
+/*!
+ \brief Execute python command in the interpreter
+ and wait until it is finished.
+
+ Block execution of main application until the python command is executed.
+ \param command string with command and arguments
+*/
+void PyConsole_Console::execAndWait( const QString& command )
+{
+ if ( myEditor )
+ myEditor->execAndWait( command );
+}
+
+/*!
+ \brief Get synchronous mode flag value.
+
+ \sa setIsSync()
+ \return True if python console works in synchronous mode
+*/
+bool PyConsole_Console::isSync() const
+{
+ return myEditor->isSync();
+}
+
+/*!
+ \brief Set synchronous mode flag value.
+
+ In synhronous mode the Python commands are executed in the GUI thread
+ and the GUI is blocked until the command is finished. In the asynchronous
+ mode each Python command is executed in the separate thread that does not
+ block the main GUI loop.
+
+ \param on synhronous mode flag
+*/
+void PyConsole_Console::setIsSync( const bool on )
+{
+ myEditor->setIsSync( on );
+}
+
+/*!
+ \brief Get suppress output flag value.
+
+ \sa setIsSuppressOutput()
+ \return True if python console output is suppressed.
+*/
+bool PyConsole_Console::isSuppressOutput() const
+{
+ return myEditor->isSuppressOutput();
+}
+
+/*!
+ \brief Set suppress output flag value.
+
+ In case if suppress output flag is true, the python
+ console output suppressed.
+
+ \param on suppress output flag
+*/
+void PyConsole_Console::setIsSuppressOutput( const bool on )
+{
+ myEditor->setIsSuppressOutput(on);
+}
+
+/*!
+ \brief Get 'show banner' flag value.
+
+ \sa setIsShowBanner()
+ \return \c true if python console shows banner
+*/
+bool PyConsole_Console::isShowBanner() const
+{
+ return myEditor->isShowBanner();
+}
+
+/*!
+ \brief Set 'show banner' flag value.
+
+ The banner is shown in the top of the python console window.
+
+ \sa isShowBanner()
+ \param on 'show banner' flag
+*/
+void PyConsole_Console::setIsShowBanner( const bool on )
+{
+ myEditor->setIsShowBanner( on );
+}
+
+/*!
+ \brief Change the python console's font.
+ \param f new font
+*/
+void PyConsole_Console::setFont( const QFont& f )
+{
+ if( myEditor )
+ myEditor->setFont( f );
+}
+
+/*!
+ \brief Get python console font.
+ \return current python console's font
+*/
+QFont PyConsole_Console::font() const
+{
+ QFont res;
+ if( myEditor )
+ res = myEditor->font();
+ return res;
+}
+
+/*!
+ \brief Event handler.
+
+ Handles context menu request event.
+
+ \param o object
+ \param e event
+ \return True if the event is processed and further processing should be stopped
+*/
+bool PyConsole_Console::eventFilter( QObject* o, QEvent* e )
+{
+ if ( o == myEditor->viewport() && e->type() == QEvent::ContextMenu )
+ {
+ //contextMenuRequest( (QContextMenuEvent*)e );
+ return true;
+ }
+ return QWidget::eventFilter( o, e );
+}
+
+/*!
+ \brief Create the context popup menu.
+
+ Fill in the popup menu with the commands.
+
+ \param menu context popup menu
+*/
+void PyConsole_Console::contextMenuPopup( QMenu* menu )
+{
+ if ( myEditor->isReadOnly() )
+ return;
+
+ menu->addAction( myActions[CopyId] );
+ menu->addAction( myActions[PasteId] );
+ menu->addAction( myActions[ClearId] );
+ menu->addSeparator();
+ menu->addAction( myActions[SelectAllId] );
+ menu->addSeparator();
+ menu->addAction( myActions[DumpCommandsId] );
+
+ //Qtx::simplifySeparators( menu );
+
+ updateActions();
+}
+
+/*!
+ \brief Set actions to be visible in the context popup menu.
+
+ Actions, which IDs are set in \a flags parameter, will be shown in the
+ context popup menu. Other actions will not be shown.
+
+ \param flags ORed together actions flags
+*/
+void PyConsole_Console::setMenuActions( const int flags )
+{
+ myActions[CopyId]->setVisible( flags & CopyId );
+ myActions[PasteId]->setVisible( flags & PasteId );
+ myActions[ClearId]->setVisible( flags & ClearId );
+ myActions[SelectAllId]->setVisible( flags & SelectAllId );
+ myActions[DumpCommandsId]->setVisible( flags & DumpCommandsId );
+}
+
+/*!
+ \brief Get menu actions which are currently visible in the context popup menu.
+ \return ORed together actions flags
+ \sa setMenuActions()
+*/
+int PyConsole_Console::menuActions() const
+{
+ int ret = 0;
+ ret = ret | ( myActions[CopyId]->isVisible() ? CopyId : 0 );
+ ret = ret | ( myActions[PasteId]->isVisible() ? PasteId : 0 );
+ ret = ret | ( myActions[ClearId]->isVisible() ? ClearId : 0 );
+ ret = ret | ( myActions[SelectAllId]->isVisible() ? SelectAllId : 0 );
+ ret = ret | ( myActions[DumpCommandsId]->isVisible() ? DumpCommandsId : 0 );
+ return ret;
+}
+
+/*!
+ \brief Create menu actions.
+
+ Create context popup menu actions.
+*/
+void PyConsole_Console::createActions()
+{
+ QAction* a = new QAction( tr( "EDIT_COPY_CMD" ), this );
+ a->setStatusTip( tr( "EDIT_COPY_CMD" ) );
+ connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( copy() ) );
+ myActions.insert( CopyId, a );
+
+ a = new QAction( tr( "EDIT_PASTE_CMD" ), this );
+ a->setStatusTip( tr( "EDIT_PASTE_CMD" ) );
+ connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( paste() ) );
+ myActions.insert( PasteId, a );
+
+ a = new QAction( tr( "EDIT_CLEAR_CMD" ), this );
+ a->setStatusTip( tr( "EDIT_CLEAR_CMD" ) );
+ connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( clear() ) );
+ myActions.insert( ClearId, a );
+
+ a = new QAction( tr( "EDIT_SELECTALL_CMD" ), this );
+ a->setStatusTip( tr( "EDIT_SELECTALL_CMD" ) );
+ connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( selectAll() ) );
+ myActions.insert( SelectAllId, a );
+
+ a = new QAction( tr( "EDIT_DUMPCOMMANDS_CMD" ), this );
+ a->setStatusTip( tr( "EDIT_DUMPCOMMANDS_CMD" ) );
+ connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( dump() ) );
+ myActions.insert( DumpCommandsId, a );
+}
+
+/*!
+ \brief Update menu actions.
+
+ Update context popup menu action state.
+*/
+void PyConsole_Console::updateActions()
+{
+ myActions[CopyId]->setEnabled( myEditor->textCursor().hasSelection() );
+ myActions[PasteId]->setEnabled( !myEditor->isReadOnly() && !QApplication::clipboard()->text().isEmpty() );
+ myActions[SelectAllId]->setEnabled( !myEditor->document()->isEmpty() );
+}
+
+/**
+ * Similar to constructor of the base class but using enhanced objects.
+ * TODO: this should really be done in a factory to avoid code duplication.
+ * @param parent
+ * @param interp
+ */
+PyConsole_EnhConsole::PyConsole_EnhConsole( QWidget* parent, PyConsole_EnhInterp* interp)
+ : PyConsole_Console(parent, interp, 0)
+{
+ // create python interpreter
+ myInterp = interp;
+ if ( !myInterp )
+ myInterp = new PyConsole_EnhInterp();
+
+ // initialize Python interpretator
+ myInterp->initialize();
+
+ // create editor console
+ QVBoxLayout* lay = new QVBoxLayout( this );
+ lay->setMargin( 0 );
+ myEditor = new PyConsole_EnhEditor( static_cast<PyConsole_EnhInterp*>(myInterp), this );
+ char* synchronous = getenv("PYTHON_CONSOLE_SYNC");
+ if (synchronous && atoi(synchronous))
+ {
+ myEditor->setIsSync(true);
+ }
+ myEditor->viewport()->installEventFilter( this );
+ lay->addWidget( myEditor );
+
+ createActions();
+}
--- /dev/null
+
+#ifndef PYCONSOLE_CONSOLE_H
+#define PYCONSOLE_CONSOLE_H
+
+#include "PyConsole.h"
+
+//#include <SUIT_PopupClient.h>
+#include <QWidget>
+#include <QMap>
+
+class PyConsole_Interp;
+class PyConsole_Editor;
+class PyConsole_EnhInterp;
+class QMenu;
+
+class PYCONSOLE_EXPORT PyConsole_Console : public QWidget//, public SUIT_PopupClient
+{
+ Q_OBJECT
+
+public:
+ //! Context popup menu actions flags
+ enum
+ {
+ CopyId = 0x01, //!< "Copy" menu action
+ PasteId = 0x02, //!< "Paste" menu action
+ ClearId = 0x04, //!< "Clear" menu action
+ SelectAllId = 0x08, //!< "Select All" menu action
+ DumpCommandsId = 0x16, //!< "DumpCommands" menu action
+ All = CopyId | PasteId | ClearId | SelectAllId | DumpCommandsId //!< all menu actions
+ };
+
+public:
+ PyConsole_Console( QWidget* parent, PyConsole_Interp* interp = 0 );
+ virtual ~PyConsole_Console();
+
+ //! \brief Get python interperter
+ PyConsole_Interp* getInterp() { return myInterp; }
+ QFont font() const;
+ virtual void setFont( const QFont& );
+
+ bool isSync() const;
+ void setIsSync( const bool );
+
+ bool isSuppressOutput() const;
+ void setIsSuppressOutput( const bool );
+
+ bool isShowBanner() const;
+ void setIsShowBanner( const bool );
+
+ void exec( const QString& );
+ void execAndWait( const QString& );
+
+ virtual bool eventFilter( QObject*, QEvent* );
+
+ //! \brief Get popup client symbolic name
+ virtual QString popupClientType() const { return QString( "PyConsole" ); }
+ virtual void contextMenuPopup( QMenu* );
+
+ void setMenuActions( const int );
+ int menuActions() const;
+
+protected:
+ void createActions();
+ void updateActions();
+
+ PyConsole_Console( QWidget* parent, PyConsole_Interp*, PyConsole_Editor*);
+
+
+ PyConsole_Interp* myInterp; //!< python interpreter
+ PyConsole_Editor* myEditor; //!< python console editor widget
+ QMap<int, QAction*> myActions; //!< menu actions list
+};
+
+/**
+ * Enhance console object providing auto-completion.
+ * Similar to PyConsole_Console except that an enhanced interpreter and enhanced editor
+ * are encapsulated.
+ */
+class PYCONSOLE_EXPORT PyConsole_EnhConsole: public PyConsole_Console
+{
+ Q_OBJECT
+
+public:
+ PyConsole_EnhConsole( QWidget* parent, PyConsole_EnhInterp* interp = 0);
+ virtual ~PyConsole_EnhConsole() {}
+};
+
+#endif // PYCONSOLE_CONSOLE_H
--- /dev/null
+
+/*!
+ \class PyConsole_Editor
+ \brief Python command line interpreter front-end GUI widget.
+
+ This class provides simple GUI interface to the Python interpreter, including basic
+ navigation operations, executing commands (both interactively and programmatically),
+ copy-paste operations, history of the commands and so on.
+
+ Here below is the shortcut keyboard boundings used for navigation and other operations:
+ - <Enter> : execute current command
+ - <Ctrl><Break> : clear current command
+ - <Escape> : clear current command
+ - <Up> : previous command in the history
+ - <Shift><Up> : move cursor one row up with selection
+ - <Ctrl><Up> : move cursor one row up without selection
+ - <Ctrl><Shift><Up> : move cursor one row up with selection
+ - <Down> : next command in the history
+ - <Shift><Down> : move cursor one row down with selection
+ - <Ctrl><Down> : move cursor one row down without selection
+ - <Ctrl><Shift><Down> : move cursor one row down with selection
+ - <Left> : move one symbol left without selection
+ - <Shift><Left> : move one symbol left with selection
+ - <Ctrl><Left> : move one word left without selection
+ - <Ctrl><Shift><Left> : move one word left with selection
+ - <Right> : move one symbol right without selection
+ - <Shift><Right> : move one symbol right with selection
+ - <Ctrl><Right> : move one word right without selection
+ - <Ctrl><Shift><Right> : move one word right with selection
+ - <PgUp> : first command in the history
+ - <Shift><PgUp> : move one page up with selection
+ - <Ctrl><PgUp> : move one page up without selection
+ - <Ctrl><Shift><PgUp> : scroll one page up
+ - <PgDn> : last command in the history
+ - <Shift><PgDn> : move one page down with selection
+ - <Ctrl><PgDn> : move one page down without selection
+ - <Ctrl><Shift><PgDn> : scroll one page down
+ - <Home> : move to the beginning of the line without selection
+ - <Shift><Home> : move to the beginning of the line with selection
+ - <Ctrl><Home> : move to the very first symbol without selection
+ - <Ctrl><Shift><Home> : move to the very first symbol with selection
+ - <End> : move to the end of the line without selection
+ - <Shift><End> : move to the end of the line with selection
+ - <Ctrl><End> : move to the very last symbol without selection
+ - <Ctrl><Shift><End> : move to the very last symbol with selection
+ - <Backspace> : delete symbol before the cursor
+ / remove selected text and put it to the clipboard (cut)
+ - <Shift><Backspace> : delete previous word
+ / remove selected text and put it to the clipboard (cut)
+ - <Ctrl><Backspace> : delete text from the cursor to the beginning of the line
+ / remove selected text and put it to the clipboard (cut)
+ - <Delete> : delete symbol after the cursor
+ / remove selected text and put it to the clipboard (cut)
+ - <Shift><Delete> : delete next word
+ / remove selected text and put it to the clipboard (cut)
+ - <Ctrl><Delete> : delete text from the cursor to the end of the line
+ / remove selected text and put it to the clipboard (cut)
+ - <Ctrl><Insert> : copy
+ - <Shift><Insert> : paste
+ - <Ctrl><V> : paste
+ - <Ctrl><C> : copy
+ - <Ctrl><X> : cut
+ - <Ctrl><V> : paste
+*/
+
+#include "PyConsole_Interp.h" // !!! WARNING !!! THIS INCLUDE MUST BE THE VERY FIRST !!!
+#include "PyConsole_Editor.h"
+#include "PyConsole_Event.h"
+#include "PyInterp_Event.h"
+#include "PyInterp_Dispatcher.h"
+#include "PyConsole_Request.h"
+
+//#include <SUIT_Tools.h>
+//#include <SUIT_FileDlg.h>
+//#include <SUIT_MessageBox.h>
+//#include <SUIT_FileValidator.h>
+
+#include <QApplication>
+#include <QClipboard>
+#include <QDropEvent>
+#include <QEvent>
+#include <QKeyEvent>
+#include <QMouseEvent>
+#include <QScrollBar>
+#include <QTextBlock>
+#include <QTextCursor>
+#include <QTextDocument>
+#include <QTextStream>
+#include <QChar>
+
+static QString READY_PROMPT = ">>> ";
+static QString DOTS_PROMPT = "... ";
+
+/*class DumpCommandsFileValidator : public SUIT_FileValidator
+{
+ public:
+ DumpCommandsFileValidator( QWidget* parent = 0 ) : SUIT_FileValidator ( parent ) {};
+ virtual ~DumpCommandsFileValidator() {};
+ virtual bool canSave( const QString& file, bool permissions );
+};
+
+bool DumpCommandsFileValidator::canSave(const QString& file, bool permissions)
+{
+ QFileInfo fi( file );
+ if ( !QRegExp( "[A-Za-z_][A-Za-z0-9_]*" ).exactMatch( fi.completeBaseName() ) ) {
+ SUIT_MessageBox::critical( parent(),
+ QObject::tr("WRN_WARNING"),
+ QObject::tr("WRN_FILE_NAME_BAD") );
+ return false;
+ }
+ return SUIT_FileValidator::canSave( file, permissions);
+}
+*/
+void staticCallbackStdout( void* data, char* c )
+{
+ if(!((PyConsole_Editor*)data)->isSuppressOutput())
+ QApplication::postEvent( (PyConsole_Editor*)data, new PrintEvent( c, false ) );
+}
+
+void staticCallbackStderr( void* data, char* c )
+{
+ if(!((PyConsole_Editor*)data)->isSuppressOutput())
+ QApplication::postEvent( (PyConsole_Editor*)data, new PrintEvent( c, true ) );
+}
+
+
+/*!
+ \brief Constructor.
+
+ Creates python editor window.
+ \param theInterp python interper
+ \param theParent parent widget
+*/
+PyConsole_Editor::PyConsole_Editor( PyConsole_Interp* theInterp,
+ QWidget* theParent )
+: QTextEdit( theParent ),
+ myInterp( 0 ),
+ myCmdInHistory( -1 ),
+ myEventLoop( 0 ),
+ myShowBanner( true ),
+ myIsSync( false ),
+ myIsSuppressOutput( false )
+{
+ //QString fntSet( "" );
+ QFont aFont = QFont( "Courier", 11 );//SUIT_Tools::stringToFont( fntSet );
+ setFont( aFont );
+ setUndoRedoEnabled( false );
+
+ myPrompt = READY_PROMPT;
+ setLineWrapMode( QTextEdit::WidgetWidth );
+ setWordWrapMode( QTextOption::WrapAnywhere );
+ setAcceptRichText( false );
+
+ theInterp->setvoutcb( staticCallbackStdout, this );
+ theInterp->setverrcb( staticCallbackStderr, this );
+
+ // san - This is necessary for troubleless initialization
+ onPyInterpChanged( theInterp );
+}
+
+/*!
+ \brief Destructor.
+
+ Does nothing for the moment.
+*/
+PyConsole_Editor::~PyConsole_Editor()
+{
+}
+
+/*!
+ \brief Get synchronous mode flag value.
+
+ \sa setIsSync()
+ \return True if python console works in synchronous mode
+*/
+bool PyConsole_Editor::isSync() const
+{
+ return myIsSync;
+}
+
+/*!
+ \brief Set synchronous mode flag value.
+
+ In synhronous mode the Python commands are executed in the GUI thread
+ and the GUI is blocked until the command is finished. In the asynchronous
+ mode each Python command is executed in the separate thread that does not
+ block the main GUI loop.
+
+ \param on synhronous mode flag
+*/
+void PyConsole_Editor::setIsSync( const bool on )
+{
+ myIsSync = on;
+}
+
+/*!
+ \brief Get suppress output flag value.
+
+ \sa setIsSuppressOutput()
+ \return \c true if python console output is suppressed.
+*/
+bool PyConsole_Editor::isSuppressOutput() const
+{
+ return myIsSuppressOutput;
+}
+
+/*!
+ \brief Set suppress output flag value.
+
+ In case if suppress output flag is true, the python
+ console output suppressed.
+
+ \param on suppress output flag
+*/
+void PyConsole_Editor::setIsSuppressOutput( const bool on )
+{
+ myIsSuppressOutput = on;
+}
+
+/*!
+ \brief Get 'show banner' flag value.
+
+ \sa setIsShowBanner()
+ \return \c true if python console shows banner
+*/
+bool PyConsole_Editor::isShowBanner() const
+{
+ return myShowBanner;
+}
+
+/*!
+ \brief Set 'show banner' flag value.
+
+ The banner is shown in the top of the python console window.
+
+ \sa isShowBanner()
+ \param on 'show banner' flag
+*/
+void PyConsole_Editor::setIsShowBanner( const bool on )
+{
+ if ( myShowBanner != on ) {
+ myShowBanner = on;
+ clear();
+ }
+}
+
+/*!
+ \brief Get size hint for the Python console window
+ \return size hint value
+*/
+QSize PyConsole_Editor::sizeHint() const
+{
+ QFontMetrics fm( font() );
+ int nbLines = ( isShowBanner() ? myBanner.split("\n").count() : 0 ) + 1;
+ QSize s(100, fm.lineSpacing()*nbLines);
+ return s;
+}
+
+/*!
+ \brief Put the string \a str to the python editor.
+ \param str string to be put in the command line of the editor
+ \param newBlock if True, then the string is printed on a new line
+ \param isError if true, the text is printed in dark red
+*/
+void PyConsole_Editor::addText( const QString& str,
+ const bool newBlock,
+ const bool isError)
+{
+ QTextCursor theCursor(textCursor());
+ QTextCharFormat cf;
+
+ moveCursor( QTextCursor::End );
+ if ( newBlock )
+ theCursor.insertBlock();
+ if (isError)
+ cf.setForeground(QBrush(Qt::red));
+ else
+ cf.setForeground(QBrush(Qt::black));
+ theCursor.insertText( str, cf);
+ moveCursor( QTextCursor::End );
+ ensureCursorVisible();
+}
+
+/*!
+ \brief Convenient method for executing a Python command,
+ as if the user typed it manually.
+ \param command python command to be executed
+
+ !!! WARNING: doesn't work properly with multi-line commands. !!!
+*/
+void PyConsole_Editor::exec( const QString& command )
+{
+ if ( isReadOnly() ) {
+ // some interactive command is being executed in this editor...
+ // shedule the command to the queue
+ myQueue.push_back( command );
+ return;
+ }
+ // remove last line
+ moveCursor( QTextCursor::End );
+ moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
+ textCursor().removeSelectedText();
+ // set "ready" prompt
+ myPrompt = READY_PROMPT;
+ // clear command buffer
+ myCommandBuffer.truncate( 0 );
+ // unset history browsing mode
+ myCmdInHistory = -1;
+ // print command line by line
+ QString cmd = command;
+ if ( !cmd.endsWith( "\n" ) ) cmd += "\n";
+ QStringList lines = command.split( "\n" );
+ for ( int i = 0; i < lines.size(); i++ ) {
+ if ( !lines[i].trimmed().isEmpty() )
+ myHistory.push_back( lines[i] );
+ addText( ( i == 0 ? READY_PROMPT : DOTS_PROMPT ) + lines[i], i != 0 );
+ }
+ // IPAL20182
+ addText( "", true );
+ // set read-only mode
+ setReadOnly( true );
+ // set busy cursor
+ setCursor( Qt::BusyCursor );
+
+ // post a request to execute Python command;
+ // editor will be informed via a custom event that execution has been completed
+ PyInterp_Dispatcher::Get()->Exec( createRequest( cmd ) );
+}
+
+/*!
+ \brief Create request to the python dispatcher for the command execution.
+
+ \param command python command to be executed
+ */
+PyInterp_Request* PyConsole_Editor::createRequest( const QString& command )
+{
+ return new ExecCommand( myInterp, command, this, isSync() );
+}
+
+/*!
+ \brief Execute command in the python interpreter
+ and wait until it is finished.
+
+ \param command python command to be executed
+ */
+void PyConsole_Editor::execAndWait( const QString& command )
+{
+ // already running ?
+ if( myEventLoop )
+ return;
+
+ // create new event loop
+ myEventLoop = new QEventLoop( this );
+ // execute command
+ exec( command );
+ // run event loop
+ myEventLoop->exec();
+ // delete event loop after command is processed
+ delete myEventLoop;
+ myEventLoop = 0;
+}
+
+/*!
+ \brief Process "Enter" key press event.
+
+ Execute the command entered by the user.
+*/
+void PyConsole_Editor::handleReturn()
+{
+ // Position cursor at the end
+ QTextCursor curs(textCursor());
+ curs.movePosition(QTextCursor::End);
+ setTextCursor(curs);
+
+ // get last line
+ QTextBlock par = document()->end().previous();
+ if ( !par.isValid() ) return;
+
+ // get command
+ QString cmd = par.text().remove( 0, promptSize() );
+ // extend the command buffer with the current command
+ myCommandBuffer.append( cmd );
+ // add command to the history
+ if ( !cmd.trimmed().isEmpty() )
+ myHistory.push_back( cmd );
+
+ // IPAL19397
+ addText( "", true );
+
+ // set read-only mode
+ setReadOnly( true );
+ // set busy cursor
+ setCursor( Qt::BusyCursor );
+
+ // post a request to execute Python command;
+ // editor will be informed via a custom event that execution has been completed
+ PyInterp_Dispatcher::Get()->Exec( createRequest( myCommandBuffer ) );
+}
+
+/*!
+ \brief Process drop event.
+
+ Paste dragged text.
+ \param event drop event
+*/
+void PyConsole_Editor::dropEvent( QDropEvent* event )
+{
+ // get the initial drop position
+ QPoint pos = event->pos();
+ QTextCursor cur = cursorForPosition( event->pos() );
+ // if the position is not in the last line move it to the end of the command line
+ if ( cur.position() < document()->end().previous().position() + promptSize() ) {
+ moveCursor( QTextCursor::End );
+ pos = cursorRect().center();
+ }
+ // create new drop event and use it instead of the original
+ QDropEvent de( pos,
+ event->possibleActions(),
+ event->mimeData(),
+ event->mouseButtons(),
+ event->keyboardModifiers(),
+ event->type() );
+ QTextEdit::dropEvent( &de );
+ // accept the original event
+ event->acceptProposedAction();
+}
+
+/*!
+ \brief Process mouse button release event.
+
+ Left mouse button: copy selection to the clipboard.
+ Middle mouse button: paste clipboard's contents.
+ \param event mouse event
+*/
+void PyConsole_Editor::mouseReleaseEvent( QMouseEvent* event )
+{
+ if ( event->button() == Qt::LeftButton ) {
+ QTextEdit::mouseReleaseEvent( event );
+ //copy();
+ }
+ else if ( event->button() == Qt::MidButton ) {
+ QTextCursor cur = cursorForPosition( event->pos() );
+ // if the position is not in the last line move it to the end of the command line
+ if ( cur.position() < document()->end().previous().position() + promptSize() ) {
+ moveCursor( QTextCursor::End );
+ }
+ else {
+ setTextCursor( cur );
+ }
+ const QMimeData* md = QApplication::clipboard()->mimeData( QApplication::clipboard()->supportsSelection() ?
+ QClipboard::Selection : QClipboard::Clipboard );
+ if ( md )
+ insertFromMimeData( md );
+ }
+ else {
+ QTextEdit::mouseReleaseEvent( event );
+ }
+}
+
+/*!
+ \brief Check if the string is command.
+
+ Return True if the string \a str is likely to be the command
+ (i.e. it is started from the '>>>' or '...').
+ \param str string to be checked
+*/
+bool PyConsole_Editor::isCommand( const QString& str ) const
+{
+ return str.startsWith( READY_PROMPT ) || str.startsWith( DOTS_PROMPT );
+}
+
+/*!
+ \brief Handle keyboard event.
+
+ Implement navigation, history browsing, copy/paste and other common
+ operations.
+ \param event keyboard event
+*/
+void PyConsole_Editor::keyPressEvent( QKeyEvent* event )
+{
+ // get cursor position
+ QTextCursor cur = textCursor();
+ int curLine = cur.blockNumber();
+ int curCol = cur.columnNumber();
+
+ // get last edited line
+ int endLine = document()->blockCount()-1;
+
+ // get pressed key code
+ int aKey = event->key();
+
+ // check if <Ctrl> is pressed
+ bool ctrlPressed = event->modifiers() & Qt::ControlModifier;
+ // check if <Shift> is pressed
+ bool shftPressed = event->modifiers() & Qt::ShiftModifier;
+
+ if ( aKey == Qt::Key_Escape || ( ctrlPressed && aKey == -1 ) ) {
+ // process <Ctrl>+<Break> key-binding and <Escape> key: clear current command
+ myCommandBuffer.truncate( 0 );
+ myPrompt = READY_PROMPT;
+ addText( myPrompt, true );
+ horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
+ return;
+ }
+ else if ( ctrlPressed && aKey == Qt::Key_C ) {
+ // process <Ctrl>+<C> key-binding : copy
+ copy();
+ return;
+ }
+ else if ( ctrlPressed && aKey == Qt::Key_X ) {
+ // process <Ctrl>+<X> key-binding : cut
+ cut();
+ return;
+ }
+ else if ( ctrlPressed && aKey == Qt::Key_V ) {
+ // process <Ctrl>+<V> key-binding : paste
+ paste();
+ return;
+ }
+
+ // check for printed key
+ // #### aKey = ( aKey < Qt::Key_Space || aKey > Qt::Key_ydiaeresis ) ? aKey : 0;
+ // Better:
+ aKey = !(QChar(aKey).isPrint()) ? aKey : 0;
+
+ switch ( aKey ) {
+ case 0 :
+ // any printed key: just print it
+ {
+ if ( curLine < endLine || curCol < promptSize() ) {
+ moveCursor( QTextCursor::End );
+ }
+ QTextEdit::keyPressEvent( event );
+ break;
+ }
+ case Qt::Key_Return:
+ case Qt::Key_Enter:
+ // <Enter> key: process the current command
+ {
+ handleReturn();
+ break;
+ }
+ case Qt::Key_Up:
+ // <Up> arrow key: process as follows:
+ // - without <Ctrl>, <Shift> modifiers: previous command in history
+ // - with <Ctrl> modifier key pressed: move cursor one row up without selection
+ // - with <Shift> modifier key pressed: move cursor one row up with selection
+ // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row up
+ {
+ if ( ctrlPressed && shftPressed ) {
+ int value = verticalScrollBar()->value();
+ int spacing = fontMetrics().lineSpacing();
+ verticalScrollBar()->setValue( value > spacing ? value-spacing : 0 );
+ }
+ else if ( shftPressed || ctrlPressed ) {
+ if ( curLine > 0 )
+ moveCursor( QTextCursor::Up,
+ shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
+ }
+ else {
+ if ( myCmdInHistory < 0 && myHistory.count() > 0 ) {
+ // set history browsing mode
+ myCmdInHistory = myHistory.count();
+ // remember current command
+ QTextBlock par = document()->end().previous();
+ myCurrentCommand = par.text().remove( 0, promptSize() );
+ }
+ if ( myCmdInHistory > 0 ) {
+ myCmdInHistory--;
+ // get previous command in the history
+ QString previousCommand = myHistory.at( myCmdInHistory );
+ // print previous command
+ moveCursor( QTextCursor::End );
+ moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
+ textCursor().removeSelectedText();
+ addText( myPrompt + previousCommand );
+ // move cursor to the end
+ moveCursor( QTextCursor::End );
+ }
+ }
+ break;
+ }
+ case Qt::Key_Down:
+ // <Down> arrow key: process as follows:
+ // - without <Ctrl>, <Shift> modifiers: next command in history
+ // - with <Ctrl> modifier key pressed: move cursor one row down without selection
+ // - with <Shift> modifier key pressed: move cursor one row down with selection
+ // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row down
+ {
+ if ( ctrlPressed && shftPressed ) {
+ int value = verticalScrollBar()->value();
+ int maxval = verticalScrollBar()->maximum();
+ int spacing = fontMetrics().lineSpacing();
+ verticalScrollBar()->setValue( value+spacing < maxval ? value+spacing : maxval );
+ }
+ else if ( shftPressed || ctrlPressed) {
+ if ( curLine < endLine )
+ moveCursor( QTextCursor::Down,
+ shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
+ }
+ else {
+ if ( myCmdInHistory >= 0 ) {
+ // get next command in the history
+ myCmdInHistory++;
+ QString nextCommand;
+ if ( myCmdInHistory < myHistory.count() ) {
+ // next command in history
+ nextCommand = myHistory.at( myCmdInHistory );
+ }
+ else {
+ // end of history is reached
+ // last printed command
+ nextCommand = myCurrentCommand;
+ // unset history browsing mode
+ myCmdInHistory = -1;
+ }
+ // print next or current command
+ moveCursor( QTextCursor::End );
+ moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
+ textCursor().removeSelectedText();
+ addText( myPrompt + nextCommand );
+ // move cursor to the end
+ moveCursor( QTextCursor::End );
+ }
+ }
+ break;
+ }
+ case Qt::Key_Left:
+ // <Left> arrow key: process as follows:
+ // - without <Ctrl>, <Shift> modifiers: move one symbol left (taking into account prompt)
+ // - with <Ctrl> modifier key pressed: move one word left (taking into account prompt)
+ // - with <Shift> modifier key pressed: move one symbol left with selection
+ // - with <Ctrl>+<Shift> modifier keys pressed: move one word left with selection
+ {
+ QString txt = textCursor().block().text();
+ if ( !shftPressed && isCommand( txt ) && curCol <= promptSize() ) {
+ moveCursor( QTextCursor::Up );
+ moveCursor( QTextCursor::EndOfBlock );
+ }
+ else {
+ QTextEdit::keyPressEvent( event );
+ }
+ break;
+ }
+ case Qt::Key_Right:
+ // <Right> arrow key: process as follows:
+ // - without <Ctrl>, <Shift> modifiers: move one symbol right (taking into account prompt)
+ // - with <Ctrl> modifier key pressed: move one word right (taking into account prompt)
+ // - with <Shift> modifier key pressed: move one symbol right with selection
+ // - with <Ctrl>+<Shift> modifier keys pressed: move one word right with selection
+ {
+ QString txt = textCursor().block().text();
+ if ( !shftPressed ) {
+ if ( curCol < txt.length() ) {
+ if ( isCommand( txt ) && curCol < promptSize() ) {
+ cur.setPosition( cur.block().position() + promptSize() );
+ setTextCursor( cur );
+ break;
+ }
+ }
+ else {
+ if ( curLine < endLine && isCommand( textCursor().block().next().text() ) ) {
+ cur.setPosition( cur.position() + promptSize()+1 );
+ setTextCursor( cur );
+ horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
+ break;
+ }
+ }
+ }
+ QTextEdit::keyPressEvent( event );
+ break;
+ }
+ case Qt::Key_PageUp:
+ // <PageUp> key: process as follows:
+ // - without <Ctrl>, <Shift> modifiers: first command in history
+ // - with <Ctrl> modifier key pressed: move cursor one page up without selection
+ // - with <Shift> modifier key pressed: move cursor one page up with selection
+ // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page up
+ {
+ if ( ctrlPressed && shftPressed ) {
+ verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub);
+ }
+ else if ( shftPressed || ctrlPressed ) {
+ bool moved = false;
+ qreal lastY = cursorRect( cur ).top();
+ qreal distance = 0;
+ // move using movePosition to keep the cursor's x
+ do {
+ qreal y = cursorRect( cur ).top();
+ distance += qAbs( y - lastY );
+ lastY = y;
+ moved = cur.movePosition( QTextCursor::Up,
+ shftPressed ? QTextCursor::KeepAnchor :
+ QTextCursor::MoveAnchor );
+ } while ( moved && distance < viewport()->height() );
+ if ( moved ) {
+ cur.movePosition( QTextCursor::Down,
+ shftPressed ? QTextCursor::KeepAnchor :
+ QTextCursor::MoveAnchor );
+ verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub );
+ }
+ setTextCursor( cur );
+ }
+ else {
+ if ( myCmdInHistory < 0 && myHistory.count() > 0 ) {
+ // set history browsing mode
+ myCmdInHistory = myHistory.count();
+ // remember current command
+ QTextBlock par = document()->end().previous();
+ myCurrentCommand = par.text().remove( 0, promptSize() );
+ }
+ if ( myCmdInHistory > 0 ) {
+ myCmdInHistory = 0;
+ // get very first command in the history
+ QString firstCommand = myHistory.at( myCmdInHistory );
+ // print first command
+ moveCursor( QTextCursor::End );
+ moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
+ textCursor().removeSelectedText();
+ addText( myPrompt + firstCommand );
+ // move cursor to the end
+ moveCursor( QTextCursor::End );
+ }
+ }
+ break;
+ }
+ case Qt::Key_PageDown:
+ // <PageDown> key: process as follows:
+ // - without <Ctrl>, <Shift> modifiers: last command in history
+ // - with <Ctrl> modifier key pressed: move cursor one page down without selection
+ // - with <Shift> modifier key pressed: move cursor one page down with selection
+ // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page down
+ {
+ if ( ctrlPressed && shftPressed ) {
+ verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd);
+ }
+ else if ( shftPressed || ctrlPressed ) {
+ bool moved = false;
+ qreal lastY = cursorRect( cur ).top();
+ qreal distance = 0;
+ // move using movePosition to keep the cursor's x
+ do {
+ qreal y = cursorRect( cur ).top();
+ distance += qAbs( y - lastY );
+ lastY = y;
+ moved = cur.movePosition( QTextCursor::Down,
+ shftPressed ? QTextCursor::KeepAnchor :
+ QTextCursor::MoveAnchor );
+ } while ( moved && distance < viewport()->height() );
+ if ( moved ) {
+ cur.movePosition( QTextCursor::Up,
+ shftPressed ? QTextCursor::KeepAnchor :
+ QTextCursor::MoveAnchor );
+ verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub );
+ }
+ setTextCursor( cur );
+ }
+ else {
+ if ( myCmdInHistory >= 0 ) {
+ // unset history browsing mode
+ myCmdInHistory = -1;
+ // print current command
+ moveCursor( QTextCursor::End );
+ moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
+ textCursor().removeSelectedText();
+ addText( myPrompt + myCurrentCommand );
+ // move cursor to the end
+ moveCursor( QTextCursor::End );
+ }
+ }
+ break;
+ }
+ case Qt::Key_Home:
+ // <Home> key: process as follows:
+ // - without <Ctrl>, <Shift> modifiers: move cursor to the beginning of the current line without selection
+ // - with <Ctrl> modifier key pressed: move cursor to the very first symbol without selection
+ // - with <Shift> modifier key pressed: move cursor to the beginning of the current line with selection
+ // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very first symbol with selection
+ {
+ if ( ctrlPressed ) {
+ moveCursor( QTextCursor::Start,
+ shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
+ }
+ else {
+ QString txt = textCursor().block().text();
+ if ( isCommand( txt ) ) {
+ if ( shftPressed ) {
+ if ( curCol > promptSize() ) {
+ cur.movePosition( QTextCursor::StartOfLine, QTextCursor::KeepAnchor );
+ cur.movePosition( QTextCursor::Right, QTextCursor::KeepAnchor, promptSize() );
+ }
+ }
+ else {
+ cur.movePosition( QTextCursor::StartOfLine );
+ cur.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, promptSize() );
+ }
+ setTextCursor( cur );
+ }
+ else {
+ moveCursor( QTextCursor::StartOfBlock,
+ shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
+ }
+ horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
+ }
+ break;
+ }
+ case Qt::Key_End:
+ // <End> key: process as follows:
+ // - without <Ctrl>, <Shift> modifiers: move cursor to the end of the current line without selection
+ // - with <Ctrl> modifier key pressed: move cursor to the very last symbol without selection
+ // - with <Shift> modifier key pressed: move cursor to the end of the current line with selection
+ // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very last symbol with selection
+ {
+ moveCursor( ctrlPressed ? QTextCursor::End : QTextCursor::EndOfBlock,
+ shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
+ break;
+ }
+ case Qt::Key_Backspace :
+ // <Backspace> key: process as follows
+ // - without any modifiers : delete symbol before the cursor / selection (taking into account prompt)
+ // - with <Shift> modifier key pressed: delete previous word
+ // - with <Ctrl> modifier key pressed: delete text from the cursor to the line beginning
+ // works only for last (command) line
+ {
+ if ( cur.hasSelection() ) {
+ cut();
+ }
+ else if ( cur.position() > document()->end().previous().position() + promptSize() ) {
+ if ( shftPressed ) {
+ moveCursor( QTextCursor::PreviousWord, QTextCursor::KeepAnchor );
+ textCursor().removeSelectedText();
+ }
+ else if ( ctrlPressed ) {
+ cur.setPosition( document()->end().previous().position() + promptSize(),
+ QTextCursor::KeepAnchor );
+ setTextCursor( cur );
+ textCursor().removeSelectedText();
+ }
+ else {
+ QTextEdit::keyPressEvent( event );
+ }
+ }
+ else {
+ cur.setPosition( document()->end().previous().position() + promptSize() );
+ setTextCursor( cur );
+ horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
+ }
+ break;
+ }
+ case Qt::Key_Delete :
+ // <Delete> key: process as follows
+ // - without any modifiers : delete symbol after the cursor / selection (taking into account prompt)
+ // - with <Shift> modifier key pressed: delete next word
+ // - with <Ctrl> modifier key pressed: delete text from the cursor to the end of line
+ // works only for last (command) line
+ {
+ if ( cur.hasSelection() ) {
+ cut();
+ }
+ else if ( cur.position() > document()->end().previous().position() + promptSize()-1 ) {
+ if ( shftPressed ) {
+ moveCursor( QTextCursor::NextWord, QTextCursor::KeepAnchor );
+ textCursor().removeSelectedText();
+ }
+ else if ( ctrlPressed ) {
+ moveCursor( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor );
+ textCursor().removeSelectedText();
+ }
+ else {
+ QTextEdit::keyPressEvent( event );
+ }
+ }
+ else {
+ cur.setPosition( document()->end().previous().position() + promptSize() );
+ setTextCursor( cur );
+ horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
+ }
+ break;
+ }
+ case Qt::Key_Insert :
+ // <Insert> key: process as follows
+ // - with <Ctrl> modifier key pressed: copy()
+ // - with <Shift> modifier key pressed: paste() to the command line
+ {
+ if ( ctrlPressed ) {
+ copy();
+ }
+ else if ( shftPressed ) {
+ paste();
+ }
+ else
+ QTextEdit::keyPressEvent( event );
+ break;
+ }
+ }
+}
+
+/*!
+ \brief Handle notification event coming from Python dispatcher.
+ \param event notification event
+*/
+void PyConsole_Editor::customEvent( QEvent* event )
+{
+ switch( event->type() )
+ {
+ case PrintEvent::EVENT_ID:
+ {
+ PrintEvent* pe=(PrintEvent*)event;
+ addText( pe->text(), false, pe->isError());
+ return;
+ }
+ case PyInterp_Event::ES_OK:
+ case PyInterp_Event::ES_ERROR:
+ {
+ // clear command buffer
+ myCommandBuffer.truncate( 0 );
+ // add caret return line if necessary
+ QTextBlock par = document()->end().previous();
+ QString txt = par.text();
+ txt.truncate( txt.length() - 1 );
+ // IPAL19397 : addText moved to handleReturn() method
+ //if ( !txt.isEmpty() )
+ // addText( "", true );
+ // set "ready" prompt
+ myPrompt = READY_PROMPT;
+ addText( myPrompt );
+ // unset busy cursor
+ unsetCursor();
+ // stop event loop (if running)
+ if ( myEventLoop )
+ myEventLoop->exit();
+ break;
+ }
+ case PyInterp_Event::ES_INCOMPLETE:
+ {
+ // extend command buffer (multi-line command)
+ myCommandBuffer.append( "\n" );
+ // add caret return line if necessary
+ QTextBlock par = document()->end().previous();
+ QString txt = par.text();
+ txt.truncate( txt.length() - 1 );
+ // IPAL19397 : addText moved to handleReturn() method
+ //if ( !txt.isEmpty() )
+ // addText( "", true );
+ // set "dot" prompt
+ myPrompt = DOTS_PROMPT;
+ addText( myPrompt/*, true*/ ); // IPAL19397
+ // unset busy cursor
+ unsetCursor();
+ // stop event loop (if running)
+ if ( myEventLoop )
+ myEventLoop->exit();
+ break;
+ }
+ default:
+ QTextEdit::customEvent( event );
+ }
+
+ // unset read-only state
+ setReadOnly( false );
+ // unset history browsing mode
+ myCmdInHistory = -1;
+
+ if ( (int)event->type() == (int)PyInterp_Event::ES_OK && myQueue.count() > 0 )
+ {
+ // process the next sheduled command from the queue (if there is any)
+ QString nextcmd = myQueue[0];
+ myQueue.pop_front();
+ exec( nextcmd );
+ }
+}
+
+/*!
+ \brief Handle Python interpreter change.
+
+ Perform initialization actions if the interpreter is changed.
+ \param interp python interpreter is being set
+*/
+void PyConsole_Editor::onPyInterpChanged( PyConsole_Interp* interp )
+{
+ if ( myInterp != interp
+ // Force read-only state and wait cursor when myInterp is NULL
+ || !myInterp ) {
+ myInterp = interp;
+ if ( myInterp ) {
+ // print banner
+ myBanner = myInterp->getbanner().c_str();
+ if ( isShowBanner() )
+ addText( myBanner );
+ // clear command buffer
+ myCommandBuffer.truncate(0);
+ // unset read-only state
+ setReadOnly( false );
+ // unset history browsing mode
+ myCmdInHistory = -1;
+ // add prompt
+ addText( myPrompt );
+ // unset busy cursor
+ viewport()->unsetCursor();
+ // stop event loop (if running)
+ if( myEventLoop)
+ myEventLoop->exit();
+ }
+ else {
+ // clear contents
+ clear();
+ // set read-only state
+ setReadOnly( true );
+ // set busy cursor
+ setCursor( Qt::WaitCursor );
+ }
+ }
+}
+
+/*!
+ \brief "Copy" operation.
+
+ Reimplemented from Qt.
+ Warning! In Qt4 this method is not virtual.
+ */
+void PyConsole_Editor::cut()
+{
+ QTextCursor cur = textCursor();
+ if ( cur.hasSelection() ) {
+ QApplication::clipboard()->setText( cur.selectedText() );
+ int startSelection = cur.selectionStart();
+ if ( startSelection < document()->end().previous().position() + promptSize() )
+ startSelection = document()->end().previous().position() + promptSize();
+ int endSelection = cur.selectionEnd();
+ if ( endSelection < document()->end().previous().position() + promptSize() )
+ endSelection = document()->end().previous().position() + promptSize();
+ cur.setPosition( startSelection );
+ cur.setPosition( endSelection, QTextCursor::KeepAnchor );
+ horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
+ setTextCursor( cur );
+ textCursor().removeSelectedText();
+ }
+}
+
+/*!
+ \brief "Paste" operation.
+
+ Reimplemented from Qt.
+ Warning! In Qt4 this method is not virtual.
+ */
+void PyConsole_Editor::paste()
+{
+ QTextCursor cur = textCursor();
+ if ( cur.hasSelection() ) {
+ int startSelection = cur.selectionStart();
+ if ( startSelection < document()->end().previous().position() + promptSize() )
+ startSelection = document()->end().previous().position() + promptSize();
+ int endSelection = cur.selectionEnd();
+ if ( endSelection < document()->end().previous().position() + promptSize() )
+ endSelection = document()->end().previous().position() + promptSize();
+ cur.setPosition( startSelection );
+ cur.setPosition( endSelection, QTextCursor::KeepAnchor );
+ horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
+ setTextCursor( cur );
+ textCursor().removeSelectedText();
+ }
+ if ( textCursor().position() < document()->end().previous().position() + promptSize() )
+ moveCursor( QTextCursor::End );
+ QTextEdit::paste();
+}
+
+/*!
+ \brief "Clear" operation.
+
+ Reimplemented from Qt.
+ Warning! In Qt4 this method is not virtual.
+ */
+void PyConsole_Editor::clear()
+{
+ QTextEdit::clear();
+ if ( isShowBanner() )
+ addText( myBanner );
+ myPrompt = READY_PROMPT;
+ addText( myPrompt );
+}
+
+/*!
+ \brief "Dump commands" operation.
+ */
+/*void PyConsole_Editor::dump()
+{
+ QStringList aFilters;
+ aFilters.append( tr( "PYTHON_FILES_FILTER" ) );
+
+ QString fileName = SUIT_FileDlg::getFileName( this, QString(),
+ aFilters, tr( "TOT_DUMP_PYCOMMANDS" ),
+ false, true, new DumpCommandsFileValidator( this ) );
+ if ( fileName != "" ) {
+ QFile file( fileName );
+ if ( !file.open( QFile::WriteOnly ) )
+ return;
+
+ QTextStream out (&file);
+
+ for( int i=0; i<myHistory.count(); i++ ) {
+ out<<myHistory[i]<<endl;
+ }
+ file.close();
+ }
+}*/
--- /dev/null
+
+#ifndef PYCONSOLE_EDITOR_H
+#define PYCONSOLE_EDITOR_H
+
+#include "PyConsole.h"
+
+#include <QTextEdit>
+
+class PyConsole_Interp;
+class PyInterp_Request;
+class QEventLoop;
+
+class PYCONSOLE_EXPORT PyConsole_Editor : public QTextEdit
+{
+ Q_OBJECT;
+
+public:
+ PyConsole_Editor( PyConsole_Interp* theInterp, QWidget *theParent = 0 );
+ ~PyConsole_Editor();
+
+ virtual void addText( const QString& str, const bool newBlock = false, const bool isError = false );
+ bool isCommand( const QString& str ) const;
+
+ virtual void exec( const QString& command );
+ void execAndWait( const QString& command );
+
+ bool isSync() const;
+ void setIsSync( const bool );
+
+ bool isSuppressOutput() const;
+ void setIsSuppressOutput(const bool);
+
+ bool isShowBanner() const;
+ void setIsShowBanner( const bool );
+
+ virtual QSize sizeHint() const;
+
+public slots:
+ void cut();
+ void paste();
+ void clear();
+ void handleReturn();
+ void onPyInterpChanged( PyConsole_Interp* );
+ //.void dump();
+
+protected:
+ virtual void dropEvent( QDropEvent* event );
+ virtual void mouseReleaseEvent( QMouseEvent* event );
+ virtual void keyPressEvent ( QKeyEvent* event);
+ virtual void customEvent( QEvent* event);
+
+ virtual PyInterp_Request* createRequest( const QString& );
+
+ /** Convenience function */
+ inline int promptSize() const { return myPrompt.size(); }
+
+ PyConsole_Interp* myInterp; //!< python interpreter
+
+ QString myCommandBuffer; //!< python command buffer
+ QString myCurrentCommand; //!< currently being printed command
+ QString myPrompt; //!< current command line prompt
+ int myCmdInHistory; //!< current history command index
+ QStringList myHistory; //!< commands history buffer
+ QEventLoop* myEventLoop; //!< internal event loop
+ QString myBanner; //!< current banner
+ bool myShowBanner; //!< 'show banner' flag
+ QStringList myQueue; //!< python commands queue
+ bool myIsSync; //!< synchronous mode flag
+ bool myIsSuppressOutput; //!< suppress output flag
+};
+
+#endif // PYCONSOLE_EDITOR_H
--- /dev/null
+
+
+#include "PyConsole.h"
+#include <Python.h>
+
+#include <QKeyEvent>
+#include <QTextBlock>
+#include <QTextCursor>
+#include <QTextCharFormat>
+#include <QRegExp>
+#include <QMimeData>
+
+#include "PyConsole_EnhEditor.h"
+#include "PyConsole_EnhInterp.h"
+#include "PyConsole_Request.h"
+#include "PyInterp_Dispatcher.h"
+
+// Initialize list of valid separators
+static const char * tmp_a[] = {" ", "(", "[","+", "-", "*", "/", ";", "^", "="};
+const std::vector<QString> PyConsole_EnhEditor::SEPARATORS = \
+ std::vector<QString>(tmp_a, tmp_a + sizeof(tmp_a)/sizeof(tmp_a[0]));
+
+/**
+ * Constructor.
+ * @param interp the interpreter linked to the editor
+ * @param parent parent widget
+ */
+PyConsole_EnhEditor::PyConsole_EnhEditor(PyConsole_EnhInterp * interp, QWidget * parent) :
+ PyConsole_Editor(interp, parent),
+ _tab_mode(false),
+ _cursor_pos(-1),
+ _multi_line_paste(false),
+ _multi_line_content()
+{
+ document()->setUndoRedoEnabled(true);
+}
+
+/**
+ * Overrides. Catches the TAB and Ctrl+TAB combinations.
+ * @param event
+ */
+void PyConsole_EnhEditor::keyPressEvent ( QKeyEvent* event)
+{
+ // check if <Ctrl> is pressed
+ bool ctrlPressed = event->modifiers() & Qt::ControlModifier;
+ // check if <Shift> is pressed
+ bool shftPressed = event->modifiers() & Qt::ShiftModifier;
+
+ if (event->key() == Qt::Key_Tab && !shftPressed)
+ {
+ if (!ctrlPressed)
+ handleTab();
+ else
+ {
+ clearCompletion();
+ handleBackTab();
+ }
+ PyConsole_Editor::keyPressEvent(event);
+ }
+ else
+ {
+ // If ctrl is not pressed (and sth else is pressed with it),
+ // or if ctrl is not pressed alone
+ if (!ctrlPressed || (ctrlPressed && event->key() != Qt::Key_Control))
+ {
+ clearCompletion();
+ _cursor_pos = -1;
+ }
+ // Discard ctrl pressed alone:
+ if (event->key() != Qt::Key_Control)
+ PyConsole_Editor::keyPressEvent(event);
+ }
+}
+
+/**
+ * Whenever the mouse is clicked, clear the completion.
+ * @param e
+ */
+void PyConsole_EnhEditor::mousePressEvent(QMouseEvent* e)
+{
+ clearCompletion();
+ _cursor_pos = -1;
+ PyConsole_Editor::mousePressEvent(e);
+}
+
+/**
+ * Clear in the editor the block of text displayed after having hit <TAB>.
+ */
+void PyConsole_EnhEditor::clearCompletion()
+{
+ // Delete completion text if present
+ if (_tab_mode)
+ {
+ // Remove completion display
+ document()->undo();
+ // Remove trailing line return:
+ QTextCursor tc(textCursor());
+ tc.setPosition(document()->characterCount()-1);
+ setTextCursor(tc);
+ textCursor().deletePreviousChar();
+ // TODO: before wait for any TAB event to be completed
+ static_cast<PyConsole_EnhInterp *>(myInterp)->clearCompletion();
+ }
+ _tab_mode = false;
+}
+
+/**
+ * Handle the sequence of events after having hit <TAB>
+ */
+void PyConsole_EnhEditor::handleTab()
+{
+ if (_tab_mode)
+ {
+ // Already tab mode - nothing to do !
+ return;
+ }
+
+ QTextCursor cursor(textCursor());
+
+ // Cursor at end of input
+ cursor.movePosition(QTextCursor::End);
+ setTextCursor(cursor);
+
+ // Save cursor position if needed
+ if (_cursor_pos == -1)
+ _cursor_pos = textCursor().position();
+
+ // get last line
+ QTextBlock par = document()->end().previous();
+ if ( !par.isValid() ) return;
+
+ // Switch to completion mode
+ _tab_mode = true;
+
+ QString cmd = par.text().mid(promptSize());
+
+ // Post completion request
+ // Editor will be informed via a custom event that completion has been run
+ PyInterp_Request* req = createTabRequest(cmd);
+ PyInterp_Dispatcher::Get()->Exec(req);
+}
+
+/**
+ * Handles what happens after hitting Ctrl-TAB
+ */
+void PyConsole_EnhEditor::handleBackTab()
+{
+ QTextCursor cursor(textCursor());
+
+ if (_cursor_pos == -1)
+ {
+ // Invalid cursor position - we can't do anything:
+ return;
+ }
+ // Ensure cursor is at the end of command line
+ cursor.setPosition(_cursor_pos);
+ cursor.movePosition(QTextCursor::EndOfLine);
+ //setCursor(cursor);
+
+ // Delete last completed text
+ int i = cursor.position() - _cursor_pos;
+ cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, i);
+ cursor.removeSelectedText();
+ _cursor_pos = -1;
+}
+
+/**
+ * Create the Python requested that will be posted to the interpreter to
+ * get the completions.
+ * @param input line typed by the user at the time TAB was hit
+ * @return a CompletionCommand
+ * @sa CompletionCommand
+ */
+PyInterp_Request* PyConsole_EnhEditor::createTabRequest( const QString& input )
+{
+ // Parse input to extract on what part the dir() has to be executed
+ QString input2(input);
+
+ // Split up to the last syntaxical separator
+ int lastSp = -1;
+ for (std::vector<QString>::const_iterator i = SEPARATORS.begin(); i != SEPARATORS.end(); i++)
+ {
+ int j = input2.lastIndexOf(*i);
+ if (j > lastSp)
+ lastSp = j;
+ }
+ if (lastSp >= 0)
+ input2 = input.mid(lastSp+1);
+
+ // Detect a qualified name (with a point)
+ int lastPt = input2.lastIndexOf(QString("."));
+
+ // Split the 2 surrounding parts of the qualified name
+ if (lastPt != -1)
+ {
+ _compl_before_point = input2.left(lastPt);
+ _compl_after_point = input2.mid(lastPt+1);
+ }
+ else
+ {
+ // No point found - do a global matching -
+ // (the following will call dir() with an empty string)
+ _compl_after_point = input2;
+ _compl_before_point = QString("");
+ }
+
+ return new CompletionCommand( static_cast<PyConsole_EnhInterp *>(myInterp), _compl_before_point,
+ _compl_after_point, this, isSync() );
+}
+
+/**
+ * Format completion results - this is where we should create 3 columns etc ...
+ * @param matches list of possible completions
+ * @param result return value
+ */
+void PyConsole_EnhEditor::formatCompletion(const std::vector<QString> & matches, QString & result) const
+{
+ int sz = matches.size();
+
+ if (sz > MAX_COMPLETIONS)
+ {
+ sz = MAX_COMPLETIONS;
+ result.append("[Too many matches! Displaying first ones only ...]\n");
+ }
+
+ for (int i = 0; i < sz; ++i)
+ {
+ result.append(matches[i]);
+ result.append("\n");
+ }
+}
+
+/**
+ * Override. Catches the events generated by the enhanced interpreter after the execution
+ * of a completion request.
+ * @param event
+ */
+void PyConsole_EnhEditor::customEvent( QEvent* event )
+{
+ std::vector<QString> matches;
+ QString first_match, comple_text, doc, base;
+ QTextCursor cursor(textCursor());
+ QTextBlockFormat bf;
+ QTextCharFormat cf;
+ PyConsole_EnhInterp * interp = static_cast<PyConsole_EnhInterp *>(myInterp);
+ int cursorPos;
+
+ switch( event->type() )
+ {
+ case PyInterp_Event::ES_TAB_COMPLETE_OK:
+ // Extract corresponding matches from the interpreter
+ matches = interp->getLastMatches();
+
+ if (matches.size() == 0)
+ {
+ // Completion successful but nothing returned.
+ _tab_mode = false;
+ _cursor_pos = -1;
+ return;
+ }
+
+ // Only one match - complete directly and update doc string window
+ doc = interp->getDocStr();
+ if (matches.size() == 1)
+ {
+ first_match = matches[0].mid(_compl_after_point.size());
+ cursor.insertText(first_match);
+ _tab_mode = false;
+ if (doc == QString(""))
+ emit updateDoc(formatDocHTML("(no documentation available)\n"));
+ else
+ emit updateDoc(formatDocHTML(doc));
+ }
+ else
+ {
+ // Detect if there is a common base to all available completion
+ // In this case append this base to the text already
+ extractCommon(matches, base);
+ first_match = base.mid(_compl_after_point.size());
+ cursor.insertText(first_match);
+
+ // If this happens to match exaclty the first completion
+ // also provide doc
+ if (base == matches[0])
+ {
+ doc = formatDocHTML(doc);
+ emit updateDoc(doc);
+ }
+
+ // Print all matching completion in a "undo-able" block
+ cursorPos = cursor.position();
+ cursor.insertBlock();
+ cursor.beginEditBlock();
+
+ // Insert all matches
+ QTextCharFormat cf;
+ cf.setForeground(QBrush(Qt::darkGreen));
+ cursor.setCharFormat(cf);
+ formatCompletion(matches, comple_text);
+ cursor.insertText(comple_text);
+ cursor.endEditBlock();
+
+ // Position cursor where it was before inserting the completion list:
+ cursor.setPosition(cursorPos);
+ setTextCursor(cursor);
+ }
+ break;
+ case PyInterp_Event::ES_TAB_COMPLETE_ERR:
+ // Tab completion was unsuccessful, switch off mode:
+ _tab_mode = false;
+ _cursor_pos = -1;
+ break;
+ case PyInterp_Event::ES_OK:
+ case PyInterp_Event::ES_ERROR:
+ case PyInterp_Event::ES_INCOMPLETE:
+ // Before everything else, call super()
+ PyConsole_Editor::customEvent(event);
+ // If we are in multi_paste_mode, process the next item:
+ multiLineProcessNextLine();
+ break;
+ default:
+ PyConsole_Editor::customEvent( event );
+ break;
+ }
+}
+
+/**
+ * Extract the common leading part of all strings in matches.
+ * @param matches
+ * @param result
+ */
+void PyConsole_EnhEditor::extractCommon(const std::vector<QString> & matches, QString & result) const
+{
+ result = "";
+ int charIdx = 0;
+
+ if (matches.size() < 2)
+ return;
+
+ while (true)
+ {
+ if (charIdx >= matches[0].size())
+ return;
+ QChar ch = matches[0][charIdx];
+ for (int j = 1; j < matches.size(); j++)
+ if (charIdx >= matches[j].size() || matches[j][charIdx] != ch)
+ return;
+ result += ch;
+ charIdx++;
+ }
+}
+
+/**
+ * Format the doc string in HTML format with the first line in bold blue
+ * @param doc initial doc string
+ * @return HTML string
+ */
+QString PyConsole_EnhEditor::formatDocHTML(const QString & doc) const
+{
+ QString templ = QString("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" ") +
+ QString(" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n ") +
+ QString("<html><head><meta name=\"qrichtext\" content=\"1\" /> ") +
+ QString("<style type=\"text/css\">\np, li { white-space: pre-wrap; }\n</style> ") +
+ QString("</head><body style=\" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;\">\n") +
+ QString("<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">") +
+ QString("<span style=\" font-weight:600; color:#0000ff;\">%1</span></p>") +
+ QString("<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">%2</p>") +
+ QString("</body></html>");
+
+ QString fst, rest("");
+
+ // Extract first line of doc
+ int idx = doc.indexOf("\n");
+ if (idx > 0)
+ {
+ fst = doc.left(idx);
+ rest = doc.mid(idx+1);
+ }
+ else
+ {
+ fst = doc;
+ }
+
+ fst = fst.replace("\n", " ");
+ rest = rest.replace("\n", " ");
+ return templ.arg(fst).arg(rest);
+}
+
+/**
+ * Handle properly multi-line pasting. Qt4 doc recommends overriding this function.
+ * If the pasted text doesn't contain a line return, no special treatment is done.
+ * @param source
+ */
+void PyConsole_EnhEditor::insertFromMimeData(const QMimeData * source)
+{
+ if (_multi_line_paste)
+ return;
+
+ if (source->hasText())
+ {
+ QString s = source->text();
+ if (s.contains("\n"))
+ multilinePaste(s);
+ else
+ PyConsole_Editor::insertFromMimeData(source);
+ }
+ else
+ {
+ PyConsole_Editor::insertFromMimeData(source);
+ }
+}
+
+
+void PyConsole_EnhEditor::multilinePaste(const QString & s)
+{
+ // Turn on multi line pasting mode
+ _multi_line_paste = true;
+
+ // Split the string:
+ QString s2 = s;
+ s2.replace("\r", ""); // Windows string format converted to Unix style
+
+ QStringList lst = s2.split(QChar('\n'), QString::KeepEmptyParts);
+
+ // Perform the proper paste operation for the first line to handle the case where
+ // sth was already there:
+ QMimeData source;
+ source.setText(lst[0]);
+ PyConsole_Editor::insertFromMimeData(&source);
+
+ // Prepare what will have to be executed after the first line:
+ _multi_line_content = std::queue<QString>();
+ for (int i = 1; i < lst.size(); ++i)
+ _multi_line_content.push(lst[i]);
+
+ // Trigger the execution of the first (mixed) line
+ handleReturn();
+
+ // See customEvent() and multiLineProcessNext() for the rest of the handling.
+}
+
+/**
+ * Process the next line in the queue of a multiple copy/paste:
+ */
+void PyConsole_EnhEditor::multiLineProcessNextLine()
+{
+ if (!_multi_line_paste)
+ return;
+
+ QString line(_multi_line_content.front());
+ _multi_line_content.pop();
+ if (!_multi_line_content.size())
+ {
+ // last line in the queue, just paste it
+ addText(line, false, false);
+ _multi_line_paste = false;
+ }
+ else
+ {
+ // paste the line and simulate a <RETURN> key stroke
+ addText(line, false, false);
+ handleReturn();
+ }
+}
--- /dev/null
+
+
+#ifndef PYCONSOLE_ENHEDITOR_H_
+#define PYCONSOLE_ENHEDITOR_H_
+
+#include "PyConsole.h"
+
+#include "PyConsole_Editor.h"
+#include <QObject>
+#include <queue>
+
+class PyConsole_EnhInterp;
+
+/**
+ * Enhanced Python editor handling tab completion.
+ */
+class PYCONSOLE_EXPORT PyConsole_EnhEditor: public PyConsole_Editor
+{
+ Q_OBJECT;
+
+public:
+ PyConsole_EnhEditor(PyConsole_EnhInterp * interp, QWidget * parent=0);
+ virtual ~PyConsole_EnhEditor() {}
+
+signals:
+ /**
+ * Signal emitted by the editor widget when the doc string should be updated.
+ * @param doc a HTML block with the formatted doc string.
+ * TODO: for now this signal is left uncaught.
+ */
+ void updateDoc(QString doc);
+
+protected:
+ /** List of separators identifying the last parsable token for completion */
+ static const std::vector<QString> SEPARATORS;
+
+ /** Maximum number of completions shown at once */
+ static const int MAX_COMPLETIONS = 70;
+
+ /** Are we in completion mode */
+ bool _tab_mode;
+
+ /** String on which the dir() comamnd is executed */
+ QString _compl_before_point;
+ /** String on which the results of the dir() are matched */
+ QString _compl_after_point;
+
+ /** Cursor position when <TAB> is hit */
+ int _cursor_pos;
+
+ /** Are we currently pasting several lines */
+ bool _multi_line_paste;
+
+ /** Queue of lines being pasted */
+ std::queue<QString> _multi_line_content;
+
+ // Overrides:
+ virtual void keyPressEvent ( QKeyEvent* event);
+ virtual void customEvent( QEvent* event);
+ virtual void mousePressEvent( QMouseEvent* event );
+ virtual void insertFromMimeData(const QMimeData * source);
+
+ virtual PyInterp_Request* createTabRequest( const QString& input );
+ virtual void handleTab();
+ virtual void handleBackTab();
+ virtual void clearCompletion();
+ virtual void formatCompletion(const std::vector<QString> & matches, QString & result) const;
+ virtual QString formatDocHTML(const QString & doc) const;
+
+ virtual void multilinePaste(const QString & s);
+ virtual void multiLineProcessNextLine();
+
+private:
+ void extractCommon(const std::vector<QString> & matches, QString & result) const;
+
+};
+
+#endif /* PYCONSOLE_ENHEDITOR_H_ */
--- /dev/null
+
+
+
+#include "PyConsole.h"
+
+#include "PyConsole_EnhInterp.h"
+
+#include <pythonrun.h>
+#include <string>
+#include <QRegExp>
+
+static const char * tmp_k[] = {"and", "as", "assert", "break", "class",
+ "continue", "def", "del",
+ "elif", "else", "except", "exec", "finally", "for", "from", "global", "if",
+ "import", "in", "is", "lambda", "not", "or", "pass", "print", "raise",
+ "return", "try", "while", "with", "yield"};
+
+const std::vector<QString> PyConsole_EnhInterp::PYTHON_KEYWORDS = \
+ std::vector<QString>(tmp_k, tmp_k+sizeof(tmp_k)/sizeof(tmp_k[0]));
+
+/*!
+ \brief Run Python dir() command and saves the result internally in _lastPy
+ \param dirArgument Python expression to pass to the dir command. The parsing of what the
+ user actually started typing is dedicated to the caller
+ \param startMatch string representing the begining of the patter to be completed. For example when
+ the user types "a_string_variable.rsp <TAB>", this is "rsp".
+ \return command exit status - 0 = success
+*/
+int PyConsole_EnhInterp::runDirCommand(const QString & dirArgument, const QString & startMatch)
+{
+ int ret;
+ std::vector<QString> v;
+
+ clearCompletion();
+ if ( (ret = runDirAndExtract(dirArgument, startMatch, _last_matches)) )
+ return ret;
+
+ // If dirArgument is empty, we append the __builtins__
+ if (dirArgument.isEmpty())
+ {
+ if ( (ret = runDirAndExtract(QString("__builtins__"), startMatch, _last_matches, false)) )
+ return ret;
+
+ // ... and we match on Python's keywords as well:
+ for (std::vector<QString>::const_iterator it = PYTHON_KEYWORDS.begin(); it != PYTHON_KEYWORDS.end(); it++)
+ if ((*it).startsWith(startMatch))
+ _last_matches.push_back(*it);
+ }
+
+ // Try to get doc string of the first match
+ if (_last_matches.size() > 0)
+ {
+ QString cmd("");
+ if (dirArgument.trimmed() != "")
+ cmd = dirArgument + ".";
+ cmd += _last_matches[0] + ".__doc__";
+ PyObject * str = PyRun_String(cmd.toStdString().c_str(), Py_eval_input, _g, _g);
+ if (!str || str == Py_None || !PyString_Check(str))
+ {
+ if (!str)
+ PyErr_Clear();
+ _doc_str = "";
+ }
+ else
+ _doc_str = QString(PyString_AsString(str));
+ Py_XDECREF(str);
+ }
+
+ // The command has been successfully executed
+ return 0;
+}
+
+/**
+ * See runDirCommand().
+ * @param dirArgument see runDirCommand()
+ * @param startMatch see runDirCommand()
+ * @param[out] result the list of matches
+ * @param discardSwig if true a regular expression is used to discard all static method generated
+ * by SWIG. typically: MEDCouplingUMesh_Blabla
+ * @return -1 if the call to dir() or the parsing of the result failed, 0 otherwise.
+ */
+int PyConsole_EnhInterp::runDirAndExtract(const QString& dirArgument,
+ const QString & startMatch, std::vector<QString> & result,
+ bool discardSwig) const
+{
+ QRegExp re("^[A-Z].+_[A-Z]+[a-z]+.+$"); // discard SWIG static method, e.g. MEDCouplingUMesh_Blabla
+ QString command("dir(" + dirArgument + ")");
+ PyObject * plst = PyRun_String(command.toStdString().c_str(), Py_eval_input, _g, _g);
+ if(!plst || plst == Py_None) {
+ if(!plst)
+ PyErr_Clear();
+
+ Py_XDECREF(plst);
+ return -1;
+ }
+
+ // Extract the returned list and convert it to a vector<>
+ if (!PySequence_Check(plst))
+ {
+ // Should never happen ...
+ //std::cerr << "not a list!" << std::endl;
+ Py_XDECREF(plst);
+ return -1;
+ }
+
+ // Convert plst to a vector of QString
+ int n = PySequence_Length(plst);
+ for (int i = 0; i < n; i++)
+ {
+ PyObject * it;
+ it = PySequence_GetItem(plst, i);
+ QString s(PyString_AsString(it));
+ // if the method is not from swig, not static (guessed from the reg exp) and matches
+ // what is already there
+ if (s.startsWith(startMatch))
+ if(!discardSwig || (!re.exactMatch(s) && !s.contains("swig")))
+ result.push_back(s);
+ Py_DECREF(it);
+ }
+ Py_DECREF(plst);
+
+ return 0;
+}
+
+/**
+ * Clear internal members containing the last completion results.
+ */
+void PyConsole_EnhInterp::clearCompletion()
+{
+ _last_matches.resize(0);
+ _doc_str = QString("");
+}
+
+
+
--- /dev/null
+
+
+#ifndef PYCONSOLE_ENHINTERP_H_
+#define PYCONSOLE_ENHINTERP_H_
+
+#include "PyConsole.h"
+
+#include <Python.h>
+#include "PyConsole_Interp.h"
+
+#include <vector>
+#include <QString>
+
+/**
+ * Enhanced Python interpreter used for auto-completion.
+ * This extends PyConsole_Interp with an API wrapping the Python dir() command nicely.
+ */
+class PYCONSOLE_EXPORT PyConsole_EnhInterp: public PyConsole_Interp
+{
+public:
+ PyConsole_EnhInterp()
+ : PyConsole_Interp(), _last_matches(0), _doc_str("")
+ {}
+
+ virtual ~PyConsole_EnhInterp() {}
+
+ const std::vector<QString>& getLastMatches() const { return _last_matches; }
+ const QString & getDocStr() const { return _doc_str; }
+
+ virtual int runDirCommand(const QString& dirArgument, const QString& startMatch);
+ virtual void clearCompletion();
+
+protected:
+ /** Hard coded list of Python keywords */
+ static const std::vector<QString> PYTHON_KEYWORDS;
+
+ /** Last computed matches */
+ std::vector<QString> _last_matches;
+ /** Doc string of the first match - when only one match it will be displayed by the Editor*/
+ QString _doc_str;
+
+ virtual int runDirAndExtract(const QString& dirArgument, const QString & startMatch,
+ std::vector<QString> & result, bool discardSwig=true) const;
+
+};
+
+#endif /* PYCONSOLE_ENHINTERP_H_ */
--- /dev/null
+
+
+#include "PyConsole_Event.h"
--- /dev/null
+
+
+#ifndef PYCONSOLE_EVENT_H
+#define PYCONSOLE_EVENT_H
+
+#include "PyConsole.h"
+
+#include <QEvent>
+#include <QString>
+
+/*!
+ \class PrintEvent
+ \brief Python command output backend event.
+ \internal
+*/
+class PrintEvent : public QEvent
+{
+public:
+ static const int EVENT_ID = 65432;
+
+ /*!
+ \brief Constructor
+ \param c message text (python trace)
+ \param isError default to false - if true indicates that an error is being printed.
+ */
+ PrintEvent( const char* c, bool isError = false) :
+ QEvent( (QEvent::Type)EVENT_ID ), myText( c ), errorFlag(isError)
+ {}
+
+ /*!
+ \brief Get message
+ \return message text (python trace)
+ */
+ QString text() const { return myText; }
+
+ /**
+ * @return true if this is an error message
+ */
+ bool isError() const { return errorFlag; }
+
+protected:
+ QString myText; //!< Event message (python trace)
+
+ /** Set to true if an error msg is to be displayed */
+ bool errorFlag;
+};
+
+#endif // PYCONSOLE_EVENT_H
--- /dev/null
+
+#include "PyConsole_Interp.h"
+
+/*!
+ \class PyConsole_Interp
+ \brief Python interpreter to be embedded to the SALOME study's GUI.
+
+ Python interpreter is created one per SALOME study.
+
+ Call initialize method defined in the base class PyInterp_Interp,
+ to intialize interpreter after instance creation.
+
+ The method initialize() calls virtuals methods
+ - initPython() to initialize global Python interpreter
+ - initState() to initialize embedded interpreter state
+ - initContext() to initialize interpreter internal context
+ - initRun() to prepare interpreter for running commands
+
+ /EDF-CCAR/
+ When SALOME uses multi Python interpreter feature,
+ every study has its own interpreter and thread state (_tstate = Py_NewInterpreter()).
+ This is fine because every study has its own modules (sys.modules) stdout and stderr.
+
+ <b>But</b> some Python modules must be imported only once. In multi interpreter
+ context Python modules (*.py) are imported several times.
+ For example, the PyQt module must be imported only once because
+ it registers classes in a C module.
+
+ It's quite the same with omniorb modules (internals and generated with omniidl).
+
+ This problem is handled with "shared modules" defined in salome_shared_modules.py.
+ These "shared modules" are imported only once and only copied in all
+ the other interpreters.
+
+ <b>But</b> it's not the only problem. Every interpreter has its own
+ __builtin__ module. That's fine but if we have copied some modules
+ and imported others problems may arise with operations that are not allowed
+ in restricted execution environment. So we must impose that all interpreters
+ have identical __builtin__ module.
+*/
+
+/*!
+ \brief Constructor.
+
+ Creates new python interpreter.
+*/
+PyConsole_Interp::PyConsole_Interp(): PyInterp_Interp()
+{
+}
+
+/*!
+ \brief Destructor.
+
+ Does nothing for the moment.
+*/
+PyConsole_Interp::~PyConsole_Interp()
+{
+}
+
+/*!
+ \brief Initialize internal Python interpreter state.
+
+ When calling initState the GIL is not held
+ It must not be held on exit
+
+ \return \c true on success
+*/
+bool PyConsole_Interp::initState()
+{
+ PyEval_AcquireLock();
+ _tstate = Py_NewInterpreter(); // create an interpreter and save current state
+ PySys_SetArgv(PyInterp_Interp::_argc,PyInterp_Interp::_argv); // initialize sys.argv
+
+ if(!builtinmodule) // PAL18041: deepcopy function don't work in Salome
+ {
+ //builtinmodule is static member of PyInterp class
+ //If it is not NULL (initialized to the builtin module of the main interpreter
+ //all the sub interpreters will have the same builtin
+ //_interp is a static member and is the main interpreter
+ //The first time we initialized it to the builtin of main interpreter
+ builtinmodule=PyDict_GetItemString(_interp->modules, "__builtin__");
+ }
+
+ //If builtinmodule has been initialized all the sub interpreters
+ // will have the same __builtin__ module
+ if(builtinmodule){
+ PyObject *m = PyImport_GetModuleDict();
+ PyDict_SetItemString(m, "__builtin__", builtinmodule);
+ _tstate->interp->builtins = PyModule_GetDict(builtinmodule);
+ Py_INCREF(_tstate->interp->builtins);
+ }
+ PyEval_ReleaseThread(_tstate);
+ return true;
+}
+
+/*!
+ \brief Initialize python interpeter context.
+
+ The GIL is assumed to be held.
+ It is the caller responsability to acquire the GIL.
+ It must still be held on initContext() exit.
+
+ \return \c true on success
+*/
+bool PyConsole_Interp::initContext()
+{
+ PyObject *m = PyImport_AddModule("__main__"); // interpreter main module (module context)
+ if(!m){
+ PyErr_Print();
+ return false;
+ }
+ _g = PyModule_GetDict(m); // get interpreter dictionnary context
+
+ if(builtinmodule){
+ PyDict_SetItemString(_g, "__builtins__", builtinmodule); // assign singleton __builtin__ module
+ }
+ return true;
+}
--- /dev/null
+
+#ifndef PYCONSOLE_INTERP_H
+#define PYCONSOLE_INTERP_H
+
+#include "PyConsole.h"
+
+#include <PyInterp_Interp.h> /// !!! WARNING !!! THIS INCLUDE MUST BE VERY FIRST !!!
+
+class PYCONSOLE_EXPORT PyConsole_Interp : public PyInterp_Interp
+{
+public:
+ PyConsole_Interp();
+ ~PyConsole_Interp();
+
+protected:
+ virtual bool initState();
+ virtual bool initContext();
+};
+
+#endif // PYCONSOLE_INTERP_H
--- /dev/null
+
+
+#include "PyConsole_Request.h"
+
+#include "PyInterp_Event.h"
+#include "PyConsole_Event.h"
+#include "PyConsole_EnhInterp.h"
+#include "PyConsole_EnhEditor.h"
+
+#include <QCoreApplication>
+
+/**
+ * Constructor.
+ * @param theInterp interpreter that will execute the command
+ * @param theCommand command text
+ * @param theListener editor object that will receive the response events after execution
+ * of the request
+ * @param sync
+ */
+ExecCommand::ExecCommand( PyInterp_Interp* theInterp,
+ const QString& theCommand,
+ PyConsole_Editor* theListener,
+ bool sync )
+ : PyInterp_LockRequest( theInterp, theListener, sync ),
+ myCommand( theCommand ), myState( PyInterp_Event::ES_OK )
+ {}
+
+/**
+ * Execute the command by calling the run() method of the embedded interpreter.
+ */
+void ExecCommand::execute()
+{
+ if ( myCommand != "" )
+ {
+ int ret = getInterp()->run( myCommand.toUtf8().data() );
+ if ( ret < 0 )
+ myState = PyInterp_Event::ES_ERROR;
+ else if ( ret > 0 )
+ myState = PyInterp_Event::ES_INCOMPLETE;
+ }
+}
+
+/**
+ * Create the event indicating the status of the request execution.
+ * @return a QEvent
+ */
+QEvent* ExecCommand::createEvent()
+{
+ if ( IsSync() )
+ QCoreApplication::sendPostedEvents( listener(), PrintEvent::EVENT_ID );
+ return new PyInterp_Event( myState, this );
+}
+
+
+/*!
+ Constructor.
+ Creates a new python completion request.
+ \param theInterp python interpreter
+ \param input string containing the dir() command to be executed
+ \param startMatch part to be matched with the results of the dir() command
+ \param theListener widget to get the notification messages
+ \param sync if True the request is processed synchronously
+*/
+CompletionCommand::CompletionCommand( PyConsole_EnhInterp* theInterp,
+ const QString& input,
+ const QString& startMatch,
+ PyConsole_EnhEditor* theListener,
+ bool sync)
+ : PyInterp_LockRequest( theInterp, theListener, sync ),
+ _tabSuccess(false), _dirArg(input), _startMatch(startMatch)
+{}
+
+/**
+ * Execute the completion command by wrapping the runDirCommand() of the
+ * embedded enhanced interpreter.
+ */
+void CompletionCommand::execute()
+{
+ PyConsole_EnhInterp * interp = static_cast<PyConsole_EnhInterp *>(getInterp());
+ int ret = interp->runDirCommand( _dirArg, _startMatch);
+ if (ret == 0)
+ _tabSuccess = true;
+ else
+ _tabSuccess = false;
+}
+
+/**
+ * Create the event indicating the return value of the completion command.
+ * @return
+ */
+QEvent* CompletionCommand::createEvent()
+{
+ int typ = _tabSuccess ? PyInterp_Event::ES_TAB_COMPLETE_OK : PyInterp_Event::ES_TAB_COMPLETE_ERR;
+
+ return new PyInterp_Event( typ, this);
+}
+
+
+
--- /dev/null
+
+
+
+#ifndef PYCONSOLE_REQUEST_H_
+#define PYCONSOLE_REQUEST_H_
+
+#include "PyConsole.h"
+#include "PyInterp_Request.h"
+
+#include <vector>
+#include <QString>
+#include <QEvent>
+
+class PyInterp_Interp;
+class PyConsole_Editor;
+
+/*!
+ \class ExecCommand
+ \brief Python command execution request.
+ \internal
+*/
+class ExecCommand : public PyInterp_LockRequest
+{
+public:
+ /*!
+ \brief Constructor.
+
+ Creates new python command execution request.
+ \param theInterp python interpreter
+ \param theCommand python command
+ \param theListener widget to get the notification messages
+ \param sync if True the request is processed synchronously
+ */
+ ExecCommand( PyInterp_Interp* theInterp,
+ const QString& theCommand,
+ PyConsole_Editor* theListener,
+ bool sync = false );
+
+protected:
+ /*!
+ \brief Execute the python command in the interpreter and
+ get its execution status.
+ */
+ virtual void execute();
+
+ /*!
+ \brief Create and return a notification event.
+ \return new notification event
+ */
+ virtual QEvent* createEvent();
+
+private:
+ QString myCommand; //!< Python command
+ int myState; //!< Python command execution status
+};
+
+class PyConsole_EnhInterp;
+class PyConsole_EnhEditor;
+
+class CompletionCommand : public PyInterp_LockRequest
+{
+public:
+ CompletionCommand( PyConsole_EnhInterp* theInterp,
+ const QString& input,
+ const QString& startMatch,
+ PyConsole_EnhEditor* theListener,
+ bool sync = false );
+
+
+protected:
+ /** List of separators identifying the last parsable token for completion */
+ static const std::vector<QString> SEPARATORS;
+
+ /** String to be passed to the dir() command */
+ QString _dirArg;
+ /** Begining of the command (as typed by the user) */
+ QString _startMatch;
+ /** was the completion command successful */
+ bool _tabSuccess;
+
+ virtual void execute();
+ virtual QEvent* createEvent();
+
+};
+
+#endif /* PYCONSOLE_REQUEST_H_ */
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="en_US">
+<context>
+ <name>PyConsole_Console</name>
+ <message>
+ <location filename="../PyConsole_Console.cxx" line="216"/>
+ <source>EDIT_COPY_CMD</source>
+ <translation>&Copy</translation>
+ </message>
+ <message>
+ <location filename="../PyConsole_Console.cxx" line="221"/>
+ <source>EDIT_PASTE_CMD</source>
+ <translation>&Paste</translation>
+ </message>
+ <message>
+ <location filename="../PyConsole_Console.cxx" line="226"/>
+ <source>EDIT_CLEAR_CMD</source>
+ <translation>Clea&r</translation>
+ </message>
+ <message>
+ <location filename="../PyConsole_Console.cxx" line="231"/>
+ <source>EDIT_SELECTALL_CMD</source>
+ <translation>Select &All</translation>
+ </message>
+ <message>
+ <source>EDIT_DUMPCOMMANDS_CMD</source>
+ <translation>D&ump commands</translation>
+ </message>
+</context>
+<context>
+ <name>PyConsole_Editor</name>
+ <message>
+ <source>TOT_DUMP_PYCOMMANDS</source>
+ <translation>Dump commands</translation>
+ </message>
+ <message>
+ <source>PYTHON_FILES_FILTER</source>
+ <translation>PYTHON Files (*.py)</translation>
+ </message>
+</context>
+</TS>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="fr_FR">
+<context>
+ <name>PyConsole_Console</name>
+ <message>
+ <location filename="../PyConsole_Console.cxx" line="216"/>
+ <source>EDIT_COPY_CMD</source>
+ <translation>&Copier</translation>
+ </message>
+ <message>
+ <location filename="../PyConsole_Console.cxx" line="221"/>
+ <source>EDIT_PASTE_CMD</source>
+ <translation>C&oller</translation>
+ </message>
+ <message>
+ <location filename="../PyConsole_Console.cxx" line="226"/>
+ <source>EDIT_CLEAR_CMD</source>
+ <translation>&Effacer</translation>
+ </message>
+ <message>
+ <location filename="../PyConsole_Console.cxx" line="231"/>
+ <source>EDIT_SELECTALL_CMD</source>
+ <translation>&Tout sélectionner</translation>
+ </message>
+ <message>
+ <source>EDIT_DUMPCOMMANDS_CMD</source>
+ <translation>&Générer le script des commandes</translation>
+ </message>
+</context>
+<context>
+ <name>PyConsole_Editor</name>
+ <message>
+ <source>TOT_DUMP_PYCOMMANDS</source>
+ <translation>&Générer le script des commandes</translation>
+ </message>
+ <message>
+ <source>PYTHON_FILES_FILTER</source>
+ <translation>Fichiers PYTHON (*.py)</translation>
+ </message>
+</context>
+</TS>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS>
+ <context>
+ <name>PyConsole_Console</name>
+ <message>
+ <location filename="../PyConsole_Console.cxx" line="216"/>
+ <source>EDIT_COPY_CMD</source>
+ <translation>コピー(&C)</translation>
+ </message>
+ <message>
+ <location filename="../PyConsole_Console.cxx" line="221"/>
+ <source>EDIT_PASTE_CMD</source>
+ <translation>貼り付け(&P)</translation>
+ </message>
+ <message>
+ <location filename="../PyConsole_Console.cxx" line="226"/>
+ <source>EDIT_CLEAR_CMD</source>
+ <translation>削除(&r)</translation>
+ </message>
+ <message>
+ <location filename="../PyConsole_Console.cxx" line="231"/>
+ <source>EDIT_SELECTALL_CMD</source>
+ <translation>すべて選択します。(&A)</translation>
+ </message>
+ <message>
+ <source>EDIT_DUMPCOMMANDS_CMD</source>
+ <translation>スクリプト コマンドを生成します。(&u)</translation>
+ </message>
+ </context>
+ <context>
+ <name>PyConsole_Editor</name>
+ <message>
+ <source>TOT_DUMP_PYCOMMANDS</source>
+ <translation>スクリプト コマンドを生成します。</translation>
+ </message>
+ <message>
+ <source>PYTHON_FILES_FILTER</source>
+ <translation>ファイル (*.py) PYTHON</translation>
+ </message>
+ </context>
+</TS>
--- /dev/null
+
+SET(PROJECT_HEADERS
+ PyEvent.h
+ PyEvent_Event.h
+ PyEvent_EventFilter.h
+)
+
+SET(PROJECT_SOURCES
+ PyEvent_Event.cxx
+ PyEvent_EventFilter.cxx
+)
+
+#INCLUDE_DIRECTORIES(${Qt5Widgets_INCLUDES})
+
+ADD_DEFINITIONS(-DPYEVENT_EXPORTS)
+
+ADD_LIBRARY(PyEvent STATIC
+ ${PROJECT_SOURCES}
+ ${PROJECT_HEADERS}
+)
+
+TARGET_LINK_LIBRARIES(PyEvent ${Qt5Widgets_LIBRARIES})
+
+#INSTALL(TARGETS PyEvent DESTINATION bin)
--- /dev/null
+
+#if !defined ( PYEVENT_H )
+#define PYEVENT_H
+
+#ifdef WIN32
+# if defined PYEVENT_EXPORTS || defined PyEvent_EXPORTS
+# define PYEVENT_EXPORT __declspec(dllexport)
+# else
+# define PYEVENT_EXPORT __declspec(dllimport)
+# endif
+#else //WIN32
+# define PYEVENT_EXPORT
+#endif //WIN32
+
+#if defined WIN32
+#pragma warning ( disable: 4251 )
+#endif
+
+#endif // PYEVENT_H
--- /dev/null
+
+#include "PyEvent_Event.h"
+
+#include <QSemaphore>
+#include <QApplication>
+
+// asv 21.02.05 : introducing multi-platform approach of thread comparison
+// - on Unix using pthread_t type for storing ThreadId
+// - on Win32 using integer type for storing ThreadId
+// NOT using integer ThreadId on both Unix and Win32 because (from documentation):
+// "...Do not allow your program to rely on the internal structure or size of the pthread_t..."
+
+#ifdef WIN32
+#include <windows.h>
+static DWORD myThread;
+#else
+#include <pthread.h>
+static pthread_t myThread;
+#endif
+
+/*!
+ \class InitEvent
+ \brief Helper event class responsible for initializing PyEvent_Event
+ mechanism by the main thread ID
+ */
+class InitEvent : public PyEvent_Event
+{
+public:
+ InitEvent();
+ virtual ~InitEvent();
+ virtual void Execute();
+};
+
+/*!
+ \brief Constructor, initializes the event mechanism by the current thread ID.
+ It is asssumed to be the main thread ID, so be careful!
+*/
+InitEvent::InitEvent()
+{
+ GetSessionThread();
+}
+
+/*!
+ \brief Destructor, does nothing.
+*/
+InitEvent::~InitEvent()
+{
+}
+
+/*!
+ \brief Nothing to be executed for this kind of event.
+*/
+void InitEvent::Execute()
+{
+}
+
+// NOTE: Here the SALOME event mechanism is initalized by the
+// current thread ID that is always assumed to be the main thread ID.
+// This should be revised as soon as the application library is no longer
+// linked against the Event library (i.e. this static object is not created or created
+// outside the main thread).
+static InitEvent myInitEvent;
+
+/*!
+ \class PyEvent_CustomEvent
+ \brief Generic event class for user-defined events
+
+ This class contains a generic void* data member that may be used
+ for transferring event-specific data to the receiver.
+
+ \warning The internal data is not destroyed by the class destructor.
+*/
+
+/*!
+ \brief Constructor.
+ \param type event type
+*/
+PyEvent_CustomEvent::PyEvent_CustomEvent( int type )
+: QEvent( (QEvent::Type)type ), d( 0 )
+{
+}
+
+/*!
+ \brief Constructor.
+ \param type event type
+ \param data custom data
+*/
+PyEvent_CustomEvent::PyEvent_CustomEvent( QEvent::Type type, void* data )
+: QEvent( type ), d( data )
+{
+}
+
+/*!
+ \brief Get custom data.
+ \return pointer to the internal data
+*/
+void* PyEvent_CustomEvent::data() const
+{
+ return d;
+}
+
+/*!
+ \brief Set custom data.
+ \param data pointer to the internal data
+*/
+void PyEvent_CustomEvent::setData( void* data )
+{
+ d = data;
+}
+
+/*!
+ \class PyEvent_Event
+ \brief The class which encapsulates data and functionality required for
+ posting component-specific events to perform arbitrary operations
+ in the main GUI thread.
+
+ PyEvent_Event objects can be posted by any thread belonging to the GUI process.
+
+ It is necessary to derive a custom event class from PyEvent_Event and
+ re-implement virtual Execute() method. This method should actually perform
+ the desirable operation. To pass all the required data to Execute() and
+ store a return value, arbitrary data fields can be added to the custom
+ event class. There is no need to protect such fields with a mutex, for only
+ one thread working with a PyEvent_Event object is active at any moment.
+
+ Usage:
+ - Create PyEvent_Event. Components can derive their own event class from
+ PyEvent_Event in order to pass custom data to the event handler.
+ - Call process() method to post the event. After process() execution
+ it is possible to examine fields of your custom event object.
+ - Perform delete operator on the event to wake up the desktop (you can also
+ set <autoRelease> parameter to \c true to automatically wake up desktop after
+ process().
+
+ The method processed() is used by the desktop to signal that event processing
+ has been completed.
+
+ To make all this work, it is necessary to call static method GetSessionThread()
+ during the application initialization, i.e. from main() function.
+ It is important to call this method from the primary application thread.
+
+ Caveats:
+ - there are no.
+*/
+
+//! Total number of semaphore resources
+const int NumberOfResources = 2;
+
+/*!
+ \brief Initialize event mechanism.
+
+ This function sets up the main application thread. It should be called
+ during the application initialization, i.e. main() function.
+*/
+void PyEvent_Event::GetSessionThread(){
+#ifdef WIN32
+ myThread = ::GetCurrentThreadId();
+#else
+ myThread = pthread_self();
+#endif
+}
+
+/*!
+ \brief Check if the processing is in the main application thread.
+ \return \c true if this method is called from the main application thread
+*/
+bool PyEvent_Event::IsSessionThread(){
+ bool aResult = false;
+#ifdef WIN32
+ aResult = myThread == ::GetCurrentThreadId();
+#else
+ aResult = myThread == pthread_self();
+#endif
+ return aResult;
+}
+
+/*!
+ \brief Constructor.
+*/
+PyEvent_Event::PyEvent_Event(){
+ // Prepare the semaphore
+ mySemaphore = new QSemaphore( NumberOfResources );
+ mySemaphore->acquire( NumberOfResources );
+}
+
+/*!
+ \brief Destructor.
+*/
+PyEvent_Event::~PyEvent_Event(){
+ if ( mySemaphore->available() < NumberOfResources )
+ mySemaphore->release( NumberOfResources - mySemaphore->available() );
+ delete mySemaphore;
+}
+
+/*!
+ \brief This method should be called by the main GUI thread
+ in order to execute the code specific for this event and finally
+ to inform the calling thread that the event
+ has been processed waking it up with help of the semaphore .
+ */
+void PyEvent_Event::ExecutePostedEvent()
+{
+ // Diagnose incorrect usage of PyEvent_Event API
+ if ( !IsSessionThread() ){
+ qWarning( "PyEvent_Event::ExecutePostedEvent() is called from a secondary thread that might mean an error in application logic!" );
+ }
+ // Actual execution specific for particular kind of event
+ Execute();
+ // Signal the calling thread that the event has been processed
+ processed();
+}
+
+/*!
+ \brief Post the event and wait for its completion.
+ process() should be called from a secondary thread only.
+ \sa processed()
+*/
+void PyEvent_Event::process()
+{
+ // Diagnose incorrect usage of PyEvent_Event API
+ if ( IsSessionThread() ){
+ qWarning( "PyEvent_Event::process() is called from the main GUI thread that might mean an error in application logic!" );
+ }
+
+ QApplication::postEvent( qApp, new PyEvent_CustomEvent( PyEvent_EVENT, (void*)this ) );
+ mySemaphore->acquire( 1 );
+}
+
+/*!
+ \brief Use this method to signal that this event has been processed.
+*/
+void PyEvent_Event::processed()
+{
+ mySemaphore->release( 1 );
+}
+
+/*!
+ \fn virtual void PyEvent_Event::Execute();
+ \brief This method should be redefined in the successor classes
+ to do real work.
+*/
+
+/*!
+ \class TMemFunEvent
+ \brief Template class for event which calls the function
+ without arguments and returning result.
+*/
+
+/*!
+ \class TVoidMemFunEvent
+ \brief Template class for event which calls the function
+ without arguments and without return value.
+*/
+
+/*!
+ \class TMemFun1ArgEvent
+ \brief Template class for event which calls the function
+ with one argument and returning result.
+*/
+
+/*!
+ \class TVoidMemFun1ArgEvent
+ \brief Template class for event which calls the function
+ with one argument and without return value.
+*/
+
+/*!
+ \class TMemFun2ArgEvent
+ \brief Template class for event which calls the function
+ with two arguments and returning result.
+*/
+
+/*!
+ \class TVoidMemFun2ArgEvent
+ \brief Template class for event which calls the function
+ with two arguments and without return value.
+*/
+
+/*!
+ \fn ProcessEvent
+ \brief Template function for processing events with return value.
+*/
+
+/*!
+ \fn ProcessVoidEvent
+ \brief Template function for processing events without return value.
+*/
--- /dev/null
+
+#ifndef PyEvent_PYEVENT_H
+#define PyEvent_PYEVENT_H
+
+#include "PyEvent.h"
+
+#include <QEvent>
+
+//! SALOME custom event type
+#define PyEvent_EVENT QEvent::Type( QEvent::User + 10000 )
+
+class PYEVENT_EXPORT PyEvent_CustomEvent : public QEvent
+{
+public:
+ PyEvent_CustomEvent( int type );
+ PyEvent_CustomEvent( QEvent::Type type, void* data );
+
+ void* data() const;
+ void setData( void* data );
+
+private:
+ void *d; //!< internal data
+};
+
+class QSemaphore;
+
+class PYEVENT_EXPORT PyEvent_Event
+{
+public:
+ PyEvent_Event();
+ virtual ~PyEvent_Event();
+
+ void ExecutePostedEvent();
+ virtual void Execute() = 0;
+
+ static bool IsSessionThread();
+ void process();
+
+protected:
+ void processed();
+ static void GetSessionThread();
+
+private:
+ QSemaphore* mySemaphore; //!< internal semaphore
+};
+
+template<class TObject, typename TRes> class TMemFunEvent : public PyEvent_Event
+{
+public:
+ typedef TRes TResult;
+ TResult myResult;
+ typedef TResult (TObject::* TAction)();
+ TMemFunEvent(TObject* theObject, TAction theAction,
+ TResult theResult = TResult()):
+ myObject(theObject),
+ myAction(theAction),
+ myResult(theResult)
+ {}
+ virtual void Execute()
+ {
+ myResult = (myObject->*myAction)();
+ }
+private:
+ TObject* myObject;
+ TAction myAction;
+};
+
+template<class TObject> class TVoidMemFunEvent : public PyEvent_Event
+{
+public:
+ typedef void (TObject::* TAction)();
+ TVoidMemFunEvent(TObject* theObject, TAction theAction):
+ myObject(theObject),
+ myAction(theAction)
+ {}
+ virtual void Execute()
+ {
+ (myObject->*myAction)();
+ }
+private:
+ TObject* myObject;
+ TAction myAction;
+};
+
+template<class TObject, typename TRes, typename TArg, typename TStoreArg = TArg>
+class TMemFun1ArgEvent : public PyEvent_Event
+{
+public:
+ typedef TRes TResult;
+ TResult myResult;
+ typedef TResult (TObject::* TAction)(TArg);
+ TMemFun1ArgEvent(TObject* theObject, TAction theAction, TArg theArg,
+ TResult theResult = TResult()):
+ myObject(theObject),
+ myAction(theAction),
+ myResult(theResult),
+ myArg(theArg)
+ {}
+ virtual void Execute()
+ {
+ myResult = (myObject->*myAction)(myArg);
+ }
+private:
+ TObject* myObject;
+ TAction myAction;
+ TStoreArg myArg;
+};
+
+template<class TObject, typename TArg, typename TStoreArg = TArg>
+class TVoidMemFun1ArgEvent : public PyEvent_Event
+{
+public:
+ typedef void (TObject::* TAction)(TArg);
+ TVoidMemFun1ArgEvent(TObject* theObject, TAction theAction, TArg theArg):
+ myObject(theObject),
+ myAction(theAction),
+ myArg(theArg)
+ {}
+ virtual void Execute()
+ {
+ (myObject->*myAction)(myArg);
+ }
+private:
+ TObject* myObject;
+ TAction myAction;
+ TStoreArg myArg;
+};
+
+template<class TObject, typename TRes, typename TArg, typename TArg1, typename TStoreArg = TArg, typename TStoreArg1 = TArg1>
+class TMemFun2ArgEvent: public PyEvent_Event
+{
+public:
+ typedef TRes TResult;
+ TResult myResult;
+ typedef TResult (TObject::* TAction)(TArg,TArg1);
+ TMemFun2ArgEvent(TObject* theObject, TAction theAction,
+ TArg theArg, TArg1 theArg1,
+ TResult theResult = TResult()):
+ myObject(theObject),
+ myAction(theAction),
+ myResult(theResult),
+ myArg(theArg),
+ myArg1(theArg1)
+ {}
+ virtual void Execute()
+ {
+ myResult = (myObject->*myAction)(myArg,myArg1);
+ }
+private:
+ TObject* myObject;
+ TAction myAction;
+ TStoreArg myArg;
+ TStoreArg1 myArg1;
+};
+
+template<class TObject, typename TArg, typename TArg1, typename TStoreArg = TArg, typename TStoreArg1 = TArg1>
+class TVoidMemFun2ArgEvent : public PyEvent_Event
+{
+public:
+ typedef void (TObject::* TAction)(TArg,TArg1);
+ TVoidMemFun2ArgEvent(TObject* theObject, TAction theAction, TArg theArg, TArg1 theArg1):
+ myObject(theObject),
+ myAction(theAction),
+ myArg(theArg),
+ myArg1(theArg1)
+ {}
+ virtual void Execute()
+ {
+ (myObject->*myAction)(myArg,myArg1);
+ }
+private:
+ TObject* myObject;
+ TAction myAction;
+ TStoreArg myArg;
+ TStoreArg1 myArg1;
+};
+
+template<class TEvent> inline typename TEvent::TResult ProcessEvent(TEvent* theEvent)
+{
+ typename TEvent::TResult aResult;
+ if(PyEvent_Event::IsSessionThread()) {
+ theEvent->Execute();
+ aResult = theEvent->myResult;
+ }
+ else {
+ theEvent->process();
+ aResult = theEvent->myResult;
+ }
+ delete theEvent;
+ return aResult;
+}
+
+inline void ProcessVoidEvent(PyEvent_Event* theEvent)
+{
+ if(PyEvent_Event::IsSessionThread()) {
+ theEvent->Execute();
+ }
+ else {
+ theEvent->process();
+ }
+ delete theEvent;
+}
+
+#endif // PyEvent_PYEVENT_H
--- /dev/null
+
+#include "PyEvent_EventFilter.h"
+#include "PyEvent_Event.h"
+
+#include <QApplication>
+
+PyEvent_EventFilter* PyEvent_EventFilter::myFilter = NULL;
+
+/*!Constructor.*/
+PyEvent_EventFilter::PyEvent_EventFilter()
+: QObject()
+{
+ /* VSR 13/01/03 : installing global event filter for the application */
+ qApp->installEventFilter( this );
+}
+
+/*!Destructor.*/
+PyEvent_EventFilter::~PyEvent_EventFilter()
+{
+ qApp->removeEventFilter( this );
+}
+
+/*!
+ Custom event filter
+*/
+bool PyEvent_EventFilter::eventFilter( QObject* o, QEvent* e )
+{
+ if ( e->type() == PyEvent_EVENT )
+ {
+ PyEvent_Event* aSE = (PyEvent_Event*)((PyEvent_CustomEvent*)e)->data();
+ processEvent(aSE);
+ ((PyEvent_CustomEvent*)e)->setData( 0 );
+ return true;
+ }
+ return QObject::eventFilter( o, e );
+}
+
+/*!Process event.*/
+void PyEvent_EventFilter::processEvent( PyEvent_Event* theEvent )
+{
+ if(theEvent)
+ theEvent->ExecutePostedEvent();
+}
+
+/*!Create new instance of PyEvent_EventFilter*/
+void PyEvent_EventFilter::Init()
+{
+ if( myFilter==NULL )
+ myFilter = new PyEvent_EventFilter();
+}
+
+/*!Destroy filter.*/
+void PyEvent_EventFilter::Destroy()
+{
+ if( myFilter )
+ {
+ delete myFilter;
+ myFilter = NULL;
+ }
+}
--- /dev/null
+
+#ifndef PyEvent_EVENTFILTER_H
+#define PyEvent_EVENTFILTER_H
+
+#include "PyEvent.h"
+#include <QObject>
+
+#if defined WIN32
+#pragma warning( disable: 4251 )
+#endif
+
+class PyEvent_Event;
+
+/*!
+ Event filter class for QApplication object that handles custom events posted by PyEvent_Event objects.
+ It assumes that such custom events are alwys posted, not sent.
+ This event filter can be installed by any application that intends to use PyEvent_Event mechanism asynchronously.
+ This class replaced SalomeApp_EventFilter.
+*/
+class PYEVENT_EXPORT PyEvent_EventFilter: public QObject
+{
+public:
+ static void Init();
+ static void Destroy();
+
+protected:
+ PyEvent_EventFilter();
+ virtual ~PyEvent_EventFilter();
+
+private:
+ /*! global event filter for qapplication */
+ virtual bool eventFilter( QObject* o, QEvent* e );
+ void processEvent( PyEvent_Event* );
+
+private:
+ static PyEvent_EventFilter* myFilter;
+};
+
+#if defined WIN32
+#pragma warning( default: 4251 )
+#endif
+
+#endif
--- /dev/null
+
+SET(CMAKE_AUTOMOC ON)
+
+# header files
+SET(PROJECT_HEADERS
+ PyInterp.h
+ PyInterp_Dispatcher.h
+ PyInterp_Event.h
+ PyInterp_Interp.h
+ PyInterp_Request.h
+ PyInterp_Watcher.h
+)
+
+SET(PROJECT_AUTOMOC
+ ${CMAKE_CURRENT_BINARY_DIR}/PyInterp_automoc.cpp
+)
+
+# sources / static
+SET(PROJECT_SOURCES
+ PyInterp_Dispatcher.cxx
+ PyInterp_Event.cxx
+ PyInterp_Interp.cxx
+ PyInterp_Request.cxx
+)
+
+SET(PROJECT_LIBRARIES
+ PyEvent
+ ${Qt5Core_LIBRARIES}
+)
+
+SOURCE_GROUP ("Generated Files" FILES ${PROJECT_AUTOMOC})
+
+ADD_DEFINITIONS(-DPYINTERP_EXPORTS -DHAVE_DEBUG_PYTHON)
+
+INCLUDE_DIRECTORIES(
+ ${PROJECT_SOURCE_DIR}/src/PyEvent
+)
+
+ADD_LIBRARY(PyInterp STATIC
+ ${PROJECT_SOURCES}
+ ${PROJECT_HEADERS}
+)
+
+TARGET_LINK_LIBRARIES(PyInterp ${PROJECT_LIBRARIES})
+
+#INSTALL(TARGETS PyInterp DESTINATION bin)
--- /dev/null
+
+#if !defined ( PYINTERP_H )
+#define PYINTERP_H
+
+// ========================================================
+// set dllexport type for Win platform
+#ifdef WIN32
+# if defined PYINTERP_EXPORTS || defined PyInterp_EXPORTS
+# define PYINTERP_EXPORT __declspec(dllexport)
+# else
+# define PYINTERP_EXPORT __declspec(dllimport)
+# endif
+#else // WIN32
+# define PYINTERP_EXPORT
+#endif // WIN32
+
+// ========================================================
+// little trick - if we do not have debug python libraries
+#ifdef _DEBUG
+ #ifndef HAVE_DEBUG_PYTHON
+ #undef _DEBUG
+ #endif
+#endif
+
+#include <Python.h>
+
+#ifdef _DEBUG
+ #ifndef HAVE_DEBUG_PYTHON
+ #define _DEBUG
+ #endif
+#endif
+
+// ========================================================
+// avoid warning messages
+#ifdef WIN32
+#pragma warning (disable : 4786)
+#pragma warning (disable : 4251)
+#endif
+
+#endif // PYINTERP_H
--- /dev/null
+
+#include "PyInterp_Dispatcher.h" // !!! WARNING !!! THIS INCLUDE MUST BE THE VERY FIRST !!!
+#include "PyInterp_Interp.h"
+#include "PyInterp_Watcher.h"
+#include "PyInterp_Request.h"
+
+#include <QObject>
+#include <QCoreApplication>
+
+PyInterp_Dispatcher* PyInterp_Dispatcher::myInstance = 0;
+
+void PyInterp_Request::process()
+{
+ safeExecute();
+
+ bool isSync = IsSync();
+
+ if ( !isSync )
+ myMutex.lock();
+
+ if ( listener() )
+ processEvent( listener() );
+
+ if ( !isSync )
+ myMutex.unlock();
+}
+
+void PyInterp_Request::safeExecute()
+{
+ //ProcessVoidEvent( new PyInterp_ExecuteEvent( this ) );
+ execute();
+}
+
+void PyInterp_Request::Destroy( PyInterp_Request* request )
+{
+ // Lock and unlock the mutex to avoid errors on its deletion
+ request->myMutex.lock();
+ request->myMutex.unlock();
+ delete request;
+}
+
+QEvent* PyInterp_Request::createEvent()
+{
+ return new PyInterp_Event( PyInterp_Event::ES_NOTIFY, this );
+}
+
+void PyInterp_Request::processEvent( QObject* o )
+{
+ if ( !o )
+ return;
+
+ QEvent* e = createEvent();
+ if ( !e )
+ return;
+
+ if ( !IsSync() )
+ QCoreApplication::postEvent( o, e );
+ else
+ {
+ QCoreApplication::sendEvent( o, e );
+ delete e;
+ }
+}
+
+void PyInterp_Request::setListener( QObject* o )
+{
+ myMutex.lock();
+ myListener = o;
+ myMutex.unlock();
+}
+
+void PyInterp_LockRequest::safeExecute()
+{
+ if ( getInterp() ){
+ PyLockWrapper aLock = getInterp()->GetLockWrapper();
+ //ProcessVoidEvent( new PyInterp_ExecuteEvent( this ) );
+ execute();
+ }
+}
+
+PyInterp_Event::~PyInterp_Event()
+{
+ PyInterp_Request::Destroy( myRequest );
+ myRequest = 0;
+}
+
+PyInterp_Dispatcher* PyInterp_Dispatcher::Get()
+{
+ if ( !myInstance )
+ myInstance = new PyInterp_Dispatcher();
+ return myInstance;
+}
+
+PyInterp_Dispatcher::PyInterp_Dispatcher()
+: QThread()
+{
+ myWatcher = new PyInterp_Watcher();
+}
+
+PyInterp_Dispatcher::~PyInterp_Dispatcher()
+{
+ // Clear the request queue
+ myQueueMutex.lock();
+
+ QListIterator<RequestPtr> it( myQueue );
+ while ( it.hasNext() )
+ PyInterp_Request::Destroy( it.next() );
+ myQueue.clear();
+
+ myQueueMutex.unlock();
+
+ // Wait for run() to finish
+ wait();
+
+ delete myWatcher;
+ myWatcher = 0;
+}
+
+bool PyInterp_Dispatcher::IsBusy() const
+{
+ return isRunning();
+}
+
+void PyInterp_Dispatcher::Exec( PyInterp_Request* theRequest )
+{
+ if ( !theRequest )
+ return;
+
+ //if ( theRequest->IsSync() && !IsBusy() ) // synchronous processing - nothing is done if dispatcher is busy!
+ if ( theRequest->IsSync() ) // synchronous processing
+ processRequest( theRequest );
+ else // asynchronous processing
+ {
+ myQueueMutex.lock();
+ myQueue.enqueue( theRequest );
+ if ( theRequest->listener() )
+ QObject::connect( theRequest->listener(), SIGNAL( destroyed( QObject* ) ), myWatcher, SLOT( onDestroyed( QObject* ) ) );
+ myQueueMutex.unlock();
+
+ if ( !IsBusy() )
+ start();
+ }
+}
+
+void PyInterp_Dispatcher::run()
+{
+// MESSAGE("*** PyInterp_Dispatcher::run(): STARTED")
+ PyInterp_Request* aRequest;
+
+ // prepare for queue size check
+ myQueueMutex.lock();
+
+ while( myQueue.size() ) {
+// MESSAGE("*** PyInterp_Dispatcher::run(): next request taken from the queue")
+ aRequest = myQueue.head();
+
+ // let other threads append their requests to the end of the queue
+ myQueueMutex.unlock();
+
+ // processRequest() may delete a request, so this pointer must not be used
+ // after request is processed!
+ processRequest( aRequest );
+
+ // prepare for removal of the first request in the queue
+ myQueueMutex.lock();
+ // IMPORTANT: the first item could have been removed by objectDestroyed() --> we have to check it
+ if ( myQueue.head() == aRequest ) // It's still here --> remove it
+ myQueue.dequeue();
+
+// MESSAGE("*** PyInterp_Dispatcher::run(): request processed")
+ }
+
+ myQueueMutex.unlock();
+// MESSAGE("*** PyInterp_Dispatcher::run(): FINISHED")
+}
+
+void PyInterp_Dispatcher::processRequest( PyInterp_Request* theRequest )
+{
+ theRequest->process();
+}
+
+void PyInterp_Dispatcher::objectDestroyed( const QObject* o )
+{
+ // prepare for modification of the queue
+ myQueueMutex.lock();
+
+ QMutableListIterator<RequestPtr> it( myQueue );
+ while ( it.hasNext() )
+ {
+ RequestPtr r = it.next();
+ if ( o == r->listener() )
+ {
+ r->setListener( 0 ); // to prevent event posting
+ it.remove();
+ }
+ }
+
+ myQueueMutex.unlock();
+}
--- /dev/null
+
+#ifndef PYINTERP_DISPATCHER_H
+#define PYINTERP_DISPATCHER_H
+
+#include "PyInterp.h" // !!! WARNING !!! THIS INCLUDE MUST BE THE VERY FIRST !!!
+
+#include "PyInterp_Request.h" // full include instead of forward declaration
+ // everyone inc'ing the Dispatcher will get the requests for free.
+
+#include <QMutex>
+#include <QThread>
+#include <QQueue>
+
+class QObject;
+class PyInterp_Watcher;
+
+class PYINTERP_EXPORT PyInterp_Dispatcher : protected QThread
+{
+ PyInterp_Dispatcher(); // private constructor
+
+public:
+ static PyInterp_Dispatcher* Get();
+
+ virtual ~PyInterp_Dispatcher();
+
+ bool IsBusy() const;
+ void Exec( PyInterp_Request* );
+
+private:
+ virtual void run();
+ void processRequest( PyInterp_Request* );
+ void objectDestroyed( const QObject* );
+
+private:
+ typedef PyInterp_Request* RequestPtr;
+
+ QQueue<RequestPtr> myQueue;
+ QMutex myQueueMutex;
+ PyInterp_Watcher* myWatcher;
+
+ static PyInterp_Dispatcher* myInstance;
+
+ friend class PyInterp_Watcher;
+};
+
+#endif // PYINTERP_DISPATCHER_H
--- /dev/null
+
+
+#include "PyInterp_Event.h"
+#include "PyInterp_Request.h"
+
+void PyInterp_ExecuteEvent::Execute()
+{
+ myRequest->execute();
+}
--- /dev/null
+
+
+#ifndef PYINTERP_EVENT_H
+#define PYINTERP_EVENT_H
+
+#include "PyInterp.h"
+
+#include <PyEvent_Event.h>
+
+#include <QEvent>
+
+class PyInterp_Request;
+
+class PyInterp_ExecuteEvent: public PyEvent_Event
+{
+public:
+ PyInterp_ExecuteEvent( PyInterp_Request* r )
+ : myRequest( r ) {}
+
+ virtual void Execute();
+
+protected:
+ PyInterp_Request* myRequest;
+};
+
+/**
+ * Events thrown by the interpreter having executed a command and indicating
+ * the return status.
+ */
+class PYINTERP_EXPORT PyInterp_Event : public QEvent
+{
+ PyInterp_Event();
+ PyInterp_Event( const PyInterp_Event& );
+
+public:
+ //Execution state
+ enum { ES_NOTIFY = QEvent::User + 5000, ES_OK, ES_ERROR, ES_INCOMPLETE,
+ ES_TAB_COMPLETE_OK, ES_TAB_COMPLETE_ERR, ES_LAST };
+
+ PyInterp_Event( int type, PyInterp_Request* request )
+ : QEvent( (QEvent::Type)type ), myRequest( request ) {}
+
+ virtual ~PyInterp_Event();
+
+ PyInterp_Request* GetRequest() const { return myRequest; }
+ operator PyInterp_Request*() const { return myRequest; }
+
+private:
+ PyInterp_Request* myRequest;
+};
+
+#endif // PYINTERP_EVENT_H
--- /dev/null
+
+#include "PyInterp_Interp.h" // !!! WARNING !!! THIS INCLUDE MUST BE THE VERY FIRST !!!
+#include <pythread.h>
+
+#include <cStringIO.h>
+#include <structmember.h>
+
+#include <string>
+#include <vector>
+#include <map>
+#include <iostream>
+#include <algorithm>
+
+#define TOP_HISTORY_PY "--- top of history ---"
+#define BEGIN_HISTORY_PY "--- begin of history ---"
+
+// a map to store python thread states that have been created for a given system thread (key=thread id,value=thread state)
+std::map<long,PyThreadState*> currentThreadMap;
+
+/*!
+ \class PyLockWrapper
+ \brief Python GIL wrapper.
+*/
+
+/*!
+ \brief Constructor. Automatically acquires GIL.
+ \param theThreadState python thread state
+*/
+PyLockWrapper::PyLockWrapper(PyThreadState* theThreadState):
+ myThreadState(theThreadState),
+ mySaveThreadState(0)
+{
+ if (myThreadState->interp == PyInterp_Interp::_interp)
+ _savestate = PyGILState_Ensure();
+ else
+ PyEval_AcquireThread(myThreadState);
+}
+
+/*!
+ \brief Destructor. Automatically releases GIL.
+*/
+PyLockWrapper::~PyLockWrapper()
+{
+ if (myThreadState->interp == PyInterp_Interp::_interp)
+ PyGILState_Release(_savestate);
+ else
+ PyEval_ReleaseThread(myThreadState);
+}
+
+/*!
+ \brief Get Python GIL wrapper.
+ \return GIL lock wrapper (GIL is automatically acquired here)
+*/
+PyLockWrapper PyInterp_Interp::GetLockWrapper()
+{
+ if (_tstate->interp == PyInterp_Interp::_interp)
+ return _tstate;
+
+ // If we are here, we have a secondary python interpreter. Try to get a thread state synchronized with the system thread
+ long currentThreadid=PyThread_get_thread_ident(); // the system thread id
+ PyThreadState* theThreadState;
+ if(currentThreadMap.count(currentThreadid) != 0)
+ {
+ //a thread state exists for this thread id
+ PyThreadState* oldThreadState=currentThreadMap[currentThreadid];
+ if(_tstate->interp ==oldThreadState->interp)
+ {
+ //The old thread state has the same python interpreter as this one : reuse the threadstate
+ theThreadState=oldThreadState;
+ }
+ else
+ {
+ //The old thread state has not the same python interpreter as this one : delete the old threadstate and create a new one
+ PyEval_AcquireLock();
+ PyThreadState_Clear(oldThreadState);
+ PyThreadState_Delete(oldThreadState);
+ PyEval_ReleaseLock();
+ theThreadState=PyThreadState_New(_tstate->interp);
+ currentThreadMap[currentThreadid]=theThreadState;
+ }
+ }
+ else
+ {
+ // no old thread state for this thread id : create a new one
+ theThreadState=PyThreadState_New(_tstate->interp);
+ currentThreadMap[currentThreadid]=theThreadState;
+ }
+ return theThreadState;
+}
+
+/*
+ The following functions are used to hook the Python
+ interpreter output.
+*/
+
+static void
+PyStdOut_dealloc(PyStdOut *self)
+{
+ PyObject_Del(self);
+}
+
+static PyObject*
+PyStdOut_write(PyStdOut *self, PyObject *args)
+{
+ char *c;
+ int l;
+ if (!PyArg_ParseTuple(args, "t#:write",&c, &l))
+ return NULL;
+ if(self->_cb==NULL) {
+ if ( self->_iscerr )
+ std::cerr << c ;
+ else
+ std::cout << c ;
+ }
+ else {
+ self->_cb(self->_data,c);
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+PyStdOut_flush(PyStdOut *self)
+{
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyMethodDef PyStdOut_methods[] = {
+ {"write", (PyCFunction)PyStdOut_write, METH_VARARGS, PyDoc_STR("write(string) -> None")},
+ {"flush", (PyCFunction)PyStdOut_flush, METH_NOARGS, PyDoc_STR("flush() -> None")},
+ {NULL, NULL} /* sentinel */
+};
+
+static PyMemberDef PyStdOut_memberlist[] = {
+ {(char*)"softspace", T_INT, offsetof(PyStdOut, softspace), 0,
+ (char*)"flag indicating that a space needs to be printed; used by print"},
+ {NULL} /* Sentinel */
+};
+
+static PyTypeObject PyStdOut_Type = {
+ /* The ob_type field must be initialized in the module init function
+ * to be portable to Windows without using C++. */
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "PyOut", /*tp_name*/
+ sizeof(PyStdOut), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ (destructor)PyStdOut_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ PyObject_GenericGetAttr, /*tp_getattro*/
+ /* softspace is writable: we must supply tp_setattro */
+ PyObject_GenericSetAttr, /* tp_setattro */
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ PyStdOut_methods, /*tp_methods*/
+ PyStdOut_memberlist, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ 0, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+};
+
+#define PyStdOut_Check(v) ((v)->ob_type == &PyStdOut_Type)
+
+static PyStdOut* newPyStdOut( bool iscerr )
+{
+ PyStdOut *self;
+ self = PyObject_New(PyStdOut, &PyStdOut_Type);
+ if (self == NULL)
+ return NULL;
+ self->softspace = 0;
+ self->_cb = NULL;
+ self->_iscerr = iscerr;
+ return self;
+}
+
+/*!
+ \class PyInterp_Interp
+ \brief Generic embedded Python interpreter.
+*/
+
+int PyInterp_Interp::_argc = 1;
+char* PyInterp_Interp::_argv[] = {(char*)""};
+PyObject* PyInterp_Interp::builtinmodule = NULL;
+PyThreadState* PyInterp_Interp::_gtstate = NULL;
+PyInterpreterState* PyInterp_Interp::_interp = NULL;
+
+/*!
+ \brief Basic constructor.
+
+ After construction the interpreter instance successor classes
+ must call virtual method initalize().
+*/
+PyInterp_Interp::PyInterp_Interp():
+ _tstate(0), _vout(0), _verr(0), _g(0)
+{
+}
+
+/*!
+ \brief Destructor.
+*/
+PyInterp_Interp::~PyInterp_Interp()
+{
+}
+
+/*!
+ \brief Initialize embedded interpreter.
+
+ This method shoud be called after construction of the interpreter.
+ The method initialize() calls virtuals methods
+ - initPython() to initialize global Python interpreter
+ - initState() to initialize embedded interpreter state
+ - initContext() to initialize interpreter internal context
+ - initRun() to prepare interpreter for running commands
+ which should be implemented in the successor classes, according to the
+ embedded Python interpreter policy (mono or multi interpreter, etc).
+*/
+void PyInterp_Interp::initialize()
+{
+ _history.clear(); // start a new list of user's commands
+ _ith = _history.begin();
+
+ initPython();
+ // Here the global lock is released
+
+ initState();
+
+ PyEval_AcquireThread(_tstate);
+
+ initContext();
+
+ // used to interpret & compile commands
+ PyObjWrapper m(PyImport_ImportModule("codeop"));
+ if(!m) {
+ PyErr_Print();
+ PyEval_ReleaseThread(_tstate);
+ return;
+ }
+
+ // Create python objects to capture stdout and stderr
+ _vout=(PyObject*)newPyStdOut( false ); // stdout
+ _verr=(PyObject*)newPyStdOut( true ); // stderr
+
+ // All the initRun outputs are redirected to the standard output (console)
+ initRun();
+ PyEval_ReleaseThread(_tstate);
+}
+
+/*!
+ \brief Initialize Python interpreter.
+
+ In case if Python is not initialized, it sets program name, initializes the interpreter, sets program arguments,
+ initializes threads.
+ Otherwise, it just obtains the global interpreter and thread states. This is important for light SALOME configuration,
+ as in full SALOME this is done at SalomeApp level.
+ \sa SalomeApp_PyInterp class
+ */
+void PyInterp_Interp::initPython()
+{
+ if (!Py_IsInitialized()){
+ // Python is not initialized
+ Py_SetProgramName(_argv[0]);
+ Py_Initialize(); // Initialize the interpreter
+ PySys_SetArgv(_argc, _argv);
+ PyEval_InitThreads(); // Create (and acquire) the interpreter lock
+ }
+
+ if ( _interp == NULL )
+ _interp = PyThreadState_Get()->interp;
+ if (PyType_Ready(&PyStdOut_Type) < 0) {
+ PyErr_Print();
+ }
+ if ( _gtstate == NULL )
+ _gtstate = PyEval_SaveThread(); // Release global thread state
+}
+
+/*!
+ \brief Get embedded Python interpreter banner.
+ \return banner string
+ */
+std::string PyInterp_Interp::getbanner()
+{
+ // Should we take the lock ?
+ // PyEval_RestoreThread(_tstate);
+ std::string aBanner("Python ");
+ aBanner = aBanner + Py_GetVersion() + " on " + Py_GetPlatform() ;
+ aBanner = aBanner + "\ntype help to get general information on environment\n";
+ //PyEval_SaveThread();
+ return aBanner;
+}
+
+/*!
+ \brief Initialize run command.
+
+ This method is used to prepare interpreter for running
+ Python commands.
+
+ \return \c true on success and \c false on error
+*/
+bool PyInterp_Interp::initRun()
+{
+ //
+ // probably all below code isn't required
+ //
+ /*
+ PySys_SetObject("stderr",_verr);
+ PySys_SetObject("stdout",_vout);
+
+ //PyObject *m = PyImport_GetModuleDict();
+
+ PySys_SetObject("stdout",PySys_GetObject("__stdout__"));
+ PySys_SetObject("stderr",PySys_GetObject("__stderr__"));
+ */
+ return true;
+}
+
+/*!
+ \brief Compile Python command and evaluate it in the
+ python dictionary context if possible.
+ \internal
+ \param command Python command string
+ \param context Python context (dictionary)
+ \return -1 on fatal error, 1 if command is incomplete and 0
+ if command is executed successfully
+ */
+static int run_command(const char *command, PyObject *context)
+{
+ PyObject *m = PyImport_AddModule("codeop");
+ if(!m) { // Fatal error. No way to go on.
+ PyErr_Print();
+ return -1;
+ }
+ PyObjWrapper v(PyObject_CallMethod(m,(char*)"compile_command",(char*)"s",command));
+ if(!v) {
+ // Error encountered. It should be SyntaxError,
+ //so we don't write out traceback
+ PyObjWrapper exception, value, tb;
+ PyErr_Fetch(&exception, &value, &tb);
+ PyErr_NormalizeException(&exception, &value, &tb);
+ PyErr_Display(exception, value, NULL);
+ return -1;
+ }
+ else if (v == Py_None) {
+ // Incomplete text we return 1 : we need a complete text to execute
+ return 1;
+ }
+ else {
+ // Complete and correct text. We evaluate it.
+ //#if PY_VERSION_HEX < 0x02040000 // python version earlier than 2.4.0
+ // PyObjWrapper r(PyEval_EvalCode(v,context,context));
+ //#else
+ PyObjWrapper r(PyEval_EvalCode((PyCodeObject *)(void *)v,context,context));
+ //#endif
+ if(!r) {
+ // Execution error. We return -1
+ PyErr_Print();
+ return -1;
+ }
+ // The command has been successfully executed. Return 0
+ return 0;
+ }
+}
+
+void replaceAll(std::string& str, const std::string& from, const std::string& to) {
+ if(from.empty())
+ return;
+ size_t start_pos = 0;
+ while((start_pos = str.find(from, start_pos)) != std::string::npos) {
+ str.replace(start_pos, from.length(), to);
+ start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
+ }
+}
+/*!
+ \brief Compile Python command and evaluate it in the
+ python dictionary context if possible. Command might correspond to
+ the execution of a script with optional arguments.
+ In this case, command is:
+ execfile(r"/absolute/path/to/script.py [args:arg1,...,argn]")
+ \internal
+ \param command Python command string
+ \param context Python context (dictionary)
+ \return -1 on fatal error, 1 if command is incomplete and 0
+ if command is executed successfully
+ */
+static int compile_command(const char *command,PyObject *context)
+{
+ // First guess if command is execution of a script with args, or a simple Python command
+ std::string singleCommand = command;
+ std::string commandArgs = "";
+
+ std::size_t pos = std::string(command).find("args:");
+ if (pos != std::string::npos) {
+ commandArgs = singleCommand.substr(pos+5);
+ commandArgs = commandArgs.substr(0, commandArgs.length()-3);
+ singleCommand = singleCommand.substr(0, pos-1)+"\")";
+ }
+
+ if (commandArgs.empty()) {
+ // process command: expression
+ // process command: execfile(r"/absolute/path/to/script.py") (no args)
+ return run_command(singleCommand.c_str(), context);
+ }
+ else {
+ // process command: execfile(r"/absolute/path/to/script.py [args:arg1,...,argn]")
+ std::string script = singleCommand.substr(11); // remove leading execfile(r"
+ script = script.substr(0, script.length()-2); // remove trailing ")
+
+ std::string preCommandBegin = "import sys; save_argv = sys.argv; sys.argv=[";
+ std::string preCommandEnd = "];";
+ replaceAll(commandArgs, ",", "\",\"");
+ commandArgs = "\""+commandArgs+"\"";
+ std::string completeCommand = preCommandBegin+"\""+script+"\","+commandArgs+preCommandEnd+singleCommand+";sys.argv=save_argv";
+ return run_command(completeCommand.c_str(), context);
+ }
+}
+
+/*!
+ \brief Run Python command.
+ \param command Python command
+ \return command status
+*/
+int PyInterp_Interp::run(const char *command)
+{
+ beforeRun();
+ return simpleRun(command);
+}
+
+/*!
+ \brief Run Python command (used internally).
+ \param command Python command
+ \param addToHistory if \c true (default), the command is added to the commands history
+ \return command status
+*/
+int PyInterp_Interp::simpleRun(const char *command, const bool addToHistory)
+{
+ if( addToHistory && strcmp(command,"") != 0 ) {
+ _history.push_back(command);
+ _ith = _history.end();
+ }
+
+ // We come from C++ to enter Python world
+ // We need to acquire the Python global lock
+ //PyLockWrapper aLock(_tstate); // san - lock is centralized now
+
+ // Reset redirected outputs before treatment
+ PySys_SetObject((char*)"stderr",_verr);
+ PySys_SetObject((char*)"stdout",_vout);
+
+ int ier = compile_command(command,_g);
+
+ // Outputs are redirected on standards outputs (console)
+ PySys_SetObject((char*)"stdout",PySys_GetObject((char*)"__stdout__"));
+ PySys_SetObject((char*)"stderr",PySys_GetObject((char*)"__stderr__"));
+
+ return ier;
+}
+
+/*!
+ \brief Get previous command in the commands history.
+ \return previous command
+*/
+const char * PyInterp_Interp::getPrevious()
+{
+ if(_ith != _history.begin()){
+ _ith--;
+ return (*_ith).c_str();
+ }
+ else
+ return BEGIN_HISTORY_PY;
+}
+
+/*!
+ \brief Get next command in the commands history.
+ \return next command
+*/
+const char * PyInterp_Interp::getNext()
+{
+ if(_ith != _history.end()){
+ _ith++;
+ }
+ if (_ith == _history.end())
+ return TOP_HISTORY_PY;
+ else
+ return (*_ith).c_str();
+}
+
+/*!
+ \brief Set Python standard output device hook.
+ \param cb callback function
+ \param data callback function parameters
+*/
+void PyInterp_Interp::setvoutcb(PyOutChanged* cb, void* data)
+{
+ ((PyStdOut*)_vout)->_cb=cb;
+ ((PyStdOut*)_vout)->_data=data;
+}
+
+/*!
+ \brief Set Python standard error device hook.
+ \param cb callback function
+ \param data callback function parameters
+*/
+void PyInterp_Interp::setverrcb(PyOutChanged* cb, void* data)
+{
+ ((PyStdOut*)_verr)->_cb=cb;
+ ((PyStdOut*)_verr)->_data=data;
+}
--- /dev/null
+
+#ifndef PYINTERP_INTERP_H
+#define PYINTERP_INTERP_H
+
+#include "PyInterp.h" // !!! WARNING !!! THIS INCLUDE MUST BE THE VERY FIRST !!!
+
+#include <list>
+#include <string>
+
+class PYINTERP_EXPORT PyLockWrapper
+{
+ PyThreadState* myThreadState;
+ PyThreadState* mySaveThreadState;
+ PyGILState_STATE _savestate;
+public:
+ PyLockWrapper(PyThreadState* theThreadState);
+ ~PyLockWrapper();
+};
+
+typedef void PyOutChanged(void* data,char * c);
+
+class PYINTERP_EXPORT PyInterp_Interp
+{
+public:
+ static int _argc;
+ static char* _argv[];
+ static PyObject *builtinmodule;
+ static PyThreadState *_gtstate;
+ static PyInterpreterState *_interp;
+
+ PyInterp_Interp();
+ virtual ~PyInterp_Interp();
+
+ void initialize();
+
+ virtual int run(const char *command);
+
+ PyLockWrapper GetLockWrapper();
+
+ std::string getbanner();
+ void setverrcb(PyOutChanged*,void*);
+ void setvoutcb(PyOutChanged*,void*);
+
+ const char * getPrevious();
+ const char * getNext();
+
+protected:
+ PyThreadState * _tstate;
+ PyObject * _vout;
+ PyObject * _verr;
+ PyObject * _g;
+ PyObject * _codeop;
+ std::list<std::string> _history;
+ std::list<std::string>::iterator _ith;
+
+ virtual int beforeRun() { return 0; }
+ int simpleRun(const char* command, const bool addToHistory = true);
+
+ virtual bool initRun();
+ virtual void initPython();
+ virtual bool initState() = 0;
+ virtual bool initContext() = 0;
+};
+
+class PYINTERP_EXPORT PyObjWrapper
+{
+ PyObject* myObject;
+public:
+ PyObjWrapper(PyObject* theObject) : myObject(theObject) {}
+ PyObjWrapper() : myObject(0) {}
+ virtual ~PyObjWrapper() { Py_XDECREF(myObject); }
+
+ operator PyObject*() { return myObject; }
+ PyObject* operator->() { return myObject; }
+ PyObject* get() { return myObject; }
+ bool operator!() { return !myObject; }
+ bool operator==(PyObject* theObject) { return myObject == theObject; }
+ PyObject** operator&() { return &myObject; }
+ PyObjWrapper& operator=(PyObjWrapper* theObjWrapper)
+ {
+ Py_XDECREF(myObject);
+ myObject = theObjWrapper->myObject;
+ return *this;
+ }
+};
+
+typedef struct {
+ PyObject_HEAD
+ int softspace;
+ PyOutChanged* _cb;
+ void* _data;
+ bool _iscerr;
+} PyStdOut;
+
+#endif // PYINTERP_INTERP_H
--- /dev/null
+
+
+#include "PyInterp_Request.h"
+
--- /dev/null
+
+
+#ifndef PYINTERP_REQUEST_H
+#define PYINTERP_REQUEST_H
+
+#include "PyInterp.h"
+#include "PyInterp_Event.h"
+
+#include <QMutex>
+#include <QObject>
+
+class PyInterp_Interp;
+class PyInterp_Watcher;
+class PyInterp_Dispatcher;
+class PyInterp_ExecuteEvent;
+class PyConsole_Editor;
+
+class PYINTERP_EXPORT PyInterp_Request
+{
+ friend class PyInterp_Dispatcher;
+ friend class PyInterp_ExecuteEvent;
+
+ PyInterp_Request();
+ PyInterp_Request( const PyInterp_Request& );
+
+protected:
+ virtual ~PyInterp_Request() {};
+ // protected destructor - to control deletion of requests
+
+public:
+ PyInterp_Request( QObject* listener, bool sync = false )
+ : myIsSync( sync ), myListener( listener ) {};
+
+ static void Destroy( PyInterp_Request* );
+ // Deletes a request
+
+ bool IsSync() const { return myIsSync; }
+ // Returns true if this request should be processed synchronously,
+ // without putting it to a queue
+
+protected:
+ virtual void safeExecute();
+
+ virtual void execute() = 0;
+ // Should be redefined in successors, contains actual request code
+
+ virtual QEvent* createEvent();
+ // This method can be overridden to customize notification event creation
+
+ virtual void processEvent( QObject* );
+
+ QObject* listener() const { return myListener; }
+ void setListener( QObject* );
+
+private:
+ void process();
+
+private:
+ QMutex myMutex;
+ bool myIsSync;
+ QObject* myListener;
+};
+
+class PYINTERP_EXPORT PyInterp_LockRequest : public PyInterp_Request
+{
+public:
+
+ PyInterp_LockRequest( PyInterp_Interp* interp, QObject* listener = 0, bool sync = false )
+ : PyInterp_Request( listener, sync ), myInterp( interp )
+ {}
+
+protected:
+ PyInterp_Interp* getInterp() const { return myInterp; }
+
+ virtual void safeExecute();
+
+private:
+ PyInterp_Interp* myInterp;
+};
+
+#endif // PYINTERP_REQUEST_H
--- /dev/null
+
+#ifndef PYINTERP_WATCHER_H
+#define PYINTERP_WATCHER_H
+
+#include "PyInterp.h" // !!! WARNING !!! THIS INCLUDE MUST BE THE VERY FIRST !!!
+
+#include "PyInterp_Dispatcher.h"
+
+#include <QObject>
+
+// Private class that keeps track of destructions of request listeners
+class PYINTERP_EXPORT PyInterp_Watcher : public QObject
+{
+ Q_OBJECT
+
+public:
+ PyInterp_Watcher() : QObject( 0 ) {}
+ virtual ~PyInterp_Watcher() {}
+
+public slots:
+ void onDestroyed( QObject* o ) { PyInterp_Dispatcher::Get()->objectDestroyed( o ); }
+};
+
+#endif // PYINTERP_WATCHER_H
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.11)
-INCLUDE(Common)
INCLUDE(FindCAS)
SET(CMAKE_AUTOMOC ON)
${Qt5Widgets_LIBRARIES}
${CAS_VIEWER}
${CAS_KERNEL}
+ PyConsole
+ PyInterp
+ PyEvent
)
QT5_ADD_RESOURCES(PROJECT_COMPILED_RESOURCES ${PROJECT_RESOURCES})
INCLUDE_DIRECTORIES (${PROJECT_SOURCE_DIR}/src/Event
${PROJECT_SOURCE_DIR}/src/Config
+ ${PROJECT_SOURCE_DIR}/src/PyInterp
+ ${PROJECT_SOURCE_DIR}/src/PyConsole
${CAS_INCLUDE_DIRS})
+LINK_DIRECTORIES($ENV{PYTHON_LIB_DIR})
+
ADD_EXECUTABLE(XGUI WIN32
${PROJECT_SOURCES}
${PROJECT_HEADERS}
: QObject(parent), myDesktop(parent)
{
parent->setTabPosition(Qt::TopDockWidgetArea, QTabWidget::North);
+ myGeneralPage = addWorkbench(tr("GEN_MENU_TITLE"));
}
XGUI_MainMenu::~XGUI_MainMenu(void)
XGUI_Workbench* addWorkbench(const QString& theId, const QString& theText = "");
XGUI_Workbench* findWorkbench(const QString& theId);
+ XGUI_Workbench* generalPage() const { return myGeneralPage; }
+
+ QDockWidget* getLastDockWindow() const { return myMenuTabs.last(); }
+
private:
XGUI_MainWindow* myDesktop;
QList<QDockWidget*> myMenuTabs;
+ XGUI_Workbench* myGeneralPage;
};
#endif
#include "XGUI_ViewWindow.h"
#include "XGUI_Viewer.h"
+#include <PyConsole_Console.h>
+#include <PyConsole_EnhInterp.h>
+
#include <QMdiArea>
#include <QTreeWidget>
#include <QDockWidget>
#include <QComboBox>
XGUI_MainWindow::XGUI_MainWindow(QWidget* parent)
- : QMainWindow(parent), myObjectBrowser(0)
+ : QMainWindow(parent),
+ myObjectBrowser(0),
+ myPythonConsole(0)
{
setWindowTitle(tr("WINDOW_TITLE"));
myMenuBar = new XGUI_MainMenu(this);
addDockWidget(Qt::LeftDockWidgetArea, aDoc);
//aDoc->hide();
- aDoc = new QDockWidget(this);
- aDoc->setFeatures(QDockWidget::AllDockWidgetFeatures | QDockWidget::DockWidgetVerticalTitleBar);
- aDoc->setMinimumHeight(0);
- aDoc->setWindowTitle("Console");
- QTextEdit* aTextEdt = new QTextEdit(aDoc);
- aTextEdt->setText(">>>");
- aDoc->setWidget(aTextEdt);
- aTextEdt->setMinimumHeight(0);
- addDockWidget(Qt::BottomDockWidgetArea, aDoc);
-
QMdiArea* aMdiArea = new QMdiArea(this);
setCentralWidget(aMdiArea);
myViewer = new XGUI_Viewer(this);
- //aMdiArea->addSubWindow(new XGUI_ViewWindow(), Qt::FramelessWindowHint);
- //aMdiArea->addSubWindow(new XGUI_ViewWindow(), Qt::FramelessWindowHint);
- fillObjectBrowser();
- addPropertyPanel();
+ //fillObjectBrowser();
+ //addPropertyPanel();
}
XGUI_MainWindow::~XGUI_MainWindow(void)
myObjectBrowser->parentWidget()->hide();
}
+//******************************************************
+void XGUI_MainWindow::showPythonConsole()
+{
+ if (!myPythonConsole) {
+
+ QDockWidget* aDoc = new QDockWidget(this);
+ aDoc->setFeatures(QDockWidget::AllDockWidgetFeatures | QDockWidget::DockWidgetVerticalTitleBar);
+ aDoc->setMinimumHeight(0);
+ aDoc->setWindowTitle("Console");
+ myPythonConsole = new PyConsole_EnhConsole( aDoc, new PyConsole_EnhInterp());
+ //myPythonConsole = new QTextEdit(aDoc);
+ //myPythonConsole->setGeometry(0,0,200, 50);
+ //myPythonConsole->setText(">>>");
+ aDoc->setWidget(myPythonConsole);
+ //myPythonConsole->setMinimumHeight(0);
+ addDockWidget(Qt::TopDockWidgetArea, aDoc);
+ tabifyDockWidget(myMenuBar->getLastDockWindow(), aDoc);
+ }
+ myPythonConsole->parentWidget()->show();
+}
+
+//******************************************************
+void XGUI_MainWindow::hidePythonConsole()
+{
+ if (myPythonConsole)
+ myPythonConsole->parentWidget()->hide();
+}
+
+
+
+//******************************************************
+
+// TEST FUNCTIONS
+
//******************************************************
void XGUI_MainWindow::fillObjectBrowser()
{
class XGUI_Viewer;
class QTreeWidget;
class QMdiArea;
+class PyConsole_EnhConsole;
class XGUI_MainWindow: public QMainWindow
{
{
return myObjectBrowser;
}
+
void showObjectBrowser();
void hideObjectBrowser();
return myViewer;
}
+ void showPythonConsole();
+ void hidePythonConsole();
+
private:
//!! For test purposes only
- //QWidget* getSubWindow();
-
void fillObjectBrowser();
void addPropertyPanel();
QTreeWidget* myObjectBrowser;
XGUI_Viewer* myViewer;
+
+ PyConsole_EnhConsole* myPythonConsole;
};
#endif
myActiveView = myPerspView;
}
myActiveView->SetSurfaceDetail(V3d_TEX_ALL);
-
- //setBackground( Qtx::BackgroundData( Qt::black ) ); // set default background
}
//***********************************************
activeView()->SetZSize(0.);
emit vpTransformed( );
}
+
+void XGUI_ViewPort::syncronizeWith( const XGUI_ViewPort* ref )
+{
+ Handle(V3d_View) refView = ref->getView();
+ Handle(V3d_View) tgtView = getView();
+
+ /* The following params are copied:
+ - view type( ortho/persp )
+ - position of view point
+ - orientation of high point
+ - position of the eye
+ - projection vector
+ - view center ( 2D )
+ - view twist
+ - view scale
+ */
+
+ /* we'll update after setting all params */
+ tgtView->SetImmediateUpdate( Standard_False );
+
+ /* perspective */
+ if ( refView->Type() == V3d_PERSPECTIVE )
+ tgtView->SetFocale( refView->Focale() );
+
+ /* copy params */
+ Standard_Real x, y, z;
+ refView->At( x, y, z ); tgtView->SetAt( x, y, z );
+ refView->Up( x, y, z ); tgtView->SetUp( x, y, z );
+ refView->Eye( x, y, z ); tgtView->SetEye( x, y, z );
+ refView->Proj( x, y, z ); tgtView->SetProj( x, y, z );
+ refView->Center( x, y ); tgtView->SetCenter( x, y );
+ tgtView->SetScale( refView->Scale() );
+ tgtView->SetTwist( refView->Twist() );
+
+ /* update */
+ tgtView->Update();
+ tgtView->SetImmediateUpdate( Standard_True );
+}
{
return myBackground;
}
+
void setBackground(const XGUI_ViewBackground& bgData);
+ void syncronizeWith( const XGUI_ViewPort* ref );
+
signals:
void vpChangeBackground(const XGUI_ViewBackground&);
void vpClosed();
#include <QMdiArea>
#include <QMdiSubWindow>
#include <QPainter>
-//#include <QTime>
+#include <QTimer>
#include <QFileDialog>
#include <TopoDS_Shape.hxx>
"................................", "................................" };
//**************************************************************************
-void ViewerToolbar::paintEvent(QPaintEvent* theEvent)
+void ViewerToolbar::repaintBackground()
{
- //QTime aTime;
- //aTime.start();
QRect aRect = rect();
QRect aVPRect = myVPort->rect();
QPoint aGlobPnt = mapToGlobal(aRect.topLeft());
QPoint aPnt = myVPort->mapFromGlobal(aGlobPnt);
- QRect aImgRect(
- QRect(aPnt.x(), aPnt.y() + aVPRect.height() - aRect.height(), aRect.width(), aRect.height()));
+ QRect aImgRect(QRect(aPnt.x(), aPnt.y() + aVPRect.height() - aRect.height(),
+ aRect.width(), aRect.height()));
QPainter(this).drawImage(aRect, myVPort->dumpView(aImgRect, false));
- //QString aMsg = QString("### Painted in %1").arg(aTime.elapsed());
- //qDebug(qPrintable(aMsg));
+}
+
+void ViewerToolbar::paintEvent(QPaintEvent* theEvent)
+{
+ repaintBackground();
+ QToolBar::paintEvent(theEvent);
}
//**************************************************************************
-void ViewerLabel::paintEvent(QPaintEvent* theEvent)
+void ViewerLabel::repaintBackground()
{
QRect aRect = rect();
QRect aVPRect = myVPort->rect();
QPoint aGlobPnt = mapToGlobal(aRect.topLeft());
QPoint aPnt = myVPort->mapFromGlobal(aGlobPnt);
- QRect aImgRect(
- QRect(aPnt.x(), aPnt.y() + aVPRect.height() - aRect.height(), aRect.width(), aRect.height()));
+ QRect aImgRect(QRect(aPnt.x(), aPnt.y() + aVPRect.height() - aRect.height(),
+ aRect.width(), aRect.height()));
QPainter(this).drawImage(aRect, myVPort->dumpView(aImgRect, false));
+}
+
+void ViewerLabel::paintEvent(QPaintEvent* theEvent)
+{
+ repaintBackground();
QLabel::paintEvent(theEvent);
}
myCurrPointType(XGUI::GRAVITY),
myPrevPointType(XGUI::GRAVITY),
myRotationPointSelection(false),
- myClosable(false)
+ myClosable(true)
{
mySelectedPoint = gp_Pnt(0., 0., 0.);
setFrameStyle(QFrame::Raised);
myGripWgt->setGeometry(BORDER_SIZE + 2, BORDER_SIZE + 2, 19, 32);
myGripWgt->setMouseTracking(true);
myGripWgt->installEventFilter(this);
- connect(myViewPort, SIGNAL(vpTransformed()), myGripWgt, SLOT(update()));
- connect(myViewPort, SIGNAL(vpUpdated()), myGripWgt, SLOT(update()));
// Create Viewer management buttons
myViewBar = new ViewerToolbar(this, myViewPort);
QAction* aBtn;
- // Dump view
- aBtn = new QAction(QIcon(":pictures/occ_view_camera_dump.png"), tr("DUMP_VIEW"), myViewBar);
- connect(aBtn, SIGNAL(triggered()), SLOT(dumpView()));
- myViewBar->addAction(aBtn);
- // Fit all
- aBtn = new QAction(QIcon(":pictures/occ_view_fitall.png"), tr("FIT_ALL"), myViewBar);
- connect(aBtn, SIGNAL(triggered()), SLOT(fitAll()));
- myViewBar->addAction(aBtn);
- // Fit area
- aBtn = new QAction(QIcon(":pictures/occ_view_fitarea.png"), tr("FIT_AREA"), myViewBar);
- connect(aBtn, SIGNAL(triggered()), SLOT(activateWindowFit()));
- myViewBar->addAction(aBtn);
- // Zoom
- aBtn = new QAction(QIcon(":pictures/occ_view_zoom.png"), tr("ZOOM_VIEW"), myViewBar);
- connect(aBtn, SIGNAL(triggered()), SLOT(activateZoom()));
- myViewBar->addAction(aBtn);
- // Pan
- aBtn = new QAction(QIcon(":pictures/occ_view_pan.png"), tr("PAN_VIEW"), myViewBar);
- connect(aBtn, SIGNAL(triggered()), SLOT(activatePanning()));
- myViewBar->addAction(aBtn);
- // Global Panning
- aBtn = new QAction(QIcon(":pictures/occ_view_glpan.png"), tr("GLOB_PAN_VIEW"), myViewBar);
- connect(aBtn, SIGNAL(triggered()), SLOT(activateGlobalPanning()));
- myViewBar->addAction(aBtn);
- // Rotation
- aBtn = new QAction(QIcon(":pictures/occ_view_rotate.png"), tr("ROTATE_VIEW"), myViewBar);
- connect(aBtn, SIGNAL(triggered()), SLOT(activateRotation()));
- myViewBar->addAction(aBtn);
- // Front view
- aBtn = new QAction(QIcon(":pictures/occ_view_front.png"), tr("FRONT_VIEW"), myViewBar);
- connect(aBtn, SIGNAL(triggered()), SLOT(frontView()));
- myViewBar->addAction(aBtn);
- // Back view
- aBtn = new QAction(QIcon(":pictures/occ_view_back.png"), tr("BACK_VIEW"), myViewBar);
- connect(aBtn, SIGNAL(triggered()), SLOT(backView()));
- myViewBar->addAction(aBtn);
- // Top view
- aBtn = new QAction(QIcon(":pictures/occ_view_top.png"), tr("TOP_VIEW"), myViewBar);
- connect(aBtn, SIGNAL(triggered()), SLOT(topView()));
- myViewBar->addAction(aBtn);
- // Bottom view
- aBtn = new QAction(QIcon(":pictures/occ_view_bottom.png"), tr("BOTTOM_VIEW"), myViewBar);
- connect(aBtn, SIGNAL(triggered()), SLOT(bottomView()));
- myViewBar->addAction(aBtn);
- // Left view
- aBtn = new QAction(QIcon(":pictures/occ_view_left.png"), tr("LEFT_VIEW"), myViewBar);
- connect(aBtn, SIGNAL(triggered()), SLOT(leftView()));
- myViewBar->addAction(aBtn);
- // Right view
- aBtn = new QAction(QIcon(":pictures/occ_view_right.png"), tr("RIGHT_VIEW"), myViewBar);
- connect(aBtn, SIGNAL(triggered()), SLOT(rightView()));
- myViewBar->addAction(aBtn);
- // Clone view
- aBtn = new QAction(QIcon(":pictures/occ_view_clone.png"), tr("CLONE_VIEW"), myViewBar);
- connect(aBtn, SIGNAL(triggered()), SLOT(cloneView()));
- myViewBar->addAction(aBtn);
-
- //Support copy of background on updating of viewer
- connect(myViewPort, SIGNAL(vpTransformed()), myViewBar, SLOT(update()));
- connect(myViewPort, SIGNAL(vpUpdated()), myViewBar, SLOT(update()));
+ // Dump view
+ aBtn = new QAction(QIcon(":pictures/occ_view_camera_dump.png"), tr("DUMP_VIEW"), myViewBar);
+ connect(aBtn, SIGNAL(triggered()), SLOT(dumpView()));
+ myViewBar->addAction(aBtn);
+ // Fit all
+ aBtn = new QAction(QIcon(":pictures/occ_view_fitall.png"), tr("FIT_ALL"), myViewBar);
+ connect(aBtn, SIGNAL(triggered()), SLOT(fitAll()));
+ myViewBar->addAction(aBtn);
+ // Fit area
+ aBtn = new QAction(QIcon(":pictures/occ_view_fitarea.png"), tr("FIT_AREA"), myViewBar);
+ connect(aBtn, SIGNAL(triggered()), SLOT(activateWindowFit()));
+ myViewBar->addAction(aBtn);
+ // Zoom
+ aBtn = new QAction(QIcon(":pictures/occ_view_zoom.png"), tr("ZOOM_VIEW"), myViewBar);
+ connect(aBtn, SIGNAL(triggered()), SLOT(activateZoom()));
+ myViewBar->addAction(aBtn);
+ // Pan
+ aBtn = new QAction(QIcon(":pictures/occ_view_pan.png"), tr("PAN_VIEW"), myViewBar);
+ connect(aBtn, SIGNAL(triggered()), SLOT(activatePanning()));
+ myViewBar->addAction(aBtn);
+ // Global Panning
+ aBtn = new QAction(QIcon(":pictures/occ_view_glpan.png"), tr("GLOB_PAN_VIEW"), myViewBar);
+ connect(aBtn, SIGNAL(triggered()), SLOT(activateGlobalPanning()));
+ myViewBar->addAction(aBtn);
+ // Rotation
+ aBtn = new QAction(QIcon(":pictures/occ_view_rotate.png"), tr("ROTATE_VIEW"), myViewBar);
+ connect(aBtn, SIGNAL(triggered()), SLOT(activateRotation()));
+ myViewBar->addAction(aBtn);
+ // Reset
+ aBtn = new QAction(QIcon(":pictures/occ_view_reset.png"), tr("RESET_VIEW"), myViewBar);
+ connect(aBtn, SIGNAL(triggered()), SLOT(reset()));
+ myViewBar->addAction(aBtn);
+ // Front view
+ aBtn = new QAction(QIcon(":pictures/occ_view_front.png"), tr("FRONT_VIEW"), myViewBar);
+ connect(aBtn, SIGNAL(triggered()), SLOT(frontView()));
+ myViewBar->addAction(aBtn);
+ // Back view
+ aBtn = new QAction(QIcon(":pictures/occ_view_back.png"), tr("BACK_VIEW"), myViewBar);
+ connect(aBtn, SIGNAL(triggered()), SLOT(backView()));
+ myViewBar->addAction(aBtn);
+ // Top view
+ aBtn = new QAction(QIcon(":pictures/occ_view_top.png"), tr("TOP_VIEW"), myViewBar);
+ connect(aBtn, SIGNAL(triggered()), SLOT(topView()));
+ myViewBar->addAction(aBtn);
+ // Bottom view
+ aBtn = new QAction(QIcon(":pictures/occ_view_bottom.png"), tr("BOTTOM_VIEW"), myViewBar);
+ connect(aBtn, SIGNAL(triggered()), SLOT(bottomView()));
+ myViewBar->addAction(aBtn);
+ // Left view
+ aBtn = new QAction(QIcon(":pictures/occ_view_left.png"), tr("LEFT_VIEW"), myViewBar);
+ connect(aBtn, SIGNAL(triggered()), SLOT(leftView()));
+ myViewBar->addAction(aBtn);
+ // Right view
+ aBtn = new QAction(QIcon(":pictures/occ_view_right.png"), tr("RIGHT_VIEW"), myViewBar);
+ connect(aBtn, SIGNAL(triggered()), SLOT(rightView()));
+ myViewBar->addAction(aBtn);
+ // Clone view
+ aBtn = new QAction(QIcon(":pictures/occ_view_clone.png"), tr("CLONE_VIEW"), myViewBar);
+ connect(aBtn, SIGNAL(triggered()), SLOT(cloneView()));
+ myViewBar->addAction(aBtn);
// Create Window management buttons
myWindowBar = new ViewerToolbar(this, myViewPort);
- connect(myViewPort, SIGNAL(vpTransformed()), myWindowBar, SLOT(update()));
- connect(myViewPort, SIGNAL(vpUpdated()), myWindowBar, SLOT(update()));
myMinimizeBtn = new QAction(myWindowBar);
myMinimizeBtn->setIcon(MinimizeIco);
myViewBar->hide();
myWindowBar->hide();
myGripWgt->hide();
+
+ //Support copy of background on updating of viewer
+ connect(myViewPort, SIGNAL(vpTransformed()), this, SLOT(updateToolBar()));
+ connect(myViewPort, SIGNAL(vpUpdated()), this, SLOT(updateToolBar()));
+ connect(this, SIGNAL(vpTransformationFinished(XGUI_ViewWindow::OperationType)),
+ this, SLOT(updateToolBar()));
+
}
//****************************************************************
//****************************************************************
void XGUI_ViewWindow::onClose()
{
- if (parentWidget()) {
- emit tryClosing(this);
- if (closable()) {
- emit closed(static_cast<QMdiSubWindow*>(parentWidget()));
- parentWidget()->close();
- }
+ if (parentWidget()) {
+ emit tryClosing(this);
+ if (closable()) {
+ emit closed(static_cast<QMdiSubWindow*>(parentWidget()));
+ parentWidget()->close();
}
+ }
}
//****************************************************************
{
if (!isMinimized()) {
myViewBar->show();
- myWindowBar->show();
+ myWindowBar->show();
if (!isMaximized())
myGripWgt->show();
}
if (processWindowControls(theObj, theEvent))
return true;
} else if (theObj == myViewPort) {
- if (processViewPort(theEvent))
+ if (processViewPort(theEvent)) {
return true;
+ }
}
return QFrame::eventFilter(theObj, theEvent);
}
void XGUI_ViewWindow::cloneView()
{
QMdiSubWindow* vw = myViewer->createView();
- //vw->show();
+ XGUI_ViewWindow* aNewWnd = static_cast<XGUI_ViewWindow*>(vw->widget());
+ aNewWnd->viewPort()->syncronizeWith(myViewPort);
emit viewCloned( vw );
}
void XGUI_ViewWindow::dumpView()
{
- QString aFilter(tr("OCC_IMAGE_FILES"));
- QString aSelectedFilter;
- QString aFileName = QFileDialog::getSaveFileName(this, "Save picture", QString(), aFilter, &aSelectedFilter);
- if (!aFileName.isNull()) {
- QApplication::setOverrideCursor( Qt::WaitCursor );
- QImage aPicture = myViewPort->dumpView();
-
- QString aFmt = extension(aFileName).toUpper();
- if( aFmt.isEmpty() )
- aFmt = QString( "BMP" ); // default format
- else if( aFmt == "JPG" )
- aFmt = "JPEG";
-
- Handle(Visual3d_View) a3dView = myViewPort->getView()->View();
- if (aFmt == "PS")
- a3dView->Export(strdup(qPrintable(aFileName)), Graphic3d_EF_PostScript);
- else if (aFmt == "EPS")
- a3dView->Export(strdup(qPrintable(aFileName)), Graphic3d_EF_EnhPostScript);
- else
- aPicture.save( aFileName, aFmt.toLatin1() );
- QApplication::restoreOverrideCursor();
- }
+ QString aFilter(tr("OCC_IMAGE_FILES"));
+ QString aSelectedFilter;
+ QString aFileName = QFileDialog::getSaveFileName(this, "Save picture", QString(), aFilter, &aSelectedFilter);
+ if (!aFileName.isNull()) {
+ QApplication::setOverrideCursor( Qt::WaitCursor );
+ QImage aPicture = myViewPort->dumpView();
+
+ QString aFmt = extension(aFileName).toUpper();
+ if( aFmt.isEmpty() )
+ aFmt = QString( "BMP" ); // default format
+ else if( aFmt == "JPG" )
+ aFmt = "JPEG";
+
+ Handle(Visual3d_View) a3dView = myViewPort->getView()->View();
+ if (aFmt == "PS")
+ a3dView->Export(strdup(qPrintable(aFileName)), Graphic3d_EF_PostScript);
+ else if (aFmt == "EPS")
+ a3dView->Export(strdup(qPrintable(aFileName)), Graphic3d_EF_EnhPostScript);
+ else
+ aPicture.save( aFileName, aFmt.toLatin1() );
+ QApplication::restoreOverrideCursor();
+ }
}
void XGUI_ViewWindow::fitAll()
{
- emit vpTransformationStarted( FITALLVIEW );
- myViewPort->fitAll();
- emit vpTransformationFinished( FITALLVIEW );
+ emit vpTransformationStarted( FITALLVIEW );
+ myViewPort->fitAll();
+ emit vpTransformationFinished( FITALLVIEW );
}
/*!
if ( myOperation != WINDOWFIT ) {
QCursor handCursor (Qt::PointingHandCursor);
- if( setTransformRequested ( WINDOWFIT ) )
- {
+ if( setTransformRequested ( WINDOWFIT ) ) {
myViewPort->setCursor ( handCursor );
myCursorIsHand = true;
}
{
emit vpTransformationStarted ( FRONTVIEW );
Handle(V3d_View) aView3d = myViewPort->getView();
- if ( !aView3d.IsNull() ) aView3d->SetProj (V3d_Xpos);
+ if ( !aView3d.IsNull() )
+ aView3d->SetProj (V3d_Xpos);
myViewPort->fitAll();
emit vpTransformationFinished ( FRONTVIEW );
}
{
emit vpTransformationStarted ( BACKVIEW );
Handle(V3d_View) aView3d = myViewPort->getView();
- if ( !aView3d.IsNull() ) aView3d->SetProj (V3d_Xneg);
+ if ( !aView3d.IsNull() )
+ aView3d->SetProj (V3d_Xneg);
myViewPort->fitAll();
emit vpTransformationFinished ( BACKVIEW );
}
{
emit vpTransformationStarted ( TOPVIEW );
Handle(V3d_View) aView3d = myViewPort->getView();
- if ( !aView3d.IsNull() ) aView3d->SetProj (V3d_Zpos);
+ if ( !aView3d.IsNull() )
+ aView3d->SetProj (V3d_Zpos);
myViewPort->fitAll();
emit vpTransformationFinished ( TOPVIEW );
}
{
emit vpTransformationStarted ( BOTTOMVIEW );
Handle(V3d_View) aView3d = myViewPort->getView();
- if ( !aView3d.IsNull() ) aView3d->SetProj (V3d_Zneg);
+ if ( !aView3d.IsNull() )
+ aView3d->SetProj (V3d_Zneg);
myViewPort->fitAll();
emit vpTransformationFinished ( BOTTOMVIEW );
}
{
emit vpTransformationStarted ( LEFTVIEW );
Handle(V3d_View) aView3d = myViewPort->getView();
- if ( !aView3d.IsNull() ) aView3d->SetProj (V3d_Yneg);
+ if ( !aView3d.IsNull() )
+ aView3d->SetProj (V3d_Yneg);
myViewPort->fitAll();
emit vpTransformationFinished ( LEFTVIEW );
}
{
emit vpTransformationStarted ( RIGHTVIEW );
Handle(V3d_View) aView3d = myViewPort->getView();
- if ( !aView3d.IsNull() ) aView3d->SetProj (V3d_Ypos);
+ if ( !aView3d.IsNull() )
+ aView3d->SetProj (V3d_Ypos);
myViewPort->fitAll();
emit vpTransformationFinished ( RIGHTVIEW );
}
+
+void XGUI_ViewWindow::reset()
+{
+ emit vpTransformationStarted( RESETVIEW );
+ bool upd = myViewPort->getView()->SetImmediateUpdate( false );
+ myViewPort->getView()->Reset( false );
+ myViewPort->fitAll( false, true, false );
+ myViewPort->getView()->SetImmediateUpdate( upd );
+ myViewPort->getView()->Update();
+ emit vpTransformationFinished( RESETVIEW );
+}
+
+
+void XGUI_ViewWindow::updateToolBar()
+{
+ myGripWgt->update();
+ myViewBar->update();
+ myWindowBar->update();
+ //QTimer::singleShot(50, Qt::VeryCoarseTimer, this, SLOT(repaintToolBar()));
+}
+
+/*void XGUI_ViewWindow::repaintToolBar()
+{
+ QApplication::sync();
+ myGripWgt->repaint();
+ myViewBar->repaint();
+ myWindowBar->repaint();
+}*/
XGUI_ViewBackground background() const;
void setBackground(const XGUI_ViewBackground& theBackground);
- bool closable() const { return myClosable; }
- void setClosable( const bool isClosable ) { myClosable = isClosable; }
+ bool closable() const { return myClosable; }
+ void setClosable( const bool isClosable ) { myClosable = isClosable; }
signals:
- void vpTransformationStarted(XGUI_ViewWindow::OperationType type);
- void vpTransformationFinished(XGUI_ViewWindow::OperationType type);
+ void vpTransformationStarted(XGUI_ViewWindow::OperationType type);
+ void vpTransformationFinished(XGUI_ViewWindow::OperationType type);
- void Show(QShowEvent *);
- void Hide(QHideEvent *);
- void maximized(XGUI_ViewWindow*, bool);
- void returnedTo3d();
+ void Show(QShowEvent *);
+ void Hide(QHideEvent *);
+ void maximized(XGUI_ViewWindow*, bool);
+ void returnedTo3d();
- void tryClosing(XGUI_ViewWindow*);
+ void tryClosing(XGUI_ViewWindow*);
void closed( QMdiSubWindow* );
- void mousePressed(XGUI_ViewWindow*, QMouseEvent*);
- void mouseReleased(XGUI_ViewWindow*, QMouseEvent*);
- void mouseDoubleClicked(XGUI_ViewWindow*, QMouseEvent*);
- void mouseMoving(XGUI_ViewWindow*, QMouseEvent*);
- void keyPressed(XGUI_ViewWindow*, QKeyEvent*);
- void keyReleased(XGUI_ViewWindow*, QKeyEvent*);
- void contextMenuRequested(QContextMenuEvent *e);
-
- void viewModified(XGUI_ViewWindow*);
+ void mousePressed(XGUI_ViewWindow*, QMouseEvent*);
+ void mouseReleased(XGUI_ViewWindow*, QMouseEvent*);
+ void mouseDoubleClicked(XGUI_ViewWindow*, QMouseEvent*);
+ void mouseMoving(XGUI_ViewWindow*, QMouseEvent*);
+ void keyPressed(XGUI_ViewWindow*, QKeyEvent*);
+ void keyReleased(XGUI_ViewWindow*, QKeyEvent*);
+ void contextMenuRequested(QContextMenuEvent *e);
+
+ void viewModified(XGUI_ViewWindow*);
void viewCloned( QMdiSubWindow* theView );
public slots:
void activateZoom();
void activateRotation();
void activatePanning();
- void activateWindowFit();
- void activateGlobalPanning();
-
- void cloneView();
- void dumpView();
- void fitAll();
-
- void frontView();
- void backView();
- void topView();
- void bottomView();
- void leftView();
- void rightView();
+ void activateWindowFit();
+ void activateGlobalPanning();
+
+ void cloneView();
+ void dumpView();
+ void fitAll();
+
+ void frontView();
+ void backView();
+ void topView();
+ void bottomView();
+ void leftView();
+ void rightView();
+
+ void reset();
+
protected:
virtual void resizeEvent(QResizeEvent* theEvent);
void onMinimize();
void onMaximize();
+ void updateToolBar();
+// void repaintToolBar();
+
private:
enum WindowState
{
bool myCursorIsHand;
bool myIsKeyFree;
bool myEventStarted; // set when transformation is in process
- bool myClosable;
+ bool myClosable;
QCursor myCursor;
ViewerToolbar(QWidget* theParent, XGUI_ViewPort* thePort)
: QToolBar(theParent), myVPort(thePort)
{
+ setBackgroundRole(QPalette::NoRole);
+ setAttribute(Qt::WA_NoSystemBackground);
+ setAttribute(Qt::WA_PaintOnScreen);
}
+ void repaintBackground();
+
protected:
virtual void paintEvent(QPaintEvent* theEvent);
ViewerLabel(QWidget* theParent, XGUI_ViewPort* thePort)
: QLabel(theParent), myVPort(thePort)
{
+ setAttribute(Qt::WA_NoSystemBackground);
}
+ void repaintBackground();
+
protected:
virtual void paintEvent(QPaintEvent* theEvent);
if (myViews.size() == 0)
setTrihedronShown(true);
- view->setBackground(XGUI_ViewBackground(XGUI::VerticalGradient, Qt::green, Qt::blue));
+ view->setBackground(XGUI_ViewBackground(XGUI::VerticalGradient, Qt::white, QColor(Qt::blue).lighter()));
+ //view->setBackground(XGUI_ViewBackground(Qt::black));
QMdiArea* aMDI = myMainWindow->mdiArea();
QMdiSubWindow* aWnd = aMDI->addSubWindow(view, Qt::FramelessWindowHint);
myMainWindow->show();
QMdiSubWindow* aWnd = myMainWindow->viewer()->createView();
aWnd->showMaximized();
+ myMainWindow->showPythonConsole();
}
//******************************************************
void XGUI_Workshop::initMenu()
{
- XGUI_Workbench* aPage = addWorkbench(tr("GEN_MENU_TITLE"));
+ XGUI_Workbench* aPage = myMainWindow->menuObject()->generalPage();
// File commands group
XGUI_MenuGroupPanel* aGroup = aPage->addGroup("Default");
</context>
<context>
<name>XGUI_Workshop</name>
- <message>
- <source>GEN_MENU_TITLE</source>
- <translation>General</translation>
- </message>
<message>
<source>NEW_MENU</source>
<translation>New</translation>
<source>MENU_TITLE</source>
<translation>Menu</translation>
</message>
+ <message>
+ <source>GEN_MENU_TITLE</source>
+ <translation>General</translation>
+ </message>
</context>
<context>
<name>XGUI_Viewer</name>
<source>RIGHT_VIEW</source>
<translation>Right</translation>
</message>
+ <message>
+ <source>RESET_VIEW</source>
+ <translation>Reset</translation>
+ </message>
</context>
</TS>
<file>pictures/ViewPort.png</file>
- <file>pictures/occ_view_ambient.png</file>
- <file>pictures/occ_view_anticlockwise.png</file>
<file>pictures/occ_view_back.png</file>
<file>pictures/occ_view_bottom.png</file>
<file>pictures/occ_view_camera_dump.png</file>
- <file>pictures/occ_view_clipping.png</file>
- <file>pictures/occ_view_clipping_pressed.png</file>
- <file>pictures/occ_view_clockwise.png</file>
<file>pictures/occ_view_clone.png</file>
<file>pictures/occ_view_fitall.png</file>
<file>pictures/occ_view_fitarea.png</file>
<file>pictures/occ_view_front.png</file>
- <file>pictures/occ_view_glpan.png</file>
- <file>pictures/occ_view_graduated_axes.png</file>
<file>pictures/occ_view_left.png</file>
- <file>pictures/occ_view_maximized.png</file>
- <file>pictures/occ_view_minimized.png</file>
<file>pictures/occ_view_pan.png</file>
- <file>pictures/occ_view_preselection.png</file>
- <file>pictures/occ_view_presets.png</file>
+ <file>pictures/occ_view_glpan.png</file>
<file>pictures/occ_view_reset.png</file>
- <file>pictures/occ_view_return_3d_view.png</file>
<file>pictures/occ_view_right.png</file>
<file>pictures/occ_view_rotate.png</file>
- <file>pictures/occ_view_rotation_point.png</file>
- <file>pictures/occ_view_scaling.png</file>
- <file>pictures/occ_view_selection.png</file>
- <file>pictures/occ_view_shoot.png</file>
- <file>pictures/occ_view_style_switch.png</file>
<file>pictures/occ_view_top.png</file>
- <file>pictures/occ_view_triedre.png</file>
<file>pictures/occ_view_zoom.png</file>
- <file>pictures/occ_view_zooming_style_switch.png</file>
<file>pictures/wnd_close.png</file>
<file>pictures/wnd_minimize.png</file>