From 71a5ac5c5a0474842b41e0373f7337955d0edc5f Mon Sep 17 00:00:00 2001 From: vsr Date: Wed, 26 Sep 2007 10:54:16 +0000 Subject: [PATCH] Imp PAL7743 (output flush is not possible in python console) --- src/PyInterp/PyInterp_base.cxx | 146 ++++++++++++++++--- src/PyInterp/PyInterp_base.h | 15 +- src/PythonConsole/PythonConsole_PyEditor.cxx | 66 +++++---- src/PythonConsole/PythonConsole_PyInterp.cxx | 3 +- 4 files changed, 177 insertions(+), 53 deletions(-) diff --git a/src/PyInterp/PyInterp_base.cxx b/src/PyInterp/PyInterp_base.cxx index 7225855fe..d2a855767 100644 --- a/src/PyInterp/PyInterp_base.cxx +++ b/src/PyInterp/PyInterp_base.cxx @@ -29,6 +29,7 @@ #include "PyInterp_base.h" // this include must be first (see PyInterp_base.h)! #include +#include using namespace std; @@ -72,6 +73,108 @@ PyLockWrapper PyInterp_base::GetLockWrapper(){ return _tstate; } +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 PyMethodDef PyStdOut_methods[] = { + {"write", (PyCFunction)PyStdOut_write, METH_VARARGS, + PyDoc_STR("write(string) -> None")}, + {NULL, NULL} /* sentinel */ +}; + +static PyMemberDef PyStdOut_memberlist[] = { + {"softspace", T_INT, offsetof(PyStdOut, softspace), 0, + "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; +} // main python interpreter (static attributes) @@ -124,12 +227,9 @@ void PyInterp_base::initialize() return; } - // Create cStringIO to capture stdout and stderr - PycString_IMPORT; - if (PycStringIO) { // CTH11627 : additional check - _vout = PycStringIO->NewOutput(128); - _verr = PycStringIO->NewOutput(128); - } + // 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(); @@ -147,6 +247,10 @@ void PyInterp_base::init_python() PySys_SetArgv(_argc, _argv); PyEval_InitThreads(); // Create (and acquire) the interpreter lock _interp = PyThreadState_Get()->interp; + if (PyType_Ready(&PyStdOut_Type) < 0) + { + PyErr_Print(); + } _gtstate = PyEval_SaveThread(); // Release global thread state } @@ -164,17 +268,18 @@ string PyInterp_base::getbanner() int PyInterp_base::initRun() { + // + // probably all below code isn't required + // + /* PySys_SetObject("stderr",_verr); PySys_SetObject("stdout",_vout); - PyObjWrapper verr(PyObject_CallMethod(_verr,"reset","")); - PyObjWrapper vout(PyObject_CallMethod(_vout,"reset","")); - //PyObject *m = PyImport_GetModuleDict(); PySys_SetObject("stdout",PySys_GetObject("__stdout__")); PySys_SetObject("stderr",PySys_GetObject("__stderr__")); - + */ return 0; } @@ -264,9 +369,6 @@ int PyInterp_base::simpleRun(const char *command) PySys_SetObject("stderr",_verr); PySys_SetObject("stdout",_vout); - PyObjWrapper verr(PyObject_CallMethod(_verr,"reset","")); - PyObjWrapper vout(PyObject_CallMethod(_vout,"reset","")); - int ier = compile_command(command,_g); // Outputs are redirected on standards outputs (console) @@ -300,17 +402,15 @@ const char * PyInterp_base::getNext() } -string PyInterp_base::getverr(){ - //PyLockWrapper aLock(_tstate); - PyObjWrapper v(PycStringIO->cgetvalue(_verr)); - string aRet(PyString_AsString(v)); - return aRet; +void PyInterp_base::setvoutcb(PyOutChanged* cb, void* data) +{ + ((PyStdOut*)_vout)->_cb=cb; + ((PyStdOut*)_vout)->_data=data; } -string PyInterp_base::getvout(){ - //PyLockWrapper aLock(_tstate); - PyObjWrapper v(PycStringIO->cgetvalue(_vout)); - string aRet(PyString_AsString(v)); - return aRet; +void PyInterp_base::setverrcb(PyOutChanged* cb, void* data) +{ + ((PyStdOut*)_verr)->_cb=cb; + ((PyStdOut*)_verr)->_data=data; } diff --git a/src/PyInterp/PyInterp_base.h b/src/PyInterp/PyInterp_base.h index 71931655c..298b1c508 100644 --- a/src/PyInterp/PyInterp_base.h +++ b/src/PyInterp/PyInterp_base.h @@ -63,6 +63,8 @@ class PYINTERP_EXPORT PyLockWrapper ~PyLockWrapper(); }; +typedef void PyOutChanged(void* data,char * c); + class PYINTERP_EXPORT PyInterp_base{ public: static int _argc; @@ -85,8 +87,8 @@ class PYINTERP_EXPORT PyInterp_base{ PyLockWrapper GetLockWrapper(); std::string getbanner(); - std::string getverr(); - std::string getvout(); + void setverrcb(PyOutChanged*,void*); + void setvoutcb(PyOutChanged*,void*); const char * getPrevious(); const char * getNext(); @@ -142,4 +144,13 @@ public: } }; + +typedef struct { + PyObject_HEAD + int softspace; + PyOutChanged* _cb; + void* _data; + bool _iscerr; +} PyStdOut; + #endif diff --git a/src/PythonConsole/PythonConsole_PyEditor.cxx b/src/PythonConsole/PythonConsole_PyEditor.cxx index 21e2566f4..deecd648f 100755 --- a/src/PythonConsole/PythonConsole_PyEditor.cxx +++ b/src/PythonConsole/PythonConsole_PyEditor.cxx @@ -38,6 +38,7 @@ #include #include #include +#include using namespace std; @@ -59,7 +60,7 @@ static QString DOTS_PROMPT = "... "; class ExecCommand : public PyInterp_LockRequest { public: - ExecCommand(PyInterp_base* theInterp, const char* theCommand, + ExecCommand(PyInterp_base* theInterp, const QString& theCommand, PythonConsole_PyEditor* theListener, bool sync = false) : PyInterp_LockRequest( theInterp, theListener, sync ), myCommand( theCommand ), myState( PyInterp_Event::OK ) @@ -67,22 +68,17 @@ public: protected: virtual void execute(){ - if(myCommand != ""){ + if( !myCommand.stripWhiteSpace().isEmpty() ) { // if(MYDEBUG) MESSAGE("*** ExecCommand::execute() started"); SUIT_Session::SetPythonExecuted(true); // disable GUI user actions int ret = getInterp()->run( myCommand.latin1() ); SUIT_Session::SetPythonExecuted(false); // enable GUI user actions // if(MYDEBUG) MESSAGE("ExecCommand::execute() - myInterp = "< 0) + else if( ret > 0 ) myState = PyInterp_Event::INCOMPLETE; - myError = getInterp()->getverr().c_str(); - myOutput = getInterp()->getvout().c_str(); // if(MYDEBUG) MESSAGE("*** ExecCommand::execute() finished"); - }else{ - myError = ""; - myOutput = ""; } } @@ -91,15 +87,26 @@ protected: return new PyInterp_Event( myState, (PyInterp_Request*)this ); } -public: - QString myError; - QString myOutput; - private: QString myCommand; int myState; }; +#define PRINT_EVENT 65432 + +class PrintEvent : public QCustomEvent +{ +public: + PrintEvent( const char* c ) : QCustomEvent( PRINT_EVENT ), myText( c ) {} + QString text() const { return myText; } +private: + QString myText; +}; + +void staticCallback( void* data, char* c ) +{ + QApplication::postEvent( (PythonConsole_PyEditor*)data, new PrintEvent( c ) ); +} /*! Constructor @@ -116,7 +123,11 @@ PythonConsole_PyEditor::PythonConsole_PyEditor(PyInterp_base* theInterp, QWidget setUndoRedoEnabled( false ); _currentPrompt = READY_PROMPT; - setWordWrap(NoWrap); + setWordWrap( WidgetWidth ); + setWrapPolicy( Anywhere ); + + theInterp->setvoutcb( staticCallback, this ); + theInterp->setverrcb( staticCallback, this ); connect(this,SIGNAL(returnPressed()),this,SLOT(handleReturn()) ); @@ -228,7 +239,7 @@ void PythonConsole_PyEditor::handleReturn() // Post a request to execute Python command // Editor will be informed via a custom event that execution has been completed - PyInterp_Dispatcher::Get()->Exec( new ExecCommand( myInterp, _buf.latin1(), this ) ); + PyInterp_Dispatcher::Get()->Exec( new ExecCommand( myInterp, _buf, this ) ); } /*! @@ -703,22 +714,21 @@ void PythonConsole_PyEditor::keyPressEvent( QKeyEvent* e ) void PythonConsole_PyEditor::customEvent(QCustomEvent* e) { switch( e->type() ) { + case PRINT_EVENT: + { + PrintEvent* pe=(PrintEvent*)e; + setText( pe->text() ); + return; + } case PyInterp_Event::OK: case PyInterp_Event::ERROR: { - PyInterp_Event* pe = dynamic_cast( e ); - if ( pe ){ - ExecCommand* ec = dynamic_cast( pe->GetRequest() ); - if ( ec ){ - // The next line has appeared dangerous in case if - // Python command execution has produced very large output. - // A more clever approach is needed... - setText(ec->myOutput); - setText(ec->myError); - } - } _buf.truncate(0); _currentPrompt = READY_PROMPT; + QString txt = text( paragraphs()-1 ); + txt.truncate( txt.length()-1 ); + if ( !txt.isEmpty() ) + setText("\n"); setText(_currentPrompt); viewport()->unsetCursor(); if( myIsInLoop ) @@ -729,6 +739,10 @@ void PythonConsole_PyEditor::customEvent(QCustomEvent* e) { _buf.append("\n"); _currentPrompt = DOTS_PROMPT; + QString txt = text( paragraphs()-1 ); + txt.truncate( txt.length()-1 ); + if ( !txt.isEmpty() ) + setText("\n"); setText(_currentPrompt); viewport()->unsetCursor(); if( myIsInLoop ) diff --git a/src/PythonConsole/PythonConsole_PyInterp.cxx b/src/PythonConsole/PythonConsole_PyInterp.cxx index e061b3677..0046f02e3 100755 --- a/src/PythonConsole/PythonConsole_PyInterp.cxx +++ b/src/PythonConsole/PythonConsole_PyInterp.cxx @@ -76,8 +76,7 @@ PythonConsole_PyInterp::~PythonConsole_PyInterp() bool PythonConsole_PyInterp::initState() { /* - * The GIL is acquired and will be held on initState output - * It is the caller responsability to release the lock if needed + * The GIL is acquired on input and released on output */ /*PyEval_AcquireLock(); #ifdef WNT -- 2.39.2