A new interpreter PyConsole_EnhInterp has also been created for this purpose and the LightApp_PyInterp and SalomeApp_PyInterp have been tweaked accordingly.
#ifndef DISABLE_PYCONSOLE
else if ( flag == WT_PyConsole )
{
- PyConsole_Console* pyCons = new PyConsole_Console( desktop(),new LightApp_PyInterp());
+ PyConsole_Console* pyCons = new PyConsole_EnhConsole( desktop(),new LightApp_PyInterp());
pyCons->setWindowTitle( tr( "PYTHON_CONSOLE" ) );
pyCons->setFont(resourceMgr()->fontValue( "PyConsole", "font" ));
pyCons->setIsShowBanner(resourceMgr()->booleanValue( "PyConsole", "show_banner", true ));
* calls initialize method defined in base class, which calls virtual methods
* initstate & initcontext redefined here.
*/
-LightApp_PyInterp::LightApp_PyInterp(): PyConsole_Interp()
+LightApp_PyInterp::LightApp_PyInterp(): PyConsole_EnhInterp()
{
}
#ifndef _LIGHTAPP_PYINTERP_H_
#define _LIGHTAPP_PYINTERP_H_
-#include <PyConsole_Interp.h> // this include must be first (see PyInterp_base.h)!
+#include <PyConsole_EnhInterp.h> // this include must be first (see PyInterp_base.h)!
-class LightApp_PyInterp : public PyConsole_Interp
+class LightApp_PyInterp : public PyConsole_EnhInterp
{
public:
LightApp_PyInterp();
SET(GUI_HEADERS
PyConsole_Editor.h
+ PyConsole_EnhEditor.h
PyConsole_Console.h
)
QT4_WRAP_CPP(GUI_HEADERS_MOC ${GUI_HEADERS})
SET(PyConsole_SOURCES
PyConsole_Console.cxx
PyConsole_Editor.cxx
+ PyConsole_EnhEditor.cxx
PyConsole_Interp.cxx
+ PyConsole_EnhInterp.cxx
PyConsole_Event.cxx
PyConsole_Request.cxx
)
PyConsole.h
PyConsole_Console.h
PyConsole_Editor.h
+ PyConsole_EnhEditor.h
PyConsole_Interp.h
+ PyConsole_EnhInterp.h
PyConsole_Event.h
PyConsole_Request.h
)
#include "PyConsole_Interp.h" /// !!! WARNING !!! THIS INCLUDE MUST BE VERY FIRST !!!
#include "PyConsole_Console.h"
-#include "PyConsole_Editor.h"
+#include "PyConsole_EnhEditor.h"
#include <Qtx.h>
\param interp python interpreter
*/
PyConsole_Console::PyConsole_Console( QWidget* parent, PyConsole_Interp* interp )
-: QWidget( parent ),
- myEditor( 0 )
+: QWidget( parent )
{
// create python interpreter
myInterp = interp;
createActions();
}
+/**
+ * Protected constructor.
+ */
+PyConsole_Console::PyConsole_Console( QWidget* parent, PyConsole_Interp* i, PyConsole_Editor* e)
+ : QWidget (parent), myEditor(e), myInterp(i)
+{}
+
/*!
\brief Destructor.
myActions[PasteId]->setEnabled( !myEditor->isReadOnly() && !QApplication::clipboard()->text().isEmpty() );
myActions[SelectAllId]->setEnabled( !myEditor->document()->isEmpty() );
}
+
+/**
+ * Similar to constructor of the base class but using enhanced objects.
+ * TODO: this should really be done in a factory to avoid code duplication.
+ * @param parent
+ * @param interp
+ */
+PyConsole_EnhConsole::PyConsole_EnhConsole( QWidget* parent, PyConsole_EnhInterp* interp)
+ : PyConsole_Console(parent, interp, 0)
+{
+ // create python interpreter
+ myInterp = interp;
+ if ( !myInterp )
+ myInterp = new PyConsole_EnhInterp();
+
+ // initialize Python interpretator
+ myInterp->initialize();
+
+ // create editor console
+ QVBoxLayout* lay = new QVBoxLayout( this );
+ lay->setMargin( 0 );
+ myEditor = new PyConsole_EnhEditor( static_cast<PyConsole_EnhInterp*>(myInterp), this );
+ char* synchronous = getenv("PYTHON_CONSOLE_SYNC");
+ if (synchronous && atoi(synchronous))
+ {
+ myEditor->setIsSync(true);
+ }
+ myEditor->viewport()->installEventFilter( this );
+ lay->addWidget( myEditor );
+
+ createActions();
+}
#include "PyConsole.h"
+#include "PyConsole_EnhInterp.h"
#include <SUIT_PopupClient.h>
#include <QWidget>
#include <QMap>
void setMenuActions( const int );
int menuActions() const;
-private:
+protected:
void createActions();
void updateActions();
-private:
+ PyConsole_Console( QWidget* parent, PyConsole_Interp*, PyConsole_Editor*);
+
+
PyConsole_Interp* myInterp; //!< python interpreter
PyConsole_Editor* myEditor; //!< python console editor widget
QMap<int, QAction*> myActions; //!< menu actions list
};
+/**
+ * Enhance console object providing auto-completion.
+ * Similar to PyConsole_Console except that an enhanced interpreter and enhanced editor
+ * are encapsulated.
+ */
+class PYCONSOLE_EXPORT PyConsole_EnhConsole: public PyConsole_Console
+{
+ Q_OBJECT
+
+public:
+ PyConsole_EnhConsole( QWidget* parent, PyConsole_EnhInterp* interp = 0);
+ virtual ~PyConsole_EnhConsole() {}
+};
+
#endif // PYCONSOLE_CONSOLE_H
--- /dev/null
+// Copyright (C) 2007-2013 CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// Author : Adrien Bruneton (CEA/DEN)
+// Created on: 4 avr. 2013
+
+#include "PyConsole.h"
+#include <Python.h>
+
+#include <QKeyEvent>
+#include <QTextBlock>
+#include <QTextCursor>
+#include <QTextCharFormat>
+#include <QRegExp>
+
+#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<QString> PyConsole_EnhEditor::SEPARATORS = \
+ std::vector<QString>(tmp_a, tmp_a + sizeof(tmp_a)/sizeof(tmp_a[0]));
+
+PyConsole_EnhEditor::PyConsole_EnhEditor(PyConsole_EnhInterp * interp, QWidget * parent) :
+ PyConsole_Editor(interp, parent),
+ _tab_mode(false),
+ _cursor_pos(-1)
+{
+ document()->setUndoRedoEnabled(true);
+}
+
+void PyConsole_EnhEditor::keyPressEvent ( QKeyEvent* event)
+{
+ // check if <Ctrl> is pressed
+ bool ctrlPressed = event->modifiers() & Qt::ControlModifier;
+ // check if <Shift> 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 (!ctrlPressed)
+ {
+ clearCompletion();
+ _cursor_pos = -1;
+ }
+ PyConsole_Editor::keyPressEvent(event);
+ }
+}
+
+void PyConsole_EnhEditor::mousePressEvent(QMouseEvent* e)
+{
+ clearCompletion();
+ _cursor_pos = -1;
+ PyConsole_Editor::mousePressEvent(e);
+}
+
+void PyConsole_EnhEditor::clearCompletion()
+{
+ // Delete completion text if present
+ if (_tab_mode)
+ {
+ // Remove completion display
+ document()->undo();
+ // Remove trailing line return:
+ QTextCursor tc(textCursor());
+ tc.setPosition(document()->characterCount()-1);
+ setTextCursor(tc);
+ textCursor().deletePreviousChar();
+ // TODO: before wait for any TAB event to be completed
+ static_cast<PyConsole_EnhInterp *>(myInterp)->clearCompletion();
+ }
+ _tab_mode = false;
+}
+
+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);
+}
+
+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;
+}
+
+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<QString>::const_iterator i = SEPARATORS.begin(); i != SEPARATORS.end(); i++)
+ {
+ int j = input2.lastIndexOf(*i);
+ if (j > lastSp)
+ lastSp = j;
+ }
+ if (lastSp >= 0)
+ input2 = input.mid(lastSp+1);
+
+ // Detect a qualified name (with a point)
+ int lastPt = input2.lastIndexOf(QString("."));
+
+ // Split the 2 surrounding parts of the qualified name
+ if (lastPt != -1)
+ {
+ _compl_before_point = input2.left(lastPt);
+ _compl_after_point = input2.mid(lastPt+1);
+ }
+ else
+ {
+ // No point found - do a global matching -
+ // (the following will call dir() with an empty string)
+ _compl_after_point = input2;
+ _compl_before_point = QString("");
+ }
+
+ return new CompletionCommand( static_cast<PyConsole_EnhInterp *>(myInterp), _compl_before_point,
+ _compl_after_point, this, isSync() );
+}
+
+/**
+ * Format completion results - this is where we should create 3 columns etc ...
+ * @param matches list of possible completions
+ * @param result return value
+ */
+void PyConsole_EnhEditor::formatCompletion(const std::vector<QString> & 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");
+ }
+}
+
+void PyConsole_EnhEditor::customEvent( QEvent* event )
+{
+ std::vector<QString> matches;
+ QString first_match, comple_text, doc, base;
+ QTextCursor cursor(textCursor());
+ QTextBlockFormat bf;
+ QTextCharFormat cf;
+ PyConsole_EnhInterp * interp = static_cast<PyConsole_EnhInterp *>(myInterp);
+
+ switch( event->type() )
+ {
+ case PyInterp_Event::ES_TAB_COMPLETE_OK:
+ // Extract corresponding matches from the interpreter
+ matches = interp->getLastMatches();
+
+ if (matches.size() == 0)
+ {
+ // Completion successful but nothing returned.
+ _tab_mode = false;
+ _cursor_pos = -1;
+ return;
+ }
+
+ // Only one match - complete directly and update doc string window
+ doc = interp->getDocStr();
+ if (matches.size() == 1)
+ {
+ first_match = matches[0].mid(_compl_after_point.size());
+ cursor.insertText(first_match);
+ _tab_mode = false;
+ if (doc == QString(""))
+ emit updateDoc(formatDocHTML("(no documentation available)\n"));
+ else
+ emit updateDoc(formatDocHTML(doc));
+ }
+ else
+ {
+ // Detect if there is a common base to all available completion
+ // In this case append this base to the text already
+ extractCommon(matches, base);
+ first_match = base.mid(_compl_after_point.size());
+ cursor.insertText(first_match);
+
+ // If this happens to match exaclty the first completion
+ // also provide doc
+ if (base == matches[0])
+ {
+ doc = formatDocHTML(doc);
+ emit updateDoc(doc);
+ }
+
+ // Print all matching completion in a "undo-able" block
+ 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();
+ }
+ break;
+ case PyInterp_Event::ES_TAB_COMPLETE_ERR:
+ // Tab completion was unsuccessful, switch off mode:
+ _tab_mode = false;
+ _cursor_pos = -1;
+ break;
+// case PyEnhInterp_Event::ES_MULTI_PASTE:
+// multilinePaste();
+// break;
+ default:
+ PyConsole_Editor::customEvent( event );
+ break;
+ }
+}
+
+/**
+ * Extract the common leading part of all strings in matches.
+ * @param matches
+ * @param result
+ */
+void PyConsole_EnhEditor::extractCommon(const std::vector<QString> & 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++;
+ }
+}
+
+QString PyConsole_EnhEditor::formatDocHTML(const QString & doc) const
+{
+ QString templ = QString("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" ") +
+ QString(" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n ") +
+ QString("<html><head><meta name=\"qrichtext\" content=\"1\" /> ") +
+ QString("<style type=\"text/css\">\np, li { white-space: pre-wrap; }\n</style> ") +
+ QString("</head><body style=\" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;\">\n") +
+ QString("<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">") +
+ QString("<span style=\" font-weight:600; color:#0000ff;\">%1</span></p>") +
+ QString("<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">%2</p>") +
+ QString("</body></html>");
+
+ 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);
+}
--- /dev/null
+// Copyright (C) 2007-2013 CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// Author : Adrien Bruneton (CEA/DEN)
+// Created on: 4 avr. 2013
+
+#ifndef PYCONSOLE_ENHEDITOR_H_
+#define PYCONSOLE_ENHEDITOR_H_
+
+#include "PyConsole.h"
+
+#include "PyConsole_Editor.h"
+#include <QObject>
+
+class PyConsole_EnhInterp;
+
+/**
+ * Enhanced Python editor handling tab completion.
+ */
+class PyConsole_EnhEditor: public PyConsole_Editor
+{
+ Q_OBJECT;
+
+public:
+ PyConsole_EnhEditor(PyConsole_EnhInterp * interp, QWidget * parent=0);
+ virtual ~PyConsole_EnhEditor() {}
+
+signals:
+ /**
+ * Signal emitted by the editor widget when the doc string should be updated.
+ * @param doc a HTML block with the formatted doc string.
+ * TODO: for now this signal is left uncaught.
+ */
+ void updateDoc(QString doc);
+
+protected:
+ /** List of separators identifying the last parsable token for completion */
+ static const std::vector<QString> 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 <TAB> is hit */
+ int _cursor_pos;
+
+// std::stack<QString> _multi_line_content;
+
+ // Overrides:
+ virtual void keyPressEvent ( QKeyEvent* event);
+ virtual void customEvent( QEvent* event);
+ virtual void mousePressEvent( QMouseEvent* event );
+// virtual void insertFromMimeData(const QMimeData * source);
+
+ virtual PyInterp_Request* createTabRequest( const QString& input );
+ virtual void handleTab();
+ virtual void handleBackTab();
+ virtual void clearCompletion();
+ virtual void formatCompletion(const std::vector<QString> & matches, QString & result) const;
+ virtual QString formatDocHTML(const QString & doc) const;
+// virtual void multilinePaste();
+
+private:
+ void extractCommon(const std::vector<QString> & matches, QString & result) const;
+
+};
+
+#endif /* PYCONSOLE_ENHEDITOR_H_ */
--- /dev/null
+// Copyright (C) 2007-2013 CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// Author : Adrien Bruneton (CEA/DEN)
+// Created on: 4 avr. 2013
+
+
+#include "PyConsole.h"
+
+#include "PyConsole_EnhInterp.h"
+
+#include <pythonrun.h>
+#include <string>
+#include <QRegExp>
+
+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<QString> PyConsole_EnhInterp::PYTHON_KEYWORDS = \
+ std::vector<QString>(tmp_k, tmp_k+sizeof(tmp_k)/sizeof(tmp_k[0]));
+
+/*!
+ \brief Run Python dir() command and saves the result internally in _lastPy
+ \param dirArgument Python expression to pass to the dir command. The parsing of what the
+ user actually started typing is dedicated to the caller
+ \param startMatch string representing the begining of the patter to be completed. For example when
+ the user types "a_string_variable.rsp <TAB>", this is "rsp".
+ \return command exit status - 0 = success
+*/
+int PyConsole_EnhInterp::runDirCommand(const QString & dirArgument, const QString & startMatch)
+{
+ int ret;
+ std::vector<QString> 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<QString>::const_iterator it = PYTHON_KEYWORDS.begin(); it != PYTHON_KEYWORDS.end(); it++)
+ if ((*it).startsWith(startMatch))
+ _last_matches.push_back(*it);
+ }
+
+ // Try to get doc string of the first match
+ if (_last_matches.size() > 0)
+ {
+ QString cmd("");
+ if (dirArgument.trimmed() != "")
+ cmd = dirArgument + ".";
+ cmd += _last_matches[0] + ".__doc__";
+ PyObject * str = PyRun_String(cmd.toStdString().c_str(), Py_eval_input, _g, _g);
+ if (!str || str == Py_None || !PyString_Check(str))
+ {
+ if (!str)
+ PyErr_Clear();
+ _doc_str = "";
+ }
+ else
+ _doc_str = QString(PyString_AsString(str));
+ Py_XDECREF(str);
+ }
+
+ // The command has been successfully executed
+ return 0;
+}
+
+/**
+ * See runDirCommand().
+ * @param dirArgument see runDirCommand()
+ * @param startMatch see runDirCommand()
+ * @param[out] result the list of matches
+ * @param discardSwig if true a regular expression is used to discard all static method generated
+ * by SWIG. typically: MEDCouplingUMesh_Blabla
+ * @return -1 if the call to dir() or the parsing of the result failed, 0 otherwise.
+ */
+int PyConsole_EnhInterp::runDirAndExtract(const QString& dirArgument,
+ const QString & startMatch, std::vector<QString> & result,
+ bool discardSwig) const
+{
+ QRegExp re("^[A-Z].+_[A-Z]+[a-z]+.+$"); // discard SWIG static method, e.g. MEDCouplingUMesh_Blabla
+ QString command("dir(" + dirArgument + ")");
+ PyObject * plst = PyRun_String(command.toStdString().c_str(), Py_eval_input, _g, _g);
+ if(!plst || plst == Py_None) {
+ if(!plst)
+ PyErr_Clear();
+
+ Py_XDECREF(plst);
+ return -1;
+ }
+
+ // Extract the returned list and convert it to a vector<>
+ if (!PySequence_Check(plst))
+ {
+ // Should never happen ...
+ //std::cerr << "not a list!" << std::endl;
+ Py_XDECREF(plst);
+ return -1;
+ }
+
+ // Convert plst to a vector of QString
+ int n = PySequence_Length(plst);
+ for (int i = 0; i < n; i++)
+ {
+ PyObject * it;
+ it = PySequence_GetItem(plst, i);
+ QString s(PyString_AsString(it));
+ // if the method is not from swig, not static (guessed from the reg exp) and matches
+ // what is already there
+ if (s.startsWith(startMatch))
+ if(!discardSwig || (!re.exactMatch(s) && !s.contains("swig")))
+ result.push_back(s);
+ Py_DECREF(it);
+ }
+ Py_DECREF(plst);
+
+ return 0;
+}
+
+/**
+ * Clear internal members containing the last completion results.
+ */
+void PyConsole_EnhInterp::clearCompletion()
+{
+ _last_matches.resize(0);
+ _doc_str = QString("");
+}
+
+
+
--- /dev/null
+// Copyright (C) 2007-2013 CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// Author : Adrien Bruneton (CEA/DEN)
+// Created on: 4 avr. 2013
+
+
+#ifndef PYCONSOLE_ENHINTERP_H_
+#define PYCONSOLE_ENHINTERP_H_
+
+#include "PyConsole.h"
+
+#include <Python.h>
+#include "PyConsole_Interp.h"
+
+#include <vector>
+#include <QString>
+
+/**
+ * Enhanced Python interpreter used for auto-completion.
+ * This extends PyConsole_Interp with an API wrapping the Python dir() command nicely.
+ */
+class PyConsole_EnhInterp: public PyConsole_Interp
+{
+public:
+ PyConsole_EnhInterp()
+ : PyConsole_Interp(), _last_matches(0), _doc_str("")
+ {}
+
+ virtual ~PyConsole_EnhInterp() {}
+
+ const std::vector<QString>& getLastMatches() const { return _last_matches; }
+ const QString & getDocStr() const { return _doc_str; }
+
+ virtual int runDirCommand(const QString& dirArgument, const QString& startMatch);
+ virtual void clearCompletion();
+
+protected:
+ /** Hard coded list of Python keywords */
+ static const std::vector<QString> PYTHON_KEYWORDS;
+
+ /** Last computed matches */
+ std::vector<QString> _last_matches;
+ /** Doc string of the first match - when only one match it will be displayed by the Editor*/
+ QString _doc_str;
+
+ virtual int runDirAndExtract(const QString& dirArgument, const QString & startMatch,
+ std::vector<QString> & result, bool discardSwig=true) const;
+
+};
+
+#endif /* PYCONSOLE_ENHINTERP_H_ */
#ifndef PYCONSOLE_EVENT_H
#define PYCONSOLE_EVENT_H
+#include "PyConsole.h"
+
#include <QEvent>
#include <QString>
/*!
\brief Constructor
\param c message text (python trace)
+ \param isError default to false - if true indicates that an error is being printed.
*/
PrintEvent( const char* c, bool isError = false) :
QEvent( (QEvent::Type)EVENT_ID ), myText( c ), errorFlag(isError)
\return message text (python trace)
*/
QString text() const { return myText; }
+
+ /**
+ * @return true if this is an error message
+ */
bool isError() const { return errorFlag; }
protected:
#include "PyInterp_Event.h"
#include "PyConsole_Event.h"
-#include "PyInterp_Interp.h"
-#include "PyConsole_Editor.h"
+#include "PyConsole_EnhInterp.h"
+#include "PyConsole_EnhEditor.h"
#include <QCoreApplication>
}
}
-QEvent* ExecCommand::createEvent() const
+QEvent* ExecCommand::createEvent()
{
if ( IsSync() )
QCoreApplication::sendPostedEvents( listener(), PrintEvent::EVENT_ID );
- return new PyInterp_Event( myState, (PyInterp_Request*)this );
+ return new PyInterp_Event( myState, this );
}
+
+
+
+CompletionCommand::CompletionCommand( PyConsole_EnhInterp* theInterp,
+ const QString& input,
+ const QString& startMatch,
+ PyConsole_EnhEditor* theListener,
+ bool sync)
+ : PyInterp_LockRequest( theInterp, theListener, sync ),
+ _tabSuccess(false), _dirArg(input), _startMatch(startMatch)
+{}
+
+void CompletionCommand::execute()
+{
+ PyConsole_EnhInterp * interp = static_cast<PyConsole_EnhInterp *>(getInterp());
+ int ret = interp->runDirCommand( _dirArg, _startMatch);
+ if (ret == 0)
+ _tabSuccess = true;
+ else
+ _tabSuccess = false;
+}
+
+QEvent* CompletionCommand::createEvent()
+{
+ int typ = _tabSuccess ? PyInterp_Event::ES_TAB_COMPLETE_OK : PyInterp_Event::ES_TAB_COMPLETE_ERR;
+
+ return new PyInterp_Event( typ, this);
+}
+
+
+
#ifndef PYCONSOLE_REQUEST_H_
#define PYCONSOLE_REQUEST_H_
+#include "PyConsole.h"
#include "PyInterp_Request.h"
+#include <vector>
#include <QString>
#include <QEvent>
\brief Create and return a notification event.
\return new notification event
*/
- virtual QEvent* createEvent() const;
+ virtual QEvent* createEvent();
private:
QString myCommand; //!< Python command
int myState; //!< Python command execution status
};
+class PyConsole_EnhInterp;
+class PyConsole_EnhEditor;
+
+class CompletionCommand : public PyInterp_LockRequest
+{
+public:
+ /*!
+ 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( PyConsole_EnhInterp* theInterp,
+ const QString& input,
+ const QString& startMatch,
+ PyConsole_EnhEditor* theListener,
+ bool sync = false );
+
+
+protected:
+ /** List of separators identifying the last parsable token for completion */
+ static const std::vector<QString> SEPARATORS;
+
+ QString _dirArg;
+ QString _startMatch;
+ bool _tabSuccess;
+
+ virtual void execute();
+ virtual QEvent* createEvent();
+
+};
#endif /* PYCONSOLE_REQUEST_H_ */
delete request;
}
-QEvent* PyInterp_Request::createEvent() const
+QEvent* PyInterp_Request::createEvent()
{
- return new PyInterp_Event( PyInterp_Event::ES_NOTIFY, (PyInterp_Request*)this );
+ return new PyInterp_Event( PyInterp_Event::ES_NOTIFY, this );
}
void PyInterp_Request::processEvent( QObject* o )
public:
//Execution state
- enum { ES_NOTIFY = QEvent::User + 5000, ES_OK, ES_ERROR, ES_INCOMPLETE, ES_LAST };
+ enum { ES_NOTIFY = QEvent::User + 5000, ES_OK, ES_ERROR, ES_INCOMPLETE,
+ ES_TAB_COMPLETE_OK, ES_TAB_COMPLETE_ERR, ES_LAST };
PyInterp_Event( int type, PyInterp_Request* request )
: QEvent( (QEvent::Type)type ), myRequest( request ) {}
#include "PyInterp_Request.h"
-/*!
- \class ExecCommand
- \brief Python command execution request.
- \internal
-*/
-class ExecCommand : public PyInterp_LockRequest
-{
-public:
- /*!
- \brief Constructor.
-
- Creates new python command execution request.
- \param theInterp python interpreter
- \param theCommand python command
- \param theListener widget to get the notification messages
- \param sync if True the request is processed synchronously
- */
- ExecCommand( PyInterp_Interp* theInterp,
- const QString& theCommand,
- PyConsole_Editor* theListener,
- bool sync = false );
-
-protected:
- /*!
- \brief Execute the python command in the interpreter and
- get its execution status.
- */
- virtual void execute();
-
- /*!
- \brief Create and return a notification event.
- \return new notification event
- */
- virtual QEvent* createEvent() const;
-
- QString myCommand; //!< Python command
- int myState; //!< Python command execution status
-};
-
virtual void execute() = 0;
// Should be redefined in successors, contains actual request code
- virtual QEvent* createEvent() const;
+ virtual QEvent* createEvent();
// This method can be overridden to customize notification event creation
virtual void processEvent( QObject* );
}
else if ( flag == WT_PyConsole )
{
- PyConsole_Console* pyCons = new PyConsole_Console( desktop(), new SalomeApp_PyInterp() );
+ PyConsole_Console* pyCons = new PyConsole_EnhConsole( desktop(), new SalomeApp_PyInterp() );
pyCons->setWindowTitle( tr( "PYTHON_CONSOLE" ) );
pyCons->setFont(resourceMgr()->fontValue( "PyConsole", "font" ));
pyCons->setIsShowBanner(resourceMgr()->booleanValue( "PyConsole", "show_banner", true ));
* initstate & initcontext redefined here.
*/
SalomeApp_PyInterp::SalomeApp_PyInterp():
- PyConsole_Interp(), myFirstRun( true )
+ PyConsole_EnhInterp(), myFirstRun( true )
{
}
#ifndef _SalomeApp_PYINTERP_H_
#define _SalomeApp_PYINTERP_H_
-#include <PyConsole_Interp.h> // this include must be first (see PyInterp_base.h)!
+#include <PyConsole_EnhInterp.h> // this include must be first (see PyInterp_base.h)!
-class SalomeApp_PyInterp : public PyConsole_Interp
+class SalomeApp_PyInterp : public PyConsole_EnhInterp
{
public:
SalomeApp_PyInterp();