From 58b7578f378ab1e49f912e1f288a7c5e05dd4c73 Mon Sep 17 00:00:00 2001 From: vsr Date: Fri, 12 Apr 2013 13:59:43 +0000 Subject: [PATCH] Merge from V6_main 12/04/2013 --- src/LightApp/LightApp_Application.cxx | 2 +- src/LightApp/LightApp_PyInterp.cxx | 2 +- src/LightApp/LightApp_PyInterp.h | 4 +- src/PyConsole/CMakeLists.txt | 12 +- src/PyConsole/Makefile.am | 17 +- src/PyConsole/PyConsole_Console.cxx | 45 +- src/PyConsole/PyConsole_Console.h | 21 +- src/PyConsole/PyConsole_Editor.cxx | 204 +++----- src/PyConsole/PyConsole_Editor.h | 22 +- src/PyConsole/PyConsole_EnhEditor.cxx | 482 ++++++++++++++++++ src/PyConsole/PyConsole_EnhEditor.h | 97 ++++ src/PyConsole/PyConsole_EnhInterp.cxx | 154 ++++++ src/PyConsole/PyConsole_EnhInterp.h | 67 +++ src/PyConsole/PyConsole_Event.cxx | 24 + src/PyConsole/PyConsole_Event.h | 69 +++ src/PyConsole/PyConsole_Request.cxx | 118 +++++ src/PyConsole/PyConsole_Request.h | 105 ++++ src/PyInterp/CMakeLists.txt | 4 + src/PyInterp/Makefile.am | 4 + src/PyInterp/PyInterp_Dispatcher.cxx | 18 +- src/PyInterp/PyInterp_Dispatcher.h | 92 +--- src/PyInterp/PyInterp_Event.cxx | 32 ++ src/PyInterp/PyInterp_Event.h | 76 +++ src/PyInterp/PyInterp_Request.cxx | 27 + src/PyInterp/PyInterp_Request.h | 104 ++++ .../SALOME_PYQT_GUILight/CMakeLists.txt | 1 + .../SALOME_PYQT_GUILight/Makefile.am | 2 +- src/SVTK/SVTK_ViewModel.cxx | 4 +- src/SalomeApp/SalomeApp_Application.cxx | 2 +- src/SalomeApp/SalomeApp_PyInterp.cxx | 2 +- src/SalomeApp/SalomeApp_PyInterp.h | 4 +- 31 files changed, 1548 insertions(+), 269 deletions(-) create mode 100644 src/PyConsole/PyConsole_EnhEditor.cxx create mode 100644 src/PyConsole/PyConsole_EnhEditor.h create mode 100644 src/PyConsole/PyConsole_EnhInterp.cxx create mode 100644 src/PyConsole/PyConsole_EnhInterp.h create mode 100644 src/PyConsole/PyConsole_Event.cxx create mode 100644 src/PyConsole/PyConsole_Event.h create mode 100644 src/PyConsole/PyConsole_Request.cxx create mode 100644 src/PyConsole/PyConsole_Request.h create mode 100644 src/PyInterp/PyInterp_Event.cxx create mode 100644 src/PyInterp/PyInterp_Event.h create mode 100644 src/PyInterp/PyInterp_Request.cxx create mode 100644 src/PyInterp/PyInterp_Request.h diff --git a/src/LightApp/LightApp_Application.cxx b/src/LightApp/LightApp_Application.cxx index 9142c8da6..8dd648f27 100644 --- a/src/LightApp/LightApp_Application.cxx +++ b/src/LightApp/LightApp_Application.cxx @@ -1839,7 +1839,7 @@ QWidget* LightApp_Application::createWindow( const int flag ) #ifndef DISABLE_PYCONSOLE else if ( flag == WT_PyConsole ) { - PyConsole_Console* pyCons = new PyConsole_Console( desktop(),new LightApp_PyInterp()); + PyConsole_Console* pyCons = new PyConsole_EnhConsole( desktop(),new LightApp_PyInterp()); pyCons->setWindowTitle( tr( "PYTHON_CONSOLE" ) ); pyCons->setFont(resourceMgr()->fontValue( "PyConsole", "font" )); pyCons->setIsShowBanner(resourceMgr()->booleanValue( "PyConsole", "show_banner", true )); diff --git a/src/LightApp/LightApp_PyInterp.cxx b/src/LightApp/LightApp_PyInterp.cxx index d6179eca3..5bc90b951 100644 --- a/src/LightApp/LightApp_PyInterp.cxx +++ b/src/LightApp/LightApp_PyInterp.cxx @@ -32,7 +32,7 @@ * calls initialize method defined in base class, which calls virtual methods * initstate & initcontext redefined here. */ -LightApp_PyInterp::LightApp_PyInterp(): PyConsole_Interp() +LightApp_PyInterp::LightApp_PyInterp(): PyConsole_EnhInterp() { } diff --git a/src/LightApp/LightApp_PyInterp.h b/src/LightApp/LightApp_PyInterp.h index 22fdb87f0..d13d6bd85 100644 --- a/src/LightApp/LightApp_PyInterp.h +++ b/src/LightApp/LightApp_PyInterp.h @@ -23,9 +23,9 @@ #ifndef _LIGHTAPP_PYINTERP_H_ #define _LIGHTAPP_PYINTERP_H_ -#include // this include must be first (see PyInterp_base.h)! +#include // this include must be first (see PyInterp_base.h)! -class LightApp_PyInterp : public PyConsole_Interp +class LightApp_PyInterp : public PyConsole_EnhInterp { public: LightApp_PyInterp(); diff --git a/src/PyConsole/CMakeLists.txt b/src/PyConsole/CMakeLists.txt index 207955106..00d0f49ca 100755 --- a/src/PyConsole/CMakeLists.txt +++ b/src/PyConsole/CMakeLists.txt @@ -27,12 +27,14 @@ INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/../Qtx ${CMAKE_CURRENT_SOURCE_DIR}/../SUIT ${CMAKE_CURRENT_SOURCE_DIR}/../PyInterp + ${CMAKE_CURRENT_SOURCE_DIR}/../Event ) -SET(COMMON_LIBS ${PYTHON_LIBRARIES} ${QT_LIBRARIES} ${SALOMELocalTrace} qtx suit PyInterp) +SET(COMMON_LIBS ${PYTHON_LIBRARIES} ${QT_LIBRARIES} ${SALOMELocalTrace} qtx suit PyInterp Event) SET(GUI_HEADERS PyConsole_Editor.h + PyConsole_EnhEditor.h PyConsole_Console.h ) QT4_WRAP_CPP(GUI_HEADERS_MOC ${GUI_HEADERS}) @@ -40,7 +42,11 @@ QT4_WRAP_CPP(GUI_HEADERS_MOC ${GUI_HEADERS}) SET(PyConsole_SOURCES PyConsole_Console.cxx PyConsole_Editor.cxx + PyConsole_EnhEditor.cxx PyConsole_Interp.cxx + PyConsole_EnhInterp.cxx + PyConsole_Event.cxx + PyConsole_Request.cxx ) SET(GUITS_SOURCES @@ -58,7 +64,11 @@ SET(COMMON_HEADERS_H PyConsole.h PyConsole_Console.h PyConsole_Editor.h + PyConsole_EnhEditor.h PyConsole_Interp.h + PyConsole_EnhInterp.h + PyConsole_Event.h + PyConsole_Request.h ) INSTALL(FILES ${COMMON_HEADERS_H} DESTINATION ${GUI_salomeinclude_HEADERS}) QT4_INSTALL_TS_RESOURCES("${GUITS_SOURCES}" "${GUI_salomeres_DATA}") diff --git a/src/PyConsole/Makefile.am b/src/PyConsole/Makefile.am index 5ae4afbc9..4bba60a39 100755 --- a/src/PyConsole/Makefile.am +++ b/src/PyConsole/Makefile.am @@ -29,15 +29,24 @@ salomeinclude_HEADERS = \ PyConsole.h \ PyConsole_Console.h \ PyConsole_Editor.h \ - PyConsole_Interp.h + PyConsole_EnhEditor.h \ + PyConsole_EnhInterp.h \ + PyConsole_Event.h \ + PyConsole_Request.h \ + PyConsole_Interp.h dist_libPyConsole_la_SOURCES = \ PyConsole_Console.cxx \ PyConsole_Editor.cxx \ - PyConsole_Interp.cxx + PyConsole_EnhEditor.cxx \ + PyConsole_Event.cxx \ + PyConsole_Request.cxx \ + PyConsole_Interp.cxx \ + PyConsole_EnhInterp.cxx MOC_FILES = \ PyConsole_Editor_moc.cxx \ + PyConsole_EnhEditor_moc.cxx \ PyConsole_Console_moc.cxx nodist_libPyConsole_la_SOURCES = $(MOC_FILES) @@ -47,7 +56,7 @@ nodist_salomeres_DATA = \ PyConsole_msg_fr.qm libPyConsole_la_CPPFLAGS = $(PYTHON_INCLUDES) $(QT_INCLUDES) \ - -I$(srcdir)/../PyInterp -I$(srcdir)/../SUIT -I$(srcdir)/../Qtx + -I$(srcdir)/../PyInterp -I$(srcdir)/../SUIT -I$(srcdir)/../Qtx -I$(srcdir)/../Event libPyConsole_la_LDFLAGS = $(PYTHON_LIBS) $(QT_MT_LIBS) $(KERNEL_LDFLAGS) -lSALOMELocalTrace -libPyConsole_la_LIBADD = ../Qtx/libqtx.la ../SUIT/libsuit.la ../PyInterp/libPyInterp.la +libPyConsole_la_LIBADD = ../Qtx/libqtx.la ../SUIT/libsuit.la ../Event/libEvent.la ../PyInterp/libPyInterp.la diff --git a/src/PyConsole/PyConsole_Console.cxx b/src/PyConsole/PyConsole_Console.cxx index 8fbd17296..131055a00 100644 --- a/src/PyConsole/PyConsole_Console.cxx +++ b/src/PyConsole/PyConsole_Console.cxx @@ -30,7 +30,8 @@ #include "PyConsole_Interp.h" /// !!! WARNING !!! THIS INCLUDE MUST BE VERY FIRST !!! #include "PyConsole_Console.h" -#include "PyConsole_Editor.h" +#include "PyConsole_EnhEditor.h" +#include "PyConsole_EnhInterp.h" #include @@ -49,8 +50,7 @@ \param interp python interpreter */ PyConsole_Console::PyConsole_Console( QWidget* parent, PyConsole_Interp* interp ) -: QWidget( parent ), - myEditor( 0 ) +: QWidget( parent ) { // create python interpreter myInterp = interp; @@ -75,6 +75,13 @@ PyConsole_Console::PyConsole_Console( QWidget* parent, PyConsole_Interp* interp createActions(); } +/** + * Protected constructor. + */ +PyConsole_Console::PyConsole_Console( QWidget* parent, PyConsole_Interp* i, PyConsole_Editor* e) + : QWidget (parent), myEditor(e), myInterp(i) +{} + /*! \brief Destructor. @@ -324,3 +331,35 @@ void PyConsole_Console::updateActions() 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(myInterp), this ); + char* synchronous = getenv("PYTHON_CONSOLE_SYNC"); + if (synchronous && atoi(synchronous)) + { + myEditor->setIsSync(true); + } + myEditor->viewport()->installEventFilter( this ); + lay->addWidget( myEditor ); + + createActions(); +} diff --git a/src/PyConsole/PyConsole_Console.h b/src/PyConsole/PyConsole_Console.h index 6258ee97f..e7b02ad19 100644 --- a/src/PyConsole/PyConsole_Console.h +++ b/src/PyConsole/PyConsole_Console.h @@ -34,6 +34,7 @@ class PyConsole_Interp; class PyConsole_Editor; +class PyConsole_EnhInterp; class PYCONSOLE_EXPORT PyConsole_Console : public QWidget, public SUIT_PopupClient { @@ -81,14 +82,30 @@ public: void setMenuActions( const int ); int menuActions() const; -private: +protected: void createActions(); void updateActions(); -private: + PyConsole_Console( QWidget* parent, PyConsole_Interp*, PyConsole_Editor*); + + PyConsole_Interp* myInterp; //!< python interpreter PyConsole_Editor* myEditor; //!< python console editor widget QMap 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 diff --git a/src/PyConsole/PyConsole_Editor.cxx b/src/PyConsole/PyConsole_Editor.cxx index 3414e26a6..636ee54a7 100644 --- a/src/PyConsole/PyConsole_Editor.cxx +++ b/src/PyConsole/PyConsole_Editor.cxx @@ -86,16 +86,14 @@ - : copy - : cut - : paste - - TODO: - - paste multiline text: process each line as separate command - (including mouse middle-button click pasting) - - the same for drag-n-drop of multiline text */ #include "PyConsole_Interp.h" // !!! WARNING !!! THIS INCLUDE MUST BE THE VERY FIRST !!! #include "PyConsole_Editor.h" -#include +#include "PyConsole_Event.h" +#include "PyInterp_Event.h" +#include "PyInterp_Dispatcher.h" +#include "PyConsole_Request.h" #include #include @@ -113,13 +111,10 @@ #include #include #include +#include static QString READY_PROMPT = ">>> "; static QString DOTS_PROMPT = "... "; -#define PROMPT_SIZE myPrompt.length() - -#define PRINT_EVENT 65432 - class DumpCommandsFileValidator : public SUIT_FileValidator { @@ -141,95 +136,19 @@ bool DumpCommandsFileValidator::canSave(const QString& file, bool permissions) return SUIT_FileValidator::canSave( file, permissions); } -/*! - \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 ) - : PyInterp_LockRequest( theInterp, theListener, sync ), - myCommand( theCommand ), myState( PyInterp_Event::ES_OK ) - {} - -protected: - /*! - \brief Execute the python command in the interpreter and - get its execution status. - */ - virtual void 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; - } - } - - /*! - \brief Create and return a notification event. - \return new notification event - */ - virtual QEvent* createEvent() const - { - if ( IsSync() ) - QCoreApplication::sendPostedEvents( listener(), PRINT_EVENT ); - return new PyInterp_Event( myState, (PyInterp_Request*)this ); - } - -private: - QString myCommand; //!< Python command - int myState; //!< Python command execution status -}; - -/*! - \class PrintEvent - \brief Python command output backend event. - \internal -*/ - -class PrintEvent : public QEvent +void staticCallbackStdout( void* data, char* c ) { -public: - /*! - \brief Constructor - \param c message text (python trace) - */ - PrintEvent( const char* c ) : QEvent( (QEvent::Type)PRINT_EVENT ), myText( c ) {} - /*! - \brief Get message - \return message text (python trace) - */ - QString text() const { return myText; } - -private: - QString myText; //!< Event message (python trace) -}; + if(!((PyConsole_Editor*)data)->isSuppressOutput()) + QApplication::postEvent( (PyConsole_Editor*)data, new PrintEvent( c, false ) ); +} -void staticCallback( void* data, char* c ) +void staticCallbackStderr( void* data, char* c ) { if(!((PyConsole_Editor*)data)->isSuppressOutput()) - QApplication::postEvent( (PyConsole_Editor*)data, new PrintEvent( c ) ); + QApplication::postEvent( (PyConsole_Editor*)data, new PrintEvent( c, true ) ); } + /*! \brief Constructor. @@ -257,8 +176,8 @@ PyConsole_Editor::PyConsole_Editor( PyConsole_Interp* theInterp, setWordWrapMode( QTextOption::WrapAnywhere ); setAcceptRichText( false ); - theInterp->setvoutcb( staticCallback, this ); - theInterp->setverrcb( staticCallback, this ); + theInterp->setvoutcb( staticCallbackStdout, this ); + theInterp->setverrcb( staticCallbackStderr, this ); // san - This is necessary for troubleless initialization onPyInterpChanged( theInterp ); @@ -366,14 +285,23 @@ QSize PyConsole_Editor::sizeHint() const \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 newBlock, + const bool isError) { + QTextCursor theCursor(textCursor()); + QTextCharFormat cf; + moveCursor( QTextCursor::End ); if ( newBlock ) - textCursor().insertBlock(); - textCursor().insertText( str ); + theCursor.insertBlock(); + if (isError) + cf.setForeground(QBrush(Qt::red)); + else + cf.setForeground(QBrush(Qt::black)); + theCursor.insertText( str, cf); moveCursor( QTextCursor::End ); ensureCursorVisible(); } @@ -382,6 +310,8 @@ void PyConsole_Editor::addText( const QString& str, \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 ) { @@ -462,12 +392,17 @@ void PyConsole_Editor::execAndWait( const QString& command ) */ 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, PROMPT_SIZE ); + QString cmd = par.text().remove( 0, promptSize() ); // extend the command buffer with the current command myCommandBuffer.append( cmd ); // add command to the history @@ -499,7 +434,7 @@ void PyConsole_Editor::dropEvent( QDropEvent* event ) 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() + PROMPT_SIZE ) { + if ( cur.position() < document()->end().previous().position() + promptSize() ) { moveCursor( QTextCursor::End ); pos = cursorRect().center(); } @@ -529,21 +464,18 @@ void PyConsole_Editor::mouseReleaseEvent( QMouseEvent* event ) //copy(); } else if ( event->button() == Qt::MidButton ) { - QString text; - if ( QApplication::clipboard()->supportsSelection() ) - text = QApplication::clipboard()->text( QClipboard::Selection ); - if ( text.isEmpty() ) - text = QApplication::clipboard()->text( QClipboard::Clipboard ); 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() + PROMPT_SIZE ) { + if ( cur.position() < document()->end().previous().position() + promptSize() ) { moveCursor( QTextCursor::End ); } else { setTextCursor( cur ); } - textCursor().clearSelection(); - textCursor().insertText( text ); + const QMimeData* md = QApplication::clipboard()->mimeData( QApplication::clipboard()->supportsSelection() ? + QClipboard::Selection : QClipboard::Clipboard ); + if ( md ) + insertFromMimeData( md ); } else { QTextEdit::mouseReleaseEvent( event ); @@ -612,13 +544,15 @@ void PyConsole_Editor::keyPressEvent( QKeyEvent* event ) } // check for printed key - aKey = ( aKey < Qt::Key_Space || aKey > Qt::Key_ydiaeresis ) ? aKey : 0; + // #### 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 < PROMPT_SIZE ) { + if ( curLine < endLine || curCol < promptSize() ) { moveCursor( QTextCursor::End ); } QTextEdit::keyPressEvent( event ); @@ -654,7 +588,7 @@ void PyConsole_Editor::keyPressEvent( QKeyEvent* event ) myCmdInHistory = myHistory.count(); // remember current command QTextBlock par = document()->end().previous(); - myCurrentCommand = par.text().remove( 0, PROMPT_SIZE ); + myCurrentCommand = par.text().remove( 0, promptSize() ); } if ( myCmdInHistory > 0 ) { myCmdInHistory--; @@ -724,7 +658,7 @@ void PyConsole_Editor::keyPressEvent( QKeyEvent* event ) // - with + modifier keys pressed: move one word left with selection { QString txt = textCursor().block().text(); - if ( !shftPressed && isCommand( txt ) && curCol <= PROMPT_SIZE ) { + if ( !shftPressed && isCommand( txt ) && curCol <= promptSize() ) { moveCursor( QTextCursor::Up ); moveCursor( QTextCursor::EndOfBlock ); } @@ -743,15 +677,15 @@ void PyConsole_Editor::keyPressEvent( QKeyEvent* event ) QString txt = textCursor().block().text(); if ( !shftPressed ) { if ( curCol < txt.length() ) { - if ( isCommand( txt ) && curCol < PROMPT_SIZE ) { - cur.setPosition( cur.block().position() + PROMPT_SIZE ); + 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() + PROMPT_SIZE+1 ); + cur.setPosition( cur.position() + promptSize()+1 ); setTextCursor( cur ); horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); break; @@ -798,7 +732,7 @@ void PyConsole_Editor::keyPressEvent( QKeyEvent* event ) myCmdInHistory = myHistory.count(); // remember current command QTextBlock par = document()->end().previous(); - myCurrentCommand = par.text().remove( 0, PROMPT_SIZE ); + myCurrentCommand = par.text().remove( 0, promptSize() ); } if ( myCmdInHistory > 0 ) { myCmdInHistory = 0; @@ -876,14 +810,14 @@ void PyConsole_Editor::keyPressEvent( QKeyEvent* event ) QString txt = textCursor().block().text(); if ( isCommand( txt ) ) { if ( shftPressed ) { - if ( curCol > PROMPT_SIZE ) { + if ( curCol > promptSize() ) { cur.movePosition( QTextCursor::StartOfLine, QTextCursor::KeepAnchor ); - cur.movePosition( QTextCursor::Right, QTextCursor::KeepAnchor, PROMPT_SIZE ); + cur.movePosition( QTextCursor::Right, QTextCursor::KeepAnchor, promptSize() ); } } else { cur.movePosition( QTextCursor::StartOfLine ); - cur.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, PROMPT_SIZE ); + cur.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, promptSize() ); } setTextCursor( cur ); } @@ -916,13 +850,13 @@ void PyConsole_Editor::keyPressEvent( QKeyEvent* event ) if ( cur.hasSelection() ) { cut(); } - else if ( cur.position() > document()->end().previous().position() + PROMPT_SIZE ) { + 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() + PROMPT_SIZE, + cur.setPosition( document()->end().previous().position() + promptSize(), QTextCursor::KeepAnchor ); setTextCursor( cur ); textCursor().removeSelectedText(); @@ -932,7 +866,7 @@ void PyConsole_Editor::keyPressEvent( QKeyEvent* event ) } } else { - cur.setPosition( document()->end().previous().position() + PROMPT_SIZE ); + cur.setPosition( document()->end().previous().position() + promptSize() ); setTextCursor( cur ); horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); } @@ -948,7 +882,7 @@ void PyConsole_Editor::keyPressEvent( QKeyEvent* event ) if ( cur.hasSelection() ) { cut(); } - else if ( cur.position() > document()->end().previous().position() + PROMPT_SIZE-1 ) { + else if ( cur.position() > document()->end().previous().position() + promptSize()-1 ) { if ( shftPressed ) { moveCursor( QTextCursor::NextWord, QTextCursor::KeepAnchor ); textCursor().removeSelectedText(); @@ -962,7 +896,7 @@ void PyConsole_Editor::keyPressEvent( QKeyEvent* event ) } } else { - cur.setPosition( document()->end().previous().position() + PROMPT_SIZE ); + cur.setPosition( document()->end().previous().position() + promptSize() ); setTextCursor( cur ); horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); } @@ -994,10 +928,10 @@ void PyConsole_Editor::customEvent( QEvent* event ) { switch( event->type() ) { - case PRINT_EVENT: + case PrintEvent::EVENT_ID: { PrintEvent* pe=(PrintEvent*)event; - addText( pe->text() ); + addText( pe->text(), false, pe->isError()); return; } case PyInterp_Event::ES_OK: @@ -1115,11 +1049,11 @@ void PyConsole_Editor::cut() if ( cur.hasSelection() ) { QApplication::clipboard()->setText( cur.selectedText() ); int startSelection = cur.selectionStart(); - if ( startSelection < document()->end().previous().position() + PROMPT_SIZE ) - startSelection = document()->end().previous().position() + PROMPT_SIZE; + if ( startSelection < document()->end().previous().position() + promptSize() ) + startSelection = document()->end().previous().position() + promptSize(); int endSelection = cur.selectionEnd(); - if ( endSelection < document()->end().previous().position() + PROMPT_SIZE ) - endSelection = document()->end().previous().position() + PROMPT_SIZE; + 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() ); @@ -1139,18 +1073,18 @@ void PyConsole_Editor::paste() QTextCursor cur = textCursor(); if ( cur.hasSelection() ) { int startSelection = cur.selectionStart(); - if ( startSelection < document()->end().previous().position() + PROMPT_SIZE ) - startSelection = document()->end().previous().position() + PROMPT_SIZE; + if ( startSelection < document()->end().previous().position() + promptSize() ) + startSelection = document()->end().previous().position() + promptSize(); int endSelection = cur.selectionEnd(); - if ( endSelection < document()->end().previous().position() + PROMPT_SIZE ) - endSelection = document()->end().previous().position() + PROMPT_SIZE; + 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() + PROMPT_SIZE ) + if ( textCursor().position() < document()->end().previous().position() + promptSize() ) moveCursor( QTextCursor::End ); QTextEdit::paste(); } diff --git a/src/PyConsole/PyConsole_Editor.h b/src/PyConsole/PyConsole_Editor.h index 6bcc53a55..2e1c80630 100644 --- a/src/PyConsole/PyConsole_Editor.h +++ b/src/PyConsole/PyConsole_Editor.h @@ -43,7 +43,7 @@ public: PyConsole_Editor( PyConsole_Interp* theInterp, QWidget *theParent = 0 ); ~PyConsole_Editor(); - virtual void addText( const QString& str, const bool newBlock = false ); + 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 ); @@ -60,6 +60,14 @@ public: 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 ); @@ -68,15 +76,9 @@ protected: virtual PyInterp_Request* createRequest( const QString& ); -public slots: - void cut(); - void paste(); - void clear(); - void handleReturn(); - void onPyInterpChanged( PyConsole_Interp* ); - void dump(); - -private: + /** Convenience function */ + inline int promptSize() const { return myPrompt.size(); } + PyConsole_Interp* myInterp; //!< python interpreter QString myCommandBuffer; //!< python command buffer diff --git a/src/PyConsole/PyConsole_EnhEditor.cxx b/src/PyConsole/PyConsole_EnhEditor.cxx new file mode 100644 index 000000000..c3bbbf414 --- /dev/null +++ b/src/PyConsole/PyConsole_EnhEditor.cxx @@ -0,0 +1,482 @@ +// Copyright (C) 2007-2013 CEA/DEN, EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// Author : Adrien Bruneton (CEA/DEN) +// Created on: 4 avr. 2013 + +#include "PyConsole.h" +#include + +#include +#include +#include +#include +#include + +#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 PyConsole_EnhEditor::SEPARATORS = \ + std::vector(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 is pressed + bool ctrlPressed = event->modifiers() & Qt::ControlModifier; + // check if 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 . + */ +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(myInterp)->clearCompletion(); + } + _tab_mode = false; +} + +/** + * Handle the sequence of events after having hit + */ +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::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(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 & 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 matches; + QString first_match, comple_text, doc, base; + QTextCursor cursor(textCursor()); + QTextBlockFormat bf; + QTextCharFormat cf; + PyConsole_EnhInterp * interp = static_cast(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 & 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("\n ") + + QString(" ") + + QString(" ") + + QString("\n") + + QString("

