From: aguerre Date: Wed, 28 Aug 2013 14:18:22 +0000 (+0000) Subject: Allow passing multiple scripts with arguments from command line X-Git-Tag: BR_hydro_v_0_3_1~75 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=91ffe0dfc02ce414998f97413bcaee678480f18c;p=modules%2Fgui.git Allow passing multiple scripts with arguments from command line --- diff --git a/src/PyInterp/PyInterp_Interp.cxx b/src/PyInterp/PyInterp_Interp.cxx index 8c01a4926..c2dcd254a 100644 --- a/src/PyInterp/PyInterp_Interp.cxx +++ b/src/PyInterp/PyInterp_Interp.cxx @@ -34,6 +34,7 @@ #include #include #include +#include #define TOP_HISTORY_PY "--- top of history ---" #define BEGIN_HISTORY_PY "--- begin of history ---" @@ -50,7 +51,7 @@ std::map currentThreadMap; \brief Constructor. Automatically acquires GIL. \param theThreadState python thread state */ -PyLockWrapper::PyLockWrapper(PyThreadState* theThreadState): +PyLockWrapper::PyLockWrapper(PyThreadState* theThreadState): myThreadState(theThreadState), mySaveThreadState(0) { @@ -113,7 +114,7 @@ PyLockWrapper PyInterp_Interp::GetLockWrapper() } /* - The following functions are used to hook the Python + The following functions are used to hook the Python interpreter output. */ @@ -237,11 +238,11 @@ PyInterpreterState* PyInterp_Interp::_interp = NULL; /*! \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) { } @@ -255,7 +256,7 @@ 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 @@ -267,7 +268,7 @@ PyInterp_Interp::~PyInterp_Interp() */ 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(); @@ -288,7 +289,7 @@ void PyInterp_Interp::initialize() } // 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) @@ -300,8 +301,8 @@ void PyInterp_Interp::initialize() \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 */ @@ -341,15 +342,15 @@ std::string PyInterp_Interp::getbanner() /*! \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 // /* @@ -357,7 +358,7 @@ bool PyInterp_Interp::initRun() PySys_SetObject("stdout",_vout); //PyObject *m = PyImport_GetModuleDict(); - + PySys_SetObject("stdout",PySys_GetObject("__stdout__")); PySys_SetObject("stderr",PySys_GetObject("__stderr__")); */ @@ -365,7 +366,7 @@ bool PyInterp_Interp::initRun() } /*! - \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 @@ -373,7 +374,7 @@ bool PyInterp_Interp::initRun() \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. @@ -411,6 +412,59 @@ static int compile_command(const char *command,PyObject *context) } } +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 @@ -487,7 +541,7 @@ const char * PyInterp_Interp::getNext() \param data callback function parameters */ void PyInterp_Interp::setvoutcb(PyOutChanged* cb, void* data) -{ +{ ((PyStdOut*)_vout)->_cb=cb; ((PyStdOut*)_vout)->_data=data; } @@ -498,7 +552,7 @@ void PyInterp_Interp::setvoutcb(PyOutChanged* cb, void* data) \param data callback function parameters */ void PyInterp_Interp::setverrcb(PyOutChanged* cb, void* data) -{ +{ ((PyStdOut*)_verr)->_cb=cb; ((PyStdOut*)_verr)->_data=data; } diff --git a/src/SalomeApp/SalomeApp_Application.cxx b/src/SalomeApp/SalomeApp_Application.cxx index 03eb095bb..3bbe37261 100644 --- a/src/SalomeApp/SalomeApp_Application.cxx +++ b/src/SalomeApp/SalomeApp_Application.cxx @@ -199,13 +199,22 @@ void SalomeApp_Application::start() 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 ); @@ -219,43 +228,30 @@ void SalomeApp_Application::start() 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 } } } @@ -279,7 +275,7 @@ void SalomeApp_Application::createActions() 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" ), @@ -660,7 +656,7 @@ SUIT_Study* SalomeApp_Application::createNewStudy() 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; @@ -795,7 +791,7 @@ void SalomeApp_Application::onDumpStudy( ) QFileInfo aFileInfo(aFileName); if( aFileInfo.isDir() ) // IPAL19257 return; - + // Issue 21377 - dump study implementation moved to SalomeApp_Study class bool res; { @@ -950,7 +946,7 @@ QWidget* SalomeApp_Application::createWindow( const int flag ) _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(); @@ -1381,7 +1377,7 @@ void SalomeApp_Application::onDblClick( SUIT_DataObject* theObj ) SUIT_DataOwnerPtrList aList; aList.append( new LightApp_DataOwner( entry ) ); selectionMgr()->setSelected( aList, false ); - + SUIT_DataBrowser* ob = objectBrowser(); QModelIndexList aSelectedIndexes = ob->selectedIndexes(); @@ -1446,7 +1442,7 @@ void SalomeApp_Application::onStudyCreated( SUIT_Study* study ) { LightApp_Application::onStudyCreated( study ); - desktop()->tabifyDockWidget( windowDock( getWindow( WT_NoteBook ) ), + desktop()->tabifyDockWidget( windowDock( getWindow( WT_NoteBook ) ), windowDock( getWindow( WT_ObjectBrowser ) ) ); loadDockWindowsState(); @@ -1475,7 +1471,7 @@ void SalomeApp_Application::onStudyOpened( SUIT_Study* study ) { LightApp_Application::onStudyOpened( study ); - desktop()->tabifyDockWidget( windowDock( getWindow( WT_NoteBook ) ), + desktop()->tabifyDockWidget( windowDock( getWindow( WT_NoteBook ) ), windowDock( getWindow( WT_ObjectBrowser ) ) ); loadDockWindowsState(); @@ -1499,7 +1495,7 @@ void SalomeApp_Application::updateSavePointDataObjects( SalomeApp_Study* study ) SUIT_DataBrowser* ob = objectBrowser(); LightApp_SelectionMgr* selMgr = selectionMgr(); - if ( !study || !ob || !selMgr ) + if ( !study || !ob || !selMgr ) return; // find GUI states root object @@ -1522,7 +1518,7 @@ void SalomeApp_Application::updateSavePointDataObjects( SalomeApp_Study* study ) 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); @@ -1830,7 +1826,7 @@ bool SalomeApp_Application::renameObject( const QString& entry, const QString& n void SalomeApp_Application::defaultWindows( QMap& aMap ) const { LightApp_Application::defaultWindows(aMap); - if ( !aMap.contains( WT_NoteBook ) ) { + if ( !aMap.contains( WT_NoteBook ) ) { if ( !myNoteBook ) { aMap.insert( WT_NoteBook, Qt::LeftDockWidgetArea ); } @@ -1859,7 +1855,7 @@ void SalomeApp_Application::onUpdateStudy() if( !updateStudy() ) SUIT_MessageBox::warning( desktop(), tr( "ERROR" ), tr( "ERR_UPDATE_STUDY_FAILED" ) ); - + QApplication::restoreOverrideCursor(); } @@ -1915,7 +1911,7 @@ bool SalomeApp_Application::updateStudy() 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 ) { @@ -1941,7 +1937,7 @@ bool SalomeApp_Application::updateStudy() onCloseDoc( false ); if( !changeDesktop ) { - ok = onRestoreStudy( aDumpScript, + ok = onRestoreStudy( aDumpScript, aStudyName, isStudySaved ); } @@ -1954,8 +1950,8 @@ bool SalomeApp_Application::updateStudy() * 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; @@ -2005,8 +2001,8 @@ void SalomeApp_Application::afterCloseDoc() { // 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();