Source code for schrodinger.maestro.markers

"""
Module for placing markers in the Workspace.
"""

import _pymaecxx

import schrodinger
from schrodinger.structutils import color

maestro = schrodinger.get_maestro()


[docs]class Icons(object): """ Constants for marker icons """ NONE = _pymaecxx.MM_GRAPHICS_ICON_NONE LOCK = _pymaecxx.MM_GRAPHICS_ICON_LOCK SPRING = _pymaecxx.MM_GRAPHICS_ICON_SPRING EYE = _pymaecxx.MM_GRAPHICS_ICON_EYE EQUALS = _pymaecxx.MM_GRAPHICS_ICON_EQUALS FRAGMARK = _pymaecxx.MM_GRAPHICS_ICON_FRAGMARK CHECKMARK = _pymaecxx.MM_GRAPHICS_ICON_CHECKMARK DIAMOND = _pymaecxx.MM_GRAPHICS_ICON_DIAMOND RSI = _pymaecxx.MM_GRAPHICS_ICON_RSI TRANSROT = _pymaecxx.MM_GRAPHICS_ICON_TRANSROT TORSIONROTATE = _pymaecxx.MM_GRAPHICS_ICON_TORSIONROTATE CHECK = _pymaecxx.MM_GRAPHICS_ICON_CHECK SCISSORS = _pymaecxx.MM_GRAPHICS_ICON_SCISSORS BREAK = _pymaecxx.MM_GRAPHICS_ICON_BREAK DDRIVE = _pymaecxx.MM_GRAPHICS_ICON_DDRIVE LONEPAIR = _pymaecxx.MM_GRAPHICS_ICON_LONEPAIR SMAGGLASS = _pymaecxx.MM_GRAPHICS_ICON_SMAGGLASS GMAGGLASS = _pymaecxx.MM_GRAPHICS_ICON_GMAGGLASS MAGGLASS = _pymaecxx.MM_GRAPHICS_ICON_MAGGLASS WMAGGLASS = _pymaecxx.MM_GRAPHICS_ICON_WMAGGLASS IMAGGLASS = _pymaecxx.MM_GRAPHICS_ICON_IMAGGLASS VMAGGLASS = _pymaecxx.MM_GRAPHICS_ICON_VMAGGLASS EMAGGLASS = _pymaecxx.MM_GRAPHICS_ICON_EMAGGLASS ARROWDOWN = _pymaecxx.MM_GRAPHICS_ICON_ARROWDOWN SCANATOM = _pymaecxx.MM_GRAPHICS_ICON_SCANATOM SCANDIST = _pymaecxx.MM_GRAPHICS_ICON_SCANDIST SCANANGLE = _pymaecxx.MM_GRAPHICS_ICON_SCANANGLE
[docs]class Marker(object): """ Class to manipulate a set of markers in the Workspace by ASLs. The only parameter that can be controlled is the color of the marker. This marker is useful for showing a picked ligand or residue, for example. """ # A count of how many names we have generated. Used to ensure that the # generated names are unique. _name_count = 1
[docs] def __init__(self, name=None, asl="NOT all", color=(1, 1, 1)): """ Create a Marker object :type name: str :param name: The name of the marker group. Used for unique identification of the marker within Maestro. By default a unique string identifier is generated. :type asl: str :param asl: The ASL defining which atoms should be marked. By default no atoms are marked. :type color: tuple of 3 floats :param red: The amount of red, green, and blue to use, each ranging from 0.0 to 1.0. Default is white (1, 1, 1). """ if name is None: # Generate a unique identifier for this Marker instance. Pad the # count with zeroes to reduce the chance of the user picking the # same name for their own marker. name = "Marker-%08i" % self._name_count self.__class__._name_count += 1 self._name = name self.setColor(color) if maestro: maestro.command('markers %s %s %s' % (self._color, self._name, asl))
@property def name(self): """ :return: Marker name :rtype: str """ return self._name
[docs] def hide(self): """ Hide the markers """ if maestro: maestro.command('hidemarkers %s' % self._name)
[docs] def show(self, asl=None): """ Show the markers. If a new ASL is specified, then the previous markers get cleared and the new atoms are marked. :type asl: str :param asl: The ASL defining which atoms should be marked. This replaces any previous ASL marked by this Marker instance. """ if maestro: if asl: self.hide() maestro.command('markers %s %s %s' % (self._color, self._name, asl)) maestro.command('showmarkers ' + self._name)
[docs] def setVisible(self, show): """ Show the marker if <show> is True; hide otherwise. Useful for hooking up to a slot of a QCheckBox. """ if show: self.show() else: self.hide()
[docs] def setAsl(self, asl): """ Update the ASL of this marker to the given string, without affecting the visibility of it. """ maestro.command('markers %s %s %s' % (self._color, self._name, asl))
[docs] def setColor(self, color): """ :param color: The amount of red, green, and blue to use, each ranging from 0.0 to 1.0. :type color: tuple(float, float, float) """ red, green, blue = color if any([x < 0.0 or x > 1.0 for x in color]): raise ValueError("Color values must be between 0 and 1") self._color = f'r={red:.2f} g={green:.2f} b={blue:.2f}'
class _BaseMarker(object): """ Base class for the marker classes. This class cannot be instantiated directly. """ SETTINGS_VARS = {"color", "icon", "text", "alt_color", "highlight"} def __init__(self): # The variable declarations here are for reference purposes only (both # for human readability and static code analyzers). This class cannot # be instantiated directly. self._color = None self._icon = None self._text = None self._alt_color = None self._atoms = None self._highlight = None self._handle = None raise NotImplementedError def _getEntryAtomNumbers(self, atoms): """ Retrieve entry ids and atom numbers by entry for a list of atoms :param atoms: A list of atoms to retrieve data for :type atoms: list(schrodinger.structure._StructureAtom) :return: A list of tuples, where each tuple contains the entry id and number-by-entry of a single atom. :rtype: list """ atom_data = [] for cur_atom in atoms: eid = cur_atom.entry_id num_by_entry = cur_atom.number_by_entry cur_atom_data = (eid, num_by_entry) atom_data.append(cur_atom_data) return atom_data def _createMarker(self): """ Create a marker using the criteria currently set in instance variables """ raise NotImplementedError def _getWorkspaceAtomNumbers(self): """ Get the workspace atom numbers for the atoms described in self._atoms :return: A list of atom numbers relative to the workspace structure :rtype: list :raise NotInWorkspaceError: If an atom described in self._atoms is not currently in the workspace. """ ws_atom_nums = [] ws_struc = maestro.workspace_get(copy=False) for (eid, num_by_entry) in self._atoms: for ws_atom in ws_struc.atom: if (ws_atom.entry_id == eid and ws_atom.number_by_entry == num_by_entry): ws_atom_nums.append(ws_atom.index) break else: err = ("Atom not found in workspace: entry %s, " "number_by_entry %i" % (eid, num_by_entry)) raise NotInWorkspaceError(err) return ws_atom_nums def hide(self): """ Hide this marker. """ if self._handle is not None: _pymaecxx.mae_remove_object(self._handle) self._handle = None def show(self): """ Show this marker. """ if self._handle is None: self._createMarker() def setVisible(self, show): """ If "show" is True, show the marker, otherwise hide it. """ if show: self.show() else: self.hide() def _colorToRgb(self, incolor): """ Given a color of any accepted format, return an RGB tuple (range 0-1). If None is specified, color white is returned. :return: Tuple of RGB values (range 0-1) :rtype: tuple """ if isinstance(incolor, (int, str)): outcolor = color.Color(incolor).rgb_float return tuple(outcolor) elif incolor is None: return (1.0, 1.0, 1.0) # white elif isinstance(incolor, color.Color): return tuple(incolor.rgb_float) elif isinstance(incolor, (tuple, list)): r, g, b = incolor if not (0 <= r <= 1 and 0 <= g <= 1 and 0 <= b <= 1): raise ValueError("RGB values must be between 0.0 and 1.0") return (r, g, b) else: raise TypeError("Unrecognized color") def _altColorToRgb(self, alt_color): """ Given a color of any accepted format, return an RGB tuple (range 0-1). If None is specified, None is returned. :return: Tuple of RGB values (range 0-1) or None if `alt_color` is None :rtype: tuple or None """ if alt_color is None: return None else: return self._colorToRgb(alt_color) def _getAltColor(self): """ Get the alternate color. :return: List of RGB values (range 0-1) :rtype: list """ if self._alt_color is None: return self._color else: return self._alt_color def setColor(self, marker_color): """ Change the marker and icon color :param marker_color: The color of the marker. May be an RGB tuple, color name, color index, or `schrodinger.structutils.color` instance. If not given, white will be used. :type marker_color: tuple, str, int, or `schrodinger.structutils.color` """ self._color = self._colorToRgb(marker_color) self.update() def setIcon(self, icon): """ Change the marker icon :param icon: The icon to draw next to the marker. Should be one the Icons.* constants. If not given, no icon will be drawn. :type icon: int """ self._icon = icon self.update() def setText(self, text): """ Change the marker text :param text: The text to display next to the marker. If not given, no text will be displayed. :type text: str """ self._text = text self.update() def setAltColor(self, alt_color): """ Change the text color :param alt_color: The color of the text. May be an RGB tuple, color name, color index, or `schrodinger.structutils.color` instance. If not given, the marker color will be used. :type alt_color: tuple, str, int, or `schrodinger.structutils.color` """ self._alt_color = self._altColorToRgb(alt_color) self.update() def setHighlight(self, highlight): """ Change whether the marker is highlighted. A highlighted marker is indicated with thicker lines and is colored using `alt_color` instead of `color`. :param highlight: Whether the marker should be highlighted. :type highlight: bool """ self._highlight = highlight self.update() def update(self): """ Update the marker in response to the workspace contents changing. If any marked atom is not in the workspace, the marker will be deleted. """ self.hide() try: self._createMarker() except NotInWorkspaceError: self._handle = None def getSettings(self): """ Get the current marker settings. :return: A dictionary of the marker settings. The keys of this dictionary will be `SETTING_VARS`. :rtype: dict """ return {name: getattr(self, "_" + name) for name in self.SETTINGS_VARS} def applySettings(self, settings): """ Apply the specified marker settings :param settings: A dictionary of marker settings. The keys of this dictionary must appear in `SETTINGS_VARS`. Note that not all keys need to be specified. :type settings: dict """ if not self.SETTINGS_VARS.issuperset(list(settings)): raise ValueError("Unrecognized marker setting key") for name, val in settings.items(): if name == "color": val = self._colorToRgb(val) elif name == "alt_color": val = self._altColorToRgb(val) setattr(self, "_" + name, val) self.update() @classmethod def defaultSettings(cls): """ Get the default marker settings :return: A dictionary of marker settings. The keys of this dictionary will be `SETTING_VARS`. :rtype: dict """ settings = dict.fromkeys(cls.SETTINGS_VARS) settings["icon"] = Icons.NONE settings["highlight"] = False if "text" in settings: settings["text"] = "" return settings
[docs]class AtomMarker(_BaseMarker): """ Class for marking a single atom. """ SETTINGS_VARS = _BaseMarker.SETTINGS_VARS - {"text"}
[docs] def __init__(self, atom, marker_color=None, icon=Icons.NONE, alt_color=None, highlight=False): """ :param atom: The atom to mark :type atom: `schrodinger.structure._StructureAtom` :param marker_color: The color of the marker. May be an RGB tuple, color name, color index, or `schrodinger.structutils.color` instance. If not given, white will be used. :type marker_color: tuple, str, int, or `schrodinger.structutils.color` :param icon: The icon to draw next to the marker. Should be one the Icons.* constants. If not given, no icon will be drawn. :type icon: int :param alt_color: The alternate marker color. This color is always used for text, and is used for the marker and icon when `highlight` is True. If not given, `color` will be used. :type alt_color: tuple, str, int, or `schrodinger.structutils.color` :param highlight: Whether the marker should be highlighted. A highlighted marker is indicated with thicker lines and is colored using `alt_color` instead of `color`. :type highlight: bool """ self._atoms = self._getEntryAtomNumbers([atom]) self._color = self._colorToRgb(marker_color) self._alt_color = self._altColorToRgb(alt_color) self._icon = icon self._highlight = highlight self._handle = None self.update()
def _createMarker(self): atoms = self._getWorkspaceAtomNumbers() r, g, b = self._color tr, tg, tb = self._getAltColor() self._handle = _pymaecxx.mae_create_atom_marker(atoms[0], r, g, b, tr, tg, tb, self._highlight, self._icon)
[docs] def setText(self, text): raise RuntimeError("Text cannot be set for atom markers")
[docs]class PairMarker(_BaseMarker): """ Class for marking two atoms. Useful for marking bonds and distances. """
[docs] def __init__(self, atom1, atom2, marker_color=None, icon=Icons.NONE, text="", alt_color=None, highlight=False): """ :param atom1: The first atom to mark :type atom1: `schrodinger.structure._StructureAtom` :param atom2: The second atom to mark :type atom2: `schrodinger.structure._StructureAtom` :param marker_color: The color of the marker. May be an RGB tuple, color name, color index, or `schrodinger.structutils.color` instance. If not given, white will be used. :type marker_color: tuple, str, int, or `schrodinger.structutils.color` :param icon: The icon to draw next to the marker. Should be one the Icons.* constants. If not given, no icon will be drawn. :type icon: int :param text: The text to display next to the marker. If not given, no text will be displayed. :type text: str :param alt_color: The alternate marker color. This color is always used for text, and is used for the marker and icon when `highlight` is True. If not given, `color` will be used. :type alt_color: tuple, str, int, or `schrodinger.structutils.color` :param highlight: Whether the marker should be highlighted. A highlighted marker is indicated with thicker lines and is colored using `alt_color` instead of `color`. :type highlight: bool :note: Either an icon or text may be displayed on a marker, but not both. If both are given, only the text will be shown. """ self._atoms = self._getEntryAtomNumbers([atom1, atom2]) self._color = self._colorToRgb(marker_color) self._icon = icon self._text = text self._alt_color = self._altColorToRgb(alt_color) self._highlight = highlight self._handle = None self.update()
def _createMarker(self): atoms = self._getWorkspaceAtomNumbers() r, g, b = self._color tr, tg, tb = self._getAltColor() self._handle = _pymaecxx.mae_create_atom_pair_marker( atoms[0], atoms[1], r, g, b, tr, tg, tb, self._highlight, self._text, self._icon)
[docs]class TripleMarker(_BaseMarker): """ Class for marking three atoms. Useful for marking bond angles. """
[docs] def __init__(self, atom1, atom2, atom3, marker_color=None, icon=Icons.NONE, text="", alt_color=None, highlight=False): """ :param atom1: The first atom to mark :type atom1: `schrodinger.structure._StructureAtom` :param atom2: The second atom to mark :type atom2: `schrodinger.structure._StructureAtom` :param atom3: The third atom to mark :type atom3: `schrodinger.structure._StructureAtom` :param marker_color: The color of the marker. May be an RGB tuple, color name, color index, or `schrodinger.structutils.color` instance. If not given, white will be used. :type marker_color: tuple, str, int, or `schrodinger.structutils.color` :param icon: The icon to draw next to the marker. Should be one the Icons.* constants. If not given, no icon will be drawn. :type icon: int :param text: The text to display next to the marker. If not given, no text will be displayed. :type text: str :param alt_color: The alternate marker color. This color is always used for text, and is used for the marker and icon when `highlight` is True. If not given, `color` will be used. :type alt_color: tuple, str, int, or `schrodinger.structutils.color` :param highlight: Whether the marker should be highlighted. A highlighted marker is indicated with thicker lines and is colored using `alt_color` instead of `color`. :type highlight: bool :note: Either an icon or text may be displayed on a marker, but not both. If both are given, only the text will be shown. """ self._atoms = self._getEntryAtomNumbers([atom1, atom2, atom3]) self._color = self._colorToRgb(marker_color) self._icon = icon self._text = text self._alt_color = self._altColorToRgb(alt_color) self._highlight = highlight self._handle = None self.update()
def _createMarker(self): atoms = self._getWorkspaceAtomNumbers() r, g, b = self._color tr, tg, tb = self._getAltColor() self._handle = _pymaecxx.mae_create_atom_triple_marker( atoms[0], atoms[1], atoms[2], r, g, b, tr, tg, tb, self._highlight, self._text, self._icon)
[docs]class QuadMarker(_BaseMarker): """ Class for marking four atoms. Useful for marking dihedral angles. """
[docs] def __init__(self, atom1, atom2, atom3, atom4, marker_color=None, icon=Icons.NONE, text="", alt_color=None, highlight=False): """ :param atom1: The first atom to mark :type atom1: `schrodinger.structure._StructureAtom` :param atom2: The second atom to mark :type atom2: `schrodinger.structure._StructureAtom` :param atom3: The third atom to mark :type atom3: `schrodinger.structure._StructureAtom` :param atom3: The fourth atom to mark :type atom3: `schrodinger.structure._StructureAtom` :param marker_color: The color of the marker. May be an RGB tuple, color name, color index, or `schrodinger.structutils.color` instance. If not given, white will be used. :type marker_color: tuple, str, int, or `schrodinger.structutils.color` :param icon: The icon to draw next to the marker. Should be one the Icons.* constants. If not given, no icon will be drawn. :type icon: int :param text: The text to display next to the marker. If not given, no text will be displayed. :type text: str :param alt_color: The alternate marker color. This color is always used for text, and is used for the marker and icon when `highlight` is True. If not given, `color` will be used. :type alt_color: tuple, str, int, or `schrodinger.structutils.color` :param highlight: Whether the marker should be highlighted. A highlighted marker is indicated with thicker lines and is colored using `alt_color` instead of `color`. :type highlight: bool :note: Either an icon or text may be displayed on a marker, but not both. If both are given, only the text will be shown. """ self._atoms = self._getEntryAtomNumbers([atom1, atom2, atom3, atom4]) self._color = self._colorToRgb(marker_color) self._icon = icon self._text = text self._alt_color = self._altColorToRgb(alt_color) self._highlight = highlight self._handle = None self.update()
def _createMarker(self): atoms = self._getWorkspaceAtomNumbers() r, g, b = self._color tr, tg, tb = self._getAltColor() self._handle = _pymaecxx.mae_create_atom_quad_marker( atoms[0], atoms[1], atoms[2], atoms[3], r, g, b, tr, tg, tb, self._highlight, self._text, self._icon)
[docs]class LineMarker(_BaseMarker): """ Class for marking two arbitrary points. Useful for marking lines between two graphics3d objects. """
[docs] def __init__(self, coord1, entry_id1, coord2, entry_id2, marker_color=None, alt_color=None, line_width=1): """ :param coord1: The first coordinate to mark :type coord1: List of int :param coord1: The second coordinate to mark :type coord1: List of int :param entry_id1: Entry ID the first coordinate is associated with. This is to simplify when the line marker should be shown and hidden. :type entry_id1: int :param entry_id2: Entry ID the second coordinate is associated with. :type entry_id2: int :param marker_color: The color of the marker. May be an RGB tuple, color name, color index, or `schrodinger.structutils.color` instance. If not given, white will be used. :type marker_color: tuple, str, int, or `schrodinger.structutils.color` :param alt_color: The alternate marker color. This color is always used for text, and is used for the marker and icon when `highlight` is True. If not given, `color` will be used. :type alt_color: tuple, str, int, or `schrodinger.structutils.color` :param line_width: Width of the line :type line_width: int """ self._color = self._colorToRgb(marker_color) self._alt_color = self._altColorToRgb(alt_color) self._line_width = line_width self._coord1 = coord1 self._coord2 = coord2 self._entry_ids = {str(entry_id1), str(entry_id2)} self._handle = None self.update()
def _createMarker(self): """ Create the line marker according to given options. """ r, g, b = self._color tr, tg, tb = self._getAltColor() x1, y1, z1 = self._coord1 x2, y2, z2 = self._coord2 self._handle = _pymaecxx.mae_create_line_marker(x1, y1, z1, x2, y2, z2, r, g, b, tr, tg, tb, self._line_width)
[docs] def update(self): """ See parent class documentation. Update the marker in response to the workspace contents changing. If either one of the entries the marker vertices belong to is no longer included in workspace, the marker will be deleted. """ self.hide() workspace_eids = maestro.get_included_entry_ids() if workspace_eids.issuperset(self._entry_ids): self._createMarker() else: self._handle = None
[docs]class NotInWorkspaceError(Exception): """ An exception when trying to display a marker covering atoms that are not currently in the workspace. """
# This class intentionally left blank