") + + QString("%1

") + + QString("

%2

") + + QString(""); + + 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(); + 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 key stroke + addText(line, false, false); + handleReturn(); + } +} diff --git a/src/PyConsole/PyConsole_EnhEditor.h b/src/PyConsole/PyConsole_EnhEditor.h new file mode 100644 index 000000000..f52dc8256 --- /dev/null +++ b/src/PyConsole/PyConsole_EnhEditor.h @@ -0,0 +1,97 @@ +// Copyright (C) 2007-2013 CEA/DEN, EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// Author : Adrien Bruneton (CEA/DEN) +// Created on: 4 avr. 2013 + +#ifndef PYCONSOLE_ENHEDITOR_H_ +#define PYCONSOLE_ENHEDITOR_H_ + +#include "PyConsole.h" + +#include "PyConsole_Editor.h" +#include +#include + +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 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 is hit */ + int _cursor_pos; + + /** Are we currently pasting several lines */ + bool _multi_line_paste; + + /** Queue of lines being pasted */ + std::queue _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 & 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 & matches, QString & result) const; + +}; + +#endif /* PYCONSOLE_ENHEDITOR_H_ */ diff --git a/src/PyConsole/PyConsole_EnhInterp.cxx b/src/PyConsole/PyConsole_EnhInterp.cxx new file mode 100644 index 000000000..c64808622 --- /dev/null +++ b/src/PyConsole/PyConsole_EnhInterp.cxx @@ -0,0 +1,154 @@ +// Copyright (C) 2007-2013 CEA/DEN, EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// Author : Adrien Bruneton (CEA/DEN) +// Created on: 4 avr. 2013 + + +#include "PyConsole.h" + +#include "PyConsole_EnhInterp.h" + +#include +#include +#include + +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 PyConsole_EnhInterp::PYTHON_KEYWORDS = \ + std::vector(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 ", this is "rsp". + \return command exit status - 0 = success +*/ +int PyConsole_EnhInterp::runDirCommand(const QString & dirArgument, const QString & startMatch) +{ + int ret; + std::vector 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::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 & 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(""); +} + + + diff --git a/src/PyConsole/PyConsole_EnhInterp.h b/src/PyConsole/PyConsole_EnhInterp.h new file mode 100644 index 000000000..113f84a65 --- /dev/null +++ b/src/PyConsole/PyConsole_EnhInterp.h @@ -0,0 +1,67 @@ +// Copyright (C) 2007-2013 CEA/DEN, EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// Author : Adrien Bruneton (CEA/DEN) +// Created on: 4 avr. 2013 + + +#ifndef PYCONSOLE_ENHINTERP_H_ +#define PYCONSOLE_ENHINTERP_H_ + +#include "PyConsole.h" + +#include +#include "PyConsole_Interp.h" + +#include +#include + +/** + * 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& 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 PYTHON_KEYWORDS; + + /** Last computed matches */ + std::vector _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 & result, bool discardSwig=true) const; + +}; + +#endif /* PYCONSOLE_ENHINTERP_H_ */ diff --git a/src/PyConsole/PyConsole_Event.cxx b/src/PyConsole/PyConsole_Event.cxx new file mode 100644 index 000000000..ae0b66594 --- /dev/null +++ b/src/PyConsole/PyConsole_Event.cxx @@ -0,0 +1,24 @@ +// Copyright (C) 2007-2013 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// Author : Vadim SANDLER (Open CASCADE S.A.S), Adrien Bruneton (CEA/DEN) + +#include "PyConsole_Event.h" diff --git a/src/PyConsole/PyConsole_Event.h b/src/PyConsole/PyConsole_Event.h new file mode 100644 index 000000000..ac6a75fa2 --- /dev/null +++ b/src/PyConsole/PyConsole_Event.h @@ -0,0 +1,69 @@ +// Copyright (C) 2007-2013 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// Author : Vadim SANDLER (Open CASCADE S.A.S), Adrien Bruneton (CEA/DEN) + +#ifndef PYCONSOLE_EVENT_H +#define PYCONSOLE_EVENT_H + +#include "PyConsole.h" + +#include +#include + +/*! + \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 diff --git a/src/PyConsole/PyConsole_Request.cxx b/src/PyConsole/PyConsole_Request.cxx new file mode 100644 index 000000000..bac6a385a --- /dev/null +++ b/src/PyConsole/PyConsole_Request.cxx @@ -0,0 +1,118 @@ +// Copyright (C) 2007-2013 CEA/DEN, EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// Author : Adrien Bruneton (CEA/DEN) +// Created on: 3 avr. 2013 + +#include "PyConsole_Request.h" + +#include "PyInterp_Event.h" +#include "PyConsole_Event.h" +#include "PyConsole_EnhInterp.h" +#include "PyConsole_EnhEditor.h" + +#include + +/** + * 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(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); +} + + + diff --git a/src/PyConsole/PyConsole_Request.h b/src/PyConsole/PyConsole_Request.h new file mode 100644 index 000000000..ccfea0c55 --- /dev/null +++ b/src/PyConsole/PyConsole_Request.h @@ -0,0 +1,105 @@ +// Copyright (C) 2007-2013 CEA/DEN, EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// Author : Adrien Bruneton (CEA/DEN) +// Created on: 3 avr. 2013 + + +#ifndef PYCONSOLE_REQUEST_H_ +#define PYCONSOLE_REQUEST_H_ + +#include "PyConsole.h" +#include "PyInterp_Request.h" + +#include +#include +#include + +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 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_ */ diff --git a/src/PyInterp/CMakeLists.txt b/src/PyInterp/CMakeLists.txt index 425fa8b05..3267a4d93 100755 --- a/src/PyInterp/CMakeLists.txt +++ b/src/PyInterp/CMakeLists.txt @@ -34,6 +34,8 @@ QT4_WRAP_CPP(GUI_HEADERS_MOC ${GUI_HEADERS}) SET(PyInterp_SOURCES PyInterp_Interp.cxx PyInterp_Dispatcher.cxx + PyInterp_Event.cxx + PyInterp_Request.cxx ) ADD_DEFINITIONS(${QT_DEFINITIONS} ${PYTHON_DEFINITIONS}) @@ -46,5 +48,7 @@ SET(COMMON_HEADERS_H PyInterp.h PyInterp_Interp.h PyInterp_Dispatcher.h + PyInterp_Event.h + PyInterp_Request.h ) INSTALL(FILES ${COMMON_HEADERS_H} DESTINATION ${GUI_salomeinclude_HEADERS}) diff --git a/src/PyInterp/Makefile.am b/src/PyInterp/Makefile.am index d19ed4e89..4bb400560 100755 --- a/src/PyInterp/Makefile.am +++ b/src/PyInterp/Makefile.am @@ -31,10 +31,14 @@ lib_LTLIBRARIES = libPyInterp.la salomeinclude_HEADERS = \ PyInterp.h \ PyInterp_Interp.h \ + PyInterp_Event.h \ + PyInterp_Request.h \ PyInterp_Dispatcher.h dist_libPyInterp_la_SOURCES = \ PyInterp_Interp.cxx \ + PyInterp_Event.cxx \ + PyInterp_Request.cxx \ PyInterp_Dispatcher.cxx MOC_FILES = PyInterp_Watcher_moc.cxx diff --git a/src/PyInterp/PyInterp_Dispatcher.cxx b/src/PyInterp/PyInterp_Dispatcher.cxx index 5e7643547..44052093a 100755 --- a/src/PyInterp/PyInterp_Dispatcher.cxx +++ b/src/PyInterp/PyInterp_Dispatcher.cxx @@ -27,23 +27,11 @@ #include "PyInterp_Dispatcher.h" // !!! WARNING !!! THIS INCLUDE MUST BE THE VERY FIRST !!! #include "PyInterp_Interp.h" #include "PyInterp_Watcher.h" -#include +#include "PyInterp_Request.h" #include #include -class PyInterp_ExecuteEvent: public SALOME_Event -{ -public: - PyInterp_Request* myRequest; - PyInterp_ExecuteEvent( PyInterp_Request* r ) - : myRequest( r ) {} - virtual void Execute() - { - myRequest->execute(); - } -}; - PyInterp_Dispatcher* PyInterp_Dispatcher::myInstance = 0; void PyInterp_Request::process() @@ -76,9 +64,9 @@ void PyInterp_Request::Destroy( PyInterp_Request* request ) delete request; } -QEvent* PyInterp_Request::createEvent() const +QEvent* PyInterp_Request::createEvent() { - return new PyInterp_Event( PyInterp_Event::ES_NOTIFY, (PyInterp_Request*)this ); + return new PyInterp_Event( PyInterp_Event::ES_NOTIFY, this ); } void PyInterp_Request::processEvent( QObject* o ) diff --git a/src/PyInterp/PyInterp_Dispatcher.h b/src/PyInterp/PyInterp_Dispatcher.h index 793124d2b..4cc9a55f3 100755 --- a/src/PyInterp/PyInterp_Dispatcher.h +++ b/src/PyInterp/PyInterp_Dispatcher.h @@ -22,106 +22,22 @@ // File : PyInterp_Dispatcher.h // Author : Sergey Anikin, OCC -// Module : SALOME +// Module : GUI // #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 #include -#include #include class QObject; - -class PyInterp_Interp; class PyInterp_Watcher; -class PyInterp_Dispatcher; -class PyInterp_ExecuteEvent; - -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() const; - // 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; -}; - -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_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; -}; class PYINTERP_EXPORT PyInterp_Dispatcher : protected QThread { diff --git a/src/PyInterp/PyInterp_Event.cxx b/src/PyInterp/PyInterp_Event.cxx new file mode 100644 index 000000000..7027f6e21 --- /dev/null +++ b/src/PyInterp/PyInterp_Event.cxx @@ -0,0 +1,32 @@ +// Copyright (C) 2007-2013 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// File : PyInterp_Request.h +// Author : Sergey Anikin, OCC, Adrien Bruneton (CEA/DEN) +// Module : GUI + +#include "PyInterp_Event.h" +#include "PyInterp_Request.h" + +void PyInterp_ExecuteEvent::Execute() +{ + myRequest->execute(); +} diff --git a/src/PyInterp/PyInterp_Event.h b/src/PyInterp/PyInterp_Event.h new file mode 100644 index 000000000..3a1640054 --- /dev/null +++ b/src/PyInterp/PyInterp_Event.h @@ -0,0 +1,76 @@ + +// Copyright (C) 2007-2013 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// File : PyInterp_Request.h +// Author : Sergey Anikin, OCC, Adrien Bruneton (CEA/DEN) +// Module : GUI + +#ifndef PYINTERP_EVENT_H +#define PYINTERP_EVENT_H + +#include "PyInterp.h" + +#include + +#include + +class PyInterp_Request; + +class PyInterp_ExecuteEvent: public SALOME_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 diff --git a/src/PyInterp/PyInterp_Request.cxx b/src/PyInterp/PyInterp_Request.cxx new file mode 100644 index 000000000..e8032c1ad --- /dev/null +++ b/src/PyInterp/PyInterp_Request.cxx @@ -0,0 +1,27 @@ +// Copyright (C) 2007-2013 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// File : PyInterp_Request.h +// Author : Sergey Anikin, OCC, Adrien Bruneton (CEA/DEN) +// Module : GUI + +#include "PyInterp_Request.h" + diff --git a/src/PyInterp/PyInterp_Request.h b/src/PyInterp/PyInterp_Request.h new file mode 100644 index 000000000..9a4291b36 --- /dev/null +++ b/src/PyInterp/PyInterp_Request.h @@ -0,0 +1,104 @@ +// Copyright (C) 2007-2013 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// File : PyInterp_Request.h +// Author : Sergey Anikin (OCC), Adrien Bruneton (CEA/DEN) +// Module : GUI + +#ifndef PYINTERP_REQUEST_H +#define PYINTERP_REQUEST_H + +#include "PyInterp.h" +#include "PyInterp_Event.h" + +#include +#include + +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 diff --git a/src/SALOME_PYQT/SALOME_PYQT_GUILight/CMakeLists.txt b/src/SALOME_PYQT/SALOME_PYQT_GUILight/CMakeLists.txt index e1e3919f9..b8765efa2 100755 --- a/src/SALOME_PYQT/SALOME_PYQT_GUILight/CMakeLists.txt +++ b/src/SALOME_PYQT/SALOME_PYQT_GUILight/CMakeLists.txt @@ -40,6 +40,7 @@ INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/../../SUITApp ${CMAKE_CURRENT_SOURCE_DIR}/../../CAM ${CMAKE_CURRENT_SOURCE_DIR}/../../STD + ${CMAKE_CURRENT_SOURCE_DIR}/../../Event ) SET(COMMON_LIBS ${PYTHON_LIBRARIES} ${PYQT_LIBRARIES} ${VTK_LIBRARIES} ${OPENGL_LIBRARIES} PyInterp LightApp) diff --git a/src/SALOME_PYQT/SALOME_PYQT_GUILight/Makefile.am b/src/SALOME_PYQT/SALOME_PYQT_GUILight/Makefile.am index 08c264dcb..cfa541401 100644 --- a/src/SALOME_PYQT/SALOME_PYQT_GUILight/Makefile.am +++ b/src/SALOME_PYQT/SALOME_PYQT_GUILight/Makefile.am @@ -74,7 +74,7 @@ libSalomePyQtGUILight_la_CPPFLAGS = $(QT_INCLUDES) $(SIP_INCLUDES) $(PYTHON_INC -I$(srcdir)/../../Plot2d -I$(srcdir)/../../OCCViewer \ -I$(srcdir)/../../CAM -I$(srcdir)/../../STD \ -I$(srcdir)/../../SUITApp \ - -I$(srcdir)/../../CAM -I$(srcdir)/../../STD + -I$(srcdir)/../../CAM -I$(srcdir)/../../STD -I$(srcdir)/../../Event if GUI_ENABLE_CORBA libSalomePyQtGUILight_la_CPPFLAGS += -I$(srcdir)/../../SalomeApp \ diff --git a/src/SVTK/SVTK_ViewModel.cxx b/src/SVTK/SVTK_ViewModel.cxx index d0e2556d1..d89674a8a 100644 --- a/src/SVTK/SVTK_ViewModel.cxx +++ b/src/SVTK/SVTK_ViewModel.cxx @@ -666,10 +666,10 @@ void SVTK_Viewer::EraseAll( const bool forced ) //Handle(SALOME_InteractiveObject) anObj = anAct->getIO(); //if(!anObj.IsNull() && anObj->hasEntry() && aStudy) // ToolsGUI::SetVisibility(aStudy,anObj->getEntry(),false,this); - if(forced) + if(forced){ if(SVTK_Renderer* aRnd = aView->GetRenderer()) aRnd->RemoveActor(anAct); - else{ + }else{ // just erase actor anAct->SetVisibility( false ); // erase dependent actors diff --git a/src/SalomeApp/SalomeApp_Application.cxx b/src/SalomeApp/SalomeApp_Application.cxx index 159997416..33d73927d 100644 --- a/src/SalomeApp/SalomeApp_Application.cxx +++ b/src/SalomeApp/SalomeApp_Application.cxx @@ -929,7 +929,7 @@ QWidget* SalomeApp_Application::createWindow( const int flag ) } else if ( flag == WT_PyConsole ) { - PyConsole_Console* pyCons = new PyConsole_Console( desktop(), new SalomeApp_PyInterp() ); + PyConsole_Console* pyCons = new PyConsole_EnhConsole( desktop(), new SalomeApp_PyInterp() ); pyCons->setWindowTitle( tr( "PYTHON_CONSOLE" ) ); pyCons->setFont(resourceMgr()->fontValue( "PyConsole", "font" )); pyCons->setIsShowBanner(resourceMgr()->booleanValue( "PyConsole", "show_banner", true )); diff --git a/src/SalomeApp/SalomeApp_PyInterp.cxx b/src/SalomeApp/SalomeApp_PyInterp.cxx index 185b01f30..a0301b6ca 100755 --- a/src/SalomeApp/SalomeApp_PyInterp.cxx +++ b/src/SalomeApp/SalomeApp_PyInterp.cxx @@ -37,7 +37,7 @@ * initstate & initcontext redefined here. */ SalomeApp_PyInterp::SalomeApp_PyInterp(): - PyConsole_Interp(), myFirstRun( true ) + PyConsole_EnhInterp(), myFirstRun( true ) { } diff --git a/src/SalomeApp/SalomeApp_PyInterp.h b/src/SalomeApp/SalomeApp_PyInterp.h index 84c7906fe..d457cf763 100755 --- a/src/SalomeApp/SalomeApp_PyInterp.h +++ b/src/SalomeApp/SalomeApp_PyInterp.h @@ -27,9 +27,9 @@ #ifndef _SalomeApp_PYINTERP_H_ #define _SalomeApp_PYINTERP_H_ -#include // this include must be first (see PyInterp_base.h)! +#include // this include must be first (see PyInterp_base.h)! -class SalomeApp_PyInterp : public PyConsole_Interp +class SalomeApp_PyInterp : public PyConsole_EnhInterp { public: SalomeApp_PyInterp(); -- 2.39.2