ctkPythonShell.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. /*=========================================================================
  2. Library: CTK
  3. Copyright (c) Kitware Inc.
  4. All rights reserved.
  5. Distributed under a BSD License. See LICENSE.txt file.
  6. This software is distributed "AS IS" WITHOUT ANY WARRANTY; without even
  7. the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  8. See the above copyright notice for more information.
  9. =========================================================================*/
  10. /*=========================================================================
  11. Program: ParaView
  12. Module: $RCSfile$
  13. Copyright (c) 2005-2008 Sandia Corporation, Kitware Inc.
  14. All rights reserved.
  15. ParaView is a free software; you can redistribute it and/or modify it
  16. under the terms of the ParaView license version 1.2.
  17. See License_v1.2.txt for the full ParaView license.
  18. A copy of this license can be obtained by contacting
  19. Kitware Inc.
  20. 28 Corporate Drive
  21. Clifton Park, NY 12065
  22. USA
  23. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  24. ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  25. LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  26. A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR
  27. CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  28. EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  29. PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  30. PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  31. LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  32. NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  33. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  34. =========================================================================*/
  35. //#include <vtkPython.h> // python first
  36. // Qt includes
  37. #include <QCoreApplication>
  38. #include <QResizeEvent>
  39. #include <QScrollBar>
  40. #include <QStringListModel>
  41. #include <QTextCharFormat>
  42. #include <QVBoxLayout>
  43. // PythonQt includes
  44. #include <PythonQt.h>
  45. #include <PythonQtObjectPtr.h>
  46. // CTK includes
  47. #include <ctkConsoleWidget.h>
  48. #include <ctkAbstractPythonManager.h>
  49. #include "ctkPythonShell.h"
  50. //----------------------------------------------------------------------------
  51. class ctkPythonShellCompleter : public ctkConsoleWidgetCompleter
  52. {
  53. public:
  54. ctkPythonShellCompleter(ctkPythonShell& p) : Parent(p)
  55. {
  56. this->setParent(&p);
  57. }
  58. virtual void updateCompletionModel(const QString& completion)
  59. {
  60. // Start by clearing the model
  61. this->setModel(0);
  62. // Don't try to complete the empty string
  63. if (completion.isEmpty())
  64. {
  65. return;
  66. }
  67. // Search backward through the string for usable characters
  68. QString textToComplete;
  69. for (int i = completion.length()-1; i >= 0; --i)
  70. {
  71. QChar c = completion.at(i);
  72. if (c.isLetterOrNumber() || c == '.' || c == '_')
  73. {
  74. textToComplete.prepend(c);
  75. }
  76. else
  77. {
  78. break;
  79. }
  80. }
  81. // Split the string at the last dot, if one exists
  82. QString lookup;
  83. QString compareText = textToComplete;
  84. int dot = compareText.lastIndexOf('.');
  85. if (dot != -1)
  86. {
  87. lookup = compareText.mid(0, dot);
  88. compareText = compareText.mid(dot+1);
  89. }
  90. // Lookup python names
  91. QStringList attrs;
  92. if (!lookup.isEmpty() || !compareText.isEmpty())
  93. {
  94. attrs = Parent.getPythonAttributes(lookup);
  95. }
  96. // Initialize the completion model
  97. if (!attrs.isEmpty())
  98. {
  99. this->setCompletionMode(QCompleter::PopupCompletion);
  100. this->setModel(new QStringListModel(attrs, this));
  101. this->setCaseSensitivity(Qt::CaseInsensitive);
  102. this->setCompletionPrefix(compareText.toLower());
  103. this->popup()->setCurrentIndex(this->completionModel()->index(0, 0));
  104. }
  105. }
  106. ctkPythonShell& Parent;
  107. };
  108. /////////////////////////////////////////////////////////////////////////
  109. // ctkPythonShell::pqImplementation
  110. struct ctkPythonShell::pqImplementation
  111. {
  112. pqImplementation(QWidget* _parent, ctkAbstractPythonManager* pythonManager)
  113. : Console(_parent), PythonManager(pythonManager)
  114. {
  115. }
  116. //----------------------------------------------------------------------------
  117. // void initialize(int argc, char* argv[])
  118. // {
  119. // // Setup Python's interactive prompts
  120. // PyObject* ps1 = PySys_GetObject(const_cast<char*>("ps1"));
  121. // if(!ps1)
  122. // {
  123. // PySys_SetObject(const_cast<char*>("ps1"), ps1 = PyString_FromString(">>> "));
  124. // Py_XDECREF(ps1);
  125. // }
  126. //
  127. // PyObject* ps2 = PySys_GetObject(const_cast<char*>("ps2"));
  128. // if(!ps2)
  129. // {
  130. // PySys_SetObject(const_cast<char*>("ps2"), ps2 = PyString_FromString("... "));
  131. // Py_XDECREF(ps2);
  132. // }
  133. // this->MultilineStatement = false;
  134. // }
  135. //----------------------------------------------------------------------------
  136. ~pqImplementation()
  137. {
  138. // this->destroyInterpretor();
  139. }
  140. //----------------------------------------------------------------------------
  141. // void destroyInterpretor()
  142. // {
  143. // if (this->Interpreter)
  144. // {
  145. // QTextCharFormat format = this->Console.getFormat();
  146. // format.setForeground(QColor(255, 0, 0));
  147. // this->Console.setFormat(format);
  148. // this->Console.printString("\n... restarting ...\n");
  149. // format.setForeground(QColor(0, 0, 0));
  150. // this->Console.setFormat(format);
  151. //
  152. // this->Interpreter->MakeCurrent();
  153. //
  154. // // Restore Python's original stdout and stderr
  155. // PySys_SetObject(const_cast<char*>("stdout"), PySys_GetObject(const_cast<char*>("__stdout__")));
  156. // PySys_SetObject(const_cast<char*>("stderr"), PySys_GetObject(const_cast<char*>("__stderr__")));
  157. // this->Interpreter->ReleaseControl();
  158. // this->Interpreter->Delete();
  159. // }
  160. // this->Interpreter = 0;
  161. // }
  162. //----------------------------------------------------------------------------
  163. void executeCommand(const QString& command)
  164. {
  165. // this->MultilineStatement =
  166. // this->Interpreter->Push(Command.toAscii().data());
  167. if (command.length())
  168. {
  169. Q_ASSERT(this->PythonManager);
  170. this->PythonManager->executeString(command);
  171. }
  172. }
  173. //----------------------------------------------------------------------------
  174. void promptForInput(const QString& indent=QString())
  175. {
  176. QTextCharFormat format = this->Console.getFormat();
  177. format.setForeground(QColor(0, 0, 0));
  178. this->Console.setFormat(format);
  179. // this->Interpreter->MakeCurrent();
  180. if(!this->MultilineStatement)
  181. {
  182. this->Console.prompt(">>> ");
  183. //this->Console.prompt(
  184. // PyString_AsString(PySys_GetObject(const_cast<char*>("ps1"))));
  185. }
  186. else
  187. {
  188. this->Console.prompt("... ");
  189. //this->Console.prompt(
  190. // PyString_AsString(PySys_GetObject(const_cast<char*>("ps2"))));
  191. }
  192. this->Console.printCommand(indent);
  193. // this->Interpreter->ReleaseControl();
  194. }
  195. /// Provides a console for gathering user input and displaying
  196. /// Python output
  197. ctkConsoleWidget Console;
  198. ctkAbstractPythonManager* PythonManager;
  199. /// Indicates if the last statement processes was incomplete.
  200. bool MultilineStatement;
  201. };
  202. /////////////////////////////////////////////////////////////////////////
  203. // ctkPythonShell
  204. //----------------------------------------------------------------------------
  205. ctkPythonShell::ctkPythonShell(ctkAbstractPythonManager* pythonManager, QWidget* _parent):
  206. Superclass(_parent),
  207. Implementation(new pqImplementation(this, pythonManager))
  208. {
  209. // Layout UI
  210. QVBoxLayout* const boxLayout = new QVBoxLayout(this);
  211. boxLayout->setMargin(0);
  212. boxLayout->addWidget(&this->Implementation->Console);
  213. this->setObjectName("pythonShell");
  214. ctkPythonShellCompleter* completer = new ctkPythonShellCompleter(*this);
  215. this->Implementation->Console.setCompleter(completer);
  216. QObject::connect(
  217. &this->Implementation->Console, SIGNAL(executeCommand(const QString&)),
  218. this, SLOT(onExecuteCommand(const QString&)));
  219. // The call to mainContext() ensures that python has been initialized.
  220. Q_ASSERT(this->Implementation->PythonManager);
  221. this->Implementation->PythonManager->mainContext();
  222. QTextCharFormat format = this->Implementation->Console.getFormat();
  223. format.setForeground(QColor(0, 0, 255));
  224. this->Implementation->Console.setFormat(format);
  225. this->Implementation->Console.printString(
  226. QString("Python %1 on %2\n").arg(Py_GetVersion()).arg(Py_GetPlatform()));
  227. this->promptForInput();
  228. Q_ASSERT(PythonQt::self());
  229. this->connect(PythonQt::self(), SIGNAL(pythonStdOut(const QString&)),
  230. SLOT(printStdout(const QString&)));
  231. this->connect(PythonQt::self(), SIGNAL(pythonStdErr(const QString&)),
  232. SLOT(printStderr(const QString&)));
  233. }
  234. //----------------------------------------------------------------------------
  235. ctkPythonShell::~ctkPythonShell()
  236. {
  237. delete this->Implementation;
  238. }
  239. //----------------------------------------------------------------------------
  240. void ctkPythonShell::clear()
  241. {
  242. this->Implementation->Console.clear();
  243. this->Implementation->promptForInput();
  244. }
  245. // //----------------------------------------------------------------------------
  246. // void ctkPythonShell::makeCurrent()
  247. // {
  248. // this->Implementation->Interpreter->MakeCurrent();
  249. // }
  250. //
  251. // //----------------------------------------------------------------------------
  252. // void ctkPythonShell::releaseControl()
  253. // {
  254. // this->Implementation->Interpreter->ReleaseControl();
  255. // }
  256. //----------------------------------------------------------------------------
  257. void ctkPythonShell::executeScript(const QString& script)
  258. {
  259. Q_UNUSED(script);
  260. this->printStdout("\n");
  261. emit this->executing(true);
  262. // this->Implementation->Interpreter->RunSimpleString(
  263. // script.toAscii().data());
  264. emit this->executing(false);
  265. this->Implementation->promptForInput();
  266. }
  267. //----------------------------------------------------------------------------
  268. QStringList ctkPythonShell::getPythonAttributes(const QString& pythonVariableName)
  269. {
  270. // this->makeCurrent();
  271. Q_ASSERT(PyThreadState_GET()->interp);
  272. PyObject* dict = PyImport_GetModuleDict();
  273. PyObject* object = PyDict_GetItemString(dict, "__main__");
  274. Py_INCREF(object);
  275. if (!pythonVariableName.isEmpty())
  276. {
  277. QStringList tmpNames = pythonVariableName.split('.');
  278. for (int i = 0; i < tmpNames.size() && object; ++i)
  279. {
  280. QByteArray tmpName = tmpNames.at(i).toLatin1();
  281. PyObject* prevObj = object;
  282. if (PyDict_Check(object))
  283. {
  284. object = PyDict_GetItemString(object, tmpName.data());
  285. Py_XINCREF(object);
  286. }
  287. else
  288. {
  289. object = PyObject_GetAttrString(object, tmpName.data());
  290. }
  291. Py_DECREF(prevObj);
  292. }
  293. PyErr_Clear();
  294. }
  295. QStringList results;
  296. if (object)
  297. {
  298. PyObject* keys = PyObject_Dir(object);
  299. if (keys)
  300. {
  301. PyObject* key;
  302. PyObject* value;
  303. QString keystr;
  304. int nKeys = PyList_Size(keys);
  305. for (int i = 0; i < nKeys; ++i)
  306. {
  307. key = PyList_GetItem(keys, i);
  308. value = PyObject_GetAttr(object, key);
  309. if (!value)
  310. {
  311. continue;
  312. }
  313. results << PyString_AsString(key);
  314. Py_DECREF(value);
  315. }
  316. Py_DECREF(keys);
  317. }
  318. Py_DECREF(object);
  319. }
  320. // this->releaseControl();
  321. return results;
  322. }
  323. //----------------------------------------------------------------------------
  324. void ctkPythonShell::printStdout(const QString& text)
  325. {
  326. QTextCharFormat format = this->Implementation->Console.getFormat();
  327. format.setForeground(QColor(0, 150, 0));
  328. this->Implementation->Console.setFormat(format);
  329. this->Implementation->Console.printString(text);
  330. QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
  331. }
  332. //----------------------------------------------------------------------------
  333. void ctkPythonShell::printMessage(const QString& text)
  334. {
  335. QTextCharFormat format = this->Implementation->Console.getFormat();
  336. format.setForeground(QColor(0, 0, 150));
  337. this->Implementation->Console.setFormat(format);
  338. this->Implementation->Console.printString(text);
  339. }
  340. //----------------------------------------------------------------------------
  341. void ctkPythonShell::printStderr(const QString& text)
  342. {
  343. QTextCharFormat format = this->Implementation->Console.getFormat();
  344. format.setForeground(QColor(255, 0, 0));
  345. this->Implementation->Console.setFormat(format);
  346. this->Implementation->Console.printString(text);
  347. QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
  348. }
  349. //----------------------------------------------------------------------------
  350. void ctkPythonShell::onExecuteCommand(const QString& Command)
  351. {
  352. QString command = Command;
  353. command.replace(QRegExp("\\s*$"), "");
  354. this->internalExecuteCommand(command);
  355. // Find the indent for the command.
  356. QRegExp regExp("^(\\s+)");
  357. QString indent;
  358. if (regExp.indexIn(command) != -1)
  359. {
  360. indent = regExp.cap(1);
  361. }
  362. this->Implementation->promptForInput(indent);
  363. }
  364. //----------------------------------------------------------------------------
  365. void ctkPythonShell::promptForInput()
  366. {
  367. this->Implementation->promptForInput();
  368. }
  369. //----------------------------------------------------------------------------
  370. void ctkPythonShell::internalExecuteCommand(const QString& command)
  371. {
  372. emit this->executing(true);
  373. this->Implementation->executeCommand(command);
  374. emit this->executing(false);
  375. }