Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages  

PythonTextInterp.C

Go to the documentation of this file.
00001 /***************************************************************************
00002  *cr
00003  *cr            (C) Copyright 1995-2016 The Board of Trustees of the
00004  *cr                        University of Illinois
00005  *cr                         All Rights Reserved
00006  *cr
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  * RCS INFORMATION:
00011  *
00012  *      $RCSfile: PythonTextInterp.C,v $
00013  *      $Author: johns $        $Locker:  $             $State: Exp $
00014  *      $Revision: 1.76 $       $Date: 2020/10/21 20:33:20 $
00015  *
00016  ***************************************************************************
00017  * DESCRIPTION:
00018  *  Python text interpreter
00019  ***************************************************************************/
00020 
00021 #include "py_commands.h"
00022 #include "Inform.h"
00023 #include "PythonTextInterp.h"
00024 #include "config.h"
00025 #include "VMDApp.h"
00026 #include "TextEvent.h"
00027 
00028 #if defined(__APPLE__)
00029 // use the Apple-provided Python framework
00030 #include "Python/errcode.h"
00031 #else
00032 #include "errcode.h"
00033 #endif
00034 
00035 #if PY_MAJOR_VERSION >= 3
00036 PyMODINIT_FUNC PyInit_vmd(void);
00037 #else
00038 extern "C" PyMODINIT_FUNC PyInit_vmd(void);
00039 #endif
00040 
00041 static PyObject *cbdict = NULL;
00042 
00043 static PyObject *add_callback(PyObject *, PyObject *args) {
00044   char *type;
00045   PyObject *temp;
00046 
00047   if (!PyArg_ParseTuple(args, "sO:add_callback", &type, &temp)) 
00048     return NULL;
00049 
00050   if (!PyCallable_Check(temp)) {
00051     PyErr_SetString(PyExc_TypeError, "parameter must be callable");
00052     return NULL;
00053   }
00054   PyObject *cblist = PyDict_GetItemString(cbdict, type);
00055   if (!cblist) {
00056     PyErr_SetString(PyExc_KeyError, type);
00057     return NULL;
00058   }
00059   PyList_Append(cblist, temp);
00060   Py_XDECREF(temp); // PyList_Append does not steal a reference
00061   Py_INCREF(Py_None);
00062   return Py_None;
00063 }
00064 
00065 static PyObject *del_callback(PyObject *, PyObject *args) {
00066   char *type;
00067   PyObject *temp;
00068 
00069   if (!PyArg_ParseTuple(args, "sO:del_callback", &type, &temp)) 
00070     return NULL;
00071 
00072   if (!PyCallable_Check(temp)) {
00073     PyErr_SetString(PyExc_TypeError, "parameter must be callable");
00074     return NULL;
00075   }
00076   PyObject *cblist = PyDict_GetItemString(cbdict, type);
00077   if (!cblist) {
00078     PyErr_SetString(PyExc_KeyError, type);
00079     return NULL;
00080   }
00081   int ind = PySequence_Index(cblist, temp);
00082   if (ind >= 0) {
00083     PySequence_DelItem(cblist, ind);
00084   }
00085   Py_INCREF(Py_None);
00086   return Py_None;
00087 }
00088 
00089 static void call_callbacks(const char *type, PyObject *arglist) {
00090   PyObject *cblist = PyDict_GetItemString(cbdict, type);
00091   if (!cblist) {
00092     msgErr << "Internal error: callback list " << type << " does not exist."
00093            << sendmsg;
00094     return;
00095   }
00096 
00097   /* The GIL must be held when callbacks are invoked, but we don't have
00098    * the GIL when Tk (as opposed to Tkinter) events come in.  This ensures
00099    * that we have the GIL in either case.
00100    */
00101   PyGILState_STATE state = PyGILState_Ensure();
00102 
00103   for (int i=0; i<PyList_GET_SIZE(cblist); i++) {
00104     PyObject *obj = PyList_GET_ITEM(cblist, i);
00105     PyObject *result = PyEval_CallObject(obj, arglist);
00106     if (result == NULL) {
00107       PyErr_Print();
00108       PySequence_DelItem(cblist, i);
00109       i--;
00110     } else {
00111       Py_DECREF(result);
00112     }
00113   }
00114   Py_DECREF(arglist);
00115 
00116   PyGILState_Release(state);
00117 }
00118   
00119 static PyMethodDef CallbackMethods[] = {
00120   {"add_callback", (PyCFunction) add_callback, METH_VARARGS },
00121   {"del_callback", (PyCFunction) del_callback, METH_VARARGS },
00122   {NULL, NULL}
00123 };
00124 
00125 #if PY_MAJOR_VERSION >= 3
00126 static struct PyModuleDef vmdcallbacksdef = {
00127     PyModuleDef_HEAD_INIT,
00128     "vmdcallbacks",
00129     NULL,
00130     -1, // global state, no sub-interpreters
00131     CallbackMethods,
00132     NULL,
00133     NULL, // m_traverse gc traversal
00134     NULL, // m_clear gc clear
00135     NULL  // m_free gc free
00136 };
00137 #endif
00138 
00139 PyObject* initvmdcallbacks(void) {
00140 #if PY_MAJOR_VERSION >= 3
00141   PyObject *m = PyModule_Create(&vmdcallbacksdef);
00142 #else
00143   PyObject *m = Py_InitModule("vmdcallbacks", CallbackMethods);
00144 #endif
00145   PyObject *dict = PyDict_New();
00146   PyDict_SetItemString(dict, "display_update", PyList_New(0));
00147   PyDict_SetItemString(dict, "frame", PyList_New(0));
00148   PyDict_SetItemString(dict, "initialize_structure", PyList_New(0));
00149   PyDict_SetItemString(dict, "molecule", PyList_New(0));
00150   PyDict_SetItemString(dict, "pick_atom", PyList_New(0));
00151   PyDict_SetItemString(dict, "pick_event", PyList_New(0));
00152   PyDict_SetItemString(dict, "pick_value", PyList_New(0));
00153   PyDict_SetItemString(dict, "timestep", PyList_New(0));
00154   PyDict_SetItemString(dict, "trajectory", PyList_New(0));
00155   PyDict_SetItemString(dict, "userkey", PyList_New(0));
00156   PyObject_SetAttrString(m, "callbacks", dict);
00157   cbdict = dict;
00158 
00159   return m;
00160 }
00161 
00162 PythonTextInterp::PythonTextInterp(VMDApp *vmdapp) : app(vmdapp) {
00163   const char *oo_modules[] = {"Molecule", "Label", "Material", NULL};
00164   PyObject *vmdmodule;
00165   int retval, i;
00166 
00167   msgInfo << "Starting Python..." << sendmsg;
00168 
00169 #if PY_MAJOR_VERSION >= 3
00170   // Import VMD builtin module automatically
00171   // Do this before Py_initialize called
00172   PyImport_AppendInittab("vmd", PyInit_vmd);
00173 #endif
00174 
00175   // Do emit DeprecationWarnings
00176 #if PY_MAJOR_VERSION >= 3
00177   PySys_AddWarnOption(L"default");
00178 #else
00179   PySys_AddWarnOption((char*) "default");
00180 #endif
00181 
00182 #if 0 && PY_MAJOR_VERSION >= 3
00183   // Set program name used to find library path etc. Defaults to 'python',
00184   // must occur before initialization.
00185   Py_SetProgramName(Py_DecodeLocale(app->argv_m[0], NULL));
00186 #endif
00187   Py_Initialize();
00188 
00189   // Some modules (like Tk) assume that os.argv has been initialized
00190 #if PY_MAJOR_VERSION >= 3
00191   int argc = app->argc_m;
00192   wchar_t **wargv = (wchar_t **) PyMem_Malloc((argc+1) * sizeof(wchar_t*));
00193   memset(wargv, 0, (argc+1) * sizeof(wchar_t*));
00194 
00195   for (i=0; i<argc; i++) {
00196     wargv[i] = Py_DecodeLocale(app->argv_m[i], NULL);
00197   }
00198 
00199   PySys_SetArgv(argc, wargv);
00200 #else
00201   PySys_SetArgv(app->argc_m, (char **)app->argv_m);
00202 #endif
00203   set_vmdapp(app);
00204 
00205   // Set up the prompts
00206   PySys_SetObject((char*) "ps1", as_pystring(""));
00207   PySys_SetObject((char*) "ps2", as_pystring("... "));
00208 
00209   vmdmodule = PyImport_ImportModule("vmd");
00210   i = 0;
00211   while (py_initializers[i].name) {
00212     const char *name = py_initializers[i].name;
00213 
00214     PyObject *module = (*(py_initializers[i].initfunc))();
00215     i++;
00216     if (!module) {
00217       msgErr << "Failed to initialize builtin module " << name << sendmsg;
00218       PyErr_Print();
00219       continue;
00220     }
00221     retval = PyModule_AddObject(vmdmodule, CAST_HACK name, module);
00222     if (retval || PyErr_Occurred()) {
00223       msgErr << "Failed to import builtin module " << name << sendmsg;
00224       exit(1);
00225     }
00226   }
00227 
00228   // Now handle the three object-oriented classes
00229   for (const char **tmp = oo_modules; *tmp; tmp++) {
00230     PyObject *module = PyImport_ImportModule(*tmp);
00231     if (!module) {
00232       msgErr << "Failed to initialize object-oriented module " << *tmp << sendmsg;
00233       continue;
00234     }
00235     retval = PyModule_AddObject(vmdmodule, CAST_HACK *tmp, module);
00236     if (retval || PyErr_Occurred()) {
00237       msgErr << "Failed to initialize object-oriented module " << *tmp << sendmsg;
00238       continue;
00239     }
00240   }
00241 
00242   // Make all modules accessible in the default namespace
00243   if (!evalString("from vmd import *")) {
00244     msgErr << "Failed to import VMD python modules" << sendmsg;
00245     exit(1);
00246   }
00247 
00248   // have_tkinter and have_vmdcallback flags are set to zero if these calls
00249   // ever fail so that we don't fail over and over again and fill up the
00250   // screen with errors.
00251   have_tkinter = 1;
00252   in_tk = 0;
00253   needPrompt = 1;
00254 }
00255 
00256 PythonTextInterp::~PythonTextInterp() {
00257   Py_Finalize();
00258   msgInfo << "Done with Python." << sendmsg;
00259 }
00260 
00261 int PythonTextInterp::doTkUpdate() {
00262   // Don't recursively call into dooneevent - it makes Tkinter crash for
00263   // some infathomable reason.
00264   if (in_tk) return 0;
00265   if (have_tkinter) {
00266     in_tk = 1;
00267   int rc = 1;
00268 // Python 3 just works for whatever reason when tkinter is imported.
00269 // I'm not sure why this is...
00270 #if PY_MAJOR_VERSION >= 3
00271     rc = evalString("import _tkinter\n");
00272 #else
00273     rc = evalString(
00274       "import Tkinter\n"
00275       "while Tkinter.tkinter.dooneevent(Tkinter.tkinter.DONT_WAIT):\n"
00276       "\tpass\n"
00277     );
00278 #endif
00279     in_tk = 0;
00280     if (rc) {
00281       return 1; // success
00282     }
00283     // give up
00284     have_tkinter = 0;
00285   }
00286   return 0;
00287 }
00288   
00289 void PythonTextInterp::doEvent() {
00290   // Call any display loop callbacks
00291   // abort if the call ever fails
00292   PyObject *arglist = Py_BuildValue("()");
00293   call_callbacks("display_update", arglist);
00294 
00295   if (needPrompt) {
00296     printf(">>> ");
00297     fflush(stdout);
00298     needPrompt = 0;
00299   }
00300 
00301   if (!vmd_check_stdin()) 
00302     return;
00303 
00304   int code = PyRun_InteractiveOne(stdin, "VMD");
00305   needPrompt = 1;
00306   if (code == E_EOF) {
00307     // Try to change to Tcl interpreter.  If that fails, UIText will
00308     // bounce us back to the Python interpreter again.
00309     app->textinterp_change("tcl");
00310   }
00311 }
00312 
00313 int PythonTextInterp::evalString(const char *s) {
00314   // evaluate the string in the interpreter
00315   // returns success.
00316   // XXX should print error message if there was one.
00317   return !PyRun_SimpleString(s);
00318 }
00319 
00320 int PythonTextInterp::evalFile(const char *s) {
00321   FILE *fid = fopen(s, "r");
00322   if (!fid) { 
00323     msgErr << "Error opening file '" << s << "'" << sendmsg;
00324     return FALSE;
00325   }
00326   int code = PyRun_SimpleFile(fid, "VMD");
00327   fclose(fid);
00328   return !code;
00329 }
00330  
00331 void PythonTextInterp::frame_cb(int molid, int frame) {
00332   PyObject *arglist = Py_BuildValue("(i,i)", molid, frame);
00333   call_callbacks("frame", arglist);
00334 }
00335 
00336 void PythonTextInterp::initialize_structure_cb(int molid, int code) {
00337   PyObject *arglist = Py_BuildValue("(i,i)", molid, code);
00338   call_callbacks("initialize_structure", arglist);
00339 }
00340 
00341 void PythonTextInterp::molecule_changed_cb(int molid, int code) {
00342   PyObject *arglist = Py_BuildValue("(i,i)", molid, code);
00343   call_callbacks("molecule", arglist);
00344 }
00345 
00346 void PythonTextInterp::pick_atom_cb(int mol, int atom, int key_shift_state, bool ispick) {
00347   PyObject *arglist = Py_BuildValue("(i,i,i)", mol, atom, key_shift_state);
00348   call_callbacks("pick_atom", arglist);
00349   if (ispick) {
00350     // if this is a user pick event, give it its own callback event
00351     // to discourage devs from inappropriately overloading all pick events
00352     PyObject *arglist = Py_BuildValue("(i)", 1);
00353     call_callbacks("pick_event", arglist);
00354   }
00355 }
00356 
00357 void PythonTextInterp::pick_value_cb(float val) {
00358   PyObject *arglist = Py_BuildValue("(f)", val);
00359   call_callbacks("pick_value", arglist);
00360 }
00361 
00362 void PythonTextInterp::timestep_cb(int id, int frame) {
00363   PyObject *arglist = Py_BuildValue("(i,i)", id, frame);
00364   call_callbacks("timestep", arglist);
00365 }
00366 
00367 void PythonTextInterp::trajectory_cb(int id, const char *name) {
00368   PyObject *arglist = Py_BuildValue("(i,s)", id, name);
00369   call_callbacks("trajectory", arglist);
00370 }
00371 
00372 void PythonTextInterp::python_cb(const char *cmd) {
00373   evalString(cmd);
00374 }
00375 
00376 void PythonTextInterp::userkey_cb(const char *keydesc) {
00377   PyObject *arglist = Py_BuildValue("(s)", keydesc);
00378   call_callbacks("userkey", arglist);
00379 }
00380 

Generated on Wed Apr 24 02:43:16 2024 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002