From: Anthony Geay Date: Mon, 22 Feb 2016 13:44:49 +0000 (+0100) Subject: Split PyConsole in order to be used easily outside GUI of SALOME. X-Git-Tag: V8_1_0a1~29 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=e5f3e29f0ae622f71877e3d296775d4063c185f1;p=modules%2Fgui.git Split PyConsole in order to be used easily outside GUI of SALOME. --- diff --git a/CMakeLists.txt b/CMakeLists.txt index ceb25ef0a..ea19bc0a7 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -389,7 +389,7 @@ ENDIF() # Python-based packages specific targets: IF(SALOME_USE_PYCONSOLE) LIST(APPEND _${PROJECT_NAME}_exposed_targets - PyInterp PyConsole SalomePyQtGUILight) + PyInterp PyConsoleBase PyConsole SalomePyQtGUILight) IF(SALOME_USE_PLOT2DVIEWER) LIST(APPEND _${PROJECT_NAME}_exposed_targets SalomePyQt) ENDIF() diff --git a/SalomeGUIConfig.cmake.in b/SalomeGUIConfig.cmake.in index d99916d85..d52f42693 100644 --- a/SalomeGUIConfig.cmake.in +++ b/SalomeGUIConfig.cmake.in @@ -198,6 +198,7 @@ SET(GUI_ObjBrowser ObjBrowser) SET(GUI_OCCViewer OCCViewer) SET(GUI_OpenGLUtils OpenGLUtils) SET(GUI_Plot2d Plot2d) +SET(GUI_PyConsoleBase PyConsoleBase) SET(GUI_PyConsole PyConsole) SET(GUI_PyInterp PyInterp) SET(GUI_PyEditor PyEditor) diff --git a/adm_local/cmake_files/FindGUI.cmake b/adm_local/cmake_files/FindGUI.cmake index 7a185c84a..ef6ffa9c4 100644 --- a/adm_local/cmake_files/FindGUI.cmake +++ b/adm_local/cmake_files/FindGUI.cmake @@ -36,6 +36,7 @@ FIND_LIBRARY(OCCViewer OCCViewer ${GUI_ROOT_DIR}/lib/salome) FIND_LIBRARY(OpenGLUtils OpenGLUtils ${GUI_ROOT_DIR}/lib/salome) FIND_LIBRARY(Plot2d Plot2d ${GUI_ROOT_DIR}/lib/salome) FIND_LIBRARY(PyConsole PyConsole ${GUI_ROOT_DIR}/lib/salome) +FIND_LIBRARY(PyConsoleBase PyConsoleBase ${GUI_ROOT_DIR}/lib/salome) FIND_LIBRARY(PyInterp PyInterp ${GUI_ROOT_DIR}/lib/salome) FIND_LIBRARY(QDS QDS ${GUI_ROOT_DIR}/lib/salome) FIND_LIBRARY(qtx qtx ${GUI_ROOT_DIR}/lib/salome) diff --git a/adm_local/cmake_files/deprecated/FindGUI.cmake b/adm_local/cmake_files/deprecated/FindGUI.cmake index cf4277198..ff29e7b16 100644 --- a/adm_local/cmake_files/deprecated/FindGUI.cmake +++ b/adm_local/cmake_files/deprecated/FindGUI.cmake @@ -32,6 +32,7 @@ FIND_LIBRARY(OCCViewer OCCViewer ${GUI_ROOT_DIR}/lib/salome) FIND_LIBRARY(OpenGLUtils OpenGLUtils ${GUI_ROOT_DIR}/lib/salome) FIND_LIBRARY(Plot2d Plot2d ${GUI_ROOT_DIR}/lib/salome) FIND_LIBRARY(PyConsole PyConsole ${GUI_ROOT_DIR}/lib/salome) +FIND_LIBRARY(PyConsoleBase PyConsoleBase ${GUI_ROOT_DIR}/lib/salome) FIND_LIBRARY(PyInterp PyInterp ${GUI_ROOT_DIR}/lib/salome) FIND_LIBRARY(QDS QDS ${GUI_ROOT_DIR}/lib/salome) FIND_LIBRARY(qtx qtx ${GUI_ROOT_DIR}/lib/salome) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6435a1ccf..ba919fa87 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -124,6 +124,7 @@ ENDIF(SALOME_USE_PYVIEWER) ## IF(SALOME_USE_PYCONSOLE) ADD_SUBDIRECTORY(PyInterp) + ADD_SUBDIRECTORY(PyConsoleBase) ADD_SUBDIRECTORY(PyConsole) ADD_SUBDIRECTORY(SALOME_PYQT) ENDIF(SALOME_USE_PYCONSOLE) diff --git a/src/LightApp/CMakeLists.txt b/src/LightApp/CMakeLists.txt index 04e309b4a..3171c6c93 100755 --- a/src/LightApp/CMakeLists.txt +++ b/src/LightApp/CMakeLists.txt @@ -82,6 +82,7 @@ IF(SALOME_USE_PYCONSOLE) INCLUDE_DIRECTORIES( ${PYTHON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/src/PyConsole + ${PROJECT_SOURCE_DIR}/src/PyConsoleBase ${PROJECT_SOURCE_DIR}/src/PyInterp ${PROJECT_SOURCE_DIR}/src/SUITApp ) diff --git a/src/LightApp/resources/LightApp.xml b/src/LightApp/resources/LightApp.xml index 8489c9f5a..892c453e2 100644 --- a/src/LightApp/resources/LightApp.xml +++ b/src/LightApp/resources/LightApp.xml @@ -73,6 +73,7 @@ + diff --git a/src/PyConsole/CMakeLists.txt b/src/PyConsole/CMakeLists.txt index 6dc393da5..0bf638f98 100755 --- a/src/PyConsole/CMakeLists.txt +++ b/src/PyConsole/CMakeLists.txt @@ -25,6 +25,8 @@ INCLUDE(UseQtExt) INCLUDE_DIRECTORIES( ${QT_INCLUDES} ${PYTHON_INCLUDE_DIRS} + ${PROJECT_SOURCE_DIR}/src/PyConsole + ${PROJECT_SOURCE_DIR}/src/PyConsoleBase ${PROJECT_SOURCE_DIR}/src/Qtx ${PROJECT_SOURCE_DIR}/src/SUIT ${PROJECT_SOURCE_DIR}/src/Event @@ -35,7 +37,7 @@ INCLUDE_DIRECTORIES( ADD_DEFINITIONS(${QT_DEFINITIONS} ${PYTHON_DEFINITIONS}) # libraries to link to -SET(_link_LIBRARIES ${QT_LIBRARIES} ${PYTHON_LIBRARIES} qtx suit PyInterp Event) +SET(_link_LIBRARIES ${QT_LIBRARIES} ${PYTHON_LIBRARIES} qtx suit PyConsoleBase) # --- headers --- @@ -49,10 +51,6 @@ SET(_moc_HEADERS # header files / no moc processing SET(_other_HEADERS PyConsole.h - PyConsole_EnhInterp.h - PyConsole_Event.h - PyConsole_Interp.h - PyConsole_Request.h ) # header files / to install @@ -60,27 +58,23 @@ SET(PyConsole_HEADERS ${_moc_HEADERS} ${_other_HEADERS}) # --- resources --- +# --- sources --- # resource files / to be processed by lrelease + SET(_ts_RESOURCES resources/PyConsole_msg_en.ts resources/PyConsole_msg_fr.ts resources/PyConsole_msg_ja.ts ) -# --- sources --- - # sources / moc wrappings QT_WRAP_MOC(_moc_SOURCES ${_moc_HEADERS}) # sources / static SET(_other_SOURCES PyConsole_Console.cxx - PyConsole_Editor.cxx PyConsole_EnhEditor.cxx - PyConsole_EnhInterp.cxx - PyConsole_Event.cxx - PyConsole_Interp.cxx - PyConsole_Request.cxx + PyConsole_Editor.cxx ) # sources / to compile @@ -94,4 +88,3 @@ INSTALL(TARGETS PyConsole EXPORT ${PROJECT_NAME}TargetGroup DESTINATION ${SALOME INSTALL(FILES ${PyConsole_HEADERS} DESTINATION ${SALOME_INSTALL_HEADERS}) QT_INSTALL_TS_RESOURCES("${_ts_RESOURCES}" "${SALOME_GUI_INSTALL_RES_DATA}") - diff --git a/src/PyConsole/PyConsole_Console.cxx b/src/PyConsole/PyConsole_Console.cxx index e684bf1e6..43e9cb6a4 100644 --- a/src/PyConsole/PyConsole_Console.cxx +++ b/src/PyConsole/PyConsole_Console.cxx @@ -30,6 +30,7 @@ #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" @@ -42,6 +43,12 @@ #include #include +PyConsole_EditorBase *PyConsole_Console::PyConsole_Interp_Creator::createEditor( PyConsole_Interp *interp, PyConsole_ConsoleBase *console ) const +{ return new PyConsole_Editor(interp,console); } + +PyConsole_Interp *PyConsole_Console::PyConsole_Interp_Creator::createInterp( ) const +{ return new PyConsole_Interp; } + /*! \brief Constructor. @@ -50,34 +57,19 @@ \param interp python interpreter */ PyConsole_Console::PyConsole_Console( QWidget* parent, PyConsole_Interp* interp ) -: QWidget( parent ) + : PyConsole_ConsoleBase( parent, interp, 0 ) { - PyConsole_Interp* anInterp = interp ? interp : new PyConsole_Interp(); - - // initialize Python interpretator - anInterp->initialize(); - - // create editor console - QVBoxLayout* lay = new QVBoxLayout( this ); - lay->setMargin( 0 ); - myEditor = new PyConsole_Editor( anInterp, this ); - char* synchronous = getenv("PYTHON_CONSOLE_SYNC"); - if (synchronous && atoi(synchronous)) - { - myEditor->setIsSync(true); - } - myEditor->viewport()->installEventFilter( this ); - lay->addWidget( myEditor ); - - createActions(); + PyConsole_Interp_Creator crea; + defaultConstructor(interp,crea); } /** * Protected constructor. */ -PyConsole_Console::PyConsole_Console( QWidget* parent, PyConsole_Interp* /*i*/, PyConsole_Editor* e ) - : QWidget (parent), myEditor(e) -{} +PyConsole_Console::PyConsole_Console( QWidget* parent, PyConsole_Interp* i, PyConsole_Editor* e ) + : PyConsole_ConsoleBase(parent,i,e) +{ +} /*! \brief Destructor. @@ -88,128 +80,16 @@ PyConsole_Console::~PyConsole_Console() { } -PyConsole_Interp* PyConsole_Console::getInterp() const -{ - return myEditor ? myEditor->getInterp() : 0; -} - /*! - \brief Execute python command in the interpreter. - \param command string with command and arguments -*/ -void PyConsole_Console::exec( const QString& command ) -{ - if ( myEditor ) - myEditor->exec( command ); -} - -/*! - \brief Execute python command in the interpreter - and wait until it is finished. - - Block execution of main application until the python command is executed. - \param command string with command and arguments -*/ -void PyConsole_Console::execAndWait( const QString& command ) -{ - if ( myEditor ) - myEditor->execAndWait( command ); -} - -/*! - \brief Get synchronous mode flag value. - - \sa setIsSync() - \return True if python console works in synchronous mode -*/ -bool PyConsole_Console::isSync() const -{ - return myEditor->isSync(); -} - -/*! - \brief Set synchronous mode flag value. - - In synhronous mode the Python commands are executed in the GUI thread - and the GUI is blocked until the command is finished. In the asynchronous - mode each Python command is executed in the separate thread that does not - block the main GUI loop. - - \param on synhronous mode flag -*/ -void PyConsole_Console::setIsSync( const bool on ) -{ - myEditor->setIsSync( on ); -} - -/*! - \brief Get suppress output flag value. - - \sa setIsSuppressOutput() - \return True if python console output is suppressed. -*/ -bool PyConsole_Console::isSuppressOutput() const -{ - return myEditor->isSuppressOutput(); -} - -/*! - \brief Set suppress output flag value. - - In case if suppress output flag is true, the python - console output suppressed. - - \param on suppress output flag -*/ -void PyConsole_Console::setIsSuppressOutput( const bool on ) -{ - myEditor->setIsSuppressOutput(on); -} - -/*! - \brief Get 'show banner' flag value. - - \sa setIsShowBanner() - \return \c true if python console shows banner -*/ -bool PyConsole_Console::isShowBanner() const -{ - return myEditor->isShowBanner(); -} - -/*! - \brief Set 'show banner' flag value. - - The banner is shown in the top of the python console window. - - \sa isShowBanner() - \param on 'show banner' flag -*/ -void PyConsole_Console::setIsShowBanner( const bool on ) -{ - myEditor->setIsShowBanner( on ); -} + \brief Create the context popup menu. -/*! - \brief Change the python console's font. - \param f new font -*/ -void PyConsole_Console::setFont( const QFont& f ) -{ - if( myEditor ) - myEditor->setFont( f ); -} + Fill in the popup menu with the commands. -/*! - \brief Get python console font. - \return current python console's font + \param menu context popup menu */ -QFont PyConsole_Console::font() const +void PyConsole_Console::contextMenuPopup( QMenu *menu ) { - QFont res; - if( myEditor ) - res = myEditor->font(); - return res; + PyConsole_ConsoleBase::contextMenuPopup(menu); } /*! @@ -231,143 +111,11 @@ bool PyConsole_Console::eventFilter( QObject* o, QEvent* e ) return QWidget::eventFilter( o, e ); } -/*! - \brief Create the context popup menu. +PyConsole_EditorBase *PyConsole_EnhConsole::PyConsole_Interp_EnhCreator::createEditor( PyConsole_Interp *interp, PyConsole_ConsoleBase *console ) const +{ return new PyConsole_EnhEditor(interp,console); } - Fill in the popup menu with the commands. - - \param menu context popup menu -*/ -void PyConsole_Console::contextMenuPopup( QMenu* menu ) -{ - if ( myEditor->isReadOnly() ) - return; - - menu->addAction( myActions[CopyId] ); - menu->addAction( myActions[PasteId] ); - menu->addAction( myActions[ClearId] ); - menu->addSeparator(); - menu->addAction( myActions[SelectAllId] ); - menu->addSeparator(); - menu->addAction( myActions[DumpCommandsId] ); - if ( !myEditor->isLogging() ) - menu->addAction( myActions[StartLogId] ); - else - menu->addAction( myActions[StopLogId] ); - - Qtx::simplifySeparators( menu ); - - updateActions(); -} - -/*! - \brief Set actions to be visible in the context popup menu. - - Actions, which IDs are set in \a flags parameter, will be shown in the - context popup menu. Other actions will not be shown. - - \param flags ORed together actions flags -*/ -void PyConsole_Console::setMenuActions( const int flags ) -{ - myActions[CopyId]->setVisible( flags & CopyId ); - myActions[PasteId]->setVisible( flags & PasteId ); - myActions[ClearId]->setVisible( flags & ClearId ); - myActions[SelectAllId]->setVisible( flags & SelectAllId ); - myActions[DumpCommandsId]->setVisible( flags & DumpCommandsId ); - myActions[StartLogId]->setVisible( flags & StartLogId ); - myActions[StopLogId]->setVisible( flags & StopLogId ); -} - -/*! - \brief Get menu actions which are currently visible in the context popup menu. - \return ORed together actions flags - \sa setMenuActions() -*/ -int PyConsole_Console::menuActions() const -{ - int ret = 0; - ret = ret | ( myActions[CopyId]->isVisible() ? CopyId : 0 ); - ret = ret | ( myActions[PasteId]->isVisible() ? PasteId : 0 ); - ret = ret | ( myActions[ClearId]->isVisible() ? ClearId : 0 ); - ret = ret | ( myActions[SelectAllId]->isVisible() ? SelectAllId : 0 ); - ret = ret | ( myActions[DumpCommandsId]->isVisible() ? DumpCommandsId : 0 ); - ret = ret | ( myActions[StartLogId]->isVisible() ? StartLogId : 0 ); - ret = ret | ( myActions[StopLogId]->isVisible() ? StopLogId : 0 ); - return ret; -} - -/*! - \brief Create menu actions. - - Create context popup menu actions. -*/ -void PyConsole_Console::createActions() -{ - QAction* a = new QAction( tr( "EDIT_COPY_CMD" ), this ); - a->setStatusTip( tr( "EDIT_COPY_CMD" ) ); - connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( copy() ) ); - myActions.insert( CopyId, a ); - - a = new QAction( tr( "EDIT_PASTE_CMD" ), this ); - a->setStatusTip( tr( "EDIT_PASTE_CMD" ) ); - connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( paste() ) ); - myActions.insert( PasteId, a ); - - a = new QAction( tr( "EDIT_CLEAR_CMD" ), this ); - a->setStatusTip( tr( "EDIT_CLEAR_CMD" ) ); - connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( clear() ) ); - myActions.insert( ClearId, a ); - - a = new QAction( tr( "EDIT_SELECTALL_CMD" ), this ); - a->setStatusTip( tr( "EDIT_SELECTALL_CMD" ) ); - connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( selectAll() ) ); - myActions.insert( SelectAllId, a ); - - a = new QAction( tr( "EDIT_DUMPCOMMANDS_CMD" ), this ); - a->setStatusTip( tr( "EDIT_DUMPCOMMANDS_CMD" ) ); - connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( dump() ) ); - myActions.insert( DumpCommandsId, a ); - - a = new QAction( tr( "EDIT_STARTLOG_CMD" ), this ); - a->setStatusTip( tr( "EDIT_STARTLOG_CMD" ) ); - connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( startLog() ) ); - myActions.insert( StartLogId, a ); - - a = new QAction( tr( "EDIT_STOPLOG_CMD" ), this ); - a->setStatusTip( tr( "EDIT_STOPLOG_CMD" ) ); - connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( stopLog() ) ); - myActions.insert( StopLogId, a ); -} - -/*! - \brief Update menu actions. - - Update context popup menu action state. -*/ -void PyConsole_Console::updateActions() -{ - myActions[CopyId]->setEnabled( myEditor->textCursor().hasSelection() ); - myActions[PasteId]->setEnabled( !myEditor->isReadOnly() && !QApplication::clipboard()->text().isEmpty() ); - myActions[SelectAllId]->setEnabled( !myEditor->document()->isEmpty() ); -} - -/*! - \brief Start python trace logging - \param fileName the path to the log file -*/ -void PyConsole_Console::startLog( const QString& fileName ) -{ - myEditor->startLog( fileName ); -} - -/*! - \brief Stop python trace logging -*/ -void PyConsole_Console::stopLog() -{ - myEditor->stopLog(); -} +PyConsole_Interp *PyConsole_EnhConsole::PyConsole_Interp_EnhCreator::createInterp( ) const +{ return new PyConsole_EnhInterp; } /** * Similar to constructor of the base class but using enhanced objects. @@ -378,22 +126,6 @@ void PyConsole_Console::stopLog() PyConsole_EnhConsole::PyConsole_EnhConsole( QWidget* parent, PyConsole_Interp* interp ) : PyConsole_Console( parent, interp, 0 ) { - PyConsole_Interp* anInterp = interp ? interp : new PyConsole_EnhInterp(); - - // initialize Python interpretator - anInterp->initialize(); - - // create editor console - QVBoxLayout* lay = new QVBoxLayout( this ); - lay->setMargin( 0 ); - myEditor = new PyConsole_EnhEditor( anInterp, this ); - char* synchronous = getenv("PYTHON_CONSOLE_SYNC"); - if (synchronous && atoi(synchronous)) - { - myEditor->setIsSync(true); - } - myEditor->viewport()->installEventFilter( this ); - lay->addWidget( myEditor ); - - createActions(); + PyConsole_Interp_EnhCreator crea; + defaultConstructor(interp,crea); } diff --git a/src/PyConsole/PyConsole_Console.h b/src/PyConsole/PyConsole_Console.h index ce8f36467..1aa68a4d6 100644 --- a/src/PyConsole/PyConsole_Console.h +++ b/src/PyConsole/PyConsole_Console.h @@ -27,7 +27,7 @@ #define PYCONSOLE_CONSOLE_H #include "PyConsole.h" - +#include #include #include #include @@ -35,65 +35,26 @@ class PyConsole_Interp; class PyConsole_Editor; -class PYCONSOLE_EXPORT PyConsole_Console : public QWidget, public SUIT_PopupClient +class PYCONSOLE_EXPORT PyConsole_Console : public PyConsole_ConsoleBase, public SUIT_PopupClient { Q_OBJECT - public: - //! Context popup menu actions flags - enum + + struct PyConsole_Interp_Creator : public PyConsole_Interp_CreatorBase { - CopyId = 0x01, //!< "Copy" menu action - PasteId = 0x02, //!< "Paste" menu action - ClearId = 0x04, //!< "Clear" menu action - SelectAllId = 0x08, //!< "Select All" menu action - DumpCommandsId = 0x10, //!< "DumpCommands" menu action - StartLogId = 0x20, //!< "Start log" menu action - StopLogId = 0x40, //!< "Stop log" menu action - All = 0xFF, //!< all menu actions + virtual PyConsole_EditorBase *createEditor( PyConsole_Interp *interp, PyConsole_ConsoleBase *console ) const; + virtual PyConsole_Interp *createInterp( ) const; }; public: PyConsole_Console( QWidget* parent, PyConsole_Interp* interp = 0 ); virtual ~PyConsole_Console(); - - //! \brief Get python interperter - PyConsole_Interp* getInterp() const; - QFont font() const; - virtual void setFont( const QFont& ); - - bool isSync() const; - void setIsSync( const bool ); - - bool isSuppressOutput() const; - void setIsSuppressOutput( const bool ); - - bool isShowBanner() const; - void setIsShowBanner( const bool ); - - void exec( const QString& ); - void execAndWait( const QString& ); - - virtual bool eventFilter( QObject*, QEvent* ); - //! \brief Get popup client symbolic name virtual QString popupClientType() const { return QString( "PyConsole" ); } virtual void contextMenuPopup( QMenu* ); - - void setMenuActions( const int ); - int menuActions() const; - - void startLog( const QString& ); - void stopLog(); - + virtual bool eventFilter( QObject*, QEvent* ); protected: - void createActions(); - void updateActions(); - PyConsole_Console( QWidget* parent, PyConsole_Interp*, PyConsole_Editor*); - - PyConsole_Editor* myEditor; //!< python console editor widget - QMap myActions; //!< menu actions list }; /** @@ -101,9 +62,16 @@ protected: * Similar to PyConsole_Console except that an enhanced interpreter and enhanced editor * are encapsulated. */ -class PYCONSOLE_EXPORT PyConsole_EnhConsole: public PyConsole_Console +class PYCONSOLE_EXPORT PyConsole_EnhConsole : public PyConsole_Console { Q_OBJECT +public: + + struct PyConsole_Interp_EnhCreator : public PyConsole_Interp_CreatorBase + { + virtual PyConsole_EditorBase *createEditor( PyConsole_Interp *interp, PyConsole_ConsoleBase *console ) const; + virtual PyConsole_Interp *createInterp( ) const; + }; public: PyConsole_EnhConsole( QWidget* parent, PyConsole_Interp* interp = 0 ); diff --git a/src/PyConsole/PyConsole_Editor.cxx b/src/PyConsole/PyConsole_Editor.cxx index 2311b58d4..2371eecae 100644 --- a/src/PyConsole/PyConsole_Editor.cxx +++ b/src/PyConsole/PyConsole_Editor.cxx @@ -95,10 +95,9 @@ #include "PyInterp_Dispatcher.h" #include "PyConsole_Request.h" -#include -#include -#include -#include +#include "SUIT_FileValidator.h" +#include "SUIT_MessageBox.h" +#include "SUIT_FileDlg.h" #include #include @@ -112,25 +111,7 @@ #include #include #include - -//VSR: uncomment below macro to support unicode text properly in SALOME -// current commented out due to regressions -//#define PAL22528_UNICODE - -namespace -{ - QString fromUtf8( const char* txt ) - { -#ifdef PAL22528_UNICODE - return QString::fromUtf8( txt ); -#else - return QString( txt ); -#endif - } -} - -static QString READY_PROMPT = ">>> "; -static QString DOTS_PROMPT = "... "; +#include class DumpCommandsFileValidator : public SUIT_FileValidator { @@ -152,25 +133,6 @@ bool DumpCommandsFileValidator::canSave(const QString& file, bool permissions) return SUIT_FileValidator::canSave( file, permissions); } -void staticCallbackStdout( void* data, char* c ) -{ - if(!((PyConsole_Editor*)data)->isSuppressOutput()) { - PyConsole_Editor* e = (PyConsole_Editor*)data; - e->putLog( fromUtf8(c) ); - QApplication::postEvent( e, new PrintEvent( fromUtf8(c), false ) ); - } -} - -void staticCallbackStderr( void* data, char* c ) -{ - if(!((PyConsole_Editor*)data)->isSuppressOutput()) { - PyConsole_Editor* e = (PyConsole_Editor*)data; - e->putLog( fromUtf8(c) ); - QApplication::postEvent( e, new PrintEvent( fromUtf8(c), true ) ); - } -} - - /*! \brief Constructor. @@ -180,29 +142,8 @@ void staticCallbackStderr( void* data, char* c ) */ PyConsole_Editor::PyConsole_Editor( PyConsole_Interp* theInterp, QWidget* theParent ) -: QTextEdit( theParent ), - myInterp( 0 ), - myCmdInHistory( -1 ), - myEventLoop( 0 ), - myShowBanner( true ), - myIsSync( true ), - myIsSuppressOutput( false ) + : PyConsole_EditorBase(theInterp,theParent) { - QString fntSet( "" ); - QFont aFont = SUIT_Tools::stringToFont( fntSet ); - setFont( aFont ); - setUndoRedoEnabled( false ); - - myPrompt = READY_PROMPT; - setLineWrapMode( QTextEdit::WidgetWidth ); - setWordWrapMode( QTextOption::WrapAnywhere ); - setAcceptRichText( false ); - - theInterp->setvoutcb( staticCallbackStdout, this ); - theInterp->setverrcb( staticCallbackStderr, this ); - - // san - This is necessary for troubleless initialization - onPyInterpChanged( theInterp ); } /*! @@ -210,991 +151,43 @@ PyConsole_Editor::PyConsole_Editor( PyConsole_Interp* theInterp, */ PyConsole_Editor::~PyConsole_Editor() { - myInterp = 0; -} - -/*! - \brief Get Python interpreter -*/ -PyConsole_Interp* PyConsole_Editor::getInterp() const -{ - return myInterp; -} - -/*! - \brief Get synchronous mode flag value. - - \sa setIsSync() - \return True if python console works in synchronous mode -*/ -bool PyConsole_Editor::isSync() const -{ - return myIsSync; -} - -/*! - \brief Set synchronous mode flag value. - - In synhronous mode the Python commands are executed in the GUI thread - and the GUI is blocked until the command is finished. In the asynchronous - mode each Python command is executed in the separate thread that does not - block the main GUI loop. - - \param on synhronous mode flag -*/ -void PyConsole_Editor::setIsSync( const bool on ) -{ - myIsSync = on; -} - -/*! - \brief Get suppress output flag value. - - \sa setIsSuppressOutput() - \return \c true if python console output is suppressed. -*/ -bool PyConsole_Editor::isSuppressOutput() const -{ - return myIsSuppressOutput; -} - -/*! - \brief Set suppress output flag value. - - In case if suppress output flag is true, the python - console output suppressed. - - \param on suppress output flag -*/ -void PyConsole_Editor::setIsSuppressOutput( const bool on ) -{ - myIsSuppressOutput = on; -} - -/*! - \brief Get 'show banner' flag value. - - \sa setIsShowBanner() - \return \c true if python console shows banner -*/ -bool PyConsole_Editor::isShowBanner() const -{ - return myShowBanner; -} - -/*! - \brief Set 'show banner' flag value. - - The banner is shown in the top of the python console window. - - \sa isShowBanner() - \param on 'show banner' flag -*/ -void PyConsole_Editor::setIsShowBanner( const bool on ) -{ - if ( myShowBanner != on ) { - myShowBanner = on; - clear(); - } -} - -/*! - \brief Check if trace logging is switched on. - - \sa startLog(), stopLog() - \return \c true if trace logging is switched on -*/ -bool PyConsole_Editor::isLogging() const -{ - return !myLogFile.isEmpty(); -} - -/*! - \brief Get size hint for the Python console window - \return size hint value -*/ -QSize PyConsole_Editor::sizeHint() const -{ - QFontMetrics fm( font() ); - int nbLines = ( isShowBanner() ? myBanner.split("\n").count() : 0 ) + 1; - QSize s(100, fm.lineSpacing()*nbLines); - return s; -} - -/*! - \brief Put the string \a str to the python editor. - \param str string to be put in the command line of the editor - \param newBlock if True, then the string is printed on a new line - \param isError if true, the text is printed in dark red -*/ -void PyConsole_Editor::addText( const QString& str, - const bool newBlock, - const bool isError) -{ - QTextCursor theCursor(textCursor()); - QTextCharFormat cf; - - moveCursor( QTextCursor::End ); - if ( newBlock ) - theCursor.insertBlock(); - if (isError) - cf.setForeground(QBrush(Qt::red)); - else - cf.setForeground(QBrush(Qt::black)); - theCursor.insertText( str, cf); - moveCursor( QTextCursor::End ); - ensureCursorVisible(); -} - -/*! - \brief Convenient method for executing a Python command, - as if the user typed it manually. - \param command python command to be executed - - !!! WARNING: doesn't work properly with multi-line commands. !!! -*/ -void PyConsole_Editor::exec( const QString& command ) -{ - if ( isReadOnly() ) { - // some interactive command is being executed in this editor... - // shedule the command to the queue - myQueue.push_back( command ); - return; - } - // remove last line - moveCursor( QTextCursor::End ); - moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor ); - textCursor().removeSelectedText(); - // set "ready" prompt - myPrompt = READY_PROMPT; - // clear command buffer - myCommandBuffer.truncate( 0 ); - // unset history browsing mode - myCmdInHistory = -1; - // print command line by line - QString cmd = command; - if ( !cmd.endsWith( "\n" ) ) cmd += "\n"; - QStringList lines = command.split( "\n" ); - for ( int i = 0; i < lines.size(); i++ ) { - if ( !lines[i].trimmed().isEmpty() ) - myHistory.push_back( lines[i] ); - addText( ( i == 0 ? READY_PROMPT : DOTS_PROMPT ) + lines[i], i != 0 ); - putLog( QString( "%1%2\n" ).arg( i == 0 ? READY_PROMPT : DOTS_PROMPT ).arg( lines[i] ) ); - } - // IPAL20182 - addText( "", true ); - // set read-only mode - setReadOnly( true ); - // set busy cursor - setCursor( Qt::BusyCursor ); - - // post a request to execute Python command; - // editor will be informed via a custom event that execution has been completed - PyInterp_Dispatcher::Get()->Exec( createRequest( cmd ) ); -} - -/*! - \brief Create request to the python dispatcher for the command execution. - - \param command python command to be executed - */ -PyInterp_Request* PyConsole_Editor::createRequest( const QString& command ) -{ - return new ExecCommand( myInterp, command, this, isSync() ); -} - -/*! - \brief Execute command in the python interpreter - and wait until it is finished. - - \param command python command to be executed - */ -void PyConsole_Editor::execAndWait( const QString& command ) -{ - // already running ? - if( myEventLoop ) - return; - - // create new event loop - bool sync = isSync(); - if ( !sync ) { - myEventLoop = new QEventLoop( this ); - } - - // execute command - exec( command ); - - if ( !sync ) { - // run event loop - myEventLoop->exec(); - // delete event loop after command is processed - delete myEventLoop; - myEventLoop = 0; - } -} - -/*! - \brief Process "Enter" key press event. - - Execute the command entered by the user. -*/ -void PyConsole_Editor::handleReturn() -{ - // Position cursor at the end - QTextCursor curs(textCursor()); - curs.movePosition(QTextCursor::End); - setTextCursor(curs); - - // get last line - QTextBlock par = document()->end().previous(); - if ( !par.isValid() ) return; - - // get command - QString cmd = par.text().remove( 0, promptSize() ); - // extend the command buffer with the current command - myCommandBuffer.append( cmd ); - // add command to the history - if ( !cmd.trimmed().isEmpty() ) - myHistory.push_back( cmd ); - putLog( QString( "%1%2\n" ).arg( myPrompt ).arg( cmd ) ); - - // IPAL19397 - addText( "", true ); - - // set read-only mode - setReadOnly( true ); - // set busy cursor - setCursor( Qt::BusyCursor ); - - // post a request to execute Python command; - // editor will be informed via a custom event that execution has been completed - PyInterp_Dispatcher::Get()->Exec( createRequest( myCommandBuffer ) ); -} - -/*! - \brief Process drop event. - - Paste dragged text. - \param event drop event -*/ -void PyConsole_Editor::dropEvent( QDropEvent* event ) -{ - // get the initial drop position - QPoint pos = event->pos(); - QTextCursor cur = cursorForPosition( event->pos() ); - // if the position is not in the last line move it to the end of the command line - if ( cur.position() < document()->end().previous().position() + promptSize() ) { - moveCursor( QTextCursor::End ); - pos = cursorRect().center(); - } - // create new drop event and use it instead of the original - QDropEvent de( pos, - event->possibleActions(), - event->mimeData(), - event->mouseButtons(), - event->keyboardModifiers(), - event->type() ); - QTextEdit::dropEvent( &de ); - // accept the original event - event->acceptProposedAction(); -} - -/*! - \brief Process mouse button release event. - - Left mouse button: copy selection to the clipboard. - Middle mouse button: paste clipboard's contents. - \param event mouse event -*/ -void PyConsole_Editor::mouseReleaseEvent( QMouseEvent* event ) -{ - if ( event->button() == Qt::LeftButton ) { - QTextEdit::mouseReleaseEvent( event ); - //copy(); - } - else if ( event->button() == Qt::MidButton ) { - QTextCursor cur = cursorForPosition( event->pos() ); - // if the position is not in the last line move it to the end of the command line - if ( cur.position() < document()->end().previous().position() + promptSize() ) { - moveCursor( QTextCursor::End ); - } - else { - setTextCursor( cur ); - } - const QMimeData* md = QApplication::clipboard()->mimeData( QApplication::clipboard()->supportsSelection() ? - QClipboard::Selection : QClipboard::Clipboard ); - if ( md ) - insertFromMimeData( md ); - } - else { - QTextEdit::mouseReleaseEvent( event ); - } -} - -/*! - \brief Check if the string is command. - - Return True if the string \a str is likely to be the command - (i.e. it is started from the '>>>' or '...'). - \param str string to be checked -*/ -bool PyConsole_Editor::isCommand( const QString& str ) const -{ - return str.startsWith( READY_PROMPT ) || str.startsWith( DOTS_PROMPT ); -} - -/*! - \brief Handle keyboard event. - - Implement navigation, history browsing, copy/paste and other common - operations. - \param event keyboard event -*/ -void PyConsole_Editor::keyPressEvent( QKeyEvent* event ) -{ - // get cursor position - QTextCursor cur = textCursor(); - int curLine = cur.blockNumber(); - int curCol = cur.columnNumber(); - - // get last edited line - int endLine = document()->blockCount()-1; - - // get pressed key code - int aKey = event->key(); - - // check if is pressed - bool ctrlPressed = event->modifiers() & Qt::ControlModifier; - // check if is pressed - bool shftPressed = event->modifiers() & Qt::ShiftModifier; - - if ( aKey == Qt::Key_Escape || ( ctrlPressed && aKey == -1 ) ) { - // process + key-binding and key: clear current command - myCommandBuffer.truncate( 0 ); - myPrompt = READY_PROMPT; - addText( myPrompt, true ); - horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); - return; - } - else if ( ctrlPressed && aKey == Qt::Key_C ) { - // process + key-binding : copy - copy(); - return; - } - else if ( ctrlPressed && aKey == Qt::Key_X ) { - // process + key-binding : cut - cut(); - return; - } - else if ( ctrlPressed && aKey == Qt::Key_V ) { - // process + key-binding : paste - paste(); - return; - } - - // check for printed key - // #### aKey = ( aKey < Qt::Key_Space || aKey > Qt::Key_ydiaeresis ) ? aKey : 0; - // Better: - aKey = !(QChar(aKey).isPrint()) ? aKey : 0; - - switch ( aKey ) { - case 0 : - // any printed key: just print it - { - if ( curLine < endLine || curCol < promptSize() ) { - moveCursor( QTextCursor::End ); - } - QTextEdit::keyPressEvent( event ); - break; - } - case Qt::Key_Return: - case Qt::Key_Enter: - // key: process the current command - { - handleReturn(); - break; - } - case Qt::Key_Up: - // arrow key: process as follows: - // - without , modifiers: previous command in history - // - with modifier key pressed: move cursor one row up without selection - // - with modifier key pressed: move cursor one row up with selection - // - with + modifier keys pressed: scroll one row up - { - if ( ctrlPressed && shftPressed ) { - int value = verticalScrollBar()->value(); - int spacing = fontMetrics().lineSpacing(); - verticalScrollBar()->setValue( value > spacing ? value-spacing : 0 ); - } - else if ( shftPressed || ctrlPressed ) { - if ( curLine > 0 ) - moveCursor( QTextCursor::Up, - shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor ); - } - else { - if ( myCmdInHistory < 0 && myHistory.count() > 0 ) { - // set history browsing mode - myCmdInHistory = myHistory.count(); - // remember current command - QTextBlock par = document()->end().previous(); - myCurrentCommand = par.text().remove( 0, promptSize() ); - } - if ( myCmdInHistory > 0 ) { - myCmdInHistory--; - // get previous command in the history - QString previousCommand = myHistory.at( myCmdInHistory ); - // print previous command - moveCursor( QTextCursor::End ); - moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor ); - textCursor().removeSelectedText(); - addText( myPrompt + previousCommand ); - // move cursor to the end - moveCursor( QTextCursor::End ); - } - } - break; - } - case Qt::Key_Down: - // arrow key: process as follows: - // - without , modifiers: next command in history - // - with modifier key pressed: move cursor one row down without selection - // - with modifier key pressed: move cursor one row down with selection - // - with + modifier keys pressed: scroll one row down - { - if ( ctrlPressed && shftPressed ) { - int value = verticalScrollBar()->value(); - int maxval = verticalScrollBar()->maximum(); - int spacing = fontMetrics().lineSpacing(); - verticalScrollBar()->setValue( value+spacing < maxval ? value+spacing : maxval ); - } - else if ( shftPressed || ctrlPressed) { - if ( curLine < endLine ) - moveCursor( QTextCursor::Down, - shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor ); - } - else { - if ( myCmdInHistory >= 0 ) { - // get next command in the history - myCmdInHistory++; - QString nextCommand; - if ( myCmdInHistory < myHistory.count() ) { - // next command in history - nextCommand = myHistory.at( myCmdInHistory ); - } - else { - // end of history is reached - // last printed command - nextCommand = myCurrentCommand; - // unset history browsing mode - myCmdInHistory = -1; - } - // print next or current command - moveCursor( QTextCursor::End ); - moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor ); - textCursor().removeSelectedText(); - addText( myPrompt + nextCommand ); - // move cursor to the end - moveCursor( QTextCursor::End ); - } - } - break; - } - case Qt::Key_Left: - // arrow key: process as follows: - // - without , modifiers: move one symbol left (taking into account prompt) - // - with modifier key pressed: move one word left (taking into account prompt) - // - with modifier key pressed: move one symbol left with selection - // - with + modifier keys pressed: move one word left with selection - { - QString txt = textCursor().block().text(); - if ( !shftPressed && isCommand( txt ) && curCol <= promptSize() ) { - moveCursor( QTextCursor::Up ); - moveCursor( QTextCursor::EndOfBlock ); - } - else { - QTextEdit::keyPressEvent( event ); - } - break; - } - case Qt::Key_Right: - // arrow key: process as follows: - // - without , modifiers: move one symbol right (taking into account prompt) - // - with modifier key pressed: move one word right (taking into account prompt) - // - with modifier key pressed: move one symbol right with selection - // - with + modifier keys pressed: move one word right with selection - { - QString txt = textCursor().block().text(); - if ( !shftPressed ) { - if ( curCol < txt.length() ) { - if ( isCommand( txt ) && curCol < promptSize() ) { - cur.setPosition( cur.block().position() + promptSize() ); - setTextCursor( cur ); - break; - } - } - else { - if ( curLine < endLine && isCommand( textCursor().block().next().text() ) ) { - cur.setPosition( cur.position() + promptSize()+1 ); - setTextCursor( cur ); - horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); - break; - } - } - } - QTextEdit::keyPressEvent( event ); - break; - } - case Qt::Key_PageUp: - // key: process as follows: - // - without , modifiers: first command in history - // - with modifier key pressed: move cursor one page up without selection - // - with modifier key pressed: move cursor one page up with selection - // - with + modifier keys pressed: scroll one page up - { - if ( ctrlPressed && shftPressed ) { - verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub); - } - else if ( shftPressed || ctrlPressed ) { - bool moved = false; - qreal lastY = cursorRect( cur ).top(); - qreal distance = 0; - // move using movePosition to keep the cursor's x - do { - qreal y = cursorRect( cur ).top(); - distance += qAbs( y - lastY ); - lastY = y; - moved = cur.movePosition( QTextCursor::Up, - shftPressed ? QTextCursor::KeepAnchor : - QTextCursor::MoveAnchor ); - } while ( moved && distance < viewport()->height() ); - if ( moved ) { - cur.movePosition( QTextCursor::Down, - shftPressed ? QTextCursor::KeepAnchor : - QTextCursor::MoveAnchor ); - verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub ); - } - setTextCursor( cur ); - } - else { - if ( myCmdInHistory < 0 && myHistory.count() > 0 ) { - // set history browsing mode - myCmdInHistory = myHistory.count(); - // remember current command - QTextBlock par = document()->end().previous(); - myCurrentCommand = par.text().remove( 0, promptSize() ); - } - if ( myCmdInHistory > 0 ) { - myCmdInHistory = 0; - // get very first command in the history - QString firstCommand = myHistory.at( myCmdInHistory ); - // print first command - moveCursor( QTextCursor::End ); - moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor ); - textCursor().removeSelectedText(); - addText( myPrompt + firstCommand ); - // move cursor to the end - moveCursor( QTextCursor::End ); - } - } - break; - } - case Qt::Key_PageDown: - // key: process as follows: - // - without , modifiers: last command in history - // - with modifier key pressed: move cursor one page down without selection - // - with modifier key pressed: move cursor one page down with selection - // - with + modifier keys pressed: scroll one page down - { - if ( ctrlPressed && shftPressed ) { - verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd); - } - else if ( shftPressed || ctrlPressed ) { - bool moved = false; - qreal lastY = cursorRect( cur ).top(); - qreal distance = 0; - // move using movePosition to keep the cursor's x - do { - qreal y = cursorRect( cur ).top(); - distance += qAbs( y - lastY ); - lastY = y; - moved = cur.movePosition( QTextCursor::Down, - shftPressed ? QTextCursor::KeepAnchor : - QTextCursor::MoveAnchor ); - } while ( moved && distance < viewport()->height() ); - if ( moved ) { - cur.movePosition( QTextCursor::Up, - shftPressed ? QTextCursor::KeepAnchor : - QTextCursor::MoveAnchor ); - verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub ); - } - setTextCursor( cur ); - } - else { - if ( myCmdInHistory >= 0 ) { - // unset history browsing mode - myCmdInHistory = -1; - // print current command - moveCursor( QTextCursor::End ); - moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor ); - textCursor().removeSelectedText(); - addText( myPrompt + myCurrentCommand ); - // move cursor to the end - moveCursor( QTextCursor::End ); - } - } - break; - } - case Qt::Key_Home: - // key: process as follows: - // - without , modifiers: move cursor to the beginning of the current line without selection - // - with modifier key pressed: move cursor to the very first symbol without selection - // - with modifier key pressed: move cursor to the beginning of the current line with selection - // - with + modifier keys pressed: move cursor to the very first symbol with selection - { - if ( ctrlPressed ) { - moveCursor( QTextCursor::Start, - shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor ); - } - else { - QString txt = textCursor().block().text(); - if ( isCommand( txt ) ) { - if ( shftPressed ) { - if ( curCol > promptSize() ) { - cur.movePosition( QTextCursor::StartOfLine, QTextCursor::KeepAnchor ); - cur.movePosition( QTextCursor::Right, QTextCursor::KeepAnchor, promptSize() ); - } - } - else { - cur.movePosition( QTextCursor::StartOfLine ); - cur.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, promptSize() ); - } - setTextCursor( cur ); - } - else { - moveCursor( QTextCursor::StartOfBlock, - shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor ); - } - horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); - } - break; - } - case Qt::Key_End: - // key: process as follows: - // - without , modifiers: move cursor to the end of the current line without selection - // - with modifier key pressed: move cursor to the very last symbol without selection - // - with modifier key pressed: move cursor to the end of the current line with selection - // - with + modifier keys pressed: move cursor to the very last symbol with selection - { - moveCursor( ctrlPressed ? QTextCursor::End : QTextCursor::EndOfBlock, - shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor ); - break; - } - case Qt::Key_Backspace : - // key: process as follows - // - without any modifiers : delete symbol before the cursor / selection (taking into account prompt) - // - with modifier key pressed: delete previous word - // - with modifier key pressed: delete text from the cursor to the line beginning - // works only for last (command) line - { - if ( cur.hasSelection() ) { - cut(); - } - else if ( cur.position() > document()->end().previous().position() + promptSize() ) { - if ( shftPressed ) { - moveCursor( QTextCursor::PreviousWord, QTextCursor::KeepAnchor ); - textCursor().removeSelectedText(); - } - else if ( ctrlPressed ) { - cur.setPosition( document()->end().previous().position() + promptSize(), - QTextCursor::KeepAnchor ); - setTextCursor( cur ); - textCursor().removeSelectedText(); - } - else { - QTextEdit::keyPressEvent( event ); - } - } - else { - cur.setPosition( document()->end().previous().position() + promptSize() ); - setTextCursor( cur ); - horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); - } - break; - } - case Qt::Key_Delete : - // key: process as follows - // - without any modifiers : delete symbol after the cursor / selection (taking into account prompt) - // - with modifier key pressed: delete next word - // - with modifier key pressed: delete text from the cursor to the end of line - // works only for last (command) line - { - if ( cur.hasSelection() ) { - cut(); - } - else if ( cur.position() > document()->end().previous().position() + promptSize()-1 ) { - if ( shftPressed ) { - moveCursor( QTextCursor::NextWord, QTextCursor::KeepAnchor ); - textCursor().removeSelectedText(); - } - else if ( ctrlPressed ) { - moveCursor( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor ); - textCursor().removeSelectedText(); - } - else { - QTextEdit::keyPressEvent( event ); - } - } - else { - cur.setPosition( document()->end().previous().position() + promptSize() ); - setTextCursor( cur ); - horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); - } - break; - } - case Qt::Key_Insert : - // key: process as follows - // - with modifier key pressed: copy() - // - with modifier key pressed: paste() to the command line - { - if ( ctrlPressed ) { - copy(); - } - else if ( shftPressed ) { - paste(); - } - else - QTextEdit::keyPressEvent( event ); - break; - } - } -} - -/*! - \brief Handle notification event coming from Python dispatcher. - \param event notification event -*/ -void PyConsole_Editor::customEvent( QEvent* event ) -{ - switch( event->type() ) - { - case PrintEvent::EVENT_ID: - { - PrintEvent* pe=(PrintEvent*)event; - addText( pe->text(), false, pe->isError()); - return; - } - case PyInterp_Event::ES_OK: - case PyInterp_Event::ES_ERROR: - { - // clear command buffer - myCommandBuffer.truncate( 0 ); - // add caret return line if necessary - QTextBlock par = document()->end().previous(); - QString txt = par.text(); - txt.truncate( txt.length() - 1 ); - // IPAL19397 : addText moved to handleReturn() method - //if ( !txt.isEmpty() ) - // addText( "", true ); - // set "ready" prompt - myPrompt = READY_PROMPT; - addText( myPrompt ); - // unset busy cursor - unsetCursor(); - // stop event loop (if running) - if ( myEventLoop ) - myEventLoop->exit(); - break; - } - case PyInterp_Event::ES_INCOMPLETE: - { - // extend command buffer (multi-line command) - myCommandBuffer.append( "\n" ); - // add caret return line if necessary - QTextBlock par = document()->end().previous(); - QString txt = par.text(); - txt.truncate( txt.length() - 1 ); - // IPAL19397 : addText moved to handleReturn() method - //if ( !txt.isEmpty() ) - // addText( "", true ); - // set "dot" prompt - myPrompt = DOTS_PROMPT; - addText( myPrompt/*, true*/ ); // IPAL19397 - // unset busy cursor - unsetCursor(); - // stop event loop (if running) - if ( myEventLoop ) - myEventLoop->exit(); - break; - } - default: - QTextEdit::customEvent( event ); - } - - // unset read-only state - setReadOnly( false ); - // unset history browsing mode - myCmdInHistory = -1; - - if ( (int)event->type() == (int)PyInterp_Event::ES_OK && myQueue.count() > 0 ) - { - // process the next sheduled command from the queue (if there is any) - QString nextcmd = myQueue[0]; - myQueue.pop_front(); - exec( nextcmd ); - } } -/*! - \brief Handle Python interpreter change. - Perform initialization actions if the interpreter is changed. - \param interp python interpreter is being set -*/ -void PyConsole_Editor::onPyInterpChanged( PyConsole_Interp* interp ) +void PyConsole_Editor::StaticDumpSlot(PyConsole_EditorBase *base) { - if ( myInterp != interp - // Force read-only state and wait cursor when myInterp is NULL - || !myInterp ) { - myInterp = interp; - if ( myInterp ) { - // print banner - myBanner = myInterp->getbanner().c_str(); - if ( isShowBanner() ) - addText( myBanner ); - // clear command buffer - myCommandBuffer.truncate(0); - // unset read-only state - setReadOnly( false ); - // unset history browsing mode - myCmdInHistory = -1; - // add prompt - addText( myPrompt ); - // unset busy cursor - viewport()->unsetCursor(); - // stop event loop (if running) - if( myEventLoop) - myEventLoop->exit(); - } - else { - // clear contents - clear(); - // set read-only state - setReadOnly( true ); - // set busy cursor - setCursor( Qt::WaitCursor ); - } - } -} - -/*! - \brief "Copy" operation. + QStringList aFilters; + aFilters.append( tr( "PYTHON_FILES_FILTER" ) ); - Reimplemented from Qt. - Warning! In Qt4 this method is not virtual. - */ -void PyConsole_Editor::cut() -{ - QTextCursor cur = textCursor(); - if ( cur.hasSelection() ) { - QApplication::clipboard()->setText( cur.selectedText() ); - int startSelection = cur.selectionStart(); - if ( startSelection < document()->end().previous().position() + promptSize() ) - startSelection = document()->end().previous().position() + promptSize(); - int endSelection = cur.selectionEnd(); - if ( endSelection < document()->end().previous().position() + promptSize() ) - endSelection = document()->end().previous().position() + promptSize(); - cur.setPosition( startSelection ); - cur.setPosition( endSelection, QTextCursor::KeepAnchor ); - horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); - setTextCursor( cur ); - textCursor().removeSelectedText(); - } -} - -/*! - \brief "Paste" operation. - - Reimplemented from Qt. - Warning! In Qt4 this method is not virtual. - */ -void PyConsole_Editor::paste() -{ - QTextCursor cur = textCursor(); - if ( cur.hasSelection() ) { - int startSelection = cur.selectionStart(); - if ( startSelection < document()->end().previous().position() + promptSize() ) - startSelection = document()->end().previous().position() + promptSize(); - int endSelection = cur.selectionEnd(); - if ( endSelection < document()->end().previous().position() + promptSize() ) - endSelection = document()->end().previous().position() + promptSize(); - cur.setPosition( startSelection ); - cur.setPosition( endSelection, QTextCursor::KeepAnchor ); - horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); - setTextCursor( cur ); - textCursor().removeSelectedText(); - } - if ( textCursor().position() < document()->end().previous().position() + promptSize() ) - moveCursor( QTextCursor::End ); - QTextEdit::paste(); -} - -/*! - \brief "Clear" operation. - - Reimplemented from Qt. - Warning! In Qt4 this method is not virtual. - */ -void PyConsole_Editor::clear() -{ - QTextEdit::clear(); - if ( isShowBanner() ) - addText( myBanner ); - myPrompt = READY_PROMPT; - addText( myPrompt ); + QString fileName = SUIT_FileDlg::getFileName( base, QString(), + aFilters, tr( "TOT_DUMP_PYCOMMANDS" ), + false, true, new DumpCommandsFileValidator( base ) ); + base->dumpImpl(fileName); } /*! \brief "Dump commands" operation. */ -void PyConsole_Editor::dump() +void PyConsole_Editor::dumpSlot() { - QStringList aFilters; - aFilters.append( tr( "PYTHON_FILES_FILTER" ) ); - - QString fileName = SUIT_FileDlg::getFileName( this, QString(), - aFilters, tr( "TOT_DUMP_PYCOMMANDS" ), - false, true, new DumpCommandsFileValidator( this ) ); - if ( !fileName.isEmpty() ) { - QFile file( fileName ); - if ( !file.open( QFile::WriteOnly ) ) - return; - - QTextStream out (&file); - - for ( int i=0; istartLogImpl( fileName ) ) { break; } else { - SUIT_MessageBox::critical( this, + SUIT_MessageBox::critical( base, QObject::tr("ERR_ERROR"), QObject::tr("ERR_FILE_NOT_WRITABLE") ); } @@ -1206,46 +199,9 @@ void PyConsole_Editor::startLog() } /*! - \brief Start python trace logging - \param fileName the path to the log file - \sa stopLog() - */ -bool PyConsole_Editor::startLog( const QString& fileName ) -{ - bool ok = false; - if ( !fileName.isEmpty() ) { - QFile file( fileName ); - if ( file.open( QFile::WriteOnly ) ) { - file.close(); - myLogFile = fileName; - ok = true; - } - } - return ok; -} - -/*! - \brief "Stop log" operation. - \sa startLog() - */ -void PyConsole_Editor::stopLog() -{ - myLogFile = QString(); -} - -/*! - \brief Put string to the log file + \brief "Start log" operation. */ -void PyConsole_Editor::putLog( const QString& s ) +void PyConsole_Editor::startLogSlot() { - if ( !myLogFile.isEmpty() ) { - QFile file( myLogFile ); - if ( !file.open( QFile::Append ) ) - return; - - QTextStream out (&file); - out << s; - - file.close(); - } + StaticStartLogSlot(this); } diff --git a/src/PyConsole/PyConsole_Editor.h b/src/PyConsole/PyConsole_Editor.h index 5dbc8d15e..a8821992e 100644 --- a/src/PyConsole/PyConsole_Editor.h +++ b/src/PyConsole/PyConsole_Editor.h @@ -28,6 +28,7 @@ #define PYCONSOLE_EDITOR_H #include "PyConsole.h" +#include "PyConsole_EditorBase.h" #include @@ -35,72 +36,17 @@ class PyConsole_Interp; class PyInterp_Request; class QEventLoop; -class PYCONSOLE_EXPORT PyConsole_Editor : public QTextEdit +class PYCONSOLE_EXPORT PyConsole_Editor : public PyConsole_EditorBase { Q_OBJECT; - public: PyConsole_Editor( PyConsole_Interp* theInterp, QWidget *theParent = 0 ); ~PyConsole_Editor(); - - PyConsole_Interp* getInterp() const; - - virtual void addText( const QString& str, const bool newBlock = false, const bool isError = false ); - bool isCommand( const QString& str ) const; - - virtual void exec( const QString& command ); - void execAndWait( const QString& command ); - - bool isSync() const; - void setIsSync( const bool ); - - bool isSuppressOutput() const; - void setIsSuppressOutput(const bool); - - bool isShowBanner() const; - void setIsShowBanner( const bool ); - - bool isLogging() const; - - virtual QSize sizeHint() const; - -public slots: - void cut(); - void paste(); - void clear(); - void handleReturn(); - void onPyInterpChanged( PyConsole_Interp* ); - void dump(); - bool startLog( const QString& ); - void startLog(); - void stopLog(); - void putLog( const QString& ); - + static void StaticDumpSlot(PyConsole_EditorBase *base); + static void StaticStartLogSlot(PyConsole_EditorBase *base); protected: - virtual void dropEvent( QDropEvent* event ); - virtual void mouseReleaseEvent( QMouseEvent* event ); - virtual void keyPressEvent ( QKeyEvent* event); - virtual void customEvent( QEvent* event); - - virtual PyInterp_Request* createRequest( const QString& ); - - /** Convenience function */ - inline int promptSize() const { return myPrompt.size(); } - - PyConsole_Interp* myInterp; //!< python interpreter - - QString myCommandBuffer; //!< python command buffer - QString myCurrentCommand; //!< currently being printed command - QString myPrompt; //!< current command line prompt - int myCmdInHistory; //!< current history command index - QString myLogFile; //!< current output log - QStringList myHistory; //!< commands history buffer - QEventLoop* myEventLoop; //!< internal event loop - QString myBanner; //!< current banner - bool myShowBanner; //!< 'show banner' flag - QStringList myQueue; //!< python commands queue - bool myIsSync; //!< synchronous mode flag - bool myIsSuppressOutput; //!< suppress output flag + virtual void dumpSlot(); + virtual void startLogSlot(); }; #endif // PYCONSOLE_EDITOR_H diff --git a/src/PyConsole/PyConsole_EnhEditor.cxx b/src/PyConsole/PyConsole_EnhEditor.cxx index d4cf274a2..dd4619b1e 100644 --- a/src/PyConsole/PyConsole_EnhEditor.cxx +++ b/src/PyConsole/PyConsole_EnhEditor.cxx @@ -30,14 +30,7 @@ #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])); +#include "PyConsole_Editor.h" /** * Constructor. @@ -45,447 +38,16 @@ const std::vector PyConsole_EnhEditor::SEPARATORS = \ * @param parent parent widget */ PyConsole_EnhEditor::PyConsole_EnhEditor(PyConsole_Interp* 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 - if ( myInterp ) - 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( 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 QStringList& matches, QString& result) const + PyConsole_EnhEditorBase(interp, parent) { - 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 ) +void PyConsole_EnhEditor::dumpSlot() { - QStringList matches; - QString first_match, comple_text, doc, base; - QTextCursor cursor(textCursor()); - QTextBlockFormat bf; - QTextCharFormat cf; - int cursorPos; - - switch( event->type() ) - { - case PyInterp_Event::ES_TAB_COMPLETE_OK: - { - // Extract corresponding matches from the interpreter - matches = getInterp()->getLastMatches(); - doc = getInterp()->getDocStr(); - - 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 - if (matches.size() == 1) - { - first_match = matches[0].mid(_compl_after_point.size()); - cursor.insertText(first_match); - _tab_mode = false; - if (doc.isEmpty()) - 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; - } - } + PyConsole_Editor::StaticDumpSlot(this); } -/** - * Extract the common leading part of all strings in matches. - * @param matches - * @param result - */ -void PyConsole_EnhEditor::extractCommon(const QStringList& matches, QString& result) const +void PyConsole_EnhEditor::startLogSlot() { - 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(); - } + PyConsole_Editor::StaticStartLogSlot(this); } diff --git a/src/PyConsole/PyConsole_EnhEditor.h b/src/PyConsole/PyConsole_EnhEditor.h index f505066d3..f14402e3b 100644 --- a/src/PyConsole/PyConsole_EnhEditor.h +++ b/src/PyConsole/PyConsole_EnhEditor.h @@ -23,7 +23,7 @@ #define PYCONSOLE_ENHEDITOR_H_ #include "PyConsole.h" -#include "PyConsole_Editor.h" +#include "PyConsole_EnhEditorBase.h" #include #include @@ -31,65 +31,15 @@ /** * Enhanced Python editor handling tab completion. */ -class PYCONSOLE_EXPORT PyConsole_EnhEditor: public PyConsole_Editor +class PYCONSOLE_EXPORT PyConsole_EnhEditor : public PyConsole_EnhEditorBase { Q_OBJECT; - public: PyConsole_EnhEditor(PyConsole_Interp* 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 QStringList& matches, QString& result) const; - virtual QString formatDocHTML(const QString & doc) const; - - virtual void multilinePaste(const QString & s); - virtual void multiLineProcessNextLine(); - -private: - void extractCommon(const QStringList& matches, QString& result) const; - + virtual void dumpSlot(); + virtual void startLogSlot(); }; #endif /* PYCONSOLE_ENHEDITOR_H_ */ diff --git a/src/PyConsole/PyConsole_EnhInterp.cxx b/src/PyConsole/PyConsole_EnhInterp.cxx deleted file mode 100644 index bb3230466..000000000 --- a/src/PyConsole/PyConsole_EnhInterp.cxx +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE -// -// 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, or (at your option) any later version. -// -// 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 Constructor -*/ -PyConsole_EnhInterp::PyConsole_EnhInterp() - : PyConsole_Interp() -{ -} - -/*! - \brief Destructor -*/ -PyConsole_EnhInterp::~PyConsole_EnhInterp() -{ -} - -QStringList PyConsole_EnhInterp::getLastMatches() const -{ - return _last_matches; -} - -QString PyConsole_EnhInterp::getDocStr() const -{ - return _doc_str; -} - -/*! - \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, _global_context, _local_context); - 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, - QStringList& 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, _global_context, _local_context); - 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.clear(); - _doc_str = ""; -} diff --git a/src/PyConsole/PyConsole_EnhInterp.h b/src/PyConsole/PyConsole_EnhInterp.h deleted file mode 100644 index b8fb04a77..000000000 --- a/src/PyConsole/PyConsole_EnhInterp.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE -// -// 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, or (at your option) any later version. -// -// 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(); - virtual ~PyConsole_EnhInterp(); - - virtual QStringList getLastMatches() const; - virtual QString getDocStr() const; - - 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 */ - QStringList _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, - QStringList& result, bool discardSwig=true) const; -}; - -#endif /* PYCONSOLE_ENHINTERP_H_ */ diff --git a/src/PyConsole/PyConsole_Event.cxx b/src/PyConsole/PyConsole_Event.cxx deleted file mode 100644 index aaeb2576f..000000000 --- a/src/PyConsole/PyConsole_Event.cxx +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (C) 2007-2015 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, or (at your option) any later version. -// -// 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 deleted file mode 100644 index 6ec1d51bb..000000000 --- a/src/PyConsole/PyConsole_Event.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (C) 2007-2015 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, or (at your option) any later version. -// -// 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 QString& 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_Interp.cxx b/src/PyConsole/PyConsole_Interp.cxx deleted file mode 100644 index ca459b582..000000000 --- a/src/PyConsole/PyConsole_Interp.cxx +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (C) 2007-2015 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, or (at your option) any later version. -// -// 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 : PyConsole_Interp.cxx -// Author : Nicolas REJNERI, Adrien BRUNETON - -#include "PyConsole_Interp.h" - -/*! - \class PyConsole_Interp - \brief Python interpreter to be embedded to the SALOME study's GUI. - - There is only one Python interpreter for the whole SALOME environment. - - Call the initialize() method defined in the base class PyInterp_Interp, - to initialize the interpreter after instance creation. - - The method initialize() calls virtuals methods - - initPython() to initialize global Python interpreter - - initContext() to initialize interpreter internal context - - initRun() to prepare interpreter for running commands -*/ - -/*! - \brief Constructor. - - Creates new python interpreter. -*/ -PyConsole_Interp::PyConsole_Interp(): PyInterp_Interp() -{ -} - -/*! - \brief Destructor. - - Does nothing for the moment. -*/ -PyConsole_Interp::~PyConsole_Interp() -{ -} - -/*! Sets the variable "__IN_SALOME_GUI_CONSOLE" to True. -* This is not attached to a module (like salome_iapp.IN_SALOME_GUI_CONSOLE) -* since modules are shared across all interpreters in SALOME. -* -* (GIL is already acquired here) -*/ -int PyConsole_Interp::beforeRun() -{ - return PyRun_SimpleString("__builtins__.__IN_SALOME_GUI_CONSOLE=True"); -} - -int PyConsole_Interp::afterRun() -{ - return PyRun_SimpleString("__builtins__.__IN_SALOME_GUI_CONSOLE=False"); -} - -QStringList PyConsole_Interp::getLastMatches() const -{ - return QStringList(); -} - -QString PyConsole_Interp::getDocStr() const -{ - return QString(); -} - -int PyConsole_Interp::runDirCommand(const QString&, const QString& ) -{ - return 0; -} - -void PyConsole_Interp::clearCompletion() -{ -} diff --git a/src/PyConsole/PyConsole_Interp.h b/src/PyConsole/PyConsole_Interp.h deleted file mode 100644 index 1aac73683..000000000 --- a/src/PyConsole/PyConsole_Interp.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (C) 2007-2015 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, or (at your option) any later version. -// -// 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 : PyConsole_Interp.h -// Author : Nicolas REJNERI, Adrien BRUNETON - -#ifndef PYCONSOLE_INTERP_H -#define PYCONSOLE_INTERP_H - -#include "PyConsole.h" -#include "PyInterp_Interp.h" /// !!! WARNING !!! THIS INCLUDE MUST BE VERY FIRST !!! - -#include - -class PYCONSOLE_EXPORT PyConsole_Interp : public PyInterp_Interp -{ -public: - PyConsole_Interp(); - ~PyConsole_Interp(); - - virtual int afterRun(); - virtual int beforeRun(); - - virtual QStringList getLastMatches() const; - virtual QString getDocStr() const; - - virtual int runDirCommand(const QString&, const QString&); - virtual void clearCompletion(); -}; - -#endif // PYCONSOLE_INTERP_H diff --git a/src/PyConsole/PyConsole_Request.cxx b/src/PyConsole/PyConsole_Request.cxx deleted file mode 100644 index 5c67b8bbb..000000000 --- a/src/PyConsole/PyConsole_Request.cxx +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE -// -// 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, or (at your option) any later version. -// -// 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 "PyConsole_Interp.h" -#include "PyConsole_Event.h" -#include "PyInterp_Event.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, - QObject* theListener, - bool theSync ) - : PyInterp_LockRequest( theInterp, theListener, theSync ), - 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.toLatin1().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( PyInterp_Interp* theInterp, - const QString& theInput, - const QString& theStartMatch, - QObject* theListener, - bool theSync ) - : PyInterp_LockRequest( theInterp, theListener, theSync ), - _tabSuccess(false), _dirArg(theInput), _startMatch(theStartMatch) -{} - -/** - * Execute the completion command by wrapping the runDirCommand() of the - * embedded enhanced interpreter. - */ -void CompletionCommand::execute() -{ - int ret = static_cast(getInterp())->runDirCommand( _dirArg, _startMatch ); - _tabSuccess = ret == 0; -} - -/** - * 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 deleted file mode 100644 index a0ed598eb..000000000 --- a/src/PyConsole/PyConsole_Request.h +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE -// -// 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, or (at your option) any later version. -// -// 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 "PyInterp_Request.h" - -#include -#include -#include - -class PyInterp_Interp; - -/*! - \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, - QObject* theListener, - bool theSync = 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 CompletionCommand : public PyInterp_LockRequest -{ -public: - CompletionCommand( PyInterp_Interp* theInterp, - const QString& theInput, - const QString& theStartMatch, - QObject* theListener, - bool theSync = 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/PyConsole/resources/PyConsole_msg_en.ts b/src/PyConsole/resources/PyConsole_msg_en.ts index 3a3b89bd9..a2d4aae39 100644 --- a/src/PyConsole/resources/PyConsole_msg_en.ts +++ b/src/PyConsole/resources/PyConsole_msg_en.ts @@ -1,41 +1,6 @@ - - PyConsole_Console - - - EDIT_COPY_CMD - &Copy - - - - EDIT_PASTE_CMD - &Paste - - - - EDIT_CLEAR_CMD - Clea&r - - - - EDIT_SELECTALL_CMD - Select &All - - - EDIT_DUMPCOMMANDS_CMD - D&ump Commands - - - EDIT_STARTLOG_CMD - Start &Log - - - EDIT_STOPLOG_CMD - Stop &Log - - PyConsole_Editor diff --git a/src/PyConsole/resources/PyConsole_msg_fr.ts b/src/PyConsole/resources/PyConsole_msg_fr.ts old mode 100755 new mode 100644 index b4f16bd0c..905d68f7c --- a/src/PyConsole/resources/PyConsole_msg_fr.ts +++ b/src/PyConsole/resources/PyConsole_msg_fr.ts @@ -1,41 +1,6 @@ - - PyConsole_Console - - - EDIT_COPY_CMD - &Copier - - - - EDIT_PASTE_CMD - C&oller - - - - EDIT_CLEAR_CMD - &Effacer - - - - EDIT_SELECTALL_CMD - &Tout sélectionner - - - EDIT_DUMPCOMMANDS_CMD - &Générer le script des commandes - - - EDIT_STARTLOG_CMD - Démarrer une &trace - - - EDIT_STOPLOG_CMD - Arrêter la &trace - - PyConsole_Editor diff --git a/src/PyConsole/resources/PyConsole_msg_ja.ts b/src/PyConsole/resources/PyConsole_msg_ja.ts index 85e2cc732..dd8b48e3e 100644 --- a/src/PyConsole/resources/PyConsole_msg_ja.ts +++ b/src/PyConsole/resources/PyConsole_msg_ja.ts @@ -1,41 +1,6 @@ - - PyConsole_Console - - - EDIT_COPY_CMD - コピー(&C) - - - - EDIT_PASTE_CMD - 貼り付け(&P) - - - - EDIT_CLEAR_CMD - 削除(&r) - - - - EDIT_SELECTALL_CMD - すべて選択します。(&A) - - - EDIT_DUMPCOMMANDS_CMD - スクリプト コマンドを生成します。(&u) - - - EDIT_STARTLOG_CMD - ログの開始 (&L) - - - EDIT_STOPLOG_CMD - ログの停止 (&L) - - PyConsole_Editor diff --git a/src/PyConsoleBase/CMakeLists.txt b/src/PyConsoleBase/CMakeLists.txt new file mode 100755 index 000000000..3e06a3fee --- /dev/null +++ b/src/PyConsoleBase/CMakeLists.txt @@ -0,0 +1,97 @@ +# Copyright (C) 2012-2015 CEA/DEN, EDF R&D, OPEN CASCADE +# +# 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, or (at your option) any later version. +# +# 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 +# + +INCLUDE(UseQtExt) + +# --- options --- + +# additional include directories +INCLUDE_DIRECTORIES( + ${QT_INCLUDES} + ${PYTHON_INCLUDE_DIRS} + ${PROJECT_SOURCE_DIR}/src/Qtx + ${PROJECT_SOURCE_DIR}/src/PyConsoleBase + ${PROJECT_SOURCE_DIR}/src/Event + ${PROJECT_SOURCE_DIR}/src/PyInterp +) + +# additional preprocessor / compiler flags +ADD_DEFINITIONS(${QT_DEFINITIONS} ${PYTHON_DEFINITIONS}) + +# libraries to link to +SET(_link_LIBRARIES ${QT_LIBRARIES} ${PYTHON_LIBRARIES} PyInterp Event qtx) + +# --- headers --- + +# header files / to be processed by moc +SET(_moc_HEADERS + PyConsole_ConsoleBase.h + PyConsole_EditorBase.h + PyConsole_EnhEditorBase.h +) + +# header files / no moc processing +SET(_other_HEADERS + PyConsoleBase.h + PyConsole_EnhInterp.h + PyConsole_Event.h + PyConsole_Interp.h + PyConsole_Request.h +) + +# header files / to install +SET(PyConsoleBase_HEADERS ${_moc_HEADERS} ${_other_HEADERS}) + +# --- resources --- + +# resource files / to be processed by lrelease +SET(_ts_RESOURCES + resources/PyConsoleBase_msg_en.ts + resources/PyConsoleBase_msg_fr.ts + resources/PyConsoleBase_msg_ja.ts +) + +# --- sources --- + +# sources / moc wrappings +QT_WRAP_MOC(_moc_SOURCES ${_moc_HEADERS}) + +# sources / static +SET(_other_SOURCES + PyConsole_ConsoleBase.cxx + PyConsole_EnhInterp.cxx + PyConsole_Event.cxx + PyConsole_Interp.cxx + PyConsole_Request.cxx + PyConsole_EditorBase.cxx + PyConsole_EnhEditorBase.cxx +) + +# sources / to compile +SET(PyConsoleBase_SOURCES ${_other_SOURCES} ${_moc_SOURCES}) + +# --- rules --- + +ADD_LIBRARY(PyConsoleBase ${PyConsoleBase_SOURCES}) +TARGET_LINK_LIBRARIES(PyConsoleBase ${_link_LIBRARIES}) +INSTALL(TARGETS PyConsoleBase EXPORT ${PROJECT_NAME}TargetGroup DESTINATION ${SALOME_INSTALL_LIBS}) + +INSTALL(FILES ${PyConsoleBase_HEADERS} DESTINATION ${SALOME_INSTALL_HEADERS}) +QT_INSTALL_TS_RESOURCES("${_ts_RESOURCES}" "${SALOME_GUI_INSTALL_RES_DATA}") + diff --git a/src/PyConsoleBase/PyConsoleBase.h b/src/PyConsoleBase/PyConsoleBase.h new file mode 100644 index 000000000..fcef126cc --- /dev/null +++ b/src/PyConsoleBase/PyConsoleBase.h @@ -0,0 +1,48 @@ +// Copyright (C) 2007-2015 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, or (at your option) any later version. +// +// 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 : PyConsoleBase.h +// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com) +// +#if !defined ( PYCONSOLEBASE_H ) +#define PYCONSOLEBASE_H + +// ======================================================== +// set dllexport type for Win platform +#ifdef WIN32 +# if defined PYCONSOLEBASE_EXPORTS || defined PyConsoleBase_EXPORTS +# define PYCONSOLEBASE_EXPORT __declspec(dllexport) +# else +# define PYCONSOLEBASE_EXPORT __declspec(dllimport) +# endif +#else // WIN32 +# define PYCONSOLEBASE_EXPORT +#endif // WIN32 + +// ======================================================== +// avoid warning messages +#ifdef WIN32 +#pragma warning (disable : 4786) +#pragma warning (disable : 4251) +#endif + +#endif // PYCONSOLEBASE_H diff --git a/src/PyConsoleBase/PyConsole_ConsoleBase.cxx b/src/PyConsoleBase/PyConsole_ConsoleBase.cxx new file mode 100644 index 000000000..306dd998c --- /dev/null +++ b/src/PyConsoleBase/PyConsole_ConsoleBase.cxx @@ -0,0 +1,413 @@ +// Copyright (C) 2007-2015 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, or (at your option) any later version. +// +// 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 : PyConsole_ConsoleBase.cxx +// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com) +// +/*! + \class PyConsole_Console + \brief Python console widget. +*/ + +#include "PyConsole_Interp.h" /// !!! WARNING !!! THIS INCLUDE MUST BE VERY FIRST !!! +#include "PyConsole_ConsoleBase.h" +#include "PyConsole_EnhEditorBase.h" +#include "PyConsole_EnhInterp.h" + +#include "Qtx.h" + +#include +#include +#include +#include +#include +#include +#include + +PyConsole_EditorBase *PyConsole_ConsoleBase::PyConsole_Interp_CreatorBase::createEditor( PyConsole_Interp *interp, PyConsole_ConsoleBase *console ) const +{ return new PyConsole_EditorBase(interp,console); } + +PyConsole_Interp *PyConsole_ConsoleBase::PyConsole_Interp_CreatorBase::createInterp( ) const +{ return new PyConsole_Interp; } + +/*! + \brief Constructor. + + Creates new python console widget. + \param parent parent widget + \param interp python interpreter +*/ +PyConsole_ConsoleBase::PyConsole_ConsoleBase( QWidget* parent, PyConsole_Interp* interp ) +: QWidget( parent ) +{ + PyConsole_ConsoleBase::PyConsole_Interp_CreatorBase crea; + defaultConstructor(interp,crea); +} + +/*! + * MUST BE NON VIRTUAL ! (called from constructor !!!!) + */ +void PyConsole_ConsoleBase::defaultConstructor( PyConsole_Interp* interp, const PyConsole_Interp_CreatorBase& crea ) +{ + PyConsole_Interp *anInterp = interp ? interp : crea.createInterp(); + + // initialize Python interpretator + anInterp->initialize(); + + // create editor console + QVBoxLayout* lay = new QVBoxLayout( this ); + lay->setMargin( 0 ); + myEditor = crea.createEditor( anInterp, this ); + char* synchronous = getenv("PYTHON_CONSOLE_SYNC"); + if (synchronous && atoi(synchronous)) + { + myEditor->setIsSync(true); + } + myEditor->viewport()->installEventFilter( this ); + lay->addWidget( myEditor ); + + createActions(); +} + +/** + * Protected constructor. + */ +PyConsole_ConsoleBase::PyConsole_ConsoleBase( QWidget* parent, PyConsole_Interp* /*i*/, PyConsole_EditorBase* e ) + : QWidget (parent), myEditor(e) +{} + +/*! + \brief Destructor. + + Does nothing for the moment. +*/ +PyConsole_ConsoleBase::~PyConsole_ConsoleBase() +{ +} + +PyConsole_Interp* PyConsole_ConsoleBase::getInterp() const +{ + return myEditor ? myEditor->getInterp() : 0; +} + +/*! + \brief Execute python command in the interpreter. + \param command string with command and arguments +*/ +void PyConsole_ConsoleBase::exec( const QString& command ) +{ + if ( myEditor ) + myEditor->exec( command ); +} + +/*! + \brief Execute python command in the interpreter + and wait until it is finished. + + Block execution of main application until the python command is executed. + \param command string with command and arguments +*/ +void PyConsole_ConsoleBase::execAndWait( const QString& command ) +{ + if ( myEditor ) + myEditor->execAndWait( command ); +} + +/*! + \brief Get synchronous mode flag value. + + \sa setIsSync() + \return True if python console works in synchronous mode +*/ +bool PyConsole_ConsoleBase::isSync() const +{ + return myEditor->isSync(); +} + +/*! + \brief Set synchronous mode flag value. + + In synhronous mode the Python commands are executed in the GUI thread + and the GUI is blocked until the command is finished. In the asynchronous + mode each Python command is executed in the separate thread that does not + block the main GUI loop. + + \param on synhronous mode flag +*/ +void PyConsole_ConsoleBase::setIsSync( const bool on ) +{ + myEditor->setIsSync( on ); +} + +/*! + \brief Get suppress output flag value. + + \sa setIsSuppressOutput() + \return True if python console output is suppressed. +*/ +bool PyConsole_ConsoleBase::isSuppressOutput() const +{ + return myEditor->isSuppressOutput(); +} + +/*! + \brief Set suppress output flag value. + + In case if suppress output flag is true, the python + console output suppressed. + + \param on suppress output flag +*/ +void PyConsole_ConsoleBase::setIsSuppressOutput( const bool on ) +{ + myEditor->setIsSuppressOutput(on); +} + +/*! + \brief Get 'show banner' flag value. + + \sa setIsShowBanner() + \return \c true if python console shows banner +*/ +bool PyConsole_ConsoleBase::isShowBanner() const +{ + return myEditor->isShowBanner(); +} + +/*! + \brief Set 'show banner' flag value. + + The banner is shown in the top of the python console window. + + \sa isShowBanner() + \param on 'show banner' flag +*/ +void PyConsole_ConsoleBase::setIsShowBanner( const bool on ) +{ + myEditor->setIsShowBanner( on ); +} + +/*! + \brief Change the python console's font. + \param f new font +*/ +void PyConsole_ConsoleBase::setFont( const QFont& f ) +{ + if( myEditor ) + myEditor->setFont( f ); +} + +/*! + \brief Get python console font. + \return current python console's font +*/ +QFont PyConsole_ConsoleBase::font() const +{ + QFont res; + if( myEditor ) + res = myEditor->font(); + return res; +} + +/*! + \brief Set actions to be visible in the context popup menu. + + Actions, which IDs are set in \a flags parameter, will be shown in the + context popup menu. Other actions will not be shown. + + \param flags ORed together actions flags +*/ +void PyConsole_ConsoleBase::setMenuActions( const int flags ) +{ + myActions[CopyId]->setVisible( flags & CopyId ); + myActions[PasteId]->setVisible( flags & PasteId ); + myActions[ClearId]->setVisible( flags & ClearId ); + myActions[SelectAllId]->setVisible( flags & SelectAllId ); + myActions[DumpCommandsId]->setVisible( flags & DumpCommandsId ); + myActions[StartLogId]->setVisible( flags & StartLogId ); + myActions[StopLogId]->setVisible( flags & StopLogId ); +} + +/*! + \brief Get menu actions which are currently visible in the context popup menu. + \return ORed together actions flags + \sa setMenuActions() +*/ +int PyConsole_ConsoleBase::menuActions() const +{ + int ret = 0; + ret = ret | ( myActions[CopyId]->isVisible() ? CopyId : 0 ); + ret = ret | ( myActions[PasteId]->isVisible() ? PasteId : 0 ); + ret = ret | ( myActions[ClearId]->isVisible() ? ClearId : 0 ); + ret = ret | ( myActions[SelectAllId]->isVisible() ? SelectAllId : 0 ); + ret = ret | ( myActions[DumpCommandsId]->isVisible() ? DumpCommandsId : 0 ); + ret = ret | ( myActions[StartLogId]->isVisible() ? StartLogId : 0 ); + ret = ret | ( myActions[StopLogId]->isVisible() ? StopLogId : 0 ); + return ret; +} + +/*! + \brief Create menu actions. + + Create context popup menu actions. +*/ +void PyConsole_ConsoleBase::createActions() +{ + QAction* a = new QAction( tr( "EDIT_COPY_CMD" ), this ); + a->setStatusTip( tr( "EDIT_COPY_CMD" ) ); + connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( copy() ) ); + myActions.insert( CopyId, a ); + + a = new QAction( tr( "EDIT_PASTE_CMD" ), this ); + a->setStatusTip( tr( "EDIT_PASTE_CMD" ) ); + connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( paste() ) ); + myActions.insert( PasteId, a ); + + a = new QAction( tr( "EDIT_CLEAR_CMD" ), this ); + a->setStatusTip( tr( "EDIT_CLEAR_CMD" ) ); + connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( clear() ) ); + myActions.insert( ClearId, a ); + + a = new QAction( tr( "EDIT_SELECTALL_CMD" ), this ); + a->setStatusTip( tr( "EDIT_SELECTALL_CMD" ) ); + connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( selectAll() ) ); + myActions.insert( SelectAllId, a ); + + a = new QAction( tr( "EDIT_DUMPCOMMANDS_CMD" ), this ); + a->setStatusTip( tr( "EDIT_DUMPCOMMANDS_CMD" ) ); + connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( dump() ) ); + myActions.insert( DumpCommandsId, a ); + + a = new QAction( tr( "EDIT_STARTLOG_CMD" ), this ); + a->setStatusTip( tr( "EDIT_STARTLOG_CMD" ) ); + connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( startLog() ) ); + myActions.insert( StartLogId, a ); + + a = new QAction( tr( "EDIT_STOPLOG_CMD" ), this ); + a->setStatusTip( tr( "EDIT_STOPLOG_CMD" ) ); + connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( stopLog() ) ); + myActions.insert( StopLogId, a ); +} + +/*! + \brief Update menu actions. + + Update context popup menu action state. +*/ +void PyConsole_ConsoleBase::updateActions() +{ + myActions[CopyId]->setEnabled( myEditor->textCursor().hasSelection() ); + myActions[PasteId]->setEnabled( !myEditor->isReadOnly() && !QApplication::clipboard()->text().isEmpty() ); + myActions[SelectAllId]->setEnabled( !myEditor->document()->isEmpty() ); +} + +/*! + \brief Start python trace logging + \param fileName the path to the log file +*/ +void PyConsole_ConsoleBase::startLog( const QString& fileName ) +{ + myEditor->startLog( fileName ); +} + +/*! + \brief Stop python trace logging +*/ +void PyConsole_ConsoleBase::stopLog() +{ + myEditor->stopLog(); +} + +/*! + \brief Create the context popup menu. + + Fill in the popup menu with the commands. + + \param menu context popup menu +*/ +void PyConsole_ConsoleBase::contextMenuPopup( QMenu *menu ) +{ + if ( myEditor->isReadOnly() ) + return; + + menu->addAction( myActions[CopyId] ); + menu->addAction( myActions[PasteId] ); + menu->addAction( myActions[ClearId] ); + menu->addSeparator(); + menu->addAction( myActions[SelectAllId] ); + menu->addSeparator(); + menu->addAction( myActions[DumpCommandsId] ); + if ( !myEditor->isLogging() ) + menu->addAction( myActions[StartLogId] ); + else + menu->addAction( myActions[StopLogId] ); + + Qtx::simplifySeparators( menu ); + + updateActions(); +} + +PyConsole_EditorBase *PyConsole_EnhConsoleBase::PyConsole_Interp_EnhCreatorBase::createEditor( PyConsole_Interp *interp, PyConsole_ConsoleBase *console ) const +{ return new PyConsole_EnhEditorBase(interp,console); } + +PyConsole_Interp *PyConsole_EnhConsoleBase::PyConsole_Interp_EnhCreatorBase::createInterp( ) const +{ return new PyConsole_EnhInterp; } + +/** + * 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_EnhConsoleBase::PyConsole_EnhConsoleBase( QWidget* parent, PyConsole_Interp* interp ) + : PyConsole_ConsoleBase( parent, interp, 0 ) +{ + PyConsole_Interp_EnhCreatorBase crea; + defaultConstructor(interp,crea); +} + +/*! + \brief Event handler. + + Handles context menu request event. + + \param o object + \param e event + \return True if the event is processed and further processing should be stopped +*/ +bool PyConsole_EnhConsoleBase::eventFilter( QObject* o, QEvent* e ) +{ + if ( o == myEditor->viewport() && e->type() == QEvent::ContextMenu ) + { + contextMenuRequest( (QContextMenuEvent*)e ); + return true; + } + return QWidget::eventFilter( o, e ); +} + +void PyConsole_EnhConsoleBase::contextMenuRequest( QContextMenuEvent * e ) +{ + QMenu *menu(new QMenu(this)); + contextMenuPopup(menu); + menu->move(e->globalPos()); + menu->show(); +} diff --git a/src/PyConsoleBase/PyConsole_ConsoleBase.h b/src/PyConsoleBase/PyConsole_ConsoleBase.h new file mode 100644 index 000000000..b0a07273d --- /dev/null +++ b/src/PyConsoleBase/PyConsole_ConsoleBase.h @@ -0,0 +1,127 @@ +// Copyright (C) 2007-2015 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, or (at your option) any later version. +// +// 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 : PyConsole_Console.h +// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com) +// +#ifndef PYCONSOLE_CONSOLEBASE_H +#define PYCONSOLE_CONSOLEBASE_H + +#include "PyConsoleBase.h" + +#include +#include +#include + +class PyConsole_Interp; +class PyConsole_EditorBase; + +class PYCONSOLEBASE_EXPORT PyConsole_ConsoleBase : public QWidget +{ + Q_OBJECT + +public: + + struct PyConsole_Interp_CreatorBase + { + virtual PyConsole_EditorBase *createEditor( PyConsole_Interp *interp, PyConsole_ConsoleBase *console ) const; + virtual PyConsole_Interp *createInterp( ) const; + }; + +public: + //! Context popup menu actions flags + enum + { + CopyId = 0x01, //!< "Copy" menu action + PasteId = 0x02, //!< "Paste" menu action + ClearId = 0x04, //!< "Clear" menu action + SelectAllId = 0x08, //!< "Select All" menu action + DumpCommandsId = 0x10, //!< "DumpCommands" menu action + StartLogId = 0x20, //!< "Start log" menu action + StopLogId = 0x40, //!< "Stop log" menu action + All = 0xFF, //!< all menu actions + }; + +public: + PyConsole_ConsoleBase( QWidget* parent, PyConsole_Interp* interp = 0 ); + virtual ~PyConsole_ConsoleBase(); + + //! \brief Get python interperter + PyConsole_Interp* getInterp() const; + QFont font() const; + virtual void setFont( const QFont& ); + + bool isSync() const; + void setIsSync( const bool ); + + bool isSuppressOutput() const; + void setIsSuppressOutput( const bool ); + + bool isShowBanner() const; + void setIsShowBanner( const bool ); + + void exec( const QString& ); + void execAndWait( const QString& ); + + void setMenuActions( const int ); + int menuActions() const; + + void startLog( const QString& ); + void stopLog(); + + virtual void contextMenuPopup( QMenu* ); + +protected: + void createActions(); + void updateActions(); + //! MUST BE NON VIRTUAL ! (called from constructor !!!!) + void defaultConstructor( PyConsole_Interp* interp, const PyConsole_Interp_CreatorBase& crea ); + PyConsole_ConsoleBase( QWidget* parent, PyConsole_Interp*, PyConsole_EditorBase*); + + PyConsole_EditorBase* 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 PYCONSOLEBASE_EXPORT PyConsole_EnhConsoleBase : public PyConsole_ConsoleBase +{ + Q_OBJECT +public: + + struct PyConsole_Interp_EnhCreatorBase : public PyConsole_Interp_CreatorBase + { + virtual PyConsole_EditorBase *createEditor( PyConsole_Interp *interp, PyConsole_ConsoleBase *console ) const; + virtual PyConsole_Interp *createInterp( ) const; + }; + +public: + PyConsole_EnhConsoleBase( QWidget* parent, PyConsole_Interp* interp = 0 ); + virtual ~PyConsole_EnhConsoleBase() {} + virtual bool eventFilter( QObject * o, QEvent * e ); + virtual void contextMenuRequest( QContextMenuEvent * e ) ; +}; + +#endif // PYCONSOLE_CONSOLEBASE_H diff --git a/src/PyConsoleBase/PyConsole_EditorBase.cxx b/src/PyConsoleBase/PyConsole_EditorBase.cxx new file mode 100644 index 000000000..a8bdd058f --- /dev/null +++ b/src/PyConsoleBase/PyConsole_EditorBase.cxx @@ -0,0 +1,1260 @@ +// Copyright (C) 2007-2015 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, or (at your option) any later version. +// +// 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 +// + +// SALOME SALOMEGUI : implementation of desktop and GUI kernel +// File : PyConsole_Editor.cxx +// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com) +// +/*! + \class PyConsole_Editor + \brief Python command line interpreter front-end GUI widget. + + This class provides simple GUI interface to the Python interpreter, including basic + navigation operations, executing commands (both interactively and programmatically), + copy-paste operations, history of the commands and so on. + + Here below is the shortcut keyboard boundings used for navigation and other operations: + - : execute current command + - : clear current command + - : clear current command + - : previous command in the history + - : move cursor one row up with selection + - : move cursor one row up without selection + - : move cursor one row up with selection + - : next command in the history + - : move cursor one row down with selection + - : move cursor one row down without selection + - : move cursor one row down with selection + - : move one symbol left without selection + - : move one symbol left with selection + - : move one word left without selection + - : move one word left with selection + - : move one symbol right without selection + - : move one symbol right with selection + - : move one word right without selection + - : move one word right with selection + - : first command in the history + - : move one page up with selection + - : move one page up without selection + - : scroll one page up + - : last command in the history + - : move one page down with selection + - : move one page down without selection + - : scroll one page down + - : move to the beginning of the line without selection + - : move to the beginning of the line with selection + - : move to the very first symbol without selection + - : move to the very first symbol with selection + - : move to the end of the line without selection + - : move to the end of the line with selection + - : move to the very last symbol without selection + - : move to the very last symbol with selection + - : delete symbol before the cursor + / remove selected text and put it to the clipboard (cut) + - : delete previous word + / remove selected text and put it to the clipboard (cut) + - : delete text from the cursor to the beginning of the line + / remove selected text and put it to the clipboard (cut) + - : delete symbol after the cursor + / remove selected text and put it to the clipboard (cut) + - : delete next word + / remove selected text and put it to the clipboard (cut) + - : delete text from the cursor to the end of the line + / remove selected text and put it to the clipboard (cut) + - : copy + - : paste + - : paste + - : copy + - : cut + - : paste +*/ + +#include "PyConsole_Interp.h" // !!! WARNING !!! THIS INCLUDE MUST BE THE VERY FIRST !!! +#include "PyConsole_EditorBase.h" +#include "PyConsole_Event.h" +#include "PyInterp_Event.h" +#include "PyInterp_Dispatcher.h" +#include "PyConsole_Request.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//VSR: uncomment below macro to support unicode text properly in SALOME +// current commented out due to regressions +//#define PAL22528_UNICODE + +namespace +{ + QString fromUtf8( const char* txt ) + { +#ifdef PAL22528_UNICODE + return QString::fromUtf8( txt ); +#else + return QString( txt ); +#endif + } +} + +static QString READY_PROMPT = ">>> "; +static QString DOTS_PROMPT = "... "; + +void staticCallbackStdout( void* data, char* c ) +{ + if(!((PyConsole_EditorBase*)data)->isSuppressOutput()) { + PyConsole_EditorBase* e = (PyConsole_EditorBase*)data; + e->putLog( fromUtf8(c) ); + QApplication::postEvent( e, new PrintEvent( fromUtf8(c), false ) ); + } +} + +void staticCallbackStderr( void* data, char* c ) +{ + if(!((PyConsole_EditorBase*)data)->isSuppressOutput()) { + PyConsole_EditorBase* e = (PyConsole_EditorBase*)data; + e->putLog( fromUtf8(c) ); + QApplication::postEvent( e, new PrintEvent( fromUtf8(c), true ) ); + } +} + + +/*! + \brief Constructor. + + Creates python editor window. + \param theInterp python interper + \param theParent parent widget +*/ +PyConsole_EditorBase::PyConsole_EditorBase( PyConsole_Interp* theInterp, + QWidget* theParent ) +: QTextEdit( theParent ), + myInterp( 0 ), + myCmdInHistory( -1 ), + myEventLoop( 0 ), + myShowBanner( true ), + myIsSync( true ), + myIsSuppressOutput( false ) +{ + QString fntSet( "" ); + QFont aFont ( Qtx::stringToFont(fntSet) ); + setFont( aFont ); + setUndoRedoEnabled( false ); + + myPrompt = READY_PROMPT; + setLineWrapMode( QTextEdit::WidgetWidth ); + setWordWrapMode( QTextOption::WrapAnywhere ); + setAcceptRichText( false ); + + theInterp->setvoutcb( staticCallbackStdout, this ); + theInterp->setverrcb( staticCallbackStderr, this ); + + // san - This is necessary for troubleless initialization + onPyInterpChanged( theInterp ); +} + +/*! + \brief Destructor. +*/ +PyConsole_EditorBase::~PyConsole_EditorBase() +{ + myInterp = 0; +} + +/*! + \brief Get Python interpreter +*/ +PyConsole_Interp* PyConsole_EditorBase::getInterp() const +{ + return myInterp; +} + +/*! + \brief Get synchronous mode flag value. + + \sa setIsSync() + \return True if python console works in synchronous mode +*/ +bool PyConsole_EditorBase::isSync() const +{ + return myIsSync; +} + +/*! + \brief Set synchronous mode flag value. + + In synhronous mode the Python commands are executed in the GUI thread + and the GUI is blocked until the command is finished. In the asynchronous + mode each Python command is executed in the separate thread that does not + block the main GUI loop. + + \param on synhronous mode flag +*/ +void PyConsole_EditorBase::setIsSync( const bool on ) +{ + myIsSync = on; +} + +/*! + \brief Get suppress output flag value. + + \sa setIsSuppressOutput() + \return \c true if python console output is suppressed. +*/ +bool PyConsole_EditorBase::isSuppressOutput() const +{ + return myIsSuppressOutput; +} + +/*! + \brief Set suppress output flag value. + + In case if suppress output flag is true, the python + console output suppressed. + + \param on suppress output flag +*/ +void PyConsole_EditorBase::setIsSuppressOutput( const bool on ) +{ + myIsSuppressOutput = on; +} + +/*! + \brief Get 'show banner' flag value. + + \sa setIsShowBanner() + \return \c true if python console shows banner +*/ +bool PyConsole_EditorBase::isShowBanner() const +{ + return myShowBanner; +} + +/*! + \brief Set 'show banner' flag value. + + The banner is shown in the top of the python console window. + + \sa isShowBanner() + \param on 'show banner' flag +*/ +void PyConsole_EditorBase::setIsShowBanner( const bool on ) +{ + if ( myShowBanner != on ) { + myShowBanner = on; + clear(); + } +} + +/*! + \brief Check if trace logging is switched on. + + \sa startLog(), stopLog() + \return \c true if trace logging is switched on +*/ +bool PyConsole_EditorBase::isLogging() const +{ + return !myLogFile.isEmpty(); +} + +/*! + \brief Get size hint for the Python console window + \return size hint value +*/ +QSize PyConsole_EditorBase::sizeHint() const +{ + QFontMetrics fm( font() ); + int nbLines = ( isShowBanner() ? myBanner.split("\n").count() : 0 ) + 1; + QSize s(100, fm.lineSpacing()*nbLines); + return s; +} + +/*! + \brief Put the string \a str to the python editor. + \param str string to be put in the command line of the editor + \param newBlock if True, then the string is printed on a new line + \param isError if true, the text is printed in dark red +*/ +void PyConsole_EditorBase::addText( const QString& str, + const bool newBlock, + const bool isError) +{ + QTextCursor theCursor(textCursor()); + QTextCharFormat cf; + + moveCursor( QTextCursor::End ); + if ( newBlock ) + theCursor.insertBlock(); + if (isError) + cf.setForeground(QBrush(Qt::red)); + else + cf.setForeground(QBrush(Qt::black)); + theCursor.insertText( str, cf); + moveCursor( QTextCursor::End ); + ensureCursorVisible(); +} + +/*! + \brief Convenient method for executing a Python command, + as if the user typed it manually. + \param command python command to be executed + + !!! WARNING: doesn't work properly with multi-line commands. !!! +*/ +void PyConsole_EditorBase::exec( const QString& command ) +{ + if ( isReadOnly() ) { + // some interactive command is being executed in this editor... + // shedule the command to the queue + myQueue.push_back( command ); + return; + } + // remove last line + moveCursor( QTextCursor::End ); + moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor ); + textCursor().removeSelectedText(); + // set "ready" prompt + myPrompt = READY_PROMPT; + // clear command buffer + myCommandBuffer.truncate( 0 ); + // unset history browsing mode + myCmdInHistory = -1; + // print command line by line + QString cmd = command; + if ( !cmd.endsWith( "\n" ) ) cmd += "\n"; + QStringList lines = command.split( "\n" ); + for ( int i = 0; i < lines.size(); i++ ) { + if ( !lines[i].trimmed().isEmpty() ) + myHistory.push_back( lines[i] ); + addText( ( i == 0 ? READY_PROMPT : DOTS_PROMPT ) + lines[i], i != 0 ); + putLog( QString( "%1%2\n" ).arg( i == 0 ? READY_PROMPT : DOTS_PROMPT ).arg( lines[i] ) ); + } + // IPAL20182 + addText( "", true ); + // set read-only mode + setReadOnly( true ); + // set busy cursor + setCursor( Qt::BusyCursor ); + + // post a request to execute Python command; + // editor will be informed via a custom event that execution has been completed + PyInterp_Dispatcher::Get()->Exec( createRequest( cmd ) ); +} + +/*! + \brief Create request to the python dispatcher for the command execution. + + \param command python command to be executed + */ +PyInterp_Request* PyConsole_EditorBase::createRequest( const QString& command ) +{ + return new ExecCommand( myInterp, command, this, isSync() ); +} + +/*! + \brief Execute command in the python interpreter + and wait until it is finished. + + \param command python command to be executed + */ +void PyConsole_EditorBase::execAndWait( const QString& command ) +{ + // already running ? + if( myEventLoop ) + return; + + // create new event loop + bool sync = isSync(); + if ( !sync ) { + myEventLoop = new QEventLoop( this ); + } + + // execute command + exec( command ); + + if ( !sync ) { + // run event loop + myEventLoop->exec(); + // delete event loop after command is processed + delete myEventLoop; + myEventLoop = 0; + } +} + +/*! + \brief Process "Enter" key press event. + + Execute the command entered by the user. +*/ +void PyConsole_EditorBase::handleReturn() +{ + // Position cursor at the end + QTextCursor curs(textCursor()); + curs.movePosition(QTextCursor::End); + setTextCursor(curs); + + // get last line + QTextBlock par = document()->end().previous(); + if ( !par.isValid() ) return; + + // get command + QString cmd = par.text().remove( 0, promptSize() ); + // extend the command buffer with the current command + myCommandBuffer.append( cmd ); + // add command to the history + if ( !cmd.trimmed().isEmpty() ) + myHistory.push_back( cmd ); + putLog( QString( "%1%2\n" ).arg( myPrompt ).arg( cmd ) ); + + // IPAL19397 + addText( "", true ); + + // set read-only mode + setReadOnly( true ); + // set busy cursor + setCursor( Qt::BusyCursor ); + + // post a request to execute Python command; + // editor will be informed via a custom event that execution has been completed + PyInterp_Dispatcher::Get()->Exec( createRequest( myCommandBuffer ) ); +} + +/*! + \brief Process drop event. + + Paste dragged text. + \param event drop event +*/ +void PyConsole_EditorBase::dropEvent( QDropEvent* event ) +{ + // get the initial drop position + QPoint pos = event->pos(); + QTextCursor cur = cursorForPosition( event->pos() ); + // if the position is not in the last line move it to the end of the command line + if ( cur.position() < document()->end().previous().position() + promptSize() ) { + moveCursor( QTextCursor::End ); + pos = cursorRect().center(); + } + // create new drop event and use it instead of the original + QDropEvent de( pos, + event->possibleActions(), + event->mimeData(), + event->mouseButtons(), + event->keyboardModifiers(), + event->type() ); + QTextEdit::dropEvent( &de ); + // accept the original event + event->acceptProposedAction(); +} + +/*! + \brief Process mouse button release event. + + Left mouse button: copy selection to the clipboard. + Middle mouse button: paste clipboard's contents. + \param event mouse event +*/ +void PyConsole_EditorBase::mouseReleaseEvent( QMouseEvent* event ) +{ + if ( event->button() == Qt::LeftButton ) { + QTextEdit::mouseReleaseEvent( event ); + //copy(); + } + else if ( event->button() == Qt::MidButton ) { + QTextCursor cur = cursorForPosition( event->pos() ); + // if the position is not in the last line move it to the end of the command line + if ( cur.position() < document()->end().previous().position() + promptSize() ) { + moveCursor( QTextCursor::End ); + } + else { + setTextCursor( cur ); + } + const QMimeData* md = QApplication::clipboard()->mimeData( QApplication::clipboard()->supportsSelection() ? + QClipboard::Selection : QClipboard::Clipboard ); + if ( md ) + insertFromMimeData( md ); + } + else { + QTextEdit::mouseReleaseEvent( event ); + } +} + +/*! + \brief Check if the string is command. + + Return True if the string \a str is likely to be the command + (i.e. it is started from the '>>>' or '...'). + \param str string to be checked +*/ +bool PyConsole_EditorBase::isCommand( const QString& str ) const +{ + return str.startsWith( READY_PROMPT ) || str.startsWith( DOTS_PROMPT ); +} + +/*! + \brief Handle keyboard event. + + Implement navigation, history browsing, copy/paste and other common + operations. + \param event keyboard event +*/ +void PyConsole_EditorBase::keyPressEvent( QKeyEvent* event ) +{ + // get cursor position + QTextCursor cur = textCursor(); + int curLine = cur.blockNumber(); + int curCol = cur.columnNumber(); + + // get last edited line + int endLine = document()->blockCount()-1; + + // get pressed key code + int aKey = event->key(); + + // check if is pressed + bool ctrlPressed = event->modifiers() & Qt::ControlModifier; + // check if is pressed + bool shftPressed = event->modifiers() & Qt::ShiftModifier; + + if ( aKey == Qt::Key_Escape || ( ctrlPressed && aKey == -1 ) ) { + // process + key-binding and key: clear current command + myCommandBuffer.truncate( 0 ); + myPrompt = READY_PROMPT; + addText( myPrompt, true ); + horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); + return; + } + else if ( ctrlPressed && aKey == Qt::Key_C ) { + // process + key-binding : copy + copy(); + return; + } + else if ( ctrlPressed && aKey == Qt::Key_X ) { + // process + key-binding : cut + cut(); + return; + } + else if ( ctrlPressed && aKey == Qt::Key_V ) { + // process + key-binding : paste + paste(); + return; + } + + // check for printed key + // #### aKey = ( aKey < Qt::Key_Space || aKey > Qt::Key_ydiaeresis ) ? aKey : 0; + // Better: + aKey = !(QChar(aKey).isPrint()) ? aKey : 0; + + switch ( aKey ) { + case 0 : + // any printed key: just print it + { + if ( curLine < endLine || curCol < promptSize() ) { + moveCursor( QTextCursor::End ); + } + QTextEdit::keyPressEvent( event ); + break; + } + case Qt::Key_Return: + case Qt::Key_Enter: + // key: process the current command + { + handleReturn(); + break; + } + case Qt::Key_Up: + // arrow key: process as follows: + // - without , modifiers: previous command in history + // - with modifier key pressed: move cursor one row up without selection + // - with modifier key pressed: move cursor one row up with selection + // - with + modifier keys pressed: scroll one row up + { + if ( ctrlPressed && shftPressed ) { + int value = verticalScrollBar()->value(); + int spacing = fontMetrics().lineSpacing(); + verticalScrollBar()->setValue( value > spacing ? value-spacing : 0 ); + } + else if ( shftPressed || ctrlPressed ) { + if ( curLine > 0 ) + moveCursor( QTextCursor::Up, + shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor ); + } + else { + if ( myCmdInHistory < 0 && myHistory.count() > 0 ) { + // set history browsing mode + myCmdInHistory = myHistory.count(); + // remember current command + QTextBlock par = document()->end().previous(); + myCurrentCommand = par.text().remove( 0, promptSize() ); + } + if ( myCmdInHistory > 0 ) { + myCmdInHistory--; + // get previous command in the history + QString previousCommand = myHistory.at( myCmdInHistory ); + // print previous command + moveCursor( QTextCursor::End ); + moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor ); + textCursor().removeSelectedText(); + addText( myPrompt + previousCommand ); + // move cursor to the end + moveCursor( QTextCursor::End ); + } + } + break; + } + case Qt::Key_Down: + // arrow key: process as follows: + // - without , modifiers: next command in history + // - with modifier key pressed: move cursor one row down without selection + // - with modifier key pressed: move cursor one row down with selection + // - with + modifier keys pressed: scroll one row down + { + if ( ctrlPressed && shftPressed ) { + int value = verticalScrollBar()->value(); + int maxval = verticalScrollBar()->maximum(); + int spacing = fontMetrics().lineSpacing(); + verticalScrollBar()->setValue( value+spacing < maxval ? value+spacing : maxval ); + } + else if ( shftPressed || ctrlPressed) { + if ( curLine < endLine ) + moveCursor( QTextCursor::Down, + shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor ); + } + else { + if ( myCmdInHistory >= 0 ) { + // get next command in the history + myCmdInHistory++; + QString nextCommand; + if ( myCmdInHistory < myHistory.count() ) { + // next command in history + nextCommand = myHistory.at( myCmdInHistory ); + } + else { + // end of history is reached + // last printed command + nextCommand = myCurrentCommand; + // unset history browsing mode + myCmdInHistory = -1; + } + // print next or current command + moveCursor( QTextCursor::End ); + moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor ); + textCursor().removeSelectedText(); + addText( myPrompt + nextCommand ); + // move cursor to the end + moveCursor( QTextCursor::End ); + } + } + break; + } + case Qt::Key_Left: + // arrow key: process as follows: + // - without , modifiers: move one symbol left (taking into account prompt) + // - with modifier key pressed: move one word left (taking into account prompt) + // - with modifier key pressed: move one symbol left with selection + // - with + modifier keys pressed: move one word left with selection + { + QString txt = textCursor().block().text(); + if ( !shftPressed && isCommand( txt ) && curCol <= promptSize() ) { + moveCursor( QTextCursor::Up ); + moveCursor( QTextCursor::EndOfBlock ); + } + else { + QTextEdit::keyPressEvent( event ); + } + break; + } + case Qt::Key_Right: + // arrow key: process as follows: + // - without , modifiers: move one symbol right (taking into account prompt) + // - with modifier key pressed: move one word right (taking into account prompt) + // - with modifier key pressed: move one symbol right with selection + // - with + modifier keys pressed: move one word right with selection + { + QString txt = textCursor().block().text(); + if ( !shftPressed ) { + if ( curCol < txt.length() ) { + if ( isCommand( txt ) && curCol < promptSize() ) { + cur.setPosition( cur.block().position() + promptSize() ); + setTextCursor( cur ); + break; + } + } + else { + if ( curLine < endLine && isCommand( textCursor().block().next().text() ) ) { + cur.setPosition( cur.position() + promptSize()+1 ); + setTextCursor( cur ); + horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); + break; + } + } + } + QTextEdit::keyPressEvent( event ); + break; + } + case Qt::Key_PageUp: + // key: process as follows: + // - without , modifiers: first command in history + // - with modifier key pressed: move cursor one page up without selection + // - with modifier key pressed: move cursor one page up with selection + // - with + modifier keys pressed: scroll one page up + { + if ( ctrlPressed && shftPressed ) { + verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub); + } + else if ( shftPressed || ctrlPressed ) { + bool moved = false; + qreal lastY = cursorRect( cur ).top(); + qreal distance = 0; + // move using movePosition to keep the cursor's x + do { + qreal y = cursorRect( cur ).top(); + distance += qAbs( y - lastY ); + lastY = y; + moved = cur.movePosition( QTextCursor::Up, + shftPressed ? QTextCursor::KeepAnchor : + QTextCursor::MoveAnchor ); + } while ( moved && distance < viewport()->height() ); + if ( moved ) { + cur.movePosition( QTextCursor::Down, + shftPressed ? QTextCursor::KeepAnchor : + QTextCursor::MoveAnchor ); + verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub ); + } + setTextCursor( cur ); + } + else { + if ( myCmdInHistory < 0 && myHistory.count() > 0 ) { + // set history browsing mode + myCmdInHistory = myHistory.count(); + // remember current command + QTextBlock par = document()->end().previous(); + myCurrentCommand = par.text().remove( 0, promptSize() ); + } + if ( myCmdInHistory > 0 ) { + myCmdInHistory = 0; + // get very first command in the history + QString firstCommand = myHistory.at( myCmdInHistory ); + // print first command + moveCursor( QTextCursor::End ); + moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor ); + textCursor().removeSelectedText(); + addText( myPrompt + firstCommand ); + // move cursor to the end + moveCursor( QTextCursor::End ); + } + } + break; + } + case Qt::Key_PageDown: + // key: process as follows: + // - without , modifiers: last command in history + // - with modifier key pressed: move cursor one page down without selection + // - with modifier key pressed: move cursor one page down with selection + // - with + modifier keys pressed: scroll one page down + { + if ( ctrlPressed && shftPressed ) { + verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd); + } + else if ( shftPressed || ctrlPressed ) { + bool moved = false; + qreal lastY = cursorRect( cur ).top(); + qreal distance = 0; + // move using movePosition to keep the cursor's x + do { + qreal y = cursorRect( cur ).top(); + distance += qAbs( y - lastY ); + lastY = y; + moved = cur.movePosition( QTextCursor::Down, + shftPressed ? QTextCursor::KeepAnchor : + QTextCursor::MoveAnchor ); + } while ( moved && distance < viewport()->height() ); + if ( moved ) { + cur.movePosition( QTextCursor::Up, + shftPressed ? QTextCursor::KeepAnchor : + QTextCursor::MoveAnchor ); + verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub ); + } + setTextCursor( cur ); + } + else { + if ( myCmdInHistory >= 0 ) { + // unset history browsing mode + myCmdInHistory = -1; + // print current command + moveCursor( QTextCursor::End ); + moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor ); + textCursor().removeSelectedText(); + addText( myPrompt + myCurrentCommand ); + // move cursor to the end + moveCursor( QTextCursor::End ); + } + } + break; + } + case Qt::Key_Home: + // key: process as follows: + // - without , modifiers: move cursor to the beginning of the current line without selection + // - with modifier key pressed: move cursor to the very first symbol without selection + // - with modifier key pressed: move cursor to the beginning of the current line with selection + // - with + modifier keys pressed: move cursor to the very first symbol with selection + { + if ( ctrlPressed ) { + moveCursor( QTextCursor::Start, + shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor ); + } + else { + QString txt = textCursor().block().text(); + if ( isCommand( txt ) ) { + if ( shftPressed ) { + if ( curCol > promptSize() ) { + cur.movePosition( QTextCursor::StartOfLine, QTextCursor::KeepAnchor ); + cur.movePosition( QTextCursor::Right, QTextCursor::KeepAnchor, promptSize() ); + } + } + else { + cur.movePosition( QTextCursor::StartOfLine ); + cur.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, promptSize() ); + } + setTextCursor( cur ); + } + else { + moveCursor( QTextCursor::StartOfBlock, + shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor ); + } + horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); + } + break; + } + case Qt::Key_End: + // key: process as follows: + // - without , modifiers: move cursor to the end of the current line without selection + // - with modifier key pressed: move cursor to the very last symbol without selection + // - with modifier key pressed: move cursor to the end of the current line with selection + // - with + modifier keys pressed: move cursor to the very last symbol with selection + { + moveCursor( ctrlPressed ? QTextCursor::End : QTextCursor::EndOfBlock, + shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor ); + break; + } + case Qt::Key_Backspace : + // key: process as follows + // - without any modifiers : delete symbol before the cursor / selection (taking into account prompt) + // - with modifier key pressed: delete previous word + // - with modifier key pressed: delete text from the cursor to the line beginning + // works only for last (command) line + { + if ( cur.hasSelection() ) { + cut(); + } + else if ( cur.position() > document()->end().previous().position() + promptSize() ) { + if ( shftPressed ) { + moveCursor( QTextCursor::PreviousWord, QTextCursor::KeepAnchor ); + textCursor().removeSelectedText(); + } + else if ( ctrlPressed ) { + cur.setPosition( document()->end().previous().position() + promptSize(), + QTextCursor::KeepAnchor ); + setTextCursor( cur ); + textCursor().removeSelectedText(); + } + else { + QTextEdit::keyPressEvent( event ); + } + } + else { + cur.setPosition( document()->end().previous().position() + promptSize() ); + setTextCursor( cur ); + horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); + } + break; + } + case Qt::Key_Delete : + // key: process as follows + // - without any modifiers : delete symbol after the cursor / selection (taking into account prompt) + // - with modifier key pressed: delete next word + // - with modifier key pressed: delete text from the cursor to the end of line + // works only for last (command) line + { + if ( cur.hasSelection() ) { + cut(); + } + else if ( cur.position() > document()->end().previous().position() + promptSize()-1 ) { + if ( shftPressed ) { + moveCursor( QTextCursor::NextWord, QTextCursor::KeepAnchor ); + textCursor().removeSelectedText(); + } + else if ( ctrlPressed ) { + moveCursor( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor ); + textCursor().removeSelectedText(); + } + else { + QTextEdit::keyPressEvent( event ); + } + } + else { + cur.setPosition( document()->end().previous().position() + promptSize() ); + setTextCursor( cur ); + horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); + } + break; + } + case Qt::Key_Insert : + // key: process as follows + // - with modifier key pressed: copy() + // - with modifier key pressed: paste() to the command line + { + if ( ctrlPressed ) { + copy(); + } + else if ( shftPressed ) { + paste(); + } + else + QTextEdit::keyPressEvent( event ); + break; + } + } +} + +/*! + \brief Handle notification event coming from Python dispatcher. + \param event notification event +*/ +void PyConsole_EditorBase::customEvent( QEvent* event ) +{ + switch( event->type() ) + { + case PrintEvent::EVENT_ID: + { + PrintEvent* pe=(PrintEvent*)event; + addText( pe->text(), false, pe->isError()); + return; + } + case PyInterp_Event::ES_OK: + case PyInterp_Event::ES_ERROR: + { + // clear command buffer + myCommandBuffer.truncate( 0 ); + // add caret return line if necessary + QTextBlock par = document()->end().previous(); + QString txt = par.text(); + txt.truncate( txt.length() - 1 ); + // IPAL19397 : addText moved to handleReturn() method + //if ( !txt.isEmpty() ) + // addText( "", true ); + // set "ready" prompt + myPrompt = READY_PROMPT; + addText( myPrompt ); + // unset busy cursor + unsetCursor(); + // stop event loop (if running) + if ( myEventLoop ) + myEventLoop->exit(); + break; + } + case PyInterp_Event::ES_INCOMPLETE: + { + // extend command buffer (multi-line command) + myCommandBuffer.append( "\n" ); + // add caret return line if necessary + QTextBlock par = document()->end().previous(); + QString txt = par.text(); + txt.truncate( txt.length() - 1 ); + // IPAL19397 : addText moved to handleReturn() method + //if ( !txt.isEmpty() ) + // addText( "", true ); + // set "dot" prompt + myPrompt = DOTS_PROMPT; + addText( myPrompt/*, true*/ ); // IPAL19397 + // unset busy cursor + unsetCursor(); + // stop event loop (if running) + if ( myEventLoop ) + myEventLoop->exit(); + break; + } + default: + QTextEdit::customEvent( event ); + } + + // unset read-only state + setReadOnly( false ); + // unset history browsing mode + myCmdInHistory = -1; + + if ( (int)event->type() == (int)PyInterp_Event::ES_OK && myQueue.count() > 0 ) + { + // process the next sheduled command from the queue (if there is any) + QString nextcmd = myQueue[0]; + myQueue.pop_front(); + exec( nextcmd ); + } +} + +/*! + \brief Handle Python interpreter change. + + Perform initialization actions if the interpreter is changed. + \param interp python interpreter is being set +*/ +void PyConsole_EditorBase::onPyInterpChanged( PyConsole_Interp* interp ) +{ + if ( myInterp != interp + // Force read-only state and wait cursor when myInterp is NULL + || !myInterp ) { + myInterp = interp; + if ( myInterp ) { + // print banner + myBanner = myInterp->getbanner().c_str(); + if ( isShowBanner() ) + addText( myBanner ); + // clear command buffer + myCommandBuffer.truncate(0); + // unset read-only state + setReadOnly( false ); + // unset history browsing mode + myCmdInHistory = -1; + // add prompt + addText( myPrompt ); + // unset busy cursor + viewport()->unsetCursor(); + // stop event loop (if running) + if( myEventLoop) + myEventLoop->exit(); + } + else { + // clear contents + clear(); + // set read-only state + setReadOnly( true ); + // set busy cursor + setCursor( Qt::WaitCursor ); + } + } +} + +/*! + \brief "Copy" operation. + + Reimplemented from Qt. + Warning! In Qt4 this method is not virtual. + */ +void PyConsole_EditorBase::cut() +{ + QTextCursor cur = textCursor(); + if ( cur.hasSelection() ) { + QApplication::clipboard()->setText( cur.selectedText() ); + int startSelection = cur.selectionStart(); + if ( startSelection < document()->end().previous().position() + promptSize() ) + startSelection = document()->end().previous().position() + promptSize(); + int endSelection = cur.selectionEnd(); + if ( endSelection < document()->end().previous().position() + promptSize() ) + endSelection = document()->end().previous().position() + promptSize(); + cur.setPosition( startSelection ); + cur.setPosition( endSelection, QTextCursor::KeepAnchor ); + horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); + setTextCursor( cur ); + textCursor().removeSelectedText(); + } +} + +/*! + \brief "Paste" operation. + + Reimplemented from Qt. + Warning! In Qt4 this method is not virtual. + */ +void PyConsole_EditorBase::paste() +{ + QTextCursor cur = textCursor(); + if ( cur.hasSelection() ) { + int startSelection = cur.selectionStart(); + if ( startSelection < document()->end().previous().position() + promptSize() ) + startSelection = document()->end().previous().position() + promptSize(); + int endSelection = cur.selectionEnd(); + if ( endSelection < document()->end().previous().position() + promptSize() ) + endSelection = document()->end().previous().position() + promptSize(); + cur.setPosition( startSelection ); + cur.setPosition( endSelection, QTextCursor::KeepAnchor ); + horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); + setTextCursor( cur ); + textCursor().removeSelectedText(); + } + if ( textCursor().position() < document()->end().previous().position() + promptSize() ) + moveCursor( QTextCursor::End ); + QTextEdit::paste(); +} + +/*! + \brief "Clear" operation. + + Reimplemented from Qt. + Warning! In Qt4 this method is not virtual. + */ +void PyConsole_EditorBase::clear() +{ + QTextEdit::clear(); + if ( isShowBanner() ) + addText( myBanner ); + myPrompt = READY_PROMPT; + addText( myPrompt ); +} + +/*! + \brief "Dump commands" operation. + */ +void PyConsole_EditorBase::dumpImpl(const QString& fileName) +{ + if ( !fileName.isEmpty() ) { + QFile file( fileName ); + if ( !file.open( QFile::WriteOnly ) ) + return; + + QTextStream out (&file); + + for ( int i=0; idump( fileName ); + else + QMessageBox::warning(this,tr("WARNING"),tr("Python file has not been written")); +} + +/*! + \brief Start python trace logging + \param fileName the path to the log file + \sa stopLog() + */ +bool PyConsole_EditorBase::startLogImpl( const QString& fileName ) +{ + bool ok = false; + if ( !fileName.isEmpty() ) { + QFile file( fileName ); + if ( file.open( QFile::WriteOnly ) ) { + file.close(); + myLogFile = fileName; + ok = true; + } + } + return ok; +} + + +/*! + \brief Start python trace logging + \param fileName the path to the log file + \sa stopLog() + */ +bool PyConsole_EditorBase::startLog( const QString& fileName ) +{ + return startLogImpl(fileName); +} + +/*! + \brief Start python trace logging + \sa stopLog() + */ +void PyConsole_EditorBase::startLog() +{ + startLogSlot(); +} + +void PyConsole_EditorBase::startLogSlot() +{ + while (1) + { + QString fileName(QFileDialog::getSaveFileName(this,tr("Choose python file where to store log"),QString(),tr("Log files ext (*.log *.txt)"))); + if ( !fileName.isEmpty() ) + { + if ( startLogImpl( fileName ) ) + break; + else + QMessageBox::warning(this,tr("WARNING"),tr("Log file is not writable")); + } + else + break; + } +} + +/*! + \brief "Stop log" operation. + \sa startLog() + */ +void PyConsole_EditorBase::stopLog() +{ + myLogFile = QString(); +} + +/*! + \brief Put string to the log file + */ +void PyConsole_EditorBase::putLog( const QString& s ) +{ + if ( !myLogFile.isEmpty() ) { + QFile file( myLogFile ); + if ( !file.open( QFile::Append ) ) + return; + + QTextStream out (&file); + out << s; + + file.close(); + } +} diff --git a/src/PyConsoleBase/PyConsole_EditorBase.h b/src/PyConsoleBase/PyConsole_EditorBase.h new file mode 100644 index 000000000..c875e7684 --- /dev/null +++ b/src/PyConsoleBase/PyConsole_EditorBase.h @@ -0,0 +1,110 @@ +// Copyright (C) 2007-2015 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, or (at your option) any later version. +// +// 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 +// + +// SALOME SALOMEGUI : implementation of desktop and GUI kernel +// File : PyConsole_Editor.h +// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com) +// +#ifndef PYCONSOLE_EDITORBASE_H +#define PYCONSOLE_EDITORBASE_H + +#include "PyConsoleBase.h" + +#include + +class PyConsole_Interp; +class PyInterp_Request; +class QEventLoop; + +class PYCONSOLEBASE_EXPORT PyConsole_EditorBase : public QTextEdit +{ + Q_OBJECT; + +public: + PyConsole_EditorBase( PyConsole_Interp* theInterp, QWidget *theParent = 0 ); + ~PyConsole_EditorBase(); + + PyConsole_Interp* getInterp() const; + + virtual void addText( const QString& str, const bool newBlock = false, const bool isError = false ); + bool isCommand( const QString& str ) const; + + virtual void exec( const QString& command ); + void execAndWait( const QString& command ); + + bool isSync() const; + void setIsSync( const bool ); + + bool isSuppressOutput() const; + void setIsSuppressOutput(const bool); + + bool isShowBanner() const; + void setIsShowBanner( const bool ); + + bool isLogging() const; + + virtual QSize sizeHint() const; + void dumpImpl(const QString& fileName); + bool startLogImpl( const QString& ); +public slots: + void cut(); + void paste(); + void clear(); + void handleReturn(); + void onPyInterpChanged( PyConsole_Interp* ); + void dump(const QString& fileName); + void dump(); + void startLog(); + bool startLog( const QString& ); + void stopLog(); + void putLog( const QString& ); + +protected: + virtual void dropEvent( QDropEvent* event ); + virtual void mouseReleaseEvent( QMouseEvent* event ); + virtual void keyPressEvent ( QKeyEvent* event); + virtual void customEvent( QEvent* event); + virtual void dumpSlot(); + virtual void startLogSlot(); + + virtual PyInterp_Request* createRequest( const QString& ); + + /** Convenience function */ + inline int promptSize() const { return myPrompt.size(); } + + PyConsole_Interp* myInterp; //!< python interpreter + + QString myCommandBuffer; //!< python command buffer + QString myCurrentCommand; //!< currently being printed command + QString myPrompt; //!< current command line prompt + int myCmdInHistory; //!< current history command index + QString myLogFile; //!< current output log + QStringList myHistory; //!< commands history buffer + QEventLoop* myEventLoop; //!< internal event loop + QString myBanner; //!< current banner + bool myShowBanner; //!< 'show banner' flag + QStringList myQueue; //!< python commands queue + bool myIsSync; //!< synchronous mode flag + bool myIsSuppressOutput; //!< suppress output flag +}; + +#endif // PYCONSOLE_EDITORBASE_H diff --git a/src/PyConsoleBase/PyConsole_EnhEditorBase.cxx b/src/PyConsoleBase/PyConsole_EnhEditorBase.cxx new file mode 100644 index 000000000..54bf7fd23 --- /dev/null +++ b/src/PyConsoleBase/PyConsole_EnhEditorBase.cxx @@ -0,0 +1,491 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// 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, or (at your option) any later version. +// +// 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 "PyConsoleBase.h" +#include + +#include +#include +#include +#include +#include +#include + +#include "PyConsole_EnhEditorBase.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_EnhEditorBase::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_EnhEditorBase::PyConsole_EnhEditorBase(PyConsole_Interp* interp, QWidget* parent) : + PyConsole_EditorBase(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_EnhEditorBase::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_EditorBase::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_EditorBase::keyPressEvent(event); + } +} + +/** + * Whenever the mouse is clicked, clear the completion. + * @param e + */ +void PyConsole_EnhEditorBase::mousePressEvent(QMouseEvent* e) +{ + clearCompletion(); + _cursor_pos = -1; + PyConsole_EditorBase::mousePressEvent(e); +} + +/** + * Clear in the editor the block of text displayed after having hit . + */ +void PyConsole_EnhEditorBase::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 + if ( myInterp ) + myInterp->clearCompletion(); + } + _tab_mode = false; +} + +/** + * Handle the sequence of events after having hit + */ +void PyConsole_EnhEditorBase::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_EnhEditorBase::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_EnhEditorBase::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( 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_EnhEditorBase::formatCompletion(const QStringList& 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_EnhEditorBase::customEvent( QEvent* event ) +{ + QStringList matches; + QString first_match, comple_text, doc, base; + QTextCursor cursor(textCursor()); + QTextBlockFormat bf; + QTextCharFormat cf; + int cursorPos; + + switch( event->type() ) + { + case PyInterp_Event::ES_TAB_COMPLETE_OK: + { + // Extract corresponding matches from the interpreter + matches = getInterp()->getLastMatches(); + doc = getInterp()->getDocStr(); + + 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 + if (matches.size() == 1) + { + first_match = matches[0].mid(_compl_after_point.size()); + cursor.insertText(first_match); + _tab_mode = false; + if (doc.isEmpty()) + 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_EditorBase::customEvent(event); + // If we are in multi_paste_mode, process the next item: + multiLineProcessNextLine(); + break; + } + default: + { + PyConsole_EditorBase::customEvent( event ); + break; + } + } +} + +/** + * Extract the common leading part of all strings in matches. + * @param matches + * @param result + */ +void PyConsole_EnhEditorBase::extractCommon(const QStringList& 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_EnhEditorBase::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_EnhEditorBase::insertFromMimeData(const QMimeData* source) +{ + if (_multi_line_paste) + return; + + if (source->hasText()) + { + QString s = source->text(); + if (s.contains("\n")) + multilinePaste(s); + else + PyConsole_EditorBase::insertFromMimeData(source); + } + else + { + PyConsole_EditorBase::insertFromMimeData(source); + } +} + + +void PyConsole_EnhEditorBase::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_EditorBase::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_EnhEditorBase::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/PyConsoleBase/PyConsole_EnhEditorBase.h b/src/PyConsoleBase/PyConsole_EnhEditorBase.h new file mode 100644 index 000000000..6c91a3d8e --- /dev/null +++ b/src/PyConsoleBase/PyConsole_EnhEditorBase.h @@ -0,0 +1,95 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// 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, or (at your option) any later version. +// +// 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_ENHEDITORBASE_H_ +#define PYCONSOLE_ENHEDITORBASE_H_ + +#include "PyConsoleBase.h" +#include "PyConsole_EditorBase.h" + +#include +#include + +/** + * Enhanced Python editor handling tab completion. + */ +class PYCONSOLEBASE_EXPORT PyConsole_EnhEditorBase : public PyConsole_EditorBase +{ + Q_OBJECT; + +public: + PyConsole_EnhEditorBase(PyConsole_Interp* interp, QWidget* parent = 0); + virtual ~PyConsole_EnhEditorBase() {} + +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 QStringList& matches, QString& result) const; + virtual QString formatDocHTML(const QString & doc) const; + + virtual void multilinePaste(const QString & s); + virtual void multiLineProcessNextLine(); + +private: + void extractCommon(const QStringList& matches, QString& result) const; + +}; + +#endif /* PYCONSOLE_ENHEDITORBASE_H_ */ diff --git a/src/PyConsoleBase/PyConsole_EnhInterp.cxx b/src/PyConsoleBase/PyConsole_EnhInterp.cxx new file mode 100644 index 000000000..eab44af6f --- /dev/null +++ b/src/PyConsoleBase/PyConsole_EnhInterp.cxx @@ -0,0 +1,177 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// 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, or (at your option) any later version. +// +// 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 "PyConsoleBase.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 Constructor +*/ +PyConsole_EnhInterp::PyConsole_EnhInterp() + : PyConsole_Interp() +{ +} + +/*! + \brief Destructor +*/ +PyConsole_EnhInterp::~PyConsole_EnhInterp() +{ +} + +QStringList PyConsole_EnhInterp::getLastMatches() const +{ + return _last_matches; +} + +QString PyConsole_EnhInterp::getDocStr() const +{ + return _doc_str; +} + +/*! + \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, _global_context, _local_context); + 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, + QStringList& 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, _global_context, _local_context); + 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.clear(); + _doc_str = ""; +} diff --git a/src/PyConsoleBase/PyConsole_EnhInterp.h b/src/PyConsoleBase/PyConsole_EnhInterp.h new file mode 100644 index 000000000..57a483b34 --- /dev/null +++ b/src/PyConsoleBase/PyConsole_EnhInterp.h @@ -0,0 +1,63 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// 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, or (at your option) any later version. +// +// 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 "PyConsoleBase.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 PYCONSOLEBASE_EXPORT PyConsole_EnhInterp: public PyConsole_Interp +{ +public: + PyConsole_EnhInterp(); + virtual ~PyConsole_EnhInterp(); + + virtual QStringList getLastMatches() const; + virtual QString getDocStr() const; + + 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 */ + QStringList _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, + QStringList& result, bool discardSwig=true) const; +}; + +#endif /* PYCONSOLE_ENHINTERP_H_ */ diff --git a/src/PyConsoleBase/PyConsole_Event.cxx b/src/PyConsoleBase/PyConsole_Event.cxx new file mode 100644 index 000000000..aaeb2576f --- /dev/null +++ b/src/PyConsoleBase/PyConsole_Event.cxx @@ -0,0 +1,24 @@ +// Copyright (C) 2007-2015 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, or (at your option) any later version. +// +// 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/PyConsoleBase/PyConsole_Event.h b/src/PyConsoleBase/PyConsole_Event.h new file mode 100644 index 000000000..28db82430 --- /dev/null +++ b/src/PyConsoleBase/PyConsole_Event.h @@ -0,0 +1,69 @@ +// Copyright (C) 2007-2015 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, or (at your option) any later version. +// +// 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 "PyConsoleBase.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 QString& 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/PyConsoleBase/PyConsole_Interp.cxx b/src/PyConsoleBase/PyConsole_Interp.cxx new file mode 100644 index 000000000..ca459b582 --- /dev/null +++ b/src/PyConsoleBase/PyConsole_Interp.cxx @@ -0,0 +1,93 @@ +// Copyright (C) 2007-2015 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, or (at your option) any later version. +// +// 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 : PyConsole_Interp.cxx +// Author : Nicolas REJNERI, Adrien BRUNETON + +#include "PyConsole_Interp.h" + +/*! + \class PyConsole_Interp + \brief Python interpreter to be embedded to the SALOME study's GUI. + + There is only one Python interpreter for the whole SALOME environment. + + Call the initialize() method defined in the base class PyInterp_Interp, + to initialize the interpreter after instance creation. + + The method initialize() calls virtuals methods + - initPython() to initialize global Python interpreter + - initContext() to initialize interpreter internal context + - initRun() to prepare interpreter for running commands +*/ + +/*! + \brief Constructor. + + Creates new python interpreter. +*/ +PyConsole_Interp::PyConsole_Interp(): PyInterp_Interp() +{ +} + +/*! + \brief Destructor. + + Does nothing for the moment. +*/ +PyConsole_Interp::~PyConsole_Interp() +{ +} + +/*! Sets the variable "__IN_SALOME_GUI_CONSOLE" to True. +* This is not attached to a module (like salome_iapp.IN_SALOME_GUI_CONSOLE) +* since modules are shared across all interpreters in SALOME. +* +* (GIL is already acquired here) +*/ +int PyConsole_Interp::beforeRun() +{ + return PyRun_SimpleString("__builtins__.__IN_SALOME_GUI_CONSOLE=True"); +} + +int PyConsole_Interp::afterRun() +{ + return PyRun_SimpleString("__builtins__.__IN_SALOME_GUI_CONSOLE=False"); +} + +QStringList PyConsole_Interp::getLastMatches() const +{ + return QStringList(); +} + +QString PyConsole_Interp::getDocStr() const +{ + return QString(); +} + +int PyConsole_Interp::runDirCommand(const QString&, const QString& ) +{ + return 0; +} + +void PyConsole_Interp::clearCompletion() +{ +} diff --git a/src/PyConsoleBase/PyConsole_Interp.h b/src/PyConsoleBase/PyConsole_Interp.h new file mode 100644 index 000000000..34f251d26 --- /dev/null +++ b/src/PyConsoleBase/PyConsole_Interp.h @@ -0,0 +1,49 @@ +// Copyright (C) 2007-2015 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, or (at your option) any later version. +// +// 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 : PyConsole_Interp.h +// Author : Nicolas REJNERI, Adrien BRUNETON + +#ifndef PYCONSOLE_INTERP_H +#define PYCONSOLE_INTERP_H + +#include "PyConsoleBase.h" +#include "PyInterp_Interp.h" /// !!! WARNING !!! THIS INCLUDE MUST BE VERY FIRST !!! + +#include + +class PYCONSOLEBASE_EXPORT PyConsole_Interp : public PyInterp_Interp +{ +public: + PyConsole_Interp(); + ~PyConsole_Interp(); + + virtual int afterRun(); + virtual int beforeRun(); + + virtual QStringList getLastMatches() const; + virtual QString getDocStr() const; + + virtual int runDirCommand(const QString&, const QString&); + virtual void clearCompletion(); +}; + +#endif // PYCONSOLE_INTERP_H diff --git a/src/PyConsoleBase/PyConsole_Request.cxx b/src/PyConsoleBase/PyConsole_Request.cxx new file mode 100644 index 000000000..5c67b8bbb --- /dev/null +++ b/src/PyConsoleBase/PyConsole_Request.cxx @@ -0,0 +1,108 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// 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, or (at your option) any later version. +// +// 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 "PyConsole_Interp.h" +#include "PyConsole_Event.h" +#include "PyInterp_Event.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, + QObject* theListener, + bool theSync ) + : PyInterp_LockRequest( theInterp, theListener, theSync ), + 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.toLatin1().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( PyInterp_Interp* theInterp, + const QString& theInput, + const QString& theStartMatch, + QObject* theListener, + bool theSync ) + : PyInterp_LockRequest( theInterp, theListener, theSync ), + _tabSuccess(false), _dirArg(theInput), _startMatch(theStartMatch) +{} + +/** + * Execute the completion command by wrapping the runDirCommand() of the + * embedded enhanced interpreter. + */ +void CompletionCommand::execute() +{ + int ret = static_cast(getInterp())->runDirCommand( _dirArg, _startMatch ); + _tabSuccess = ret == 0; +} + +/** + * 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/PyConsoleBase/PyConsole_Request.h b/src/PyConsoleBase/PyConsole_Request.h new file mode 100644 index 000000000..a0ed598eb --- /dev/null +++ b/src/PyConsoleBase/PyConsole_Request.h @@ -0,0 +1,99 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// 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, or (at your option) any later version. +// +// 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 "PyInterp_Request.h" + +#include +#include +#include + +class PyInterp_Interp; + +/*! + \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, + QObject* theListener, + bool theSync = 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 CompletionCommand : public PyInterp_LockRequest +{ +public: + CompletionCommand( PyInterp_Interp* theInterp, + const QString& theInput, + const QString& theStartMatch, + QObject* theListener, + bool theSync = 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/PyConsoleBase/resources/PyConsoleBase_msg_en.ts b/src/PyConsoleBase/resources/PyConsoleBase_msg_en.ts new file mode 100644 index 000000000..1586df266 --- /dev/null +++ b/src/PyConsoleBase/resources/PyConsoleBase_msg_en.ts @@ -0,0 +1,70 @@ + + + + + PyConsole_ConsoleBase + + + EDIT_COPY_CMD + &Copy + + + + EDIT_PASTE_CMD + &Paste + + + + EDIT_CLEAR_CMD + Clea&r + + + + EDIT_SELECTALL_CMD + Select &All + + + EDIT_DUMPCOMMANDS_CMD + D&ump Commands + + + EDIT_STARTLOG_CMD + Start &Log + + + EDIT_STOPLOG_CMD + Stop &Log + + + + PyConsole_EditorBase + + Choose python file where to store + Choose python file where to store dump + + + Python scripts ext (*.py) + Python scripts (*.py) + + + WARNING + WARNING ! + + + Python file has not been written + Python file has not been written ! + + + Choose python file where to store log + Choose python file where to store log + + + Log files ext (*.log *.txt) + Log files extension (*.log *.txt) + + + Log file is not writable + Log file is not writable ! + + + diff --git a/src/PyConsoleBase/resources/PyConsoleBase_msg_fr.ts b/src/PyConsoleBase/resources/PyConsoleBase_msg_fr.ts new file mode 100644 index 000000000..ff40a32c5 --- /dev/null +++ b/src/PyConsoleBase/resources/PyConsoleBase_msg_fr.ts @@ -0,0 +1,70 @@ + + + + + PyConsole_ConsoleBase + + + EDIT_COPY_CMD + &Copier + + + + EDIT_PASTE_CMD + C&oller + + + + EDIT_CLEAR_CMD + &Effacer + + + + EDIT_SELECTALL_CMD + &Tout sélectionner + + + EDIT_DUMPCOMMANDS_CMD + &Générer le script des commandes + + + EDIT_STARTLOG_CMD + Démarrer une &trace + + + EDIT_STOPLOG_CMD + Arrêter la &trace + + + + PyConsole_EditorBase + + Choose python file where to store + Choisissez un fichier python où sauver le dump + + + Python scripts ext (*.py) + Scripts Python (*.py) + + + WARNING + Attention ! + + + Python file has not been written + Le fichier Python n'a pas été écrit ! + + + Choose python file where to store log + Choisissez un fichier python où sauver le log + + + Log files ext (*.log *.txt) + Extensions possible des fichiers log (*.log *.txt) + + + Log file is not writable + Le fichier log sélectionné n'est pas writable ! + + + diff --git a/src/PyConsoleBase/resources/PyConsoleBase_msg_ja.ts b/src/PyConsoleBase/resources/PyConsoleBase_msg_ja.ts new file mode 100644 index 000000000..ca1aacee3 --- /dev/null +++ b/src/PyConsoleBase/resources/PyConsoleBase_msg_ja.ts @@ -0,0 +1,70 @@ + + + + + PyConsole_ConsoleBase + + + EDIT_COPY_CMD + コピー(&C) + + + + EDIT_PASTE_CMD + 貼り付け(&P) + + + + EDIT_CLEAR_CMD + 削除(&r) + + + + EDIT_SELECTALL_CMD + すべて選択します。(&A) + + + EDIT_DUMPCOMMANDS_CMD + スクリプト コマンドを生成します。(&u) + + + EDIT_STARTLOG_CMD + ログの開始 (&L) + + + EDIT_STOPLOG_CMD + ログの停止 (&L) + + + + PyConsole_EditorBase + + Choose python file where to store + Choose python file where to store dump + + + Python scripts ext (*.py) + Python scripts (*.py) + + + WARNING + WARNING ! + + + Python file has not been written + Python file has not been written ! + + + Choose python file where to store log + Choose python file where to store log + + + Log files ext (*.log *.txt) + Log files extension (*.log *.txt) + + + Log file is not writable + Log file is not writable ! + + + diff --git a/src/Qtx/Qtx.cxx b/src/Qtx/Qtx.cxx index cf3dc958f..b7e4e775f 100755 --- a/src/Qtx/Qtx.cxx +++ b/src/Qtx/Qtx.cxx @@ -2095,6 +2095,17 @@ QString Qtx::qtDir( const QString& context ) return qtPath; } +/*! + Creates font from string description +*/ +QFont Qtx::stringToFont( const QString& fontDescription ) +{ + QFont font; + if ( fontDescription.trimmed().isEmpty() || !font.fromString( fontDescription ) ) + font = QFont( "Courier", 11 ); + return font; +} + #ifndef WIN32 #include diff --git a/src/Qtx/Qtx.h b/src/Qtx/Qtx.h index 807002e74..20eac5f92 100755 --- a/src/Qtx/Qtx.h +++ b/src/Qtx/Qtx.h @@ -44,6 +44,7 @@ #include #include +#include #include #include #include @@ -276,6 +277,8 @@ public: static QString qtDir( const QString& = QString()); + static QFont stringToFont( const QString& fontDescription ); + #ifndef WIN32 static void* getDisplay(); static Qt::HANDLE getVisual(); diff --git a/src/SALOME_PYQT/SalomePyQt/CMakeLists.txt b/src/SALOME_PYQT/SalomePyQt/CMakeLists.txt index bba191dd1..ee6a6feae 100755 --- a/src/SALOME_PYQT/SalomePyQt/CMakeLists.txt +++ b/src/SALOME_PYQT/SalomePyQt/CMakeLists.txt @@ -41,6 +41,7 @@ INCLUDE_DIRECTORIES( ${PROJECT_SOURCE_DIR}/src/OBJECT ${PROJECT_SOURCE_DIR}/src/ObjBrowser ${PROJECT_SOURCE_DIR}/src/PyInterp + ${PROJECT_SOURCE_DIR}/src/PyConsoleBase ${PROJECT_SOURCE_DIR}/src/PyConsole ${PROJECT_SOURCE_DIR}/src/Qtx ${PROJECT_SOURCE_DIR}/src/SALOME_PYQT/SALOME_PYQT_GUILight diff --git a/src/SUIT/SUIT_Tools.cxx b/src/SUIT/SUIT_Tools.cxx index 11a8ddb92..2d5ab0162 100755 --- a/src/SUIT/SUIT_Tools.cxx +++ b/src/SUIT/SUIT_Tools.cxx @@ -62,17 +62,6 @@ QRect SUIT_Tools::makeRect( const int x1, const int y1, const int x2, const int return QRect( qMin( x1, x2 ), qMin( y1, y2 ), qAbs( x2 - x1 ), qAbs( y2 - y1 ) ); } -/*! - Creates font from string description -*/ -QFont SUIT_Tools::stringToFont( const QString& fontDescription ) -{ - QFont font; - if ( fontDescription.trimmed().isEmpty() || !font.fromString( fontDescription ) ) - font = QFont( "Courier", 11 ); - return font; -} - /*! Creates font's string description */ diff --git a/src/SUIT/SUIT_Tools.h b/src/SUIT/SUIT_Tools.h index dbb64a7da..40da4718a 100755 --- a/src/SUIT/SUIT_Tools.h +++ b/src/SUIT/SUIT_Tools.h @@ -42,7 +42,6 @@ public: static QRect makeRect( const int x1, const int y1, const int x2, const int y2 ); static QString fontToString( const QFont& font ); - static QFont stringToFont( const QString& fontDescription ); static void centerWidget( QWidget* src, const QWidget* ref ); }; diff --git a/src/SalomeApp/CMakeLists.txt b/src/SalomeApp/CMakeLists.txt index 8c29843b4..5e8ff2e0b 100755 --- a/src/SalomeApp/CMakeLists.txt +++ b/src/SalomeApp/CMakeLists.txt @@ -57,6 +57,7 @@ IF(SALOME_USE_PYCONSOLE) INCLUDE_DIRECTORIES( ${PYTHON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/src/PyConsole + ${PROJECT_SOURCE_DIR}/src/PyConsoleBase ${PROJECT_SOURCE_DIR}/src/PyInterp ) ENDIF()