ctkAbstractPythonManager.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. /*=========================================================================
  2. Library: CTK
  3. Copyright (c) Kitware Inc.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0.txt
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. =========================================================================*/
  14. // Qt includes
  15. #include <QDir>
  16. #include <QDebug>
  17. // CTK includes
  18. #include "ctkAbstractPythonManager.h"
  19. #include "ctkScriptingPythonCoreConfigure.h"
  20. // PythonQT includes
  21. #include <PythonQt.h>
  22. #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
  23. #include <PythonQt_QtBindings.h>
  24. #endif
  25. // STD includes
  26. #include <csignal>
  27. #ifdef __GNUC__
  28. // Disable warnings related to signal() function
  29. // See http://gcc.gnu.org/onlinedocs/gcc/Diagnostic-Pragmas.html
  30. // Note: Ideally the incriminated functions and macros should be fixed upstream ...
  31. #pragma GCC diagnostic ignored "-Wold-style-cast"
  32. #endif
  33. //-----------------------------------------------------------------------------
  34. class ctkAbstractPythonManagerPrivate
  35. {
  36. Q_DECLARE_PUBLIC(ctkAbstractPythonManager);
  37. protected:
  38. ctkAbstractPythonManager* q_ptr;
  39. public:
  40. ctkAbstractPythonManagerPrivate(ctkAbstractPythonManager& object);
  41. virtual ~ctkAbstractPythonManagerPrivate();
  42. void (*InitFunction)();
  43. int PythonQtInitializationFlags;
  44. };
  45. //-----------------------------------------------------------------------------
  46. // ctkAbstractPythonManagerPrivate methods
  47. //-----------------------------------------------------------------------------
  48. ctkAbstractPythonManagerPrivate::ctkAbstractPythonManagerPrivate(ctkAbstractPythonManager &object) :
  49. q_ptr(&object)
  50. {
  51. this->InitFunction = 0;
  52. this->PythonQtInitializationFlags = PythonQt::IgnoreSiteModule | PythonQt::RedirectStdOut;
  53. }
  54. //-----------------------------------------------------------------------------
  55. ctkAbstractPythonManagerPrivate::~ctkAbstractPythonManagerPrivate()
  56. {
  57. }
  58. //-----------------------------------------------------------------------------
  59. // ctkAbstractPythonManager methods
  60. //-----------------------------------------------------------------------------
  61. ctkAbstractPythonManager::ctkAbstractPythonManager(QObject* _parent) : Superclass(_parent),
  62. d_ptr(new ctkAbstractPythonManagerPrivate(*this))
  63. {
  64. }
  65. //-----------------------------------------------------------------------------
  66. ctkAbstractPythonManager::~ctkAbstractPythonManager()
  67. {
  68. PythonQt::cleanup();
  69. if (Py_IsInitialized())
  70. {
  71. Py_Finalize();
  72. }
  73. }
  74. //-----------------------------------------------------------------------------
  75. void ctkAbstractPythonManager::setInitializationFlags(int flags)
  76. {
  77. Q_D(ctkAbstractPythonManager);
  78. if (PythonQt::self())
  79. {
  80. return;
  81. }
  82. d->PythonQtInitializationFlags = flags;
  83. }
  84. //-----------------------------------------------------------------------------
  85. int ctkAbstractPythonManager::initializationFlags()const
  86. {
  87. Q_D(const ctkAbstractPythonManager);
  88. return d->PythonQtInitializationFlags;
  89. }
  90. //-----------------------------------------------------------------------------
  91. bool ctkAbstractPythonManager::initialize()
  92. {
  93. Q_D(ctkAbstractPythonManager);
  94. if (!PythonQt::self())
  95. {
  96. this->initPythonQt(d->PythonQtInitializationFlags);
  97. }
  98. return this->isPythonInitialized();
  99. }
  100. //-----------------------------------------------------------------------------
  101. PythonQtObjectPtr ctkAbstractPythonManager::mainContext()
  102. {
  103. bool initalized = this->initialize();
  104. if (initalized)
  105. {
  106. return PythonQt::self()->getMainModule();
  107. }
  108. return PythonQtObjectPtr();
  109. }
  110. //-----------------------------------------------------------------------------
  111. void ctkAbstractPythonManager::initPythonQt(int flags)
  112. {
  113. Q_D(ctkAbstractPythonManager);
  114. PythonQt::init(flags);
  115. // Python maps SIGINT (control-c) to its own handler. We will remap it
  116. // to the default so that control-c works.
  117. #ifdef SIGINT
  118. signal(SIGINT, SIG_DFL);
  119. #endif
  120. // Forward signal from PythonQt::self() to this instance of ctkAbstractPythonManager
  121. this->connect(PythonQt::self(), SIGNAL(systemExitExceptionRaised(int)),
  122. SIGNAL(systemExitExceptionRaised(int)));
  123. this->connect(PythonQt::self(), SIGNAL(pythonStdOut(QString)),
  124. SLOT(printStdout(QString)));
  125. this->connect(PythonQt::self(), SIGNAL(pythonStdErr(QString)),
  126. SLOT(printStderr(QString)));
  127. #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
  128. PythonQt_init_QtBindings();
  129. #endif
  130. QStringList initCode;
  131. // Update 'sys.path'
  132. initCode << "import sys";
  133. foreach (const QString& path, this->pythonPaths())
  134. {
  135. initCode << QString("sys.path.append('%1')").arg(QDir::fromNativeSeparators(path));
  136. }
  137. PythonQtObjectPtr _mainContext = PythonQt::self()->getMainModule();
  138. _mainContext.evalScript(initCode.join("\n"));
  139. this->preInitialization();
  140. if (d->InitFunction)
  141. {
  142. (*d->InitFunction)();
  143. }
  144. emit this->pythonPreInitialized();
  145. this->executeInitializationScripts();
  146. emit this->pythonInitialized();
  147. }
  148. //-----------------------------------------------------------------------------
  149. bool ctkAbstractPythonManager::isPythonInitialized()const
  150. {
  151. return PythonQt::self() != 0;
  152. }
  153. //-----------------------------------------------------------------------------
  154. bool ctkAbstractPythonManager::pythonErrorOccured()const
  155. {
  156. return PythonQt::self()->hadError();
  157. }
  158. //-----------------------------------------------------------------------------
  159. void ctkAbstractPythonManager::resetErrorFlag()
  160. {
  161. PythonQt::self()->clearError();
  162. }
  163. //-----------------------------------------------------------------------------
  164. QStringList ctkAbstractPythonManager::pythonPaths()
  165. {
  166. return QStringList();
  167. }
  168. //-----------------------------------------------------------------------------
  169. void ctkAbstractPythonManager::preInitialization()
  170. {
  171. }
  172. //-----------------------------------------------------------------------------
  173. void ctkAbstractPythonManager::executeInitializationScripts()
  174. {
  175. }
  176. //-----------------------------------------------------------------------------
  177. void ctkAbstractPythonManager::registerPythonQtDecorator(QObject* decorator)
  178. {
  179. PythonQt::self()->addDecorators(decorator);
  180. }
  181. //-----------------------------------------------------------------------------
  182. void ctkAbstractPythonManager::registerClassForPythonQt(const QMetaObject* metaobject)
  183. {
  184. PythonQt::self()->registerClass(metaobject);
  185. }
  186. //-----------------------------------------------------------------------------
  187. void ctkAbstractPythonManager::registerCPPClassForPythonQt(const char* name)
  188. {
  189. PythonQt::self()->registerCPPClass(name);
  190. }
  191. //-----------------------------------------------------------------------------
  192. bool ctkAbstractPythonManager::systemExitExceptionHandlerEnabled()const
  193. {
  194. return PythonQt::self()->systemExitExceptionHandlerEnabled();
  195. }
  196. //-----------------------------------------------------------------------------
  197. void ctkAbstractPythonManager::setSystemExitExceptionHandlerEnabled(bool value)
  198. {
  199. PythonQt::self()->setSystemExitExceptionHandlerEnabled(value);
  200. }
  201. //-----------------------------------------------------------------------------
  202. QVariant ctkAbstractPythonManager::executeString(const QString& code,
  203. ctkAbstractPythonManager::ExecuteStringMode mode)
  204. {
  205. int start = -1;
  206. switch(mode)
  207. {
  208. case ctkAbstractPythonManager::FileInput: start = Py_file_input; break;
  209. case ctkAbstractPythonManager::SingleInput: start = Py_single_input; break;
  210. case ctkAbstractPythonManager::EvalInput:
  211. default: start = Py_eval_input; break;
  212. }
  213. QVariant ret;
  214. PythonQtObjectPtr main = ctkAbstractPythonManager::mainContext();
  215. if (main)
  216. {
  217. ret = main.evalScript(code, start);
  218. }
  219. return ret;
  220. }
  221. //-----------------------------------------------------------------------------
  222. void ctkAbstractPythonManager::executeFile(const QString& filename)
  223. {
  224. PythonQtObjectPtr main = ctkAbstractPythonManager::mainContext();
  225. if (main)
  226. {
  227. QString path = QFileInfo(filename).absolutePath();
  228. // See http://nedbatchelder.com/blog/200711/rethrowing_exceptions_in_python.html
  229. QStringList code = QStringList()
  230. << "import sys"
  231. << QString("sys.path.insert(0, '%1')").arg(path)
  232. << "_updated_globals = globals()"
  233. << QString("_updated_globals['__file__'] = '%1'").arg(filename)
  234. << "_ctk_executeFile_exc_info = None"
  235. << "try:"
  236. << QString(" execfile('%1', _updated_globals)").arg(filename)
  237. << "except Exception, e:"
  238. << " _ctk_executeFile_exc_info = sys.exc_info()"
  239. << "finally:"
  240. << " del _updated_globals"
  241. << QString(" if sys.path[0] == '%1': sys.path.pop(0)").arg(path)
  242. << " if _ctk_executeFile_exc_info:"
  243. << " raise _ctk_executeFile_exc_info[1], None, _ctk_executeFile_exc_info[2]";
  244. this->executeString(code.join("\n"));
  245. //PythonQt::self()->handleError(); // Clear errorOccured flag
  246. }
  247. }
  248. //-----------------------------------------------------------------------------
  249. void ctkAbstractPythonManager::setInitializationFunction(void (*initFunction)())
  250. {
  251. Q_D(ctkAbstractPythonManager);
  252. d->InitFunction = initFunction;
  253. }
  254. //----------------------------------------------------------------------------
  255. QStringList ctkAbstractPythonManager::pythonAttributes(const QString& pythonVariableName,
  256. const QString& module,
  257. bool appendParenthesis) const
  258. {
  259. Q_ASSERT(PyThreadState_GET()->interp);
  260. PyObject* dict = PyImport_GetModuleDict();
  261. // Split module by '.' and retrieve the object associated if the last module
  262. PyObject* object = 0;
  263. PyObject* prevObject = 0;
  264. QStringList moduleList = module.split(".", QString::SkipEmptyParts);
  265. foreach(const QString& module, moduleList)
  266. {
  267. object = PyDict_GetItemString(dict, module.toLatin1().data());
  268. if (prevObject) { Py_DECREF(prevObject); }
  269. if (!object)
  270. {
  271. break;
  272. }
  273. Py_INCREF(object);
  274. dict = PyModule_GetDict(object);
  275. prevObject = object;
  276. }
  277. if (!object)
  278. {
  279. return QStringList();
  280. }
  281. // PyObject* object = PyDict_GetItemString(dict, module.toLatin1().data());
  282. // if (!object)
  283. // {
  284. // return QStringList();
  285. // }
  286. // Py_INCREF(object);
  287. if (!pythonVariableName.isEmpty())
  288. {
  289. QStringList tmpNames = pythonVariableName.split('.');
  290. for (int i = 0; i < tmpNames.size() && object; ++i)
  291. {
  292. QByteArray tmpName = tmpNames.at(i).toLatin1();
  293. PyObject* prevObj = object;
  294. if (PyDict_Check(object))
  295. {
  296. object = PyDict_GetItemString(object, tmpName.data());
  297. Py_XINCREF(object);
  298. }
  299. else
  300. {
  301. object = PyObject_GetAttrString(object, tmpName.data());
  302. }
  303. Py_DECREF(prevObj);
  304. }
  305. PyErr_Clear();
  306. }
  307. QStringList results;
  308. if (object)
  309. {
  310. PyObject* keys = PyObject_Dir(object);
  311. if (keys)
  312. {
  313. PyObject* key;
  314. PyObject* value;
  315. int nKeys = PyList_Size(keys);
  316. for (int i = 0; i < nKeys; ++i)
  317. {
  318. key = PyList_GetItem(keys, i);
  319. value = PyObject_GetAttr(object, key);
  320. if (!value)
  321. {
  322. continue;
  323. }
  324. QString key_str(PyString_AsString(key));
  325. // Append "()" if the associated object is a function
  326. if (appendParenthesis && PyCallable_Check(value))
  327. {
  328. key_str.append("()");
  329. }
  330. results << key_str;
  331. Py_DECREF(value);
  332. }
  333. Py_DECREF(keys);
  334. }
  335. Py_DECREF(object);
  336. }
  337. return results;
  338. }
  339. //-----------------------------------------------------------------------------
  340. void ctkAbstractPythonManager::addObjectToPythonMain(const QString& name, QObject* obj)
  341. {
  342. PythonQtObjectPtr main = ctkAbstractPythonManager::mainContext();
  343. if (main && obj)
  344. {
  345. main.addObject(name, obj);
  346. }
  347. }
  348. //-----------------------------------------------------------------------------
  349. void ctkAbstractPythonManager::addWrapperFactory(PythonQtForeignWrapperFactory* factory)
  350. {
  351. PythonQt::self()->addWrapperFactory(factory);
  352. }
  353. //-----------------------------------------------------------------------------
  354. QVariant ctkAbstractPythonManager::getVariable(const QString& name)
  355. {
  356. PythonQtObjectPtr main = ctkAbstractPythonManager::mainContext();
  357. if (main)
  358. {
  359. return PythonQt::self()->getVariable(main, name);
  360. }
  361. return QVariant();
  362. }
  363. //-----------------------------------------------------------------------------
  364. void ctkAbstractPythonManager::printStdout(const QString& text)
  365. {
  366. std::cout << qPrintable(text);
  367. }
  368. //-----------------------------------------------------------------------------
  369. void ctkAbstractPythonManager::printStderr(const QString& text)
  370. {
  371. std::cerr << qPrintable(text);
  372. }