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

py_label.C

Go to the documentation of this file.
00001 /***************************************************************************
00002  *cr
00003  *cr            (C) Copyright 1995-2019 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: py_label.C,v $
00013  *      $Author: johns $        $Locker:  $             $State: Exp $
00014  *      $Revision: 1.31 $       $Date: 2019/05/30 20:07:50 $
00015  *
00016  ***************************************************************************
00017  * DESCRIPTION:
00018  *  Python interface to labelling functions
00019  ***************************************************************************/
00020 
00021 #include "py_commands.h"
00022 #include "VMDApp.h"
00023 #include "GeometryList.h"
00024 #include "Molecule.h"
00025 #include "MoleculeList.h"
00026 #include "config.h"
00027 
00028 // helper function to turn a GeometryMol object into a dictionary
00029 static PyObject* geom2dict(GeometryMol *g) {
00030   PyObject *newdict, *molid_tuple, *atomid_tuple, *value, *on;
00031   int n = g->items();
00032 
00033   newdict = PyDict_New();
00034 
00035   // Populate molid and atomid tuples
00036   molid_tuple = PyTuple_New(n);
00037   atomid_tuple = PyTuple_New(n);
00038   for (int i=0; i<n; i++) {
00039     PyTuple_SET_ITEM(molid_tuple, i, as_pyint(g->obj_index(i)));
00040     PyTuple_SET_ITEM(atomid_tuple, i, as_pyint(g->com_index(i)));
00041   }
00042   PyDict_SetItemString(newdict, "molid", molid_tuple);
00043   PyDict_SetItemString(newdict, "atomid", atomid_tuple);
00044 
00045   // Value is a float or None
00046   value = g->ok() ? PyFloat_FromDouble(g->calculate()) : Py_None;
00047   PyDict_SetItemString(newdict, "value", value);
00048 
00049   // On is if it's displayed
00050   on = g->displayed() ? Py_True : Py_False;
00051   PyDict_SetItemString(newdict, "on", on);
00052   Py_INCREF(on);
00053 
00054   if (PyErr_Occurred()) {
00055     Py_DECREF(newdict);
00056     return NULL;
00057   } 
00058   return newdict; 
00059 }
00060 
00061 
00062 // helper function to check if a label dictionary matches a GeometryMol object
00063 static int dict2geom(PyObject *dict, GeometryMol *g) {
00064   PyObject *molid, *atomid;
00065   int m, a, numitems;
00066 
00067   if (!PyDict_Check(dict)) {
00068     PyErr_SetString(PyExc_RuntimeError, "Non-dict passed to dict2geom");
00069     return 0;
00070   }
00071 
00072   molid = PyDict_GetItemString(dict, (char *)"molid");
00073   atomid = PyDict_GetItemString(dict, (char *)"atomid");
00074 
00075   if (!molid || !atomid || !PyTuple_Check(molid) || !PyTuple_Check(atomid))
00076     return 0;
00077 
00078   numitems = PyTuple_Size(molid);
00079   if (numitems != PyTuple_Size(atomid) || numitems != g->items()) {
00080     return 0;
00081   }
00082 
00083   for (int i=0; i<numitems; i++) {
00084     m = as_int(PyTuple_GET_ITEM(molid, i));
00085     a = as_int(PyTuple_GET_ITEM(atomid, i));
00086 
00087     if (PyErr_Occurred()) {
00088       PyErr_Clear();
00089       return 0;
00090     }
00091 
00092     if (m != g->obj_index(i) || a != g->com_index(i))
00093       return 0;
00094   }
00095 
00096   return 1;
00097 }
00098 
00099 
00100 static const char listall_doc[] =
00101 "Get labels in the given label category.\n\n"
00102 "Args:\n"
00103 "    category (str): Label category to list, in ['atoms','bonds','dihedrals']\n"
00104 "Returns:\n"
00105 "    (dict): All labels, with keys 'molid', 'atomid', 'value', and 'on'.\n"
00106 "        Molid and atomid will be tuples, value will be a float or None, and\n"
00107 "        on will be True or False.";
00108 static PyObject* py_listall(PyObject *self, PyObject *args, PyObject *kwargs) {
00109   const char *kwlist[] = {"category", NULL};
00110   PyObject *newlist, *obj;
00111   int cat, gnum;
00112   char *type;
00113 
00114   if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s:label.listall",
00115                                    (char**) kwlist, &type))
00116     return NULL;
00117 
00118   VMDApp *app;
00119   if (!(app = get_vmdapp()))
00120     return NULL;
00121 
00122   cat = app->geometryList->geom_list_index(type);
00123   if (cat < 0) {
00124     PyErr_Format(PyExc_ValueError, "Unknown label category '%s'", type);
00125     return NULL;
00126   }
00127 
00128   GeomListPtr glist = app->geometryList->geom_list(cat);
00129   gnum = glist->num();
00130   newlist = PyList_New(gnum);
00131   for (int i=0; i<gnum; i++) {
00132     obj = geom2dict((*glist)[i]);
00133 
00134     if (!obj) {
00135       Py_DECREF(newlist);
00136       return NULL;
00137     }
00138     PyList_SET_ITEM(newlist, i, obj);
00139   }
00140 
00141   return newlist;
00142 }
00143 
00144 
00145 // add(category, (molids), (atomids))
00146 static const char label_add_doc[] =
00147 "Add a label of the given type. If label already exists, no action is \n"
00148 "performed. The number of elements in the molids and atomids tuple is \n"
00149 "determined by\nArgs:\n"
00150 "    category (str): Label category to add. Must be one of the following:\n"
00151 "        'Atoms', 'Bonds', 'Angles', 'Dihedrals', 'Springs'\n"
00152 "    molids (list or tuple): Molids to label. Length as required to describe\n"
00153 "        label category-- 1 for atoms, 2 bonds, etc.\n"
00154 "    atomids (list or tuple): Atom IDs to label. Must be same length as molids\n"
00155 "Returns:\n"
00156 "    (dict) Dictionary describing the label, can be used as input to other\n"
00157 "        label module functions";
00158 static PyObject* py_label_add(PyObject *self, PyObject *args, PyObject *kwargs) {
00159   const char *kwlist[] = {"category", "molids", "atomids",  NULL};
00160   PyObject *molseq = NULL, *atomseq = NULL;
00161   int cat, numitems, ind, i, m[4], a[4];
00162   PyObject *molids, *atomids;
00163   GeomListPtr glist;
00164   Molecule *mol;
00165   char *type;
00166 
00167   if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sOO:label.add", (char**)
00168                                    kwlist, &type, &molids, &atomids))
00169     return NULL;
00170 
00171   VMDApp *app;
00172   if (!(app = get_vmdapp()))
00173     return NULL;
00174 
00175   // Get label category and check it is valid
00176   cat = app->geometryList->geom_list_index(type);
00177   if (cat < 0) {
00178     PyErr_Format(PyExc_ValueError, "Unknown label category '%s'", type);
00179     return NULL;
00180   }
00181 
00182   // PySequence_Fast type checks molids and atomids are a sequence
00183   if (!(molseq = PySequence_Fast(molids, "molids must be a list or tuple")))
00184     goto failure;
00185   if (!(atomseq = PySequence_Fast(atomids, "atomids must be a list or tuple")))
00186     goto failure;
00187 
00188   // Validate length of molids and atomids tuples
00189   numitems = PySequence_Fast_GET_SIZE(molseq);
00190   if (numitems != PySequence_Fast_GET_SIZE(atomseq)) {
00191     PyErr_SetString(PyExc_ValueError, "atomids and molids must be same length");
00192     return NULL;
00193   }
00194 
00195   // Check appropriate num items for type
00196   if (!strcmp(type, "Atoms") && numitems != 1) {
00197     PyErr_Format(PyExc_ValueError, "Atom labels require 1 item, got %d",
00198                  numitems);
00199     return NULL;
00200   } else if (!strcmp(type, "Bonds") && numitems != 2) {
00201     PyErr_Format(PyExc_ValueError, "Bond labels require 2 items, got %d",
00202                  numitems);
00203     return NULL;
00204   } else if (!strcmp(type, "Angles") && numitems != 3) {
00205     PyErr_Format(PyExc_ValueError, "Angle labels require 3 items, got %d",
00206                  numitems);
00207     return NULL;
00208   } else if (!strcmp(type, "Dihedrals") && numitems != 4) {
00209     PyErr_Format(PyExc_ValueError, "Dihedral labels require 4 items, got %d",
00210                  numitems);
00211     return NULL;
00212   } else if (!strcmp(type, "Springs") && numitems != 4) {
00213     PyErr_Format(PyExc_ValueError, "Spring labels require 4 items, got %d",
00214                  numitems);
00215     return NULL;
00216   }
00217 
00218   // Unpack label tuples, checking molid and atomid for validity
00219   for (i = 0; i < numitems; i++) {
00220     m[i] = as_int(PySequence_Fast_GET_ITEM(molseq, i));
00221     a[i] = as_int(PySequence_Fast_GET_ITEM(atomseq, i));
00222 
00223     if (PyErr_Occurred())
00224       goto failure;
00225 
00226     // Check molid is correct
00227     mol = app->moleculeList->mol_from_id(m[i]);
00228     if (!mol) {
00229       PyErr_Format(PyExc_ValueError, "Invalid molecule id '%d'", m[i]);
00230       goto failure;
00231     }
00232 
00233     // Check atomid is correct
00234     if (a[i] < 0 || a[i] >= mol->nAtoms) {
00235       PyErr_Format(PyExc_ValueError, "Invalid atom id '%d'", a[i]);
00236       goto failure;
00237     }
00238   }
00239 
00240   // Add the label, but don't toggle the on/off status.
00241   ind = app->label_add(type, numitems, m, a, NULL, 0.0f, 0);
00242   if (ind < 0) {
00243     PyErr_SetString(PyExc_RuntimeError, "Could not add label. Is the number "
00244                     "of atoms correct for the label type?");
00245     goto failure;
00246   }
00247 
00248   // Get the dict corresponding to this label. The dict we return
00249   // corresponds to either the label we just created, or to an existing
00250   // label whose state we have not changed.  This makes it safe to use
00251   // label.add to get a proxy to a VMD label.
00252   glist = app->geometryList->geom_list(cat);
00253   return geom2dict((*glist)[ind]);
00254 
00255 failure:
00256   Py_XDECREF(molseq);
00257   Py_XDECREF(atomseq);
00258   return NULL;
00259 }
00260 
00261 
00262 static const char label_visible_doc[] =
00263 "Sets a label to be visible\n\n"
00264 "Args:\n"
00265 "    category (str): Label category, in ['atoms','bonds','dihedrals']\n"
00266 "    label (dict): Label to show, from output of label.add or similar\n"
00267 "    visible (bool): True to show, False to hide the label";
00268 static PyObject* py_label_visible(PyObject *self, PyObject *args,
00269                                   PyObject *kwargs) {
00270   const char *kwlist[] = {"category", "label", "visible", NULL};
00271   int shown = 1, rc = 0;
00272   PyObject *labeldict;
00273   char *type;
00274   int cat;
00275 
00276   if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO!O&:label.show", (char**)
00277                                    kwlist, &type, &PyDict_Type, &labeldict,
00278                                    convert_bool, &shown))
00279     return NULL;
00280 
00281   VMDApp *app;
00282   if (!(app = get_vmdapp()))
00283     return NULL;
00284 
00285   // Get label category
00286   cat = app->geometryList->geom_list_index(type);
00287   if (cat < 0) {
00288     PyErr_Format(PyExc_ValueError, "Unknown label category '%s'", type);
00289     return NULL;
00290   }
00291 
00292   // Look through all labels in category for one that matches
00293   GeomListPtr glist = app->geometryList->geom_list(cat);
00294   for (int i=0; i<glist->num(); i++) {
00295     if (dict2geom(labeldict, (*glist)[i])) {
00296       rc = app->label_show(type, i, shown);
00297       break;
00298     }
00299   }
00300 
00301   // If no label matched or show failed, rc will be FALSE
00302   if (!rc) {
00303     PyErr_SetString(PyExc_ValueError, "Could not show/hide label");
00304     return NULL;
00305   }
00306 
00307   Py_INCREF(Py_None);
00308   return Py_None;
00309 }
00310 
00311 
00312 static const char label_del_doc[] =
00313 "Delete a label\n\nArgs:\n"
00314 "    label (dict): Label to delete. Dictionary format generated by label.add\n";
00315 static PyObject* py_label_delete(PyObject *self, PyObject *args, PyObject *kwargs) {
00316   const char *kwlist[] = {"label", NULL};
00317   PyObject *labeldict;
00318   char *type;
00319   int rc = 0;
00320   int cat;
00321 
00322   if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO!:label.delete", (char**)
00323                                    kwlist, &type, &PyDict_Type, &labeldict))
00324     return NULL;
00325 
00326   VMDApp *app;
00327   if (!(app = get_vmdapp()))
00328     return NULL;
00329 
00330   // Get category
00331   cat = app->geometryList->geom_list_index(type);
00332   if (cat < 0) {
00333     PyErr_SetString(PyExc_ValueError, (char *)"Unknown label category");
00334     return NULL;
00335   }
00336 
00337   // Look for our label
00338   GeomListPtr glist = app->geometryList->geom_list(cat);
00339   for (int i = 0; i < glist->num(); i++) {
00340     if (dict2geom(labeldict, (*glist)[i])) {
00341       rc = app->label_delete(type, i);
00342       break;
00343     }
00344   }
00345 
00346   // rc will be TRUE only if deletion was successful
00347   if (!rc) {
00348     PyErr_SetString(PyExc_ValueError, "Could not delete label");
00349     return NULL;
00350   }
00351 
00352   Py_INCREF(Py_None);
00353   return Py_None;
00354 }
00355 
00356 
00357 // return Python list of values for this label.  Return None if this label
00358 // has no values (e.g. Atom labels), or NULL on error.
00359 static PyObject* getvalues(GeometryMol *g) {
00360   if (!g->has_value()) {
00361     Py_INCREF(Py_None);
00362     return Py_None;
00363   }
00364 
00365   ResizeArray<float> gValues(1024);
00366   if (!g->calculate_all(gValues)) {
00367     PyErr_SetString(PyExc_ValueError, "Cannot calculate label values");
00368     return NULL;
00369   }
00370 
00371   const int n = gValues.num();
00372   PyObject *newlist = PyList_New(n);
00373   for (int i=0; i<n; i++) {
00374     PyList_SET_ITEM(newlist, i, PyFloat_FromDouble(gValues[i]));
00375   }
00376 
00377   if (PyErr_Occurred()) {
00378     PyErr_SetString(PyExc_ValueError, "Problem getting label values");
00379     Py_DECREF(newlist);
00380     return NULL;
00381   }
00382 
00383   return newlist;
00384 }
00385 
00386 
00387 // getvalues(category, labeldict) -> list
00388 // returns list containing value of label for every frame in the label
00389 // if the label contains atoms from more than one molecule, only the first
00390 // molecule is cycled (this the behavior of GeometryMol).
00391 // XXX Note: this command is bad: the GeometryMol::calculate_all method
00392 // twiddles the frame of the molecule, so this command isn't read only as
00393 // its semantics would imply.  But it should be possible to fix this
00394 // in GeometryMol.
00395 static const char label_values_doc[] =
00396 "Get label values for every frame in the molecule. 'value' refers to the "
00397 "quantity the label measures-- for example, bond labels give distance.\n\n"
00398 "Args:\n"
00399 "    category (str): Label category to list, in ['atoms','bonds','dihedrals']\n"
00400 "    label (dict): Label to query, from output of label.add\n"
00401 "Returns:\n"
00402 "    (list of float) Label values for each frame in label, or None if\n"
00403 "        label has no values, like atom labels.";
00404 static PyObject* py_label_getvalues(PyObject *self, PyObject *args,
00405                                     PyObject *kwargs) {
00406   const char* kwlist[] = {"category", "label", NULL};
00407   PyObject *result = NULL;
00408   PyObject *labeldict;
00409   char *type;
00410   int cat;
00411 
00412   if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO!:label:get_values",
00413                                    (char**) kwlist, &type, &PyDict_Type,
00414                                    &labeldict))
00415     return NULL;
00416 
00417   VMDApp *app;
00418   if (!(app = get_vmdapp()))
00419     return NULL;
00420 
00421   cat = app->geometryList->geom_list_index(type);
00422   if (cat < 0) {
00423     PyErr_Format(PyExc_ValueError, "Unknown label category '%s'", type);
00424     return NULL;
00425   }
00426 
00427   GeomListPtr glist = app->geometryList->geom_list(cat);
00428   for (int i=0; i<glist->num(); i++) {
00429     if (dict2geom(labeldict, (*glist)[i])) {
00430       result = getvalues((*glist)[i]);
00431       break;
00432     }
00433   }
00434 
00435   if (!result) {
00436     PyErr_SetString(PyExc_ValueError, "Could not find label");
00437     return NULL;
00438   }
00439 
00440   return result;
00441 }
00442 
00443 static const char label_size_doc[] =
00444 "Sets text size for all labels.\n\n"
00445 "Args:\n"
00446 "    size (float): Text size, optional. Must be greater than zero.\n"
00447 "        If None, just returns size\n"
00448 "Returns:\n"
00449 "    (float) Text size";
00450 static PyObject* py_label_textsize(PyObject *self, PyObject *args,
00451                                    PyObject *kwargs) {
00452   const char *kwlist[] = {"size", NULL};
00453   PyObject *newobj = NULL;
00454   float newsize;
00455 
00456   if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O:label.text_size",
00457                                    (char**) kwlist, &newobj))
00458     return NULL;
00459 
00460   VMDApp *app;
00461   if (!(app = get_vmdapp()))
00462     return NULL;
00463 
00464   if (newobj) {
00465     if (!PyFloat_Check(newobj)) {
00466       PyErr_SetString(PyExc_TypeError, "Text size must be a float");
00467       return NULL;
00468     }
00469 
00470     newsize = PyFloat_AsDouble(newobj);
00471 
00472     if (PyErr_Occurred() || newsize <= 0) {
00473       PyErr_SetString(PyExc_ValueError, "Text size must be > 0");
00474       return NULL;
00475     }
00476 
00477     app->label_set_text_size(newsize);
00478   }
00479 
00480   return PyFloat_FromDouble(app->label_get_text_size());
00481 }
00482 
00483 
00484 static const char label_thick_doc[] =
00485 "Sets text thickness for all labels.\n\n"
00486 "Args:\n"
00487 "    thickness (float): Thickness, optional. Must be greater than zero.\n"
00488 "        If None, just returns thickness.\n"
00489 "Returns:\n"
00490 "    (float) Text thickness";
00491 static PyObject *py_label_textthickness(PyObject *self, PyObject *args,
00492                                         PyObject *kwargs) {
00493   const char *kwlist[] = {"thickness", NULL};
00494   PyObject *newobj = NULL;
00495   float newthick;
00496 
00497   if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O:label.text_thickness",
00498                                    (char**) kwlist, &newobj))
00499     return NULL;
00500 
00501   VMDApp *app;
00502   if (!(app = get_vmdapp()))
00503     return NULL;
00504 
00505   if (newobj) {
00506     if (!PyFloat_Check(newobj)) {
00507       PyErr_SetString(PyExc_TypeError, "Text thickness must be a float");
00508       return NULL;
00509     }
00510 
00511     newthick = PyFloat_AsDouble(newobj);
00512 
00513     if (PyErr_Occurred() || newthick <= 0) {
00514       PyErr_SetString(PyExc_ValueError, "Text thickness must be > 0");
00515       return NULL;
00516     }
00517 
00518     app->label_set_text_thickness(newthick);
00519   }
00520 
00521   return PyFloat_FromDouble(app->label_get_text_thickness());
00522 }
00523 
00524 
00525 static PyMethodDef LabelMethods[] = {
00526   {"listall", (PyCFunction)py_listall, METH_VARARGS | METH_KEYWORDS, listall_doc },
00527   {"add", (PyCFunction)py_label_add, METH_VARARGS | METH_KEYWORDS, label_add_doc },
00528   {"set_visible", (PyCFunction)py_label_visible, METH_VARARGS | METH_KEYWORDS, label_visible_doc},
00529   {"delete", (PyCFunction)py_label_delete, METH_VARARGS | METH_KEYWORDS, label_del_doc },
00530   {"get_values", (PyCFunction)py_label_getvalues, METH_VARARGS | METH_KEYWORDS, label_values_doc},
00531   {"text_size", (PyCFunction)py_label_textsize, METH_VARARGS | METH_KEYWORDS, label_size_doc},
00532   {"text_thickness", (PyCFunction)py_label_textthickness, METH_VARARGS | METH_KEYWORDS, label_thick_doc},
00533   {NULL, NULL}
00534 };
00535 
00536 
00537 static const char label_moddoc[] =
00538 "Methods to control the visibility and style of molecule labels";
00539 
00540 #if PY_MAJOR_VERSION >= 3
00541 static struct PyModuleDef labeldef = {
00542   PyModuleDef_HEAD_INIT,
00543   "label",
00544   label_moddoc,
00545   -1,
00546   LabelMethods,
00547 };
00548 #endif
00549 
00550 
00551 PyObject* initlabel(void) {
00552 #if PY_MAJOR_VERSION >= 3
00553   PyObject *m = PyModule_Create(&labeldef);
00554 #else
00555   PyObject *m = Py_InitModule3("label", LabelMethods, label_moddoc);
00556 #endif
00557 
00558   PyModule_AddStringConstant(m, "ATOM", "Atoms");
00559   PyModule_AddStringConstant(m, "BOND", "Bonds");
00560   PyModule_AddStringConstant(m, "ANGLE", "Angles");
00561   PyModule_AddStringConstant(m, "DIHEDRAL", "Dihedrals");
00562 
00563   return m;
00564 }
00565 

Generated on Fri Mar 29 02:46:04 2024 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002