From 79b88bd6d1d95ee99c126a2b29a67b51aec14459 Mon Sep 17 00:00:00 2001 From: Fabien Castan Date: Fri, 13 Jul 2012 18:59:02 +0200 Subject: [PATCH] add TuttleOFX submodule and move qSam from Tuttle to Buttle --- .gitmodules | 3 + TuttleOFX | 1 + applications/qSam/Jamfile.v2 | 49 ++ applications/qSam/SConscript | 68 +++ .../GUI/python/QtCommandInputEdit.cpp | 513 ++++++++++++++++++ .../GUI/python/QtCommandInputEdit.hpp | 67 +++ .../GUI/python/QtPythonTerminal.cpp | 157 ++++++ .../GUI/python/QtPythonTerminal.hpp | 99 ++++ .../GUI/python/QtPythonTerminal.ui | 57 ++ applications/qSam/src/application/main.cpp | 19 + .../qSam/src/common/python/EmbeddedPython.cpp | 109 ++++ .../qSam/src/common/python/EmbeddedPython.hpp | 150 +++++ .../qSam/src/common/python/PythonRedirect.cpp | 152 ++++++ .../qSam/src/common/python/PythonRedirect.hpp | 68 +++ .../qSam/src/common/python/PythonRedirect.i | 10 + 15 files changed, 1522 insertions(+) create mode 100644 .gitmodules create mode 160000 TuttleOFX create mode 100644 applications/qSam/Jamfile.v2 create mode 100644 applications/qSam/SConscript create mode 100644 applications/qSam/src/application/GUI/python/QtCommandInputEdit.cpp create mode 100644 applications/qSam/src/application/GUI/python/QtCommandInputEdit.hpp create mode 100644 applications/qSam/src/application/GUI/python/QtPythonTerminal.cpp create mode 100644 applications/qSam/src/application/GUI/python/QtPythonTerminal.hpp create mode 100644 applications/qSam/src/application/GUI/python/QtPythonTerminal.ui create mode 100644 applications/qSam/src/application/main.cpp create mode 100644 applications/qSam/src/common/python/EmbeddedPython.cpp create mode 100644 applications/qSam/src/common/python/EmbeddedPython.hpp create mode 100644 applications/qSam/src/common/python/PythonRedirect.cpp create mode 100644 applications/qSam/src/common/python/PythonRedirect.hpp create mode 100644 applications/qSam/src/common/python/PythonRedirect.i diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..328b83ab --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "TuttleOFX"] + path = TuttleOFX + url = git@github.com:tuttleofx/TuttleOFX.git diff --git a/TuttleOFX b/TuttleOFX new file mode 160000 index 00000000..416b659f --- /dev/null +++ b/TuttleOFX @@ -0,0 +1 @@ +Subproject commit 416b659f73cb1c393eeb49089d036e15a42391e9 diff --git a/applications/qSam/Jamfile.v2 b/applications/qSam/Jamfile.v2 new file mode 100644 index 00000000..fa40bbb5 --- /dev/null +++ b/applications/qSam/Jamfile.v2 @@ -0,0 +1,49 @@ +import os ; + +using qt : /usr/lib64/qt4 ; + +############################################################################### +# Global project requirements +############################################################################### +project + : requirements + src/application/GUI/python + src/application + src/common + : + build-dir $(BUILD) + ; + + +exe qSam : + src/application/main.cpp + [ glob src/common/python/*.cpp ] + [ glob src/application/GUI/python/* ] + + /boost//python + /python + /qt//QtGui + /qt//QtCore +; + +alias qSam-install : qSam-install-libs qSam-install-bins ; + +install qSam-install-bins + : + qSam + : + debug:$(DIST_DEBUG)/bin + release:$(DIST_RELEASE)/bin + ; + +install qSam-install-libs + : + /boost//python + /qt//QtGui + /qt//QtCore + : + debug:$(DIST_DEBUG)/lib + release:$(DIST_RELEASE)/lib + on + LIB + ; diff --git a/applications/qSam/SConscript b/applications/qSam/SConscript new file mode 100644 index 00000000..cedd71d9 --- /dev/null +++ b/applications/qSam/SConscript @@ -0,0 +1,68 @@ +Import( 'project', 'libs' ) +import os.path + +name = project.getName() + +incdirs = ['src/common', 'src/application'] +src_common = project.scanFiles( ['src/common'], accept=['*.cpp','*.c'] ) +src_application = project.scanFiles( ['src/application'], accept=['*.cpp','*.c'] ) +src_ui = project.scanFiles( ['src/application'], accept=['*.ui'] ) +src_swig = project.scanFiles( ['src/common'], accept=['*.i'] ) + +#print 'src_ui:', src_ui + +libraries = [ + libs.boost, + libs.m, + libs.pthread, + libs.python, + ] +librariesGui = [ + libs.qt4(modules=['QtCore', 'QtGui', 'QtOpenGL'], uiFiles=src_ui) + ] + +project.StaticLibrary( + 'qSamCommon', + sources = src_common, + libraries = libraries, + shared = True, + ) + +pythonOutputDir = os.path.join( project.inOutputDir(), 'python' ) + +swigGlobalFlags={ + 'CPPPATH': incdirs, + 'SWIGFLAGS': ['-python','-c++','-Wall'], + 'SWIGPATH': incdirs, + 'SWIGOUTDIR': pythonOutputDir, + } +swigReplaceFlags = { + 'SHLIBPREFIX': '', + 'LIBPREFIX': '', + } + +swigWrap = project.SharedLibrary( + target = '_'+name, + sources = src_swig, + globalEnvFlags = swigGlobalFlags, + replaceLocalEnvFlags = swigReplaceFlags, + libraries = [libs.qSamCommon], + installDir = pythonOutputDir, + ) + +appli = project.Program( + target = name, + sources = src_application, + libraries = [libs.qSamCommon]+librariesGui, + globalEnvFlags = {'CPPPATH':incdirs}, + installDir = project.inOutputBin(), + ) + +Depends( appli, swigWrap ) + +#Alias( 'all', swigWrap ) +#Alias( 'all', appli ) +#Alias( name, swigWrap ) +Alias( name, appli ) + + diff --git a/applications/qSam/src/application/GUI/python/QtCommandInputEdit.cpp b/applications/qSam/src/application/GUI/python/QtCommandInputEdit.cpp new file mode 100644 index 00000000..0b3b252f --- /dev/null +++ b/applications/qSam/src/application/GUI/python/QtCommandInputEdit.cpp @@ -0,0 +1,513 @@ +#include + +#include "QtCommandInputEdit.hpp" +#include +#include +#include +#include +#include +#include +#include + +QtCommandInputEdit::QtCommandInputEdit( QWidget* parent ) + : QTextEdit( parent ) + , m_completer( NULL ) + , m_navigation( 0 ) +{ + initCompleter(); + setAutoFormatting( QTextEdit::AutoAll ); + setTabStopWidth( 20 ); +} + +void QtCommandInputEdit::scriptExecuted() +{ + m_navigation = false; +} + +void QtCommandInputEdit::keyPressEvent( QKeyEvent* e ) +{ + if( m_completer->popup()->isVisible() ) + { + // The following keys are forwarded by the completer to the widget + switch( e->key() ) + { + case Qt::Key_Enter: + case Qt::Key_Return: + case Qt::Key_Escape: + case Qt::Key_Tab: + case Qt::Key_Backtab: + e->ignore(); + return; // let the completer do default behavior + default: + break; + } + } + + if( ( e->key() == Qt::Key_Enter ) || ( e->key() == Qt::Key_Return && e->modifiers() == Qt::ControlModifier ) ) + { + Q_EMIT executeScript(); + scriptExecuted(); + return; + } + else if( e->modifiers() == Qt::ControlModifier ) + { + switch( e->key() ) + { + case Qt::Key_Space: + autocomplete(); + return; + case Qt::Key_E: + { + removeLine(); + return; + } + } + } + else if( e->modifiers() & Qt::MetaModifier ) + { + switch( e->key() ) + { + case Qt::Key_Q: + { + std::cout << ":::::::::::::::" << std::endl; + std::cout << "MetaModifier::Q" << std::endl; + QTextCursor tc = textCursor(); + std::cout << "anchor: " << tc.anchor() << std::endl; + std::cout << "position: " << tc.position() << std::endl; + std::cout << "selectionStart: " << tc.selectionStart() << std::endl; + std::cout << "selectionEnd: " << tc.selectionEnd() << std::endl; + std::cout << ":::::::::::::::" << std::endl; + + return; + } + case Qt::Key_Down: + navigateCommandNext(); + return; + case Qt::Key_Up: + navigateCommandPrevious(); + return; + case Qt::Key_Left: + navigateOutputPrevious(); + return; + case Qt::Key_Right: + navigateOutputNext(); + return; + case Qt::Key_A: + { + moveCursor( QTextCursor::StartOfBlock ); + return; + } + case Qt::Key_E: + { + moveCursor( QTextCursor::EndOfLine ); + return; + } + case Qt::Key_W: + { + moveCursor( QTextCursor::NextWord ); + return; + } + case Qt::Key_B: + { + moveCursor( QTextCursor::PreviousWord ); + return; + } + case Qt::Key_H: + { + moveCursor( QTextCursor::PreviousCharacter ); + return; + } + case Qt::Key_J: + { + moveCursor( QTextCursor::NextCell ); + return; + } + case Qt::Key_K: + { + moveCursor( QTextCursor::PreviousCell ); + return; + } + case Qt::Key_L: + { + moveCursor( QTextCursor::NextCharacter ); + return; + } + case Qt::Key_Less: // add tabulation + { + removeTab(); + return; + } + case Qt::Key_Greater: // remove tabulation + { + addTab(); + return; + } + } + } + else if( e->modifiers() & Qt::AltModifier && e->modifiers() & Qt::ShiftModifier ) // Alt + Shift + { + switch( e->key() ) + { + case Qt::Key_Left: // add tabulation + { + removeTab(); + return; + } + case Qt::Key_Right: // remove tabulation + { + addTab(); + return; + } + } + } + + QTextEdit::keyPressEvent( e ); + + if( m_completer->popup()->isVisible() ) + { + // maybe we can use the same list, but there is many particular cases + // if( e->key() == Qt::Key_Backspace || e->text().contains( QRegExp( "\\w" ) ) ) + // completerUpdate( ); + // if( e->text().contains( "." ) ) + autocomplete(); // ask to python each time... + } +} + +void QtCommandInputEdit::addTab() +{ + QTextCursor tc = textCursor(); + + if( !tc.hasSelection() ) // cas simple pas de selection + { + tc.movePosition( QTextCursor::StartOfBlock ); + tc.insertText( "\t" ); + } + else // if selection... need to add tabulation for each line + { + tc.beginEditBlock(); + + int begin = tc.selectionStart(); + int end = tc.selectionEnd(); + + //// Select the correct area + // move position and anchor to startOfBlock + tc.setPosition( begin, QTextCursor::MoveAnchor ); + if( !tc.atBlockStart() ) + ++begin; // to replace the selection at the end + tc.movePosition( QTextCursor::StartOfBlock, QTextCursor::MoveAnchor ); + int begin_move = begin - tc.position(); // offset to compensate the StartOfBlock shift + // move position a end + tc.setPosition( end, QTextCursor::KeepAnchor ); + + QString str = tc.selection().toPlainText(); + + str.insert( 0, QString( "\t" ) ); + str.replace( QString( "\n" ), QString( "\n\t" ) ); + tc.removeSelectedText(); // replace old text by str + tc.insertText( str ); + + // put the original selection (lost by insertText()) + tc.setPosition( begin, QTextCursor::MoveAnchor ); + tc.setPosition( begin + str.size() - begin_move, QTextCursor::KeepAnchor ); + this->setTextCursor( tc ); + tc.endEditBlock(); + } +} + +void QtCommandInputEdit::removeTab() +{ + QTextCursor tc = textCursor(); + + if( !tc.hasSelection() ) // simple case, no selection + { + tc.movePosition( QTextCursor::StartOfBlock ); + tc.movePosition( QTextCursor::NextCharacter, QTextCursor::KeepAnchor ); + if( tc.selectedText() == "\t" ) + tc.deleteChar(); + } + else // if selection... need to remove tabulation for each line + { + tc.beginEditBlock(); + + int begin = tc.selectionStart(); + int end = tc.selectionEnd(); + int gap = 0; // recreate the selection : shift of 1 for begin or not + + //// Select the correct area + // move position and anchor to startOfBlock + tc.setPosition( begin, QTextCursor::MoveAnchor ); + tc.movePosition( QTextCursor::StartOfBlock, QTextCursor::MoveAnchor ); + int begin_move = begin - tc.position(); // offset to compensate the StartOfBlock shift + // move position a end + tc.setPosition( end, QTextCursor::KeepAnchor ); + + QString str = tc.selection().toPlainText(); + if( str[0] == '\t' ) + { + str.remove( 0, 1 ); + if( begin_move ) + ++gap; + } + str.replace( QString( "\n\t" ), QString( "\n" ) ); + tc.removeSelectedText(); // replace old text by str + tc.insertText( str ); + + // put the original selection (lost by insertText()) + tc.setPosition( begin - gap, QTextCursor::MoveAnchor ); + tc.setPosition( begin + str.size() - begin_move, QTextCursor::KeepAnchor ); + this->setTextCursor( tc ); + tc.endEditBlock(); + } +} + +void QtCommandInputEdit::removeLine() +{ + QTextCursor tc = textCursor(); + + tc.beginEditBlock(); + if( !tc.hasSelection() ) // simple case, no selection + { + tc.movePosition( QTextCursor::StartOfBlock, QTextCursor::MoveAnchor ); + tc.movePosition( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor ); + tc.movePosition( QTextCursor::NextCharacter, QTextCursor::KeepAnchor ); + // tc.movePosition( QTextCursor::NextBlock, QTextCursor::KeepAnchor ); + } + else + { + int begin = tc.selectionStart(); + int end = tc.selectionEnd(); + tc.setPosition( begin, QTextCursor::MoveAnchor ); + tc.movePosition( QTextCursor::StartOfBlock, QTextCursor::MoveAnchor ); + tc.setPosition( end, QTextCursor::KeepAnchor ); + tc.movePosition( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor ); + tc.movePosition( QTextCursor::NextCharacter, QTextCursor::KeepAnchor ); + // tc.movePosition( QTextCursor::NextBlock, QTextCursor::KeepAnchor ); + } + // tc.select( QTextCursor::BlockUnderCursor ); + tc.removeSelectedText(); + tc.endEditBlock(); +} + +void QtCommandInputEdit::setCompleterList( QStringList& words ) +{ + m_completer->setModel( new QStringListModel( words, m_completer ) ); + + completerUpdate(); +} + +void QtCommandInputEdit::completerUpdate() +{ + QString completionPrefix = wordUnderCursor(); + + // std::cout << "completionPrefix: " << completionPrefix.toStdString( ) << std::endl; + // std::cout << "m_completer->completionPrefix(): " << m_completer->completionPrefix( ).toStdString( ) << std::endl; + + // if( completionPrefix != m_completer->completionPrefix( ) ) + // { + m_completer->setCompletionPrefix( completionPrefix ); + + QTextCursor tc = textCursor(); + tc.movePosition( QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, completionPrefix.size() ); + + // Specify the size or use the widget width + QRect cr = cursorRect( tc ); + cr.setWidth( m_completer->popup()->sizeHintForColumn( 0 ) + m_completer->popup()->verticalScrollBar()->sizeHint().width() ); + + m_completer->complete( cr ); // popup it up! + // } +} + +void QtCommandInputEdit::initCompleter() +{ + m_completer = new QCompleter( this ); + + m_completer->setWidget( this ); + m_completer->setCompletionMode( QCompleter::PopupCompletion ); + m_completer->setModelSorting( QCompleter::CaseInsensitivelySortedModel ); + m_completer->setCaseSensitivity( Qt::CaseInsensitive ); + m_completer->setWrapAround( false ); + QObject::connect( m_completer, SIGNAL( activated( const QString & ) ), this, SLOT( insertCompletion( const QString & ) ) ); +} + +void QtCommandInputEdit::insertCompletion( const QString& completion ) +{ + if( m_completer->widget() != this ) + return; + + int extra = completion.length() - m_completer->completionPrefix().length(); + if( extra == 0 ) // if the completion is full + return; + + QTextCursor tc = textCursor(); + tc.beginEditBlock(); + // tc.movePosition( QTextCursor::Left ); + QTextCursor tmp = textCursor(); + tmp.movePosition( QTextCursor::NextCharacter, QTextCursor::KeepAnchor ); + if( tmp.selectedText().contains( QRegExp( "\\w" ) ) ) // if the next character if a word type + tc.movePosition( QTextCursor::EndOfWord, QTextCursor::KeepAnchor ); // replace to the end of the word + tc.insertText( completion.right( extra ) ); + setTextCursor( tc ); + tc.endEditBlock(); +} + +const char* QtCommandInputEdit::textToExecute() +{ + QTextCursor textCursor = this->textCursor(); + + if( textCursor.hasSelection() ) + return ( char* )( textCursor.selectedText().toLatin1().data() ); + else + return ( char* )( this->toPlainText().toLatin1().data() ); +} + +QString QtCommandInputEdit::charUnderCursor() const +{ + QTextCursor tc = textCursor(); + + tc.movePosition( QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor ); + QString str = tc.selectedText(); + return str; +} + +QString QtCommandInputEdit::wordUnderCursor() const +{ + QTextCursor tc = textCursor(); + int tc_pos = tc.columnNumber(); + + tc.select( QTextCursor::LineUnderCursor ); + + QString str = tc.selectedText(); + str.truncate( tc_pos ); + // QStringList list = str.split( QRegExp( "\\s+" ) ); // cut on spaces + QStringList list = str.split( QRegExp( "[^\\w\\.]" ) ); // cut if it's neither an alphabetic character nor '.' + + if( list.isEmpty() ) + return QString( "" ); + + return list.last(); +} + +std::string QtCommandInputEdit::wordUnderCursor_std() const +{ + return wordUnderCursor().toStdString(); +} + +void QtCommandInputEdit::focusInEvent( QFocusEvent* e ) +{ + if( m_completer ) + m_completer->setWidget( this ); + QTextEdit::focusInEvent( e ); +} + +bool QtCommandInputEdit::navigateNext() +{ + if( !m_navigation ) + { + // std::cout << "navigateCommandNext - no navigation" << std::endl; + return false; + } + ++m_navigation_iterator; + if( m_navigation_iterator == m_embeddedPython->m_commands.end() ) + { + // std::cout << "navigateCommandNext - list end" << std::endl; + m_navigation = false; + setTextColor( QColor( 0, 0, 0 ) ); + setPlainText( m_navigation_saveCommand ); + return false; + } + return true; +} + +bool QtCommandInputEdit::navigatePrevious() +{ + if( !m_navigation ) // not currently in the history + { + // std::cout << "navigateCommandPrevious - begin history" << std::endl; + m_navigation = true; + m_navigation_saveCommand = toPlainText(); + m_navigation_iterator = m_embeddedPython->m_commands.end(); + } + if( m_navigation_iterator == m_embeddedPython->m_commands.begin() ) + { + // std::cout << "navigateCommandPrevious - list begining" << std::endl; + return false; + } + // std::cout << "navigateCommandPrevious" << std::endl; + --m_navigation_iterator; + return true; +} + +void QtCommandInputEdit::navigateCommandNext() +{ + if( navigateNext() ) + { + // std::cout << "navigateCommandNext" << std::endl; + setTextColor( QColor( 0, 0, 0 ) ); + setPlainText( ( *m_navigation_iterator ).m_command.c_str() ); + } +} + +void QtCommandInputEdit::navigateCommandPrevious() +{ + if( navigatePrevious() ) + { + setTextColor( QColor( 0, 0, 0 ) ); + setPlainText( ( *m_navigation_iterator ).m_command.c_str() ); + // QTextCursor tc = textCursor( ); + // tc.insertText( m_navigation_iterator.m_command.c_str( ) ); + } +} + +void QtCommandInputEdit::navigateOutputNext() +{ + if( navigateNext() ) + { + // std::cout << "navigateCommandNext" << std::endl; + setTextColor( QColor( 0, 0, 0 ) ); + setPlainText( ( *m_navigation_iterator ).m_output.c_str() ); + } +} + +void QtCommandInputEdit::navigateOutputPrevious() +{ + if( navigatePrevious() ) + { + setTextColor( QColor( 0, 0, 0 ) ); + setPlainText( ( *m_navigation_iterator ).m_output.c_str() ); + // QTextCursor tc = textCursor( ); + // tc.insertText( m_navigation_iterator.m_command.c_str( ) ); + } +} + +void QtCommandInputEdit::autocomplete() +{ + std::string text = wordUnderCursor_std(); + + // std::cout << "autocomplete : " << text << std::endl; + std::list comp = m_embeddedPython->getAutocompletion( text ); + + if( comp.size() == 0 ) + return; + + QStringList wordList; + std::list::iterator it = comp.begin(); + std::list::iterator it_end = comp.end(); + for(; it != it_end; it++ ) + { + // std::cout << "AUTOCOMPLETE: " << *it << std::endl; + wordList.append( ( *it ).c_str() ); + } + setCompleterList( wordList ); + /* + QListView *listBox; + listBox->clear(); + listBox->insertStringList( wordList ); + QPoint point = textCursorPoint(); + // adjustListBoxSize(qApp->desktop()->height() - point.y(), width() / 2); + listBox->move(point); + listBox->show(); + listBox->raise(); + listBox->setActiveWindow(); + */ +} + diff --git a/applications/qSam/src/application/GUI/python/QtCommandInputEdit.hpp b/applications/qSam/src/application/GUI/python/QtCommandInputEdit.hpp new file mode 100644 index 00000000..a4ae619a --- /dev/null +++ b/applications/qSam/src/application/GUI/python/QtCommandInputEdit.hpp @@ -0,0 +1,67 @@ +#ifndef C_INPUT_EDIT_H +#define C_INPUT_EDIT_H + +#include + +#include + +#include +#include + +class QtCommandInputEdit : public QTextEdit +{ +Q_OBJECT + +public: + QtCommandInputEdit( QWidget* parent = 0 ); + +public: + void setEmbeddedPython( EmbeddedPython* python ) { m_embeddedPython = python; } + const char* textToExecute(); + QString charUnderCursor() const; + QString wordUnderCursor() const; + std::string wordUnderCursor_std() const; + +Q_SIGNALS: + void executeScript(); + +private: + void scriptExecuted(); + +protected: + void keyPressEvent( QKeyEvent* e ); + void focusInEvent( QFocusEvent* e ); + +protected: + bool navigateNext(); + bool navigatePrevious(); + void navigateCommandNext(); + void navigateCommandPrevious(); + void navigateOutputNext(); + void navigateOutputPrevious(); + + void autocomplete(); + +protected: + void addTab(); + void removeTab(); + void removeLine(); + +private Q_SLOTS: + void insertCompletion( const QString& completion ); + +private: + void setCompleterList( QStringList& words ); + void completerUpdate(); + void initCompleter(); + +private: + EmbeddedPython* m_embeddedPython; + QCompleter* m_completer; + std::list::iterator m_navigation_iterator; // Lorsque l'on navigue dans l'historique des CommandAndResult + QString m_navigation_saveCommand; // Lorsque l'on navigue dans l'historique, on garde la commande en cours + bool m_navigation; // True : lorsque l'on navigue dans l'historique, False sinon +}; + +#endif //C_INPUT_EDIT_H + diff --git a/applications/qSam/src/application/GUI/python/QtPythonTerminal.cpp b/applications/qSam/src/application/GUI/python/QtPythonTerminal.cpp new file mode 100644 index 00000000..24f25ff6 --- /dev/null +++ b/applications/qSam/src/application/GUI/python/QtPythonTerminal.cpp @@ -0,0 +1,157 @@ +#include "QtPythonTerminal.hpp" + +#include +#include + +#include +#include + +QtPythonTerminal::QtPythonTerminal() +// : m_command_iterator( NULL ) +// , m_output_iterator ( NULL ) +{ + m_ui.setupUi( this ); + + // register to python as listener... + m_embeddedPython.addPythonOutput( this ); + m_ui.m_input->setEmbeddedPython( &m_embeddedPython ); +} + +QtPythonTerminal::~QtPythonTerminal() {} + +void QtPythonTerminal::executeScript( const std::string in_command ) +{ + // std::cout << "in_command:" << in_command << std::endl; + + // write command in "m_history" + writeCommandInHistory( in_command ); + + // execute command + int ret = m_embeddedPython.pyRun_SimpleString( in_command ); + + // empty text box "m_input" if no selection + QTextCursor textCursor = m_ui.m_input->textCursor(); + if( !ret && !textCursor.hasSelection() ) + m_ui.m_input->clear(); + + // warns that the script has been executed + EmitScriptExecuted(); +} + +void QtPythonTerminal::writeCommandInHistory( const std::string& in_text ) +{ + m_ui.m_history->setTextColor( QColor( 0, 0, 255 ) ); + QString command = "> "; + command += in_text.c_str(); + command.replace( "\n", "\n> " ); + command += "\n"; + writeInHistory( command.toLatin1().data(), eCommand ); + m_ui.m_history->setTextColor( QColor( 0, 0, 0 ) ); +} + +void QtPythonTerminal::writeInHistory( const char* str, const ELogType type ) +{ + const QColor colorCommand( 0, 0, 255 ); + const QColor colorOutput( 0, 0, 0 ); + const QColor colorOutputError( 255, 0, 0 ); + + switch( type ) + { + case eCommand: + m_ui.m_history->setTextColor( colorCommand ); + break; + case eOutputError: + m_ui.m_history->setTextColor( colorOutputError ); + break; + case eOutput: + default: + m_ui.m_history->setTextColor( colorOutput ); + break; + } + + m_ui.m_history->append( str ); //insert +} + +void QtPythonTerminal::writeInInput( const char* str ) +{ + m_ui.m_input->setTextColor( QColor( 0, 0, 0 ) ); + m_ui.m_input->setPlainText( str ); +} + +void QtPythonTerminal::writeLog( char* str, bool isError ) +{ + using namespace boost::algorithm; + std::string s = str; + + std::string is_empty = s; + boost::algorithm::erase_all( is_empty, " " ); + boost::algorithm::erase_all( is_empty, "\t" ); + boost::algorithm::erase_all( is_empty, "\n" ); + if( is_empty.size() == 0 ) + return; + + // CommandAndResult& current_command = m_commands.back( ); + // std::cout << "_________________________________" << std::endl; + // std::cout << "writeLog : " << s << std::endl; + + // current_command.m_output += s; + // current_command.m_output += "\n"; + + ELogType type = isError ? eOutputError : eOutput; + writeInHistory( str, type ); +} + +void QtPythonTerminal::writeErr( char* str ) +{ + // CommandAndResult& current_command = m_commands.back( ); + std::cout << "writeErr : " << str << std::endl; + + // current_command.m_output_error += str; + // current_command.m_output_error += "\n"; + + writeInHistory( str, eOutputError ); +} + +void QtPythonTerminal::clearLog() +{ + m_ui.m_history->clear(); +} + +void QtPythonTerminal::EmitScriptExecuted() +{ + Q_EMIT scriptExecuted(); +} + +void QtPythonTerminal::EmitScriptPathChanged( const char* path ) +{ + // setScriptPath( path ); + Q_EMIT scriptPathChanged( path ); +} + +/* + QStringList QtPythonTerminal::autocompleteCommand( std::string& cmd ) + { + char run[255]; + int n =0; + QStringList list; + resultString=""; + do { + snprintf(run,255,"print completer.complete(\"%s\",%d)\n",cmd.ascii(),n); + PyRun_SimpleString(run); + resultString=resultString.stripWhiteSpace(); //strip trialing newline + if (resultString!="None") + { + list.append(resultString); + resultString=""; + } + else + { + resultString=""; + break; + } + n++; + } while (true); + return list; + } + */ + diff --git a/applications/qSam/src/application/GUI/python/QtPythonTerminal.hpp b/applications/qSam/src/application/GUI/python/QtPythonTerminal.hpp new file mode 100644 index 00000000..c4fa1d99 --- /dev/null +++ b/applications/qSam/src/application/GUI/python/QtPythonTerminal.hpp @@ -0,0 +1,99 @@ +#ifndef __QTPYTHONTERMINAL_HPP__ +#define __QTPYTHONTERMINAL_HPP__ + +#include "ui_QtPythonTerminal.h" + +#include + +#include + +class QtPythonTerminal : public QWidget + , public PythonOutput +{ +Q_OBJECT + +public: + QtPythonTerminal(); + ~QtPythonTerminal(); + +public: + typedef enum ELogType + { + eCommand, + eOutput, + eOutputError, + eLogTypeSize, + } ELogType; + +public Q_SLOTS: + void on_m_input_executeScript() { executeScript( m_ui.m_input->textToExecute() ); } + +protected: + void executeScript( const std::string in_command ); + +public: + void writeCommandInHistory( const std::string& in_text ); + void writeInHistory( const char* str, const ELogType type ); + void writeInInput( const char* str ); + + void writeLog( char* str, bool isError = false ); + void writeErr( char* str ); + void clearLog(); + +protected: + void EmitScriptExecuted(); + void EmitScriptPathChanged( const char* path ); + +Q_SIGNALS: + void scriptExecuted(); + void scriptPathChanged( const char* ); + void scriptCommandManager(); + +protected: + struct CommandAndResult + { + CommandAndResult( std::string command, std::string output = std::string( "" ), std::string output_error = std::string( "" ) ) + : m_command( command ) + , m_output( output ) + , m_output_error( output_error ) + {} + + CommandAndResult( const CommandAndResult& in_command ) + : m_command( in_command.m_command ) + , m_output( in_command.m_output ) + , m_output_error( in_command.m_output_error ) + {} + + CommandAndResult() + {} + + ~CommandAndResult() + {} + + std::string m_command; ///< la commande + std::string m_output; ///< sortie standard + std::string m_output_error; ///< sortie d'erreur + }; + +protected: + EmbeddedPython m_embeddedPython; + +private: + Ui::QtPythonTerminal m_ui; +}; + +// +//#include +// +//class QtCompleterListMenu : public QMenu +//{ +// Q_OBJECT +//public: +// QtCompleterListMenu( std::list comp, QWidget* parent ); +// ~QtCompleterListMenu(){} +// +//}; +// + +#endif + diff --git a/applications/qSam/src/application/GUI/python/QtPythonTerminal.ui b/applications/qSam/src/application/GUI/python/QtPythonTerminal.ui new file mode 100644 index 00000000..3dc77cf2 --- /dev/null +++ b/applications/qSam/src/application/GUI/python/QtPythonTerminal.ui @@ -0,0 +1,57 @@ + + + QtPythonTerminal + + + + 0 + 0 + 522 + 542 + + + + Python Terminal + + + + + + Qt::Vertical + + + + Qt::NoFocus + + + true + + + background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(100, 100, 100, 255), stop:1 rgba(200, 200, 200, 255)); + + + true + + + + + QTextEdit::AutoAll + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextEditable|Qt::TextEditorInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + QtCommandInputEdit + QTextEdit +
QtCommandInputEdit.hpp
+
+
+ + +
diff --git a/applications/qSam/src/application/main.cpp b/applications/qSam/src/application/main.cpp new file mode 100644 index 00000000..e1fcae04 --- /dev/null +++ b/applications/qSam/src/application/main.cpp @@ -0,0 +1,19 @@ +#include + +#include + +#include +#include + +int main( int argc, char** argv ) +{ + QApplication qapp( argc, argv ); + + QtPythonTerminal m_PythonWindow; + m_PythonWindow.show(); + + qapp.exec(); + + return 0; +} + diff --git a/applications/qSam/src/common/python/EmbeddedPython.cpp b/applications/qSam/src/common/python/EmbeddedPython.cpp new file mode 100644 index 00000000..25c4bbca --- /dev/null +++ b/applications/qSam/src/common/python/EmbeddedPython.cpp @@ -0,0 +1,109 @@ +#include "EmbeddedPython.hpp" + +#include +#include +#include + +void EmbeddedPythonBase::initialize() +{ + Py_Initialize(); + + this->pyRun_SimpleString( + "#-*- coding: utf-8 -*- \n" // using UTF-8 encoding + "import sys\n" // + "import os\n" // + "import rlcompleter\n" // for completion + "completer = rlcompleter.Completer()\n" + ); + setvbuf( stdout, (char*) NULL, _IONBF, BUFSIZ ); +} + +void EmbeddedPythonBase::uninitialize() +{ + this->pyRun_SimpleString( "sys.stderr = 0" ); + this->pyRun_SimpleString( "sys.stdout = 0" ); + Py_Finalize(); +} + +int EmbeddedPythonBase::pyRun_SimpleString( const std::string in_command ) +{ + PyRun_SimpleString( in_command.c_str() ); + return 0; +} + +int EmbeddedPythonBase::pyRun_SimpleFile( const std::string in_fileName ) +{ + FILE* file = fopen( in_fileName.c_str(), "r" /*r+*/ ); + + PyRun_SimpleFile( file, in_fileName.c_str() ); + if( file != NULL ) + fclose( file ); // needed ? + return 0; +} + +//______________________________________________________________________________ + +void EmbeddedPython::writeLog( char* str, bool isError ) +{ + CommandAndResult& current_command = m_commands.back(); + + // std::cout << "writeLog : " << str << std::endl; + + current_command.m_output += str; + current_command.m_output += "\n"; +} + +void EmbeddedPython::writeErr( char* str ) +{ + CommandAndResult& current_command = m_commands.back(); + + // std::cout << "writeErr : " << str << std::endl; + + current_command.m_output_error += str; + current_command.m_output_error += "\n"; +} + +void EmbeddedPython::clearLog() +{ + // std::cout << "clearLog" << std::endl; +} + +std::list EmbeddedPython::getAutocompletion( const std::string in_cmd ) +{ + int n = 0; + + std::list autocompletion_list; + m_pythonRedirect.enableSimpleCommand( true ); + std::string result; + do + { + std::ostringstream run; + run << "print completer.complete('''" << in_cmd << "'''," << n << ")"; + this->pyRun_SimpleString( run.str().c_str() ); + result = m_pythonRedirect.getSimpleCommandResult(); + boost::algorithm::trim( result ); + + if( result.size() == 0 || result == "None" ) + { + break; + } + autocompletion_list.push_back( result ); + } + while( ++n < 100 ); + m_pythonRedirect.enableSimpleCommand( false ); + return autocompletion_list; +} + +/* + #completer = rlcompleter.Completer() + texte = 'p' + id = 0 + i = True + while True : + i = completer.complete( texte, id ) + id = id + 1 + if i == None : + break + print i + */ + diff --git a/applications/qSam/src/common/python/EmbeddedPython.hpp b/applications/qSam/src/common/python/EmbeddedPython.hpp new file mode 100644 index 00000000..a888a7ea --- /dev/null +++ b/applications/qSam/src/common/python/EmbeddedPython.hpp @@ -0,0 +1,150 @@ +#ifndef _EMBEDDEDPYTHON_HPP +#define _EMBEDDEDPYTHON_HPP + +#include "PythonRedirect.hpp" + +#include + +#include +#include +#include + +/** + * @brief Centralised Python interface + * + * @todo launch commands or scripts inside different python contexts + * @todo threads creation + */ +class EmbeddedPythonBase +{ + +public: + EmbeddedPythonBase() + { + initialize(); + } + + virtual ~EmbeddedPythonBase() + { + uninitialize(); + } + +private: + void initialize(); + void uninitialize(); + +public: + virtual int pyRun_SimpleString( const std::string in_command ); + virtual int pyRun_SimpleFile( const std::string in_fileName ); + // std::list auto_complete( const std::string& in_cmd ); + +}; + +//______________________________________________________________________________ + +/** + * @brief Centralised Python interface + * Save commands history + */ +class EmbeddedPython : public EmbeddedPythonBase + , public PythonOutput +{ +public: + EmbeddedPython() : EmbeddedPythonBase() + , PythonOutput() + { + m_pythonRedirect.init(); + m_pythonRedirect.addPythonOutput( this ); + } + + virtual ~EmbeddedPython() {} + +public: + int pyRun_SimpleString( const std::string in_command ) + { + m_pythonRedirect.clearSimpleCommandResult(); + m_commands.push_back( CommandAndResult( in_command ) ); + // std::cout << "command: " << m_commands.back().m_command << std::endl; + return EmbeddedPythonBase::pyRun_SimpleString( in_command ); + } + + int pyRun_SimpleFile( const std::string in_fileName ) + { + m_pythonRedirect.clearSimpleCommandResult(); + //m_commands.push_back( CommandAndResult( in_fileName ) ); // mettre le contenu du fichier ?? + return EmbeddedPythonBase::pyRun_SimpleFile( in_fileName ); + } + + std::list getAutocompletion( const std::string in_cmd ); + +public: + void addPythonOutput( PythonOutput* inout_pythonLog ) + { + m_pythonRedirect.addPythonOutput( inout_pythonLog ); + } + + void writeLog( char* str, bool isError = false ); + void writeErr( char* str ); + void clearLog(); + +public: + struct CommandAndResult + { + + CommandAndResult( std::string command, std::string output = std::string( "" ), std::string output_error = std::string( "" ) ) + : m_command( command ) + , m_output( output ) + , m_output_error( output_error ) {} + + CommandAndResult( const CommandAndResult& in_command ) + : m_command( in_command.m_command ) + , m_output( in_command.m_output ) + , m_output_error( in_command.m_output_error ) {} + + CommandAndResult() {} + + ~CommandAndResult() {} + + std::string m_command; ///< command + std::string m_output; ///< standard output + std::string m_output_error; ///< erreur output + }; + + std::list m_commands; ///< list of commands with their result + +protected: + PythonRedirect m_pythonRedirect; +}; + +/* + // sub classe pour les threads + class EmbeddedPython + { + public: + class PythonThread + { + PythonThread(){} + ~PythonThread(){} + + }; + std::vector m_threadsList; + }; + */ + +/* + class EmbeddedPythonSingleton : public EmbeddedPython, public Singleton + { + SINGLETON_BLABLA + public: + EmbeddedPythonSingleton(){}; + ~EmbeddedPythonSingleton(){}; + public: + // static EmbeddedPythonSingleton& get() + // { + // return *this; + // } + }; + */ + +#endif + diff --git a/applications/qSam/src/common/python/PythonRedirect.cpp b/applications/qSam/src/common/python/PythonRedirect.cpp new file mode 100644 index 00000000..47570306 --- /dev/null +++ b/applications/qSam/src/common/python/PythonRedirect.cpp @@ -0,0 +1,152 @@ +#include "PythonRedirect.hpp" + +#include +#include + +//#ifdef BUILD_PYTHON + +//PythonLog* PythonRedirect::m_log = NULL; + +std::vector PythonRedirect::m_pythonOutput; +bool PythonRedirect::m_python_simpleCommand = 0; +std::string PythonRedirect::m_python_simpleCommandResult; + +PythonRedirect::PythonRedirect() +{} + +PythonRedirect::~PythonRedirect() +{} + +/** + * @brief La classe qui doit etre appelee pour les differentes redirections. + */ +void PythonRedirect::addPythonOutput( PythonOutput* inout_pythonLog ) +{ + m_pythonOutput.push_back( inout_pythonLog ); +} + +/** + * @brief Initialise les redirections. + * Need Python to be intialized + */ +bool PythonRedirect::init() +{ + initLogFunctions(); + + Py_InitModule( (char*) packageName(), &m_embMethods[0] ); + + std::ostringstream importCmd; + + importCmd << + "import sys\n" + "import os\n" + "\n" + "import " << packageName() << "\n" + "\n" + // redirection des messages de python + "class Redirect:\n" + " def write(self, str):\n" + " " << packageName() << ".writeLog(str)\n" + " def isatty(self):\n" + " pass\n" + "\n" + "class Redirect_err:\n" + " def write(self, str):\n" + " " << packageName() << ".writeErr(str)\n" + " def isatty(self):\n" + " pass\n" + "\n" + "sys.stdout = Redirect()\n" + "sys.stderr = Redirect_err()\n"; + + const int retVal = PyRun_SimpleString( importCmd.str().c_str() ); + + return retVal; +} + +/** + * @brief Le nom avec lequel une librairie exportee pour Python est disponible pour cette classe. + */ +const char* PythonRedirect::packageName() const +{ + return "PythonRedirect"; + // return "myApp"; +} + +bool PythonRedirect::registerPyMethods( char* name, PyCFunction method, int flags, char* doc ) +{ + PyMethodDef newMeth; + + newMeth.ml_name = name; + newMeth.ml_meth = method; + newMeth.ml_flags = flags; + newMeth.ml_doc = doc; + + m_embMethods.push_back( newMeth ); + return true; +} + +PyObject* PythonRedirect::emb_writeLog( PyObject* /*self*/, PyObject* args ) +{ + char* str; + + PyArg_ParseTuple( args, "s", &str ); + + if( m_python_simpleCommand ) + { + m_python_simpleCommandResult += str; + } + else + { + std::vector::iterator it = m_pythonOutput.begin(); + std::vector::iterator it_end = m_pythonOutput.end(); + for(; it != it_end; it++ ) + ( *it )->writeLog( str ); + } + + return Py_BuildValue( "s", args ); +} + +PyObject* PythonRedirect::emb_writeErr( PyObject* /*self*/, PyObject* args ) +{ + char* str; + + PyArg_ParseTuple( args, "s", &str ); + + if( m_python_simpleCommand ) + { + m_python_simpleCommandResult = ""; + } + else + { + std::vector::iterator it = m_pythonOutput.begin(); + std::vector::iterator it_end = m_pythonOutput.end(); + for(; it != it_end; it++ ) + ( *it )->writeErr( str ); + } + + return Py_BuildValue( "s", args ); +} + +PyObject* PythonRedirect::emb_clearLog( PyObject* /*self*/, PyObject* /*args*/ ) +{ + std::vector::iterator it = m_pythonOutput.begin(); + std::vector::iterator it_end = m_pythonOutput.end(); + for(; it != it_end; it++ ) + ( *it )->clearLog(); + + return Py_BuildValue( "" ); +} + +bool PythonRedirect::initLogFunctions() +{ + registerPyMethods( "writeLog", emb_writeLog, METH_VARARGS, "Write the string to the Log window" ); + registerPyMethods( "writeErr", emb_writeErr, METH_VARARGS, "Write the string to the Log window" ); + registerPyMethods( "clearLog", emb_clearLog, METH_VARARGS, "Clear the Log window" ); + registerPyMethods( NULL, NULL, 0, NULL ); + + return true; +} + +//#endif + diff --git a/applications/qSam/src/common/python/PythonRedirect.hpp b/applications/qSam/src/common/python/PythonRedirect.hpp new file mode 100644 index 00000000..cd73f52b --- /dev/null +++ b/applications/qSam/src/common/python/PythonRedirect.hpp @@ -0,0 +1,68 @@ +#ifndef _PYTHONREDIRECT_HPP_ +#define _PYTHONREDIRECT_HPP_ + +#define BUILD_PYTHON 1 + +#ifdef BUILD_PYTHON + #include +#endif + +#include +#include + +class PythonOutput +{ +public: + PythonOutput() {} + virtual ~PythonOutput() {} + +public: + virtual void writeLog( char* str, bool isError = false ) = 0; + virtual void writeErr( char* str ) = 0; + virtual void clearLog() = 0; +}; + +/** + * @brief Redirect python outputs into PythonLog subclasses + */ +class PythonRedirect +{ +public: + PythonRedirect(); + virtual ~PythonRedirect(); + + //#ifdef BUILD_PYTHON + bool init(); + #ifndef SWIG + void addPythonOutput( PythonOutput* inout_pythonLog ); + inline void enableSimpleCommand( bool b ) { m_python_simpleCommand = b; } + inline void clearSimpleCommandResult() { m_python_simpleCommandResult = ""; } + inline std::string getSimpleCommandResult() const { return m_python_simpleCommandResult; } + #endif + +protected: + #ifndef SWIG + virtual bool initLogFunctions(); + virtual const char* packageName() const; + + bool registerPyMethods( char* name, PyCFunction method, int flag, char* doc ); + + static PyObject* emb_writeLog( PyObject* self, PyObject* args ); + static PyObject* emb_writeErr( PyObject* self, PyObject* args ); + static PyObject* emb_clearLog( PyObject* self, PyObject* args ); + #endif + +protected: + #ifndef SWIG + static std::vector m_pythonOutput; + + static bool m_python_simpleCommand; + static std::string m_python_simpleCommandResult; + + std::vector m_embMethods; + #endif + //#endif +}; + +#endif + diff --git a/applications/qSam/src/common/python/PythonRedirect.i b/applications/qSam/src/common/python/PythonRedirect.i new file mode 100644 index 00000000..a94635bd --- /dev/null +++ b/applications/qSam/src/common/python/PythonRedirect.i @@ -0,0 +1,10 @@ +%include +%include + +%module PythonRedirect +%{ +#include +%} + +%include +