1#A* -------------------------------------------------------------------
2#B* This file contains source code for the PyMOL computer program
3#C* Copyright (c) Schrodinger, LLC.
4#D* -------------------------------------------------------------------
5#E* It is unlawful to modify or remove this copyright notice.
6#F* -------------------------------------------------------------------
7#G* Please see the accompanying LICENSE file for further information.
8#H* -------------------------------------------------------------------
9#I* Additional authors of this source file include:
10#-*
11#-*
12#-*
13#Z* -------------------------------------------------------------------
14
15from __future__ import print_function
16
17# must match layer1/Setting.h
18cSetting_tuple = -1
19cSetting_blank = 0
20cSetting_boolean = 1
21cSetting_int = 2
22cSetting_float = 3
23cSetting_float3 = 4
24cSetting_color = 5
25cSetting_string = 6
26
27if True:
28
29    import traceback
30    from . import selector
31    from .shortcut import Shortcut
32    cmd = __import__("sys").modules["pymol.cmd"]
33    from .cmd import _cmd,lock,lock_attempt,unlock,QuietException, \
34          is_string, \
35          _feedback,fb_module,fb_mask, \
36          DEFAULT_ERROR, DEFAULT_SUCCESS, _raising, is_ok, is_error
37
38    # name -> index mapping
39    index_dict = _cmd.get_setting_indices()
40
41    # index -> name mapping
42    name_dict = dict((v,k) for (k,v) in index_dict.items())
43
44    name_list = list(index_dict.keys())
45    setting_sc = Shortcut(name_list)
46
47    # legacy
48    index_dict['ray_shadows'] =     index_dict['ray_shadow']
49
50    boolean_dict = {
51        "true" : 1,
52        "false": 0,
53        "on"   : 1,
54        "off"  : 0,
55        "1"    : 1,
56        "0"    : 0,
57        "1.0"  : 1,
58        "0.0"  : 0,
59        }
60
61    boolean_sc = Shortcut(boolean_dict.keys())
62
63    def _get_index(name):
64        '''Get setting index for given name. `name` may be abbreviated.
65        Raises QuietException for unknown names or ambiguous abbreviations.'''
66        if isinstance(name, int) or name.isdigit():
67            return int(name)
68        if name not in index_dict:
69            name = setting_sc.auto_err(name, 'Setting')
70        return index_dict[name]
71
72    def _get_name(index):
73        # legacy, in case someone used that in a script
74        return name_dict.get(index, "")
75
76    def get_index_list():
77        # legacy, in case someone used that in a script (e.g. grepset)
78        return list(name_dict.keys())
79
80    def get_name_list():
81        return name_list
82
83    def _validate_value(type, value):
84        if type == cSetting_boolean:  # (also support non-zero float for truth)
85            try: # number, non-zero, then interpret as TRUE
86                return 1 if float(value) else 0
87            except:
88                pass
89            return boolean_dict[boolean_sc.auto_err(str(value), "boolean")]
90        if type in (cSetting_int, cSetting_float):
91            if is_string(value) and boolean_sc.has_key(value):
92                value = boolean_dict[boolean_sc.auto_err(str(value), "boolean")]
93            if type == cSetting_int:
94                return int(value)
95            return float(value)
96        if type == cSetting_float3:  # some legacy handling req.
97            if not is_string(value):
98                v = value
99            elif ',' in value:
100                v = cmd.safe_eval(value)
101            else:
102                v = value.split()
103            return (float(v[0]), float(v[1]), float(v[2]))
104        if type == cSetting_color:
105            return str(value)
106        if type == cSetting_string:
107            v = str(value)
108            # strip outermost quotes (cheesy approach)
109            if len(v) > 1 and v[0] == v[-1] and v[0] in ('"', "'"):
110                v = v[1:-1]
111            return v
112        raise Exception
113
114    ###### API functions
115
116    def set_bond(name, value, selection1, selection2=None,
117                 state=0, updates=1, log=0, quiet=1, _self=cmd):
118        '''
119DESCRIPTION
120
121    "set_bond" changes per-bond settings for all bonds which exist
122    between two selections of atoms.
123
124USAGE
125
126    set_bond name, value, selection1 [, selection2 ]
127
128ARGUMENTS
129
130    name = string: name of the setting
131
132    value = string: new value to use
133
134    selection1 = string: first set of atoms
135
136    selection2 = string: seconds set of atoms {default: (selection1)}
137
138EXAMPLE
139
140    set_bond stick_transparency, 0.7, */n+c+ca+o
141
142
143NOTES
144
145    The following per-bond settings are currently implemented.  Others
146    may seem to be recognized but will currently have no effect when
147    set at the per-bond level.
148
149    * valence
150    * line_width
151    * line_color
152    * stick_radius
153    * stick_color
154    * stick_transparency
155
156    Note that if you attempt to use the "set" command with a per-bond
157    setting over a selection of atoms, the setting change will appear
158    to take, but no change will be observed.
159
160PYMOL API
161
162    cmd.set_bond ( string name, string value,
163                   string selection1,
164                   string selection2,
165                   int state, int updates, log=0, quiet=1)
166
167       '''
168        r = DEFAULT_ERROR
169        selection1 = selector.process(selection1)
170        selection2 = selector.process(selection2) if selection2 else selection1
171        index = _get_index(str(name))
172        if log:
173            name = name_dict.get(index, name)
174            _self.log('', "cmd.set_bond('%s',%s,%s,%s,%s)\n" % (name, repr(value),
175                repr(selection1), repr(selection2), state))
176        if True:
177            try:
178                _self.lock(_self)
179                type = _cmd.get_setting_type(index)
180                if type < 0:
181                    print("Error: unable to get setting type.")
182                    raise QuietException
183                try:
184                    v = (type, _validate_value(type, value))
185                    r = _cmd.set_bond(_self._COb,int(index),v,
186                                 "("+selection1+")","("+selection2+")",
187                                 int(state)-1,int(quiet),
188                                 int(updates))
189                except QuietException:
190                    pass
191                except:
192                    if(_feedback(fb_module.cmd,fb_mask.debugging,_self)):
193                        traceback.print_exc()
194                    raise _self.pymol.CmdException("invalid value: %s" % repr(value))
195            finally:
196                _self.unlock(r,_self)
197        if _self._raising(r,_self): raise QuietException
198        return r
199
200
201    def set(name, value=1, selection='', state=0, updates=1, log=0,
202            quiet=1,_self=cmd):
203
204        '''
205DESCRIPTION
206
207    "set" changes global, object, object-state, or per-atom settings.
208
209USAGE
210
211    set name [,value [,selection [,state ]]]
212
213ARGUMENTS
214
215    name = string: setting name
216
217    value = string: a setting value {default: 1}
218
219    selection = string: name-pattern or selection-expression
220    {default:'' (global)}
221
222    state = a state number {default: 0 (per-object setting)}
223
224EXAMPLES
225
226    set orthoscopic
227
228    set line_width, 3
229
230    set surface_color, white, 1hpv
231
232    set sphere_scale, 0.5, elem C
233
234NOTES
235
236    The default behavior (with a blank selection) is global.  If the
237    selection is "all", then the setting entry in each individual
238    object will be changed.  Likewise, for a given object, if state is
239    zero, then the object setting will be modified.  Otherwise, the
240    setting for the indicated state within the object will be
241    modified.
242
243    If a selection is provided as opposed to an object name, then the
244    atomic setting entries are modified.
245
246    The following per-atom settings are currently implemented.  Others
247    may seem to be recognized but will have no effect when set on a
248    per-atom basis.
249
250    * sphere_color
251    * surface_color
252    * mesh_color
253    * label_color
254    * dot_color
255    * cartoon_color
256    * ribbon_color
257    * transparency (for surfaces)
258    * sphere_transparency
259
260    Note that if you attempt to use the "set" command with a per-bond
261    setting over a selection of atoms, the setting change will appear
262    to take, but no change will be observed.  Please use the
263    "set_bond" command for per-bond settings.
264
265
266PYMOL API
267
268    cmd.set(string name, string value, string selection, int state,
269            int updates, int quiet)
270
271SEE ALSO
272
273    get, set_bond
274
275'''
276        r = DEFAULT_ERROR
277        selection = selector.process(selection)
278        index = _get_index(name)
279        if log:
280            name = name_dict.get(index, name)
281            _self.log('', "cmd.set('%s',%s,%s,%s)\n" % (name, repr(value), repr(selection), state))
282        if True:
283            try:
284                _self.lock(_self)
285                type = _cmd.get_setting_type(index)
286                if type < 0:
287                    print("Error: unable to get setting type.")
288                    raise QuietException
289                try:
290                    v = (type, _validate_value(type, value))
291                    r = _cmd.set(_self._COb,int(index),v,
292                                     selection,
293                                     int(state)-1,int(quiet),
294                                     int(updates))
295                except QuietException:
296                    pass
297                except:
298                    if(_feedback(fb_module.cmd,fb_mask.debugging,_self)):
299                        traceback.print_exc()
300                    raise _self.pymol.CmdException("invalid value: %s" % repr(value))
301            finally:
302                _self.unlock(r,_self)
303        if _self._raising(r,_self): raise QuietException
304        return r
305
306    def unset(name, selection='', state=0, updates=1, log=0, quiet=1, _self=cmd):
307        '''
308DESCRIPTION
309
310    "unset" clear non-global settings and zeros out global settings.
311
312USAGE
313
314    unset name [,selection [,state ]]
315
316EXAMPLE
317
318    unset orthoscopic
319
320    unset surface_color, 1hpv
321
322    unset sphere_scale, elem C
323
324NOTES
325
326    If selection is not provided, unset changes the named global
327    setting to a zero or off value.
328
329    If a selection is provided, then "unset" undefines per-object,
330    per-state, or per-atom settings.
331
332PYMOL API
333
334    cmd.unset(string name, string selection, int state, int updates,
335                int log)
336
337SEE ALSO
338
339    set, set_bond
340
341        '''
342        r = DEFAULT_ERROR
343        selection = selector.process(selection)
344        index = _get_index(str(name))
345        if log:
346            name = name_dict.get(index, name)
347            _self.log('', "cmd.unset('%s',%s,%s)\n" % (name, repr(selection), state))
348        if True:
349                try:
350                    _self.lock(_self)
351                    try:
352                        r = _cmd.unset(_self._COb,int(index),selection,
353                                            int(state)-1,int(quiet),
354                                            int(updates))
355                    except:
356                        if(_feedback(fb_module.cmd,fb_mask.debugging,_self)):
357                            traceback.print_exc()
358                            raise QuietException
359                        print("Error: unable to unset setting value.")
360                finally:
361                    _self.unlock(r,_self)
362        return r
363
364    def unset_bond(name,selection1,selection2=None,state=0,updates=1,log=0,quiet=1,_self=cmd):
365        '''
366DESCRIPTION
367
368    "unset_bond" removes a per-bond setting for a given set of bonds.
369
370USAGE
371
372    unset name [,selection [, selection [,state ]]]
373
374        '''
375        r = DEFAULT_ERROR
376        selection1 = selector.process(selection1)
377        selection2 = selector.process(selection2) if selection2 else selection1
378        index = _get_index(str(name))
379        if log:
380            name = name_dict.get(index, name)
381            _self.log('', "cmd.unset_bond('%s',%s,%s,%s)\n" % (name,
382                repr(selection1), repr(selection2), state))
383        if True:
384            try:
385                _self.lock(_self)
386                try:
387                    r = _cmd.unset_bond(_self._COb,int(index),selection1,selection2,
388                                   int(state)-1,int(quiet),
389                                   int(updates))
390                except:
391                    if(_feedback(fb_module.cmd,fb_mask.debugging,_self)):
392                        traceback.print_exc()
393                        raise QuietException
394                    print("Error: unable to unset setting value.")
395            finally:
396                _self.unlock(r,_self)
397        if _self._raising(r,_self): raise QuietException
398        return r
399
400    def get_setting(name,object='',state=0,_self=cmd): # INTERNAL
401        return get_setting_tuple_new(name, object, state, _self)[1]
402
403    def get(name, selection='', state=0, quiet=1, _self=cmd):
404        '''
405DESCRIPTION
406
407    "get" prints out the current value of a setting.
408
409USAGE
410
411    get name [, selection [, state ]]
412
413EXAMPLE
414
415    get line_width
416
417ARGUMENTS
418
419    name = string: setting name
420
421    selection = string: object name (selections not yet supported)
422
423    state = integer: state number
424
425NOTES
426
427    "get" currently only works with global, per-object, and per-state
428    settings.  Atom level settings get be queried with "iterate" (e.g.
429    iterate all, print s.line_width)
430
431PYMOL API
432
433    cmd.get(string name, string object, int state, int quiet)
434
435SEE ALSO
436
437    set, set_bond, get_bond
438
439    '''
440
441        state = int(state)
442        i = _get_index(name)
443        r = get_setting_text(i, str(selection), state, _self)
444        if is_ok(r) and (r is not None):
445            if not int(quiet):
446                name = name_dict.get(i, name)
447                r_str = str(r)
448                if len(r_str) > 200:
449                    r_str = r_str[:185] + '... (truncated)'
450                if(selection==''):
451                    print(" get: %s = %s"%(name,r_str))
452                elif state<=0:
453                    print(" get: %s = %s in object %s"%(name,r_str,selection))
454                else:
455                    print(" get: %s = %s in object %s state %d"%(name,r_str,selection,state))
456        return r
457
458    def get_setting_tuple_new(name,object='',state=0,_self=cmd): # INTERNAL
459        i = _get_index(name)
460        with _self.lockcm:
461            return _cmd.get_setting_of_type(_self._COb, i, str(object), int(state) - 1, cSetting_tuple)
462
463    def get_setting_tuple(name,object='',state=0,_self=cmd): # INTERNAL
464        r = get_setting_tuple_new(name, object, state, _self)
465        if r[0] != cSetting_float3:
466            # legacy API
467            r = (r[0], (r[1],))
468        return r
469
470    def get_setting_boolean(name,object='',state=0,_self=cmd): # INTERNAL
471        i = _get_index(name)
472        with _self.lockcm:
473            return _cmd.get_setting_of_type(_self._COb, i, str(object), int(state) - 1, cSetting_boolean)
474
475    def get_setting_int(name,object='',state=0,_self=cmd): # INTERNAL
476        i = _get_index(name)
477        with _self.lockcm:
478            return _cmd.get_setting_of_type(_self._COb, i, str(object), int(state) - 1, cSetting_int)
479
480    def get_setting_float(name,object='',state=0,_self=cmd): # INTERNAL
481        i = _get_index(name)
482        with _self.lockcm:
483            return _cmd.get_setting_of_type(_self._COb, i, str(object), int(state) - 1, cSetting_float)
484
485    def get_setting_text(name,object='',state=0,_self=cmd):  # INTERNAL
486        i = _get_index(name)
487        with _self.lockcm:
488            return _cmd.get_setting_of_type(_self._COb, i, str(object), int(state) - 1, cSetting_string)
489
490    def get_setting_updates(object='', state=0, _self=cmd): # INTERNAL
491        r = []
492        if lock_attempt(_self):
493            try:
494                r = _cmd.get_setting_updates(_self._COb, object, state-1)
495            finally:
496                _self.unlock(r,_self)
497        return r
498
499    def get_bond(name, selection1, selection2=None,
500                 state=0, updates=1, quiet=1, _self=cmd):
501        '''
502DESCRIPTION
503
504    "get_bond" gets per-bond settings for all bonds which exist
505    between two selections of atoms.
506
507USAGE
508
509    get_bond name, selection1 [, selection2 ]
510
511ARGUMENTS
512
513    name = string: name of the setting
514
515    selection1 = string: first set of atoms
516
517    selection2 = string: seconds set of atoms {default: (selection1)}
518
519EXAMPLE
520
521    get_bond stick_transparency, */n+c+ca+o
522
523
524NOTES
525
526    The following per-bond settings are currently implemented.  Others
527    may seem to be recognized but will currently have no effect when
528    set at the per-bond level.
529
530    * valence
531    * line_width
532    * line_color
533    * stick_radius
534    * stick_color
535    * stick_transparency
536
537PYMOL API
538
539    cmd.get_bond ( string name,
540                   string selection1,
541                   string selection2,
542                   int state, int updates, quiet=1)
543
544       '''
545        state, quiet = int(state), int(quiet)
546        r = DEFAULT_ERROR
547        selection1 = selector.process(selection1)
548        selection2 = selector.process(selection2) if selection2 else selection1
549
550        index = _get_index(str(name))
551        if True:
552            try:
553                _self.lock(_self)
554                try:
555                    r = _cmd.get_bond(_self._COb,int(index),
556                                 "("+selection1+")","("+selection2+")",
557                                 int(state)-1,int(quiet),
558                                 int(updates))
559                except:
560                    traceback.print_exc()
561                    if(_feedback(fb_module.cmd,fb_mask.debugging,_self)):
562                        traceback.print_exc()
563                        print("Error: unable to get_bond info.")
564                    raise QuietException
565            finally:
566                _self.unlock(r,_self)
567        if _self._raising(r,_self): raise QuietException
568        if not quiet:
569            name = name_dict.get(index, name)
570            suffix = ' state %d' % state if state > 0 else ''
571            for model, vlist in r:
572                print(' %s = %s for object %s' % (name, _self.get(name, model), model))
573                for idx1, idx2, value in vlist:
574                    if value is None:
575                        continue
576                    print(' %s = %s between (%s`%d)-(%s`%d%s)' % (name,
577                            value, model, idx1, model, idx2, suffix))
578        return r
579
580    def unset_deep(settings='', object='*', updates=1, quiet=1, _self=cmd):
581        '''
582DESCRIPTION
583
584    Unset all object, object-state, atom, and bond level settings.
585
586    Note: Does currently NOT unset atom-state level settings. Check for
587    atom-state level settings with:
588    PyMOL> iterate_state 1, *, print(list(s))
589    Unset e.g. atom-state level "label_screen_point" (index 728) with:
590    PyMOL> alter_state 1, *, del s[728]
591
592ARGUMENTS
593
594    settings = str: space separated list of setting names or empty string
595    for all settings {default: }
596
597    object = str: name of one object or * for all objects {default: *}
598        '''
599        quiet = int(quiet)
600        kwargs = {'quiet': quiet, 'updates': 0, '_self': _self}
601
602        if not settings:
603            settings = iter(name_dict) # index iterator
604        elif _self.is_string(settings):
605            settings = settings.split()
606
607        if object in ['all', '*']:
608            object = '*'
609            selection = '(*)'
610        else:
611            selection = None
612            try:
613                if _self.get_type(object) in (
614                        'object:group', 'object:molecule'):
615                    selection = '(' + object + ')'
616            except:
617                pass
618
619        # 0 (object-level) and 1-N (object-state-level)
620        states = range(_self.count_states(object) + 1)
621
622        for setting in settings:
623            try:
624                for state in states:
625                    unset(setting, object, state=state, **kwargs)
626                if selection:
627                    unset(setting, selection, **kwargs)
628            except:
629                if not quiet:
630                    print(' Setting: %s unset failed' % setting)
631
632        if int(updates):
633            _self.rebuild(object)
634