ctkPythonShell.cpp

Go to the documentation of this file.
00001 /*=========================================================================
00002 
00003   Library:   CTK
00004  
00005   Copyright (c) 2010  Kitware Inc.
00006 
00007   Licensed under the Apache License, Version 2.0 (the "License");
00008   you may not use this file except in compliance with the License.
00009   You may obtain a copy of the License at
00010 
00011       http://www.commontk.org/LICENSE
00012 
00013   Unless required by applicable law or agreed to in writing, software
00014   distributed under the License is distributed on an "AS IS" BASIS,
00015   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00016   See the License for the specific language governing permissions and
00017   limitations under the License.
00018  
00019 =========================================================================*/
00020 /*=========================================================================
00021 
00022    Program: ParaView
00023    Module:    $RCSfile$
00024 
00025    Copyright (c) 2005-2008 Sandia Corporation, Kitware Inc.
00026    All rights reserved.
00027 
00028    ParaView is a free software; you can redistribute it and/or modify it
00029    under the terms of the ParaView license version 1.2. 
00030 
00031    See License_v1.2.txt for the full ParaView license.
00032    A copy of this license can be obtained by contacting
00033    Kitware Inc.
00034    28 Corporate Drive
00035    Clifton Park, NY 12065
00036    USA
00037 
00038 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00039 ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00040 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
00041 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR
00042 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00043 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00044 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00045 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00046 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00047 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00048 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00049 
00050 =========================================================================*/
00051 
00052 //#include <vtkPython.h> // python first
00053 
00054 // Qt includes
00055 #include <QCoreApplication>
00056 #include <QResizeEvent>
00057 #include <QScrollBar>
00058 #include <QStringListModel>
00059 #include <QTextCharFormat>
00060 #include <QVBoxLayout>
00061 
00062 // PythonQt includes
00063 #include <PythonQt.h>
00064 #include <PythonQtObjectPtr.h>
00065 
00066 // CTK includes
00067 #include <ctkConsoleWidget.h>
00068 #include <ctkAbstractPythonManager.h>
00069 #include "ctkPythonShell.h"
00070 
00071 //----------------------------------------------------------------------------
00072 class ctkPythonShellCompleter : public ctkConsoleWidgetCompleter
00073 {
00074 public:
00075   ctkPythonShellCompleter(ctkPythonShell& p) : Parent(p)
00076     {
00077     this->setParent(&p);
00078     }
00079 
00080   virtual void updateCompletionModel(const QString& completion)
00081     {
00082     // Start by clearing the model
00083     this->setModel(0);
00084 
00085     // Don't try to complete the empty string
00086     if (completion.isEmpty())
00087       {
00088       return;
00089       }
00090 
00091     // Search backward through the string for usable characters
00092     QString textToComplete;
00093     for (int i = completion.length()-1; i >= 0; --i)
00094       {
00095       QChar c = completion.at(i);
00096       if (c.isLetterOrNumber() || c == '.' || c == '_')
00097         {
00098         textToComplete.prepend(c);
00099         }
00100       else
00101         {
00102         break;
00103         }
00104       }
00105 
00106     // Split the string at the last dot, if one exists
00107     QString lookup;
00108     QString compareText = textToComplete;
00109     int dot = compareText.lastIndexOf('.');
00110     if (dot != -1)
00111       {
00112       lookup = compareText.mid(0, dot);
00113       compareText = compareText.mid(dot+1);
00114       }
00115 
00116     // Lookup python names
00117     QStringList attrs;
00118     if (!lookup.isEmpty() || !compareText.isEmpty())
00119       {
00120       attrs = Parent.getPythonAttributes(lookup);
00121       }
00122 
00123     // Initialize the completion model
00124     if (!attrs.isEmpty())
00125       {
00126       this->setCompletionMode(QCompleter::PopupCompletion);
00127       this->setModel(new QStringListModel(attrs, this));
00128       this->setCaseSensitivity(Qt::CaseInsensitive);
00129       this->setCompletionPrefix(compareText.toLower());
00130       this->popup()->setCurrentIndex(this->completionModel()->index(0, 0));
00131       }
00132     }
00133   ctkPythonShell& Parent;
00134 };
00135 
00136 
00138 // ctkPythonShell::pqImplementation
00139 
00140 struct ctkPythonShell::pqImplementation
00141 {
00142   pqImplementation(QWidget* _parent, ctkAbstractPythonManager* pythonManager)
00143     : Console(_parent), PythonManager(pythonManager)
00144   {
00145   }
00146 
00147 //----------------------------------------------------------------------------
00148 //   void initialize(int argc, char* argv[])
00149 //   {
00150 //     // Setup Python's interactive prompts
00151 //     PyObject* ps1 = PySys_GetObject(const_cast<char*>("ps1"));
00152 //     if(!ps1)
00153 //       {
00154 //       PySys_SetObject(const_cast<char*>("ps1"), ps1 = PyString_FromString(">>> "));
00155 //       Py_XDECREF(ps1);
00156 //       }
00157 // 
00158 //     PyObject* ps2 = PySys_GetObject(const_cast<char*>("ps2"));
00159 //     if(!ps2)
00160 //       {
00161 //       PySys_SetObject(const_cast<char*>("ps2"), ps2 = PyString_FromString("... "));
00162 //       Py_XDECREF(ps2);
00163 //       }
00164 //     this->MultilineStatement = false;
00165 //   }
00166 
00167 //----------------------------------------------------------------------------
00168   ~pqImplementation()
00169   {
00170 //     this->destroyInterpretor();
00171   }
00172 
00173 //----------------------------------------------------------------------------
00174 //   void destroyInterpretor()
00175 //     {
00176 //     if (this->Interpreter)
00177 //       {
00178 //       QTextCharFormat format = this->Console.getFormat();
00179 //       format.setForeground(QColor(255, 0, 0));
00180 //       this->Console.setFormat(format);
00181 //       this->Console.printString("\n... restarting ...\n");
00182 //       format.setForeground(QColor(0, 0, 0));
00183 //       this->Console.setFormat(format);
00184 // 
00185 //       this->Interpreter->MakeCurrent();
00186 // 
00187 //       // Restore Python's original stdout and stderr
00188 //       PySys_SetObject(const_cast<char*>("stdout"), PySys_GetObject(const_cast<char*>("__stdout__")));
00189 //       PySys_SetObject(const_cast<char*>("stderr"), PySys_GetObject(const_cast<char*>("__stderr__")));
00190 //       this->Interpreter->ReleaseControl();
00191 //       this->Interpreter->Delete();
00192 //       }
00193 //     this->Interpreter = 0;
00194 //     }
00195 
00196 //----------------------------------------------------------------------------
00197   void executeCommand(const QString& command)
00198   {
00199 //     this->MultilineStatement = 
00200 //       this->Interpreter->Push(Command.toAscii().data());
00201     if (command.length())
00202       {
00203       Q_ASSERT(this->PythonManager);
00204       this->PythonManager->executeString(command);
00205       }
00206   }
00207   
00208 //----------------------------------------------------------------------------
00209   void promptForInput(const QString& indent=QString())
00210   {
00211     QTextCharFormat format = this->Console.getFormat();
00212     format.setForeground(QColor(0, 0, 0));
00213     this->Console.setFormat(format);
00214 
00215 //     this->Interpreter->MakeCurrent();
00216     if(!this->MultilineStatement)
00217       {
00218       this->Console.prompt(">>> ");
00219       //this->Console.prompt(
00220       //  PyString_AsString(PySys_GetObject(const_cast<char*>("ps1"))));
00221       }
00222     else
00223       {
00224       this->Console.prompt("... ");
00225       //this->Console.prompt(
00226       //  PyString_AsString(PySys_GetObject(const_cast<char*>("ps2"))));
00227       }
00228     this->Console.printCommand(indent);
00229 //     this->Interpreter->ReleaseControl();
00230   }
00231 
00234   ctkConsoleWidget Console;
00235 
00236   ctkAbstractPythonManager* PythonManager;
00237 
00239   bool MultilineStatement;
00240 };
00241 
00243 // ctkPythonShell
00244 
00245 //----------------------------------------------------------------------------
00246 ctkPythonShell::ctkPythonShell(ctkAbstractPythonManager* pythonManager, QWidget* _parent):
00247   Superclass(_parent),
00248   Implementation(new pqImplementation(this, pythonManager))
00249 {
00250   // Layout UI
00251   QVBoxLayout* const boxLayout = new QVBoxLayout(this);
00252   boxLayout->setMargin(0);
00253   boxLayout->addWidget(&this->Implementation->Console);
00254 
00255   this->setObjectName("pythonShell");
00256 
00257   ctkPythonShellCompleter* completer = new ctkPythonShellCompleter(*this);
00258   this->Implementation->Console.setCompleter(completer);
00259   
00260   QObject::connect(
00261     &this->Implementation->Console, SIGNAL(executeCommand(const QString&)), 
00262     this, SLOT(onExecuteCommand(const QString&)));
00263 
00264   // The call to mainContext() ensures that python has been initialized.
00265   Q_ASSERT(this->Implementation->PythonManager);
00266   this->Implementation->PythonManager->mainContext();
00267 
00268   QTextCharFormat format = this->Implementation->Console.getFormat();
00269   format.setForeground(QColor(0, 0, 255));
00270   this->Implementation->Console.setFormat(format);
00271   this->Implementation->Console.printString(
00272     QString("Python %1 on %2\n").arg(Py_GetVersion()).arg(Py_GetPlatform()));
00273   this->promptForInput();
00274 
00275   Q_ASSERT(PythonQt::self());
00276 
00277   this->connect(PythonQt::self(), SIGNAL(pythonStdOut(const QString&)),
00278                 SLOT(printStdout(const QString&)));
00279   this->connect(PythonQt::self(), SIGNAL(pythonStdErr(const QString&)),
00280                 SLOT(printStderr(const QString&)));
00281 }
00282 
00283 //----------------------------------------------------------------------------
00284 ctkPythonShell::~ctkPythonShell()
00285 {
00286   delete this->Implementation;
00287 }
00288 
00289 //----------------------------------------------------------------------------
00290 void ctkPythonShell::clear()
00291 {
00292   this->Implementation->Console.clear();
00293   this->Implementation->promptForInput();
00294 }
00295 
00296 // //----------------------------------------------------------------------------
00297 // void ctkPythonShell::makeCurrent()
00298 // {
00299 //   this->Implementation->Interpreter->MakeCurrent();
00300 // }
00301 // 
00302 // //----------------------------------------------------------------------------
00303 // void ctkPythonShell::releaseControl()
00304 // {
00305 //   this->Implementation->Interpreter->ReleaseControl();
00306 // }
00307 
00308 //----------------------------------------------------------------------------
00309 void ctkPythonShell::executeScript(const QString& script)
00310 {
00311   Q_UNUSED(script);
00312   
00313   this->printStdout("\n");
00314   emit this->executing(true);
00315 //   this->Implementation->Interpreter->RunSimpleString(
00316 //     script.toAscii().data());
00317   emit this->executing(false);
00318   this->Implementation->promptForInput();
00319 }
00320 
00321 //----------------------------------------------------------------------------
00322 QStringList ctkPythonShell::getPythonAttributes(const QString& pythonVariableName)
00323 {
00324 //   this->makeCurrent();
00325 
00326   Q_ASSERT(PyThreadState_GET()->interp);
00327   PyObject* dict = PyImport_GetModuleDict();
00328   PyObject* object = PyDict_GetItemString(dict, "__main__");
00329   Py_INCREF(object);
00330 
00331 
00332   if (!pythonVariableName.isEmpty())
00333     {
00334     QStringList tmpNames = pythonVariableName.split('.');
00335     for (int i = 0; i < tmpNames.size() && object; ++i)
00336       {
00337       QByteArray tmpName = tmpNames.at(i).toLatin1();
00338       PyObject* prevObj = object;
00339       if (PyDict_Check(object))
00340         {
00341         object = PyDict_GetItemString(object, tmpName.data());
00342         Py_XINCREF(object);
00343         }
00344       else
00345         {
00346         object = PyObject_GetAttrString(object, tmpName.data());
00347         }
00348       Py_DECREF(prevObj);
00349       }
00350     PyErr_Clear();
00351     }
00352 
00353   QStringList results;
00354   if (object)
00355     {
00356     PyObject* keys = PyObject_Dir(object);
00357     if (keys)
00358       {
00359       PyObject* key;
00360       PyObject* value;
00361       QString keystr;
00362       int nKeys = PyList_Size(keys);
00363       for (int i = 0; i < nKeys; ++i)
00364         {
00365         key = PyList_GetItem(keys, i);
00366         value = PyObject_GetAttr(object, key);
00367         if (!value)
00368           {
00369           continue;
00370           }
00371 
00372         results << PyString_AsString(key);
00373         Py_DECREF(value);
00374         }
00375       Py_DECREF(keys);
00376       }
00377     Py_DECREF(object);
00378     }
00379 
00380 //   this->releaseControl();
00381   return results;
00382 }
00383 
00384 //----------------------------------------------------------------------------
00385 void ctkPythonShell::printStdout(const QString& text)
00386 {
00387   QTextCharFormat format = this->Implementation->Console.getFormat();
00388   format.setForeground(QColor(0, 150, 0));
00389   this->Implementation->Console.setFormat(format);
00390   
00391   this->Implementation->Console.printString(text);
00392   
00393   QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
00394 }
00395 
00396 //----------------------------------------------------------------------------
00397 void ctkPythonShell::printMessage(const QString& text)
00398 {
00399   QTextCharFormat format = this->Implementation->Console.getFormat();
00400   format.setForeground(QColor(0, 0, 150));
00401   this->Implementation->Console.setFormat(format);
00402   
00403   this->Implementation->Console.printString(text);
00404 }
00405 
00406 //----------------------------------------------------------------------------
00407 void ctkPythonShell::printStderr(const QString& text)
00408 {
00409   QTextCharFormat format = this->Implementation->Console.getFormat();
00410   format.setForeground(QColor(255, 0, 0));
00411   this->Implementation->Console.setFormat(format);
00412   
00413   this->Implementation->Console.printString(text);
00414   
00415   QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
00416 }
00417 
00418 //----------------------------------------------------------------------------
00419 void ctkPythonShell::onExecuteCommand(const QString& Command)
00420 {
00421   QString command = Command;
00422   command.replace(QRegExp("\\s*$"), "");
00423   this->internalExecuteCommand(command);
00424 
00425   // Find the indent for the command.
00426   QRegExp regExp("^(\\s+)");
00427   QString indent;
00428   if (regExp.indexIn(command) != -1)
00429     {
00430     indent = regExp.cap(1);
00431     }
00432   this->Implementation->promptForInput(indent);
00433 }
00434 
00435 //----------------------------------------------------------------------------
00436 void ctkPythonShell::promptForInput()
00437 {
00438   this->Implementation->promptForInput();
00439 }
00440 
00441 //----------------------------------------------------------------------------
00442 void ctkPythonShell::internalExecuteCommand(const QString& command)
00443 {
00444   emit this->executing(true);  
00445   this->Implementation->executeCommand(command);
00446   emit this->executing(false);
00447 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines

Generated on 21 May 2010 for CTK by  doxygen 1.6.1