00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
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
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
00046 value = g->ok() ? PyFloat_FromDouble(g->calculate()) : Py_None;
00047 PyDict_SetItemString(newdict, "value", value);
00048
00049
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
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
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
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
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
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
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
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
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
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
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
00249
00250
00251
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
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
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
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
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
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
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
00358
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
00388
00389
00390
00391
00392
00393
00394
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