#include <vector>
#include <map>
#include <iostream>
+#include <algorithm>
#define TOP_HISTORY_PY "--- top of history ---"
#define BEGIN_HISTORY_PY "--- begin of history ---"
\brief Constructor. Automatically acquires GIL.
\param theThreadState python thread state
*/
-PyLockWrapper::PyLockWrapper(PyThreadState* theThreadState):
+PyLockWrapper::PyLockWrapper(PyThreadState* theThreadState):
myThreadState(theThreadState),
mySaveThreadState(0)
{
}
/*
- The following functions are used to hook the Python
+ The following functions are used to hook the Python
interpreter output.
*/
/*!
\brief Basic constructor.
-
- After construction the interpreter instance successor classes
+
+ After construction the interpreter instance successor classes
must call virtual method initalize().
*/
-PyInterp_Interp::PyInterp_Interp():
+PyInterp_Interp::PyInterp_Interp():
_tstate(0), _vout(0), _verr(0), _g(0)
{
}
/*!
\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
*/
void PyInterp_Interp::initialize()
{
- _history.clear(); // start a new list of user's commands
+ _history.clear(); // start a new list of user's commands
_ith = _history.begin();
initPython();
}
// Create python objects to capture stdout and stderr
- _vout=(PyObject*)newPyStdOut( false ); // stdout
+ _vout=(PyObject*)newPyStdOut( false ); // stdout
_verr=(PyObject*)newPyStdOut( true ); // stderr
// All the initRun outputs are redirected to the standard output (console)
\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,
+ 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
*/
/*!
\brief Initialize run command.
-
- This method is used to prepare interpreter for running
+
+ 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("stdout",_vout);
//PyObject *m = PyImport_GetModuleDict();
-
+
PySys_SetObject("stdout",PySys_GetObject("__stdout__"));
PySys_SetObject("stderr",PySys_GetObject("__stderr__"));
*/
}
/*!
- \brief Compile Python command and evaluate it in the
+ \brief Compile Python command and evaluate it in the
python dictionary context if possible.
\internal
\param command Python command string
\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)
+static int run_command(const char *command, PyObject *context)
{
PyObject *m = PyImport_AddModule("codeop");
if(!m) { // Fatal error. No way to go on.
}
}
+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
\param data callback function parameters
*/
void PyInterp_Interp::setvoutcb(PyOutChanged* cb, void* data)
-{
+{
((PyStdOut*)_vout)->_cb=cb;
((PyStdOut*)_vout)->_data=data;
}
\param data callback function parameters
*/
void PyInterp_Interp::setverrcb(PyOutChanged* cb, void* data)
-{
+{
((PyStdOut*)_verr)->_cb=cb;
((PyStdOut*)_verr)->_data=data;
}
hdffile = fi.absoluteFilePath();
}
else {
- QRegExp rxp ("--pyscript=(.+)");
+ QRegExp rxp ("--pyscript=\\[(.+)\\]");
if ( rxp.indexIn( QString(qApp->argv()[i]) ) >= 0 && rxp.capturedTexts().count() > 1 ) {
- QStringList files = rxp.capturedTexts()[1].split(",",QString::SkipEmptyParts);
- pyfiles += files;
+ QStringList dictList = rxp.capturedTexts()[1].split("},", QString::SkipEmptyParts);
+ for (int k = 0; k < dictList.count(); ++k) {
+ QRegExp rxd ("[\\s]*\\{?([^\\{\\}]+)\\}?[\\s]*");
+ if ( rxd.indexIn( dictList[k] ) >= 0 && rxd.capturedTexts().count() > 1 ) {
+ for (int m = 1; m < rxd.capturedTexts().count(); ++m) {
+ pyfiles += rxd.capturedTexts()[m];
+ }
+ }
+ }
}
}
}
+ // Here pyfiles elements are: "script_name": [list_of_"arg"s]
+ // For example: "/absolute/path/to/my_script.py": ["1", "2"]
if ( !hdffile.isEmpty() ) // open hdf file given as parameter
onOpenDoc( hdffile );
if ( appStudy && pyConsole ) {
_PTR(Study) aStudy = appStudy->studyDS();
if ( !aStudy->GetProperties()->IsLocked() ) {
+ // pyfiles[j] is a dictionary: {"/absolute/path/to/script.py": [script_args]}
+ // Path is absolute, script has .py extension
for (uint j = 0; j < pyfiles.count(); j++ ) {
- QFileInfo fi ( pyfiles[j] );
- QFileInfo fipy ( pyfiles[j] + ".py" );
- QString command = QString( "execfile(r\"%1\")" );
- if ( fi.isAbsolute() ) {
- if ( fi.exists() )
- pyConsole->exec( command.arg( fi.absoluteFilePath() ) );
- else if ( fipy.exists() )
- pyConsole->exec( command.arg( fipy.absoluteFilePath() ) );
- else
- qDebug() << "Can't execute file" << pyfiles[j];
- }
- else {
- bool found = false;
- QStringList dirs;
- dirs << QDir::currentPath();
- if ( ::getenv( "PYTHONPATH" ) )
- dirs += QString( ::getenv( "PYTHONPATH" ) ).split( QRegExp( "[:|;]" ) );
- foreach( QString dir, dirs ) {
- qDebug() << "try" << QFileInfo( dir, pyfiles[j] ).absoluteFilePath();
- qDebug() << "try" << QFileInfo( dir, pyfiles[j] + ".py" ).absoluteFilePath();
- if ( QFileInfo( dir, pyfiles[j] ).exists() ) {
- pyConsole->exec( command.arg( QFileInfo( dir, pyfiles[j] ).absoluteFilePath() ) );
- found = true;
- break;
- }
- else if ( QFileInfo( dir, pyfiles[j] + ".py" ).exists() ) {
- pyConsole->exec( command.arg( QFileInfo( dir, pyfiles[j] + ".py" ).absoluteFilePath() ) );
- found = true;
- break;
- }
+ // Extract scripts and their arguments, if any
+ QRegExp rxp ("\"(.+)\":[\\s]*\\[(.*)\\]");
+ if ( rxp.indexIn( pyfiles[j] ) >= 0 && rxp.capturedTexts().count() == 3 ) {
+ QString script = rxp.capturedTexts()[1];
+ QString args = "";
+ QStringList argList = rxp.capturedTexts()[2].split(",", QString::SkipEmptyParts);
+ for (uint k = 0; k < argList.count(); k++ ) {
+ QString arg = argList[k].trimmed();
+ arg.remove( QRegExp("^[\"]") );
+ arg.remove( QRegExp("[\"]$") );
+ args += arg+",";
}
- if ( !found ) {
- qDebug() << "Can't execute file" << pyfiles[j];
+ args.remove( QRegExp("[,]$") );
+ if (!args.isEmpty()) {
+ args = "args:"+args;
}
+ QString cmd = script+" "+args;
+ QString command = QString( "execfile(r\"%1\")" ).arg(cmd.trimmed());
+ pyConsole->exec(command);
}
- }
+ } // end for loop on pyfiles QStringList
}
}
}
createAction( DumpStudyId, tr( "TOT_DESK_FILE_DUMP_STUDY" ), QIcon(),
tr( "MEN_DESK_FILE_DUMP_STUDY" ), tr( "PRP_DESK_FILE_DUMP_STUDY" ),
Qt::CTRL+Qt::Key_D, desk, false, this, SLOT( onDumpStudy() ) );
-
+
//! Load script
createAction( LoadScriptId, tr( "TOT_DESK_FILE_LOAD_SCRIPT" ), QIcon(),
tr( "MEN_DESK_FILE_LOAD_SCRIPT" ), tr( "PRP_DESK_FILE_LOAD_SCRIPT" ),
connect( aStudy, SIGNAL( closed ( SUIT_Study* ) ), this, SLOT( onStudyClosed ( SUIT_Study* ) ) );
//to receive signal in application that NoteBook's variable was modified
- connect( aStudy, SIGNAL(notebookVarUpdated(QString)),
+ connect( aStudy, SIGNAL(notebookVarUpdated(QString)),
this, SIGNAL(notebookVarUpdated(QString)) );
return aStudy;
QFileInfo aFileInfo(aFileName);
if( aFileInfo.isDir() ) // IPAL19257
return;
-
+
// Issue 21377 - dump study implementation moved to SalomeApp_Study class
bool res;
{
_PTR(Study) aStudy = appStudy->studyDS();
setNoteBook( new SalomeApp_NoteBook( desktop(), aStudy ) );
//to receive signal in NoteBook that it's variable was modified
- connect( this, SIGNAL( notebookVarUpdated( QString ) ),
+ connect( this, SIGNAL( notebookVarUpdated( QString ) ),
getNoteBook(), SLOT( onVarUpdate( QString ) ) );
}
wid = getNoteBook();
SUIT_DataOwnerPtrList aList;
aList.append( new LightApp_DataOwner( entry ) );
selectionMgr()->setSelected( aList, false );
-
+
SUIT_DataBrowser* ob = objectBrowser();
QModelIndexList aSelectedIndexes = ob->selectedIndexes();
{
LightApp_Application::onStudyCreated( study );
- desktop()->tabifyDockWidget( windowDock( getWindow( WT_NoteBook ) ),
+ desktop()->tabifyDockWidget( windowDock( getWindow( WT_NoteBook ) ),
windowDock( getWindow( WT_ObjectBrowser ) ) );
loadDockWindowsState();
{
LightApp_Application::onStudyOpened( study );
- desktop()->tabifyDockWidget( windowDock( getWindow( WT_NoteBook ) ),
+ desktop()->tabifyDockWidget( windowDock( getWindow( WT_NoteBook ) ),
windowDock( getWindow( WT_ObjectBrowser ) ) );
loadDockWindowsState();
SUIT_DataBrowser* ob = objectBrowser();
LightApp_SelectionMgr* selMgr = selectionMgr();
- if ( !study || !ob || !selMgr )
+ if ( !study || !ob || !selMgr )
return;
// find GUI states root object
selMgr->clearSelected();
ob->setAutoUpdate(true);
DataObjectList ch = guiRootObj->children();
- for( int i = 0; i < ch.size(); i++ )
+ for( int i = 0; i < ch.size(); i++ )
delete ch[i];
delete guiRootObj;
ob->setAutoUpdate(isAutoUpdate);
void SalomeApp_Application::defaultWindows( QMap<int, int>& aMap ) const
{
LightApp_Application::defaultWindows(aMap);
- if ( !aMap.contains( WT_NoteBook ) ) {
+ if ( !aMap.contains( WT_NoteBook ) ) {
if ( !myNoteBook ) {
aMap.insert( WT_NoteBook, Qt::LeftDockWidgetArea );
}
if( !updateStudy() )
SUIT_MessageBox::warning( desktop(), tr( "ERROR" ), tr( "ERR_UPDATE_STUDY_FAILED" ) );
-
+
QApplication::restoreOverrideCursor();
}
int anIndex = aList.indexOf( this );
// Disconnect dialog from application desktop in case if:
- // 1) Application is not the first application in the session
+ // 1) Application is not the first application in the session
// 2) Application is the first application in session but not the only.
bool changeDesktop = ((anIndex > 0) || (anIndex == 0 && aList.count() > 1));
if( changeDesktop ) {
onCloseDoc( false );
if( !changeDesktop ) {
- ok = onRestoreStudy( aDumpScript,
+ ok = onRestoreStudy( aDumpScript,
aStudyName,
isStudySaved );
}
* Purpose : Load the dumped study from Python script
*/
//============================================================================
-bool SalomeApp_Application::onRestoreStudy( const QString& theDumpScript,
- const QString& theStudyName,
+bool SalomeApp_Application::onRestoreStudy( const QString& theDumpScript,
+ const QString& theStudyName,
bool theIsStudySaved )
{
bool ok = true;
{
// emit signal to restore study from Python script
if ( myNoteBook ) {
- emit dumpedStudyClosed( myNoteBook->getDumpedStudyScript(),
- myNoteBook->getDumpedStudyName(),
+ emit dumpedStudyClosed( myNoteBook->getDumpedStudyScript(),
+ myNoteBook->getDumpedStudyName(),
myNoteBook->isDumpedStudySaved() );
}
LightApp_Application::afterCloseDoc();