Source code for schrodinger.application.phase.phase_markers

"""
Phase pharmacophore workspace markers.

Copyright Schrodinger, LLC. All rights reserved.
"""

import math
import types
from collections import OrderedDict
from functools import partial

import numpy

import schrodinger
from schrodinger.application.phase import constants
from schrodinger.application.phase import pt_hypothesis
from schrodinger.graphics3d import arrow
from schrodinger.graphics3d import common
from schrodinger.graphics3d import sphere
from schrodinger.graphics3d import torus
from schrodinger.infra import mm
from schrodinger.infra import phase
from schrodinger.infra.mmbitset import Bitset
from schrodinger.Qt import QtCore
from schrodinger.Qt import QtWidgets
from schrodinger.structutils import analyze
from schrodinger.structutils import measure
from schrodinger.ui.qt.utils import maestro_required

maestro = schrodinger.get_maestro()

markers = None

LABEL_FONT_NAME = "Sans Serif"
if maestro:
    LABEL_FONT_NAME = maestro.get_command_option("labelatom", "font")


def _calculate_arrow_tail(xyz, pxyz, rad, rcyl):
    """
    Calculate coordinates of the projected point arrow tail position.
    Tail position is calculated in such a way that arrow cylinder 'grows'
    from the surface of the pharmacophore sphere.

    :param xyz: coordinates of the pharmacophore site
    :type xyz: (float, float, float)

    :param pxyz: coordinates of the projected point (arrow end point)
    :type pxyz: (float, float, float)

    :param rad: pharmacophore feature sphere radius
    :type rad: float

    :param rcyl: radius of the arrow cylinder
    :type rcyl: float

    :return: tuple that contains xyz coordinates of the arrow tail point
    :rtype: tuple
    """
    difference = numpy.asarray(pxyz) - numpy.asarray(xyz)
    delta = math.sqrt(rad * rad - rcyl * rcyl)
    distance = measure.measure_distance(xyz, pxyz)
    return numpy.asarray(xyz) + difference * delta / distance


[docs]def set_freestyle_site_type(site, feature_type): """ Updates a site with the given pharmacophore feature type, and if applicable assigns projected coordinates of the corresponding free-style site type. :return: pharmacophore site :rtype: `phase.PhpSite` :param feature_type: site's feature type :type feature_type: str """ # Update the site type site.setSiteType(feature_type) # Assign free-style projected vector coordinates proj_coords = [] x, y, z = site.getCoordinates() if feature_type in [constants.FEATURE_A, constants.FEATURE_D]: proj_coords.append([x, y, z + phase.PHASE_DEFAULT_EXTENDED_DISTANCE]) elif feature_type == constants.FEATURE_R: proj_coords.append([x, y, z + phase.PHASE_DEFAULT_EXTENDED_DISTANCE]) proj_coords.append([x, y, z - phase.PHASE_DEFAULT_EXTENDED_DISTANCE]) site.setProjCoords(proj_coords)
[docs]def is_site_grouped(site): """ Get whether site is grouped :param site: pharmacophore site :type site: phase.PhpSite :return: whether or not site is grouped :rtype: bool """ return site.getMaskValue() not in constants.MASK_NAMES.values()
[docs]def get_mask_name(site): """ Get the name of the site's mask :param site: pharmacophore site :type site: phase.PhpSite :return: site mask min match name :rtype: str """ site_mask = site.getMaskValue() for name, mask in constants.MASK_NAMES.items(): if mask == site_mask: return name # Otherwise the mask is part of a group return constants.GROUPED_MASK_NAME
[docs]class PhaseFeatureMarker(object): """ Class defining a single Phase marker. """ # Radius of the arrow that is drawn between site and projected points ARROW_RADII = 0.065 # Radius of spherical (non-aromatic) pharmacophore site SPHERE_RADII = 0.4 # Radius and tube radius of aromatic pharmacophore site TORUS_RADII = 0.45 TORUS_TUBE_RADII = 0.12 # Marker opacity default OPACITY_DEFAULT = 0.4 # Marker opacity when selected OPACITY_SELECTED = 1.0 # Color used to show outer glow when mouse is hovered on Phase marker GLOW_COLOR = (0.95, 0.5, 0.25) # Color used to show outer glow when feature is selected SELECTED_COLOR = (0.25, 0.5, 0.95) # Color used when feature is masked MASKED_COLOR = (0.35, 0.35, 0.35) # Names of feature properties shown in a 'long' label FEATURE_TYPE = "Feature Type" USE_IN_SCREEN = "Use in Screening" ALLOWED_TO_MATCH = "Allowed to match" FORBIDDEN_TO_MATCH = "Forbidden to match" TOLERANCE = "Tolerance" XP_SCORE = "XP Score" feature_counter = 1
[docs] def __init__(self, site, entry_id, color, use_highlight, allow_picking=True): """ Feature marker initializer. :param site: pharmacophore site for this marker :type site: `phase.PhpSite` :param entry_id: entry id of structure this feature belongs to :type entry_id: int :param color: feature marker color defined as a list of rgb values :type color: list :param use_highlight: whether this marker should be highlighted :type use_highlight: bool :param allow_picking: whether this marker can be 'picked', which allows showing a context menu when right-clicked :type allow_picking: bool """ self.id = PhaseFeatureMarker.feature_counter PhaseFeatureMarker.feature_counter += 1 self.site = site self.entry_id = entry_id # Display options self.color = color self.opacity = self.OPACITY_DEFAULT if use_highlight: self.opacity = self.OPACITY_SELECTED # Picking and selection self.allow_picking = allow_picking self.pick_category = constants.PICK_CATEGORY() self.selected = False # Initalize 3D object groups self.marker_group = common.Group() self.text_objects = [] # Create a torus marker if an aromatic feature with projected coords if self.feature_type == constants.FEATURE_R and site.getProjCoords(): self._createAromaticFeature() else: self._createFeature() # Create feature property labels. self.feature_props = self._getFeatureProperties() self.show_feature_labels = True self.show_prop_labels = False self.show_full_labels = False if maestro: self.createLabels()
@property def feature_type(self): """Phase type string of the pharmacophore site""" return self.site.getSiteType() @property def feature_name(self): """Display name of the pharmacophore site""" return self.site.getDisplayName() @property def feature_coordinates(self): """List of coordinates of the main site and any projected coordinates""" return [self.site.getCoordinates()] + list(self.site.getProjCoords()) @property def is_projected(self): """Whether the marker represents a Q projected feature""" return self.feature_type == constants.FEATURE_Q def __del__(self, _isinstance=isinstance, _module=types.ModuleType, _maestro=maestro): """ Remove graphics text objects, not handled by Python garbage collector """ # _maestro is stored at function level to avoid collection on # interpreter exit. similarly use __bool__() instead of literal eval if _isinstance(_maestro, _module) or _maestro.__bool__(): self.clearLabels()
[docs] def getHypoID(self): """ Return the hypothesis name/ID for the hypothesis from which this feature is from. """ pt = maestro.project_table_get() return pt[self.entry_id][phase.PHASE_HYPO_ID]
def _getFeatureProperties(self): """ Returns feature properties such as feature rules, tolerance etc from the hypothesis. If there is no hypothesis associated with this feature returns None. :return: dictionary of feature properties keyed on the feature name :rtype: dict or None """ props = OrderedDict() props[self.FEATURE_TYPE] = phase.FEATURE_DESCRIPTION[self.feature_type] # If projected points are shown as features only show feature type if self.is_projected: return props # 'Use in screening' property props[self.USE_IN_SCREEN] = get_mask_name(self.site) # Feature matching rules permitted = self.site.getPermitted() prohibited = self.site.getProhibited() # If there are no 'prohibited' features show 'None' instead if not prohibited: prohibited = 'None' props[self.ALLOWED_TO_MATCH] = permitted props[self.FORBIDDEN_TO_MATCH] = prohibited # Feature matching tolerance props[self.TOLERANCE] = "%.2f A" % self.site.getTol() # Glide XP score if self.site.hasXP(): props[self.XP_SCORE] = "%.2f kcal/mol" % self.site.getXP() return props def _createFeature(self): """ Creates a sphere marker for a pharmacophore site. If the site has projected coordinates, arrows are created pointing to/from the sphere. """ x, y, z = self.site.getCoordinates() sphere_marker = sphere.MaestroSphere(x=x, y=y, z=z, radius=self.SPHERE_RADII, resolution=20, opacity=self.opacity, color=self.color) self._addToMarkerGroup(sphere_marker) self._addPersistentName(sphere_marker) # Add arrows for projected coordinates of the site for pxyz in self.site.getProjCoords(): txyz = _calculate_arrow_tail((x, y, z), pxyz, self.SPHERE_RADII, self.ARROW_RADII) # Reverse direction of the acceptor arrow if self.feature_type == constants.FEATURE_A: pxyz, txyz = txyz, pxyz (px, py, pz), (tx, ty, tz) = pxyz, txyz arrow_marker = arrow.MaestroArrow(xhead=px, yhead=py, zhead=pz, xtail=tx, ytail=ty, ztail=tz, color=self.color, opacity=self.opacity, radius=self.ARROW_RADII, body_percentage=0.6, remove_endcaps=True) self._addToMarkerGroup(arrow_marker, False) # Do not allow right clicking for projected point spheres if not self.is_projected: self.setRightClickHandler(self.pick_category) def _createAromaticFeature(self): """ Creates a torus marker for an aromatic pharmacophore site. """ # Although this should not happen, assure there are projected coords if not self.site.getProjCoords(): raise RuntimeError("%s does not have projected coordinates to " \ "create a torus" % self.feature_name) x0, y0, z0 = self.site.getCoordinates() x1, y1, z1 = self.site.getProjCoords()[0] torus_marker = torus.MaestroTorus(x0=x0, y0=y0, z0=z0, x1=x1, y1=y1, z1=z1, color=self.color, radius=self.TORUS_RADII, opacity=self.opacity, tube_radius=self.TORUS_TUBE_RADII) self._addToMarkerGroup(torus_marker) self._addPersistentName(torus_marker) self.setRightClickHandler(self.pick_category) def _addToMarkerGroup(self, maestro_object, pickable=True): """ Assigns marker properties to the Maestro object, and adds it to the marker group. :param maestro_object: Maestro 3D object :type maestro_object: `graphics3d` object :param pickable: whether the object is pickable :type pickable: bool """ # cast for maestro.set_entry_id maestro_object.setEntryID(str(self.entry_id)) if pickable: maestro_object.pick_id = self.id maestro_object.pick_category = self.pick_category self.marker_group.add(maestro_object) def _addPersistentName(self, maestro_object): """ Assigns a persistent name for picking. No spaces are allowed in names--it messes up parsing. """ name = str(self.feature_name) + "(" + str(self.entry_id) + ")" maestro_object.persistent_name = name
[docs] def setRightClickHandler(self, pick_category): """ Sets right click handler for a given pick category. :param pick_category: picking category of markers :param type: str """ maestro.set_rightclick_on_pick_category(pick_category, "phase.edit_feature_dialog", "right_click_handler")
[docs] def createLabels(self): """ Create the labels for the sites of this feature in the Workspace. """ if not maestro: return self.clearLabels() if not self.marker_group.isShown(): return # Build label string label_str = f'{self.feature_name}\n' if self.show_feature_labels else "" if self.show_prop_labels and self.feature_props: if self.show_full_labels: label_str += "\n".join( ["%s: %s" % (k, v) for k, v in self.feature_props.items()]) else: label_str += "\n".join(list(self.feature_props.values())) x, y, z = self.site.getCoordinates() label = maestro.create_multiline_text(label_str, x, y, z, LABEL_FONT_NAME) maestro.set_entry_id(label, str(self.entry_id)) self.text_objects.append(label)
[docs] def clearLabels(self): """ Remove all marker labels. """ for obj in self.text_objects: maestro.remove_object(obj) self.text_objects = []
[docs] def showLabels(self): """ Show all marker labels. """ for obj in self.text_objects: maestro.show_object(obj)
[docs] def hideLabels(self): """ Hide all marker labels. """ for obj in self.text_objects: maestro.hide_object(obj)
[docs] def setHovered(self, hovered: bool): """ Enable or disable hover effect for the marker. """ self.setIsGlowing(hovered)
[docs] def setIsGlowing(self, is_glowing): """ Enables or disables glow effect for the object. :param is_glowing: whether the object is glowing :type is_glowing: bool """ self._setGlowing(is_glowing, self.GLOW_COLOR) # If feature is 'glowing' show full labels if self.show_full_labels != is_glowing: self.show_full_labels = is_glowing if self.show_prop_labels: if maestro: self.createLabels() # If this feature is 'selected' restore selection glow effect when # mouse is no longer hovering over this feature. if not is_glowing and self.selected: self._setGlowing(True, self.SELECTED_COLOR)
[docs] def setSelected(self, select): """ Shows glow effect when this feature is selected. :param select: whether this feature should be selected :type select: bool """ self.selected = select self._setGlowing(select, self.SELECTED_COLOR) opacity = self.opacity if select: opacity = self.OPACITY_SELECTED for primitive in self.marker_group.allPrimitives(): primitive.opacity = opacity
def _setGlowing(self, is_glowing, glow_color): """ Enables or disables glow effect for the object. :param is_glowing: whether the object is glowing :type is_glowing: bool :param glow_color: color that should be used for the glow effect :type glow_color: tuple of rgb values """ for primitive in self.marker_group.allPrimitives(): primitive.setGlowColor(*glow_color) primitive.setIsGlowing(is_glowing) if maestro: # need to force Workspace redraw to hide glow effect maestro.redraw() def _setColor(self, color): """ Sets the marker color. :param color: color for all marker graphic objects. :type color: tuple of rgb values """ for primitive in self.marker_group.allPrimitives(): primitive.setRGBColors(*color)
[docs] def show(self): self.marker_group.show() if maestro: self.showLabels()
[docs] def hide(self): self.marker_group.hide() if maestro: self.hideLabels()
[docs] def clear(self): self.marker_group.clear() if maestro: self.clearLabels()
[docs] def mask(self): """ Mask the feature in workspace by setting a dull grey color to marker. """ self._setColor(self.MASKED_COLOR)
[docs] def unmask(self): """ Unmask the feature in workspace by setting default color to marker. """ self._setColor(self.color)
[docs]class PhaseSphere(sphere.MaestroSphere): """ Phase sphere graphics object. """
[docs] def __init__(self, *args, **kwargs): # See base class for documentation. super(PhaseSphere, self).__init__(*args, **kwargs) # determines whether this sphere was 'selected' self.is_selected = False
[docs] def setSelected(self, selected, color=None): """ Shows sphere as selected (with a glow effect). Glow effect color only needs to be specified when turning it 'on'. :param selected: whether sphere should be selected :type selected: bool :param color: color used for selected glow effect. :type color: tuple """ if selected: self.setGlowColor(*color) self.is_selected = selected self.setIsGlowing(selected)
[docs]class PhaseSphereMarkers(object): """ Base class that defines group of sphere markers. """ # default sphere color SPHERE_COLOR = (1.0, 1.0, 0.0) # Color used to show outer glow when sphere is selected SELECTED_COLOR = (0.25, 0.5, 0.95) # default sphere opacity SPHERE_OPACITY = 0.1 # default sphere resolution SPHERE_RESOLUTION = 20 # flag to use angle dependent transparency ANGLE_DEP_TRANSPARENCY = True next_sphere_id = 1
[docs] def __init__(self, entry_id): """ Marker initializer. :param entry_id: hypothesis entry ID :type entry_id: int """ self.entry_id = entry_id self.group = common.Group() self.spheres = [] self.interactive = False
[docs] def addSphere(self, xyz, radius): """ Adds spehere to marker group. :param xyz: sphere coordinates :type xyz: (float, float, float) :param radius: sphere radius :type radius: float """ x, y, z = xyz sphere = PhaseSphere(x=x, y=y, z=z, radius=radius, opacity=self.SPHERE_OPACITY, color=self.SPHERE_COLOR, resolution=self.SPHERE_RESOLUTION, angle_dep_transparency=self.ANGLE_DEP_TRANSPARENCY) sphere.setEntryID(str(self.entry_id)) # cast for maestro.set_entry_id if self.interactive: sphere.pick_category = constants.XVOL_PICK_CATEGORY sphere.pick_id = PhaseSphereMarkers.next_sphere_id self.spheres.append(sphere) self.group.add(sphere) PhaseSphereMarkers.next_sphere_id += 1 return sphere
[docs] def setSphereRadiusAndCoords(self, sphere_index, r, x, y, z): """ Sets sphere radius and coordinates. :param sphere_index: index of sphere in self.spheres list :type sphere_index: int :param r: sphere radius :type r: float :param x: sphere x-coordinate :param x: float :param y: sphere y-coordinate :param y: float :param z: sphere z-coordinate :param z: float """ sphere = self.spheres[sphere_index] sphere._setRadius(r) sphere.setXYZCoords(x, y, z)
[docs] def removeSpheres(self, sphere_indices): """ Removes spheres from the group. :param sphere_indices: list of indices to the self.sphere list :type sphere_indices: list """ sphere_indices.sort(reverse=True) try: for sphere_idx in sphere_indices: sphere = self.spheres.pop(sphere_idx) self.group.remove(sphere) self.show() except KeyError: err = "Specified sphere does not exist" raise ValueError(err)
[docs] def setSelected(self, sphere_indices): """ Shows the specified spheres as selected (with a glow effect). :param sphere_indices: list of indices to the self.sphere list :type sphere_indices: list """ self.resetSelected() for sphere_idx in sphere_indices: sphere = self.spheres[sphere_idx] sphere.setSelected(True, self.SELECTED_COLOR)
[docs] def resetSelected(self): """ Resets current selection. """ for sphere in self.spheres: if sphere.is_selected: sphere.setSelected(False)
[docs] def show(self): """ Show all spheres for this group. """ self.group.show()
[docs] def hide(self): """ Hide all spheres for this group. """ self.group.hide()
[docs] def showSphere(self, sphere_idx): """ Show the specified sphere. :param sphere_idx: index of the sphere in self.spheres list to show. :type sphere_idx: int """ sphere = self.spheres[sphere_idx] sphere.show()
[docs] def hideSphere(self, sphere_idx): """ Hide the specified sphere. :param sphere_idx: index of the sphere in self.spheres list to hide. :type sphere_idx: int """ sphere = self.spheres[sphere_idx] sphere.hide()
[docs] def clear(self): """ Deletes all spheres for this group. """ self.group.clear()
[docs]class PhaseXvolMarkers(PhaseSphereMarkers): """ Class that defines group of spheres for the excluded volume. """ # exclude volume sphere color SPHERE_COLOR = (0.2, 0.9, 0.95) # excluded volume sphere opacity SPHERE_OPACITY = 0.2
[docs] def __init__(self, entry_id, x_vol, interactive=False): """ Marker initializer. :param entry_id: hypothesis entry ID :type entry_id: int :param x_vol: excluded volume object :type x_vol: `phase.PhpExclVol` """ super(PhaseXvolMarkers, self).__init__(entry_id) self.interactive = interactive self.addExcludedVolumes(x_vol)
[docs] def addExcludedVolumes(self, x_vol): """ Adds excluded volume spheres to the current group. :param x_vol: excluded volume object :type x_vol: `phase.PhpExclVol` """ for sphere_idx in range(0, x_vol.numSpheres()): radius = x_vol.getSphereRadius(sphere_idx) xyz = x_vol.getSphereXYZ(sphere_idx) self.addSphere(xyz, radius)
[docs]class PhaseTolMarkers(PhaseSphereMarkers): """ Class that defines group of tolerance spheres. """ # tolerance sphere color SPHERE_COLOR = (0.5, 0.5, 0.5) # tolerance sphere opacity SPHERE_OPACITY = 0.5
[docs] def __init__(self, entry_id, sites): """ Marker initializer. :param entry_id: hypothesis entry ID :type entry_id: int :param sites: Phase hypothesis sites :type sites: list of `phase.PhpSite` """ super(PhaseTolMarkers, self).__init__(entry_id) # keep track of the tolerance sphere markers self._tol_markers = {} for site in sites: sphere = self.addSphere(site.getCoordinates(), site.getTol()) tol_marker_key = self._genTolMarkerKey(site) self._tol_markers[tol_marker_key] = self.spheres.index(sphere)
[docs] def showTolMarker(self, site): """ Show tolerance marker for the given site. :param site: Phase hypothesis site :type site: `phase.PhpSite` """ tol_marker_key = self._genTolMarkerKey(site) sph_idx = self._tol_markers.get(tol_marker_key) if sph_idx is not None: self.showSphere(sph_idx)
[docs] def hideTolMarker(self, site): """ Hide tolerance marker for the given site. :param site: Phase hypothesis site :type site: `phase.PhpSite` """ tol_marker_key = self._genTolMarkerKey(site) sph_idx = self._tol_markers.get(tol_marker_key) if sph_idx is not None: self.hideSphere(sph_idx)
def _genTolMarkerKey(self, site): """ Creates an internal key for the given marker PhpSite. """ return self.entry_id, site.getDisplayName()
[docs]class PhaseMarkers(QtCore.QObject): """ Class for adding pharmacophore feature markers and controlling their visibility. :cvar editingFinished: signal that gets emitted when feature editing is finished. This signal is emitted regardless of whether feature was changed or not. :vartype editingFinished: `QtCore.pyqtSignal` :cvar closeFeatureEditDialog: signal that gets emitted when any open edit feature dialog should be closed. This signal is emitted when the Phase GUI workflow changes, or the panel is closed. :vartype closeFeatureEditDialog: `QtCore.pyqtSignal` :cvar workspaceMarkersChanged: signal that gets emitted when features are shown or hidden in the Workspace. :vartype workspaceMarkersChanged: `QtCore.pyqtSignal` :ivar _phase_features: dictionary mapping feature key tuples to markers associated with this panel :vartype _phase_features: dict[(int, str), PhaseFeatureMarker] """ editingFinished = QtCore.pyqtSignal() closeFeatureEditDialog = QtCore.pyqtSignal() workspaceMarkersChanged = QtCore.pyqtSignal()
[docs] def __init__(self, *args, **kwargs): self._phase_features = OrderedDict() self._phase_xvol = {} self._phase_tol = {} super(PhaseMarkers, self).__init__(*args, **kwargs)
[docs] def featureExists(self, entry_id): """ This function checks whether pharmacophore features exist for a structure with a given entry_id and returns True or False. :param entry_id: structure entry_id :type entry_id: int :return: True if features were already added and False otherwise :rtype: bool """ if entry_id in self._getEntryIDs(): return True return False
[docs] def addStructureFeatures(self, st, mapper, entry_id, allow_picking=True): """ Add all pharmacophore feature markers for a given structure. :param st: molecule structure :type st: `structure.Structure` :param mapper: Site mapper :type mapper: phase.PhpSiteMapper :param entry_id: structure entry id :type entry_id: int :param allow_picking: whether this marker can be 'picked', which allows showing a context menu when right-clicked :type allow_picking: bool :return: list of sites that were added for this structure :rtype: list """ try: sites = mapper.mapSites(st.handle) except phase.PhpException as exc: msg = "Error encountered applying feature definitions to entry " \ "\'%s\'. Unable to generate features.\n\n%s" % (st.title, exc) QtWidgets.QMessageBox.critical(None, "Error", msg) return None self.addSites(sites, entry_id, allow_picking=allow_picking) return list(sites)
[docs] def addSites(self, sites, entry_id, use_highlight=False, allow_picking=True): """ Add pharmacophore features for given sites. :param sites: list of pharmacophore sites :type sites: list of `phase.PhpSite` :param entry_id: structure entry id :type entry_id: int :param use_highlight: indicates whether sites should be 'highlighted' :type use_highlight: list :param allow_picking: whether this marker can be 'picked', which allows showing a context menu when right-clicked :type allow_picking: bool """ self.addCustomSites(sites, entry_id, use_highlight, allow_picking, marker_class=PhaseFeatureMarker)
[docs] def addCustomSites(self, sites, entry_id, use_highlight=False, allow_picking=True, site_color_map=constants.FEATURE_COLORS, *_, marker_class): """ Add pharmacophore features for given sites using a custom marker class. See `addSites` for additional argument documentation. :param site_color_map: Mapping of phase interaction type -> color :type site_color_map: Dict {phase.InteractionType: (r, g, b)} :param marker_class: Marker class (Mandatory keyword-only argument) :type marker_class: PhaseFeatureMarker """ for site in sites: feature_key = self._genFeatureKey(entry_id, site.getDisplayName()) if feature_key in self._phase_features: # Assume that the marker for this feature was already generated continue color = site_color_map[site.getSiteType()] marker = marker_class(site, entry_id, color, use_highlight, allow_picking) self._phase_features[feature_key] = marker
[docs] def getFeature(self, feature_name, entry_id): """ Retrieve markers for the specified feature. :param feature_name: feature name :type feature_name: str :param entry_id: entry id of the structure that this feature is associated with :type entry_id: int :return: tuple that contains pharmacophore and projected points marker :rtype: tuple """ feature_key = self._genFeatureKey(entry_id, feature_name) try: return self._phase_features[feature_key] except KeyError: err = "No feature exists for the specified feature name." raise ValueError(err)
[docs] def getAllFeatures(self): """ Retrieve markers for all features. :return: list of pharmacophore markers :rtype: list """ return list(self._phase_features.values())
[docs] def getSites(self, entry_id): """ Get list of sites for all features with a given entry id. :param entry_id: entry_id :type entry_id: int :return: list of sites :rtype: list """ sites = [] for feature_name in self.getFeatureNames(entry_id): feature = self.getFeature(feature_name, entry_id) sites.append(feature.site) return sites
[docs] def getFeatureNames(self, entry_id): """ Get list of feature names that were found for the structure with a given entry id. Feature names are sorted in alphabetical order. :param entry_id: entry_id :type entry_id: int :return; list of feature names :rtype: list """ # Feature names contain a single character followed by the feature # number as in 'A1', 'R11' etc. We want to sort feature names # alphabetically first and by the feature number second as in: # 'A1', 'A2', 'A11', 'D3', 'D4', 'P5' etc def feature_key_func(feature_name): return feature_name[0], int(feature_name[1:]) feature_names = [k[1] for k in self._getKeysForEntry(entry_id)] return sorted(feature_names, key=feature_key_func)
[docs] @maestro_required def addFeaturesForWorkspaceLigands(self, fds, allow_picking=True): """ Add feature markers for each included ligand PT entry, according to the given feature definitions. :param fds: list of feature definitions :type st: list :param allow_picking: indicates whether feature can be 'picked'. When enabled this would make possible to show context menu when feature is right-clicked. Default is True. :type: allow_picking :rtype: list of ints :return: List of entry IDs for the included ligands. """ sts = maestro.get_included_entries() # Make a list of all ligands in the Workspace (included entries). ligand_sts = [ ligand.st for st in sts for ligand in analyze.find_ligands(st) ] entry_ids = [] mapper = phase.PhpSiteMapper(fds) for ligand_st in ligand_sts: # get entry id as int from the ligand string property try: entry_id = int(ligand_st.atom[1].entry_id) except ValueError: continue # create features only if needed if not self.featureExists(entry_id): self.addStructureFeatures(ligand_st, mapper, entry_id, allow_picking=allow_picking) entry_ids.append(entry_id) # delay call to ensure Maestro workspace changes were applied self._updateFeatures() return entry_ids
def _updateFeatures(self): """ Show or hide features for entries that are included or excluded in the workspace. Delete features for entries, which were deleted from the project. """ entry_ids = self._getEntryIDs() pt = maestro.project_table_get() for eid in entry_ids: row = pt.getRow(eid) if row and row.in_workspace: self.showAllFeaturesForEntry(eid) else: self.removeAllFeaturesForEntry(eid) self.workspaceMarkersChanged.emit() # --------- Methods to remove pharmacophore features ----------------
[docs] def removeFeature(self, feature_name, entry_id): """ Removes markers for the specified pharmacophore feature. :param feature_name: feature name :param feature_name: str :param entry_id: entry id of the structure that this feature is associated with :type entry_id: int """ try: feature_key = self._genFeatureKey(entry_id, feature_name) self._hideFeatureForKey(feature_key) self._phase_features[feature_key].clear() del self._phase_features[feature_key] except KeyError: err = "Specified feature does not exist" raise ValueError(err)
[docs] def removeAllFeatures(self): """ Removes markers for all pharmacophore features. """ self.hideAllFeatures() self._phase_features.clear()
[docs] def removeAllFeaturesForEntry(self, entry_id): """ Removes markers for all features associated with a given entry. :param entry_id: entry id of the structure that this feature is associated with :type entry_id: int """ self.hideAllFeaturesForEntry(entry_id) for feature_key in self._getKeysForEntry(entry_id): self._phase_features[feature_key].marker_group.clear() del self._phase_features[feature_key]
# --------- Methods to show pharmacophore features ----------------
[docs] def showFeature(self, feature_name, entry_id): """ Show markers for the specified pharmacophore feature. :param feature_name: feature name :param feature_name: str :param entry_id: entry id of the structure that this feature is associated with :type entry_id: int """ feature_key = self._genFeatureKey(entry_id, feature_name) self._showFeatureForKey(feature_key)
[docs] def showAllFeatures(self): """ Shows markers for all pharmacophore features. """ for feature_key in self._phase_features: self._showFeatureForKey(feature_key)
[docs] def showAllFeaturesForEntry(self, entry_id): """ Shows markers for all features associated with a given entry. :param entry_id: entry id of the structure that this feature is associated with :type entry_id: int """ for feature_key in self._getKeysForEntry(entry_id): self._showFeatureForKey(feature_key)
[docs] def showAllFeaturesForEntryByType(self, entry_id, feature_types): """ Show markers for all given feature types associated with a given entry. :param entry_id: entry id of the structure that these features are associated with. :type entry_id: int :param feature_types: list of feature types that markers should be shown for. :type feature_types: List of str """ for feature_key in self._getKeysForEntry(entry_id): marker = self._phase_features[feature_key] if marker.feature_type in feature_types: marker.show() self.showTolMarker(entry_id, marker.site) else: marker.hide() self.hideTolMarker(entry_id, marker.site)
def _showFeatureForKey(self, feature_key): """ Show markers for a given feature key. :param feature_key: unique feature key. :type feature_key: str """ marker = self._phase_features[feature_key] marker.show() # --------- Methods to hide pharmacophore features ----------------
[docs] def hideFeature(self, feature_name, entry_id): """ Hide markers for the specified pharmacophore feature. :param feature_name: feature name :param feature_name: str :param entry_id: entry id of the structure that this feature is associated with :type entry_id: int """ feature_key = self._genFeatureKey(entry_id, feature_name) self._hideFeatureForKey(feature_key)
[docs] def hideAllFeatures(self): """ Hide markers for all pharmacophore features. """ for feature_key in self._phase_features: self._hideFeatureForKey(feature_key)
[docs] def hideAllFeaturesForEntry(self, entry_id): """ Hide markers for all features associated with a given entry. :param entry_id: entry id of the structure that this feature is associated with :type entry_id: int """ for feature_key in self._getKeysForEntry(entry_id): self._hideFeatureForKey(feature_key)
def _hideFeatureForKey(self, feature_key): """ Hide markers for a given feature key. :param feature_key: unique feature key. :type feature_key: str """ marker = self._phase_features[feature_key] marker.hide() # --------- Excluded Volume methods -------------------------------
[docs] def hasXvolMarkers(self, entry_id): """ Checks whether excluded volume markers for a given entry ID exist. :param entry_id: entry ID :type entry_id: int """ return entry_id in self._phase_xvol
[docs] def removeXvolMarkers(self, entry_id): """ Removes all excluded volume markers for a given entry. :param entry_id: entry ID :type entry_id: int """ # If there are no excluded volume markers, do nothing if not self.hasXvolMarkers(entry_id): return xvol_group = self._phase_xvol[entry_id] xvol_group.hide() xvol_group.clear() del self._phase_xvol[entry_id]
[docs] def removeXvolMarkersWithIDs(self, entry_id, sphere_indices): """ Removes excluded volume markers with specified sphere ids for a given entry. :param entry_id: entry ID :type entry_id: int :param sphere_indices: list of sphere indices :type sphere_indices: list """ xvol_group = self._phase_xvol[entry_id] xvol_group.removeSpheres(sphere_indices)
[docs] def removeAllXvolMarkers(self): """ Removes all excluded volume markers. """ for xvol_key in list(self._phase_xvol): self.removeXvolMarkers(xvol_key)
[docs] def addXvolMarkers(self, entry_id, x_vol, interactive=False): """ Adds excluded volume markers for a given entry ID. :param entry_id: entry ID :type entry_id: int :param x_vol: data object that contains excluded volumes :type x_vol: `phase.PhpExclVol` :param interactive: indicates whether markers should be 'interactive' :type interactive: bool """ if entry_id in self._phase_xvol: # add new excluded volume markers to existing group xvol_group = self._phase_xvol[entry_id] xvol_group.addExcludedVolumes(x_vol) else: # create new markers group xvol_group = PhaseXvolMarkers(entry_id, x_vol, interactive=interactive) self._phase_xvol[entry_id] = xvol_group xvol_group.show()
[docs] def findXvolSphereIndex(self, entry_id, pick_id): """ Finds excluded volume sphere index for a given sphere pick id. Pick id is used to identify graphics3d objects picked in the Workspace. :param entry_id: entry ID :type entry_id: int :param pick_id: pick id of graphics3d object :type pick_id: int :return: index of sphere with a given pick id in the list that PhaseXvolMarkers object maintains. :rtype: int """ xvol_group = self._phase_xvol[entry_id] for row_id, sphere in enumerate(xvol_group.spheres): if sphere.pick_id == pick_id: return row_id raise ValueError("Specified sphere does not exist.")
[docs] def setXvolSelected(self, entry_id, sphere_indices): """ Selects spheres with given sphere ids for a given entry id. :param entry_id: entry ID :type entry_id: int :param sphere_indices: list of sphere indices :type sphere_indices: list """ xvol_group = self._phase_xvol[entry_id] xvol_group.setSelected(sphere_indices)
[docs] def updateXvolMarker(self, entry_id, sphere_index, r, x, y, z): """ This function is called to change radius and coordinates of excluded volume with a given sphere id and entry id. :param entry_id: entry ID :type entry_id: int :param sphere_index: sphere index :type sphere_index: int :param r: new radius :type r: float :param x: new x-coordinate :type x: float :param y: new y-coordinate :type y: float :param z: new z-coordinate :type z: float """ xvol_group = self._phase_xvol[entry_id] xvol_group.setSphereRadiusAndCoords(sphere_index, r, x, y, z)
# --------- Tolerance Sphere methods -------------------------------
[docs] def hasTolMarkers(self, entry_id): """ Checks whether tolerance markers for a given entry ID exist. :param entry_id: entry ID :type entry_id: int """ return entry_id in self._phase_tol
[docs] def removeTolMarkers(self, entry_id): """ Removes all tolerance markers for a given entry. :param entry_id: entry ID :type entry_id: int """ # If there are no tolerance markers, do nothing if not self.hasTolMarkers(entry_id): return tol_group = self._phase_tol[entry_id] tol_group.hide() tol_group.clear() del self._phase_tol[entry_id]
[docs] def removeAllTolMarkers(self): """ Removes all tolerance markers. """ for tol_key in list(self._phase_tol): self.removeTolMarkers(tol_key)
[docs] def addTolMarkers(self, entry_id, sites): """ Adds tolerance markers for a given entry ID. :param entry_id: entry ID :type entry_id: int :param sites: Phase hypothesis sites :type sites: list of `phase.PhpSite` """ tol_group = PhaseTolMarkers(entry_id, sites) self._phase_tol[entry_id] = tol_group tol_group.show()
[docs] def showTolMarker(self, entry_id, site): """ Show tolerance marker for the given entry ID and site. :param entry_id: hypothesis entry ID :type entry_id: int :param site: Phase hypothesis site :type site: `phase.PhpSite` """ tol_group = self._phase_tol.get(entry_id) if tol_group: tol_group.showTolMarker(site)
[docs] def hideTolMarker(self, entry_id, site): """ Hide tolerance marker for the given entry ID and site. :param entry_id: hypothesis entry ID :type entry_id: int :param site: Phase hypothesis site :type site: `phase.PhpSite` """ tol_group = self._phase_tol.get(entry_id) if tol_group: tol_group.hideTolMarker(site)
[docs] def projectModel(self): """ Returns a Maestro project object. :rtype: `schrodinger.MM_Project` :return: Maestro project object. """ pt = maestro.project_table_get() pt_model = pt.project_model return pt_model
# --------- Reference Ligand methods -------------------------------
[docs] def hasRefCtMarker(self, entry_id): """ Checks whether refernce ligand marker for a given entry ID exists. :param entry_id: entry ID :type entry_id: int """ pt_model = self.projectModel() # Int cast is needed here to make the sip wrappings compatible. return pt_model.hypothesisHasReferenceLigandEntryId(int(entry_id))
[docs] def removeRefCtMarker(self, entry_id): """ Removes all reference ligand markers for a given entry. :param entry_id: entry ID :type entry_id: int """ # If there are no reference ligand markers, do nothing if not self.hasRefCtMarker(entry_id): return pt_model = self.projectModel() # Int cast is needed here to make the sip wrappings compatible. ref_ligand_entry_id = pt_model.getHypothesisReferenceLigandEntryId( int(entry_id)) if ref_ligand_entry_id: maestro.command("entrydelete entry %s" % ref_ligand_entry_id) # Int cast is needed here to make the sip wrappings compatible. pt_model.clearReferenceLigandEntryIdFromHypothesis(int(entry_id))
[docs] def removeAllRefCtMarkers(self): """ Removes all reference ligand markers. """ pt_model = self.projectModel() pt_model.removeHypothesisReferenceLigandEntries()
[docs] def addRefCtMarker(self, entry_id, ref_ct): """ Adds reference ligand markers for a given entry ID. :param entry_id: entry ID :type entry_id: str :param ref_ct: data object that contains reference ligands :type ref_ct: int :note ownership is transferred to maestro project. """ pt_model = self.projectModel() # If user is doing undo, then we dont have to add reference ct # it should already be present in the project's undo object. if self.hasRefCtMarker(entry_id): return pt = maestro.project_table_get() title = "Reference Ligand (" + pt[entry_id].title + ")" # Int cast is needed here to make the sip wrappings compatible. ref_ligand_entry_id = pt_model.addReferenceLigandEntry( ref_ct, title, int(entry_id)) if ref_ligand_entry_id: maestro.command("entrywsincludelock entry %s" % ref_ligand_entry_id)
# --------- Methods to toggle feature 'selection' -----------------
[docs] def setSelection(self, features, entry_id, selected): """ Sets selection mode for multiple features. :param features: list of feature names :type features: list :param entry_id: entry id of the structure that this feature is associated with :type entry_id: int :param selected: indicates whether feature should be selected or not :type selected: bool """ for feature_name in features: self.setFeatureSelection(feature_name, entry_id, selected)
[docs] def setFeatureSelection(self, feature_name, entry_id, selected): """ Set specified pharmacophore feature selection mode. Shows glow effect around markers. :param feature_name: feature name :param feature_name: str :param entry_id: entry id of the structure that this feature is associated with :type entry_id: int :param selected: indicates whether feature should be selected or not :type selected: bool """ feature_key = self._genFeatureKey(entry_id, feature_name) marker = self._phase_features[feature_key] marker.setSelected(selected)
[docs] def clearSelection(self): """ Clear 'selection' for all pharmacophore features. """ for feature_key in self._phase_features: marker = self._phase_features[feature_key] marker.setSelected(False)
# --------- Methods to show feature labels ------------------------
[docs] def setFeatureLabelVisible(self, entry_id, visible): """ Shows or hides feature label for all markers. :param entry_id: entry id of the structure that these features are associated with :type entry_id: int :param visible: whether to make feature labels visible or not :type visible: bool """ for feature_name in self.getFeatureNames(entry_id): feature_marker = self.getFeature(feature_name, entry_id) feature_marker.show_feature_labels = visible if maestro: feature_marker.createLabels()
# --------- Methods to show feature property labels ---------------
[docs] def setPropertyLabelVisible(self, entry_id, visible): """ Shows or hides detailed (property) label for all feature markers. :param entry_id: entry id of the structure that these features are associated with :type entry_id: int :param visible: whether to make property labels visible or not :type visible: bool """ for feature_name in self.getFeatureNames(entry_id): feature_marker = self.getFeature(feature_name, entry_id) feature_marker.show_prop_labels = visible if maestro: feature_marker.createLabels()
# --------- Utility methods --------------------------------------- def _genFeatureKey(self, entry_id, feature_name): """ Create a unique feature key. :param entry_id: entry id of the structure that this feature is associated with :type entry_id: int :param feature_name: feature name :param feature_name: str :return: tuple of (maestro entry ID, pharamcophore site name) :rtype: (int, str) """ return entry_id, feature_name def _getKeysForEntry(self, entry_id): """ Get a list of all feature keys for a given entry id. :param entry_id: entry id of the structure that this feature is associated with :type entry_id: int :return: list of feature keys :rtype: list """ feature_keys = [h for h in self._phase_features if h[0] == entry_id] return feature_keys def _getEntryIDs(self): """ Get a list of unique entry ids that feature markers stored in this mixin are associated with. :return: set of unique entry ids :rtype: set """ return {f[0] for f in self._phase_features}
[docs] def getFeatureFromId(self, marker_id): """ Return the marker with the given ID. :param marker_id: ID of the marker (.id attribute) :type marker_id: int :return Marker object :rtype `PhaseFeatureMarker` """ for marker in self._phase_features.values(): if marker.id == marker_id: return marker raise ValueError("Marker with ID %s was not found" % marker_id)
[docs]def get_phase_markers(): """ This function returns global PhaseMarkers object that should be used to generate Phase marker throughout Maestro session. :return: PhaseMarkers object :rtype: `PhaseMarkers` """ global markers if markers is None: markers = PhaseMarkers() return markers
[docs]def update_hypothesis_entry(hypo, entry_id): """ Updates the structure in the project table for the given entry ID with the current PhaseHypothesis, updating the reference ligand if needed. :param hypo: hypothesis to assign to the given PT entry :type hypo: `hypothesis.PhaseHypothesis` :param entry_id: entry id to set hypothesis to :type entry_id: int or str """ # If neccessary, remove reference ligand entry from the project table; # it will be recreated and shown again after the update markers = get_phase_markers() if markers.hasRefCtMarker(entry_id): markers.removeRefCtMarker(entry_id) pt_hypothesis.update_hypothesis_entry(hypo, entry_id)
[docs]def show_workspace_phase_markers(entry_ids, include_Q=False): """ Delays the call to show workspace phase markers with a single shot to allow for the project table project_model to update appropriately. :param entry_ids: list of Phase hypothesis entries :type entry_ids: list of int :param include_Q: whether to include Q site phase markers :type include_Q: bool """ # Reference ligand must be added in the project as part of current command # which triggered this call. If we execute it through single slot, # then current command finishes before single shot timer slot # gets called, therefore we are not able to track undo state of changes. eid_hypo_map = {eid: _get_hypothesis(eid) for eid in entry_ids} _show_reference_ligand(eid_hypo_map) show_markers_func = partial(_show_workspace_phase_markers, eid_hypo_map, include_Q) QtCore.QTimer.singleShot(0, show_markers_func)
def _undisplay_entry_atoms(entry_id): """ Undisplay all atoms associated with the given entry id in the workspace ct. We manipulate the workspace directly here instead of issuing a maestro command so that we don't break the user's ability to undo inclusion (MAE-38226). This function should only be called when we need to bypass maestro command framework. :param entry_id : Phase hypothesis entry id :type entry_id : int """ st = maestro.workspace_get(copy=False) atoms_bs = Bitset(size=st.atom_total) # Cast entry ID to str for mmct call mm.mmct_ct_get_entry_atoms(st, str(entry_id), atoms_bs) for atom in atoms_bs: mm.mmctg_atom_set_visible(st, atom, False) maestro.workspace_set(st, regenerate_markers=True, copy=False) def _show_reference_ligand(eid_hypo_map): """ Show reference ligand for the entries in `eid_hypo_map` that have a hypothesis associated with them. @NOTE - MAE-38321, MAE-38324 This function has to be kept separate from _show_workspace_phase_markers() because it has to be executed immediately rather than running through single slot initiated to execute _show_workspace_phase_markers(). Any project changes have to run always under command execution code in order to allow UNDO. If this function is run through _show_workspace_phase_markers(), then command gets completed before executing this function and we lose the ability to undo. :param eid_hypo_map: dictionary mapping of entry ids to hypothesis objects :type eid_hypo_map: Dict[int, hypothesis.PhaseHypothesis] """ for eid, hypo in eid_hypo_map.items(): if hypo is not None: set_reference_ligand_visible(eid, hypo.visibleRefCt(), hypo) def _get_hypothesis(eid): """ Get hypothesis associated with an entry id. :param entry_id: entry id of the structure that this hypothesis associated with :type eid: int :return: Hypothesis. :rtype: `hypothesis.PhaseHypothesis` """ try: hypo = pt_hypothesis.get_hypothesis_from_project(eid) return hypo except RuntimeError as e: QtWidgets.QMessageBox.warning(None, "Warning", str(e)) return None def _show_workspace_phase_markers(eid_hypo_map, include_Q): """ Adds Phase pharmacophore site markers for the entries in `eid_hypo_map` that have a hypothesis associated with them. :param eid_hypo_map: dictionary mapping of entry ids to hypothesis objects :type eid_hypo_map: Dict[int, hypothesis.PhaseHypothesis] :param include_Q: whether to include Q site phase markers :type include_Q: bool """ markers = get_phase_markers() for eid, hypo in eid_hypo_map.items(): # PANEL-8876: Hide dummy atom particle at feature center _undisplay_entry_atoms(eid) markers.removeAllFeaturesForEntry(eid) if hypo is not None: sites = hypo.getHypoSites(include_Q) markers.addSites(sites, eid, use_highlight=True) set_excluded_volume_visible(eid, hypo.visibleXvol(), hypo) set_feature_tolerance_visible(eid, hypo.visibleTol(), hypo) set_property_labels_visible(eid, hypo.visiblePropLabels(), hypo) # Update project table to make in-sync with newly added _phasehypo_ props maestro.command('projectupdateviews')
[docs]def hide_workspace_phase_markers(entry_ids): """ Removes Phase pharmacophore site markers for given entries. :param entry_ids: list of Phase hypothesis entries :type entry_ids: list """ markers = get_phase_markers() for eid in entry_ids: markers.removeAllFeaturesForEntry(eid) markers.removeXvolMarkers(eid) markers.removeTolMarkers(eid) # When removing programmatically, we should not change visibility # state, so don't call set_reference_ligand_visible(). markers.removeRefCtMarker(eid)
[docs]def project_close(): """ Delete all Phase feature markers. """ markers = get_phase_markers() markers.removeAllFeatures() markers.removeAllXvolMarkers() markers.removeAllTolMarkers() markers.removeAllRefCtMarkers()
[docs]def setup_project_close(): """ Sets up function that should be called when project is closed. """ maestro.project_close_callback_add(project_close)
[docs]def set_entry_property(entry_id, prop_name, value): """ Sets entry ct property to a given value. This function is used to set special hypothesis properties that define whether excluded volumes, tolerances and reference structure should be visible. :param entry_id: hypothesis entry id :type entry_id: int :param prop_name: property name :type prop_name: str :param value: property value :type value: float or int or str """ pt = maestro.project_table_get() if entry_id not in pt: return pt[entry_id][prop_name] = value
[docs]def set_excluded_volume_visible(entry_id, visible, hypo=None): """ Toggles hypothesis properties which can be shown in the workspace for hypothesis with a given entry id. :param entry_id: Phase hypothesis entry id :type entry_id: int :param visible: whether to make visible in the workspace or not :type visible: bool :param hypo: the hypothesis or None. If None, the hypothesis will be obtained from the project entry id. :type hypo: hypothesis.PhaseHypothesis """ markers = get_phase_markers() hypo = hypo or pt_hypothesis.get_hypothesis_from_project(entry_id) if not hypo or not hypo.hasXvol(): return markers.removeXvolMarkers(entry_id) if visible: markers.addXvolMarkers(entry_id, hypo.getXvol()) # Set the visible hypothesis property to the current state and update if hypo.property.get(constants.HYPO_XVOL_VISIBLE, None) != visible: set_entry_property(entry_id, constants.HYPO_XVOL_VISIBLE, visible)
[docs]def set_reference_ligand_visible(entry_id, visible, hypo=None): """ Toggles display of reference ligand in the Workspace for hypothesis with a given entry id. :param entry_id: Phase hypothesis entry id :type entry_id: int :param visible: whether to make visible in the workspace or not :type visible: bool :param hypo: the hypothesis or None. If None, the hypothesis will be obtained from the project entry id. :type hypo: hypothesis.PhaseHypothesis """ markers = get_phase_markers() hypo = hypo or pt_hypothesis.get_hypothesis_from_project(entry_id) if not hypo or not hypo.hasRefCt(): return if visible: markers.addRefCtMarker(entry_id, hypo.getRefCt()) else: markers.removeRefCtMarker(entry_id) # Set the visible hypothesis property to the current state and update if hypo.property.get(constants.HYPO_REFCT_VISIBLE, None) != visible: set_entry_property(entry_id, constants.HYPO_REFCT_VISIBLE, visible)
[docs]def set_feature_tolerance_visible(entry_id, visible, hypo=None): """ Toggles display of feature tolerance spheres in the Workspace for hypothesis with a given entry id. :param entry_id: Phase hypothesis entry id :type entry_id: int :param visible: whether to make visible in the workspace or not :type visible: bool :param hypo: the hypothesis or None. If None, the hypothesis will be obtained from the project entry id. :type hypo: hypothesis.PhaseHypothesis """ markers = get_phase_markers() hypo = hypo or pt_hypothesis.get_hypothesis_from_project(entry_id) if not hypo: return markers.removeTolMarkers(entry_id) if visible: markers.addTolMarkers(entry_id, hypo.getHypoSites()) # Set the visible hypothesis property to the current state and update if hypo.property.get(constants.HYPO_TOL_VISIBLE, None) != visible: set_entry_property(entry_id, constants.HYPO_TOL_VISIBLE, visible)
[docs]def set_feature_labels_visible(entry_id, visible, hypo=None): """ Toggles display of feature labels for hypothesis with a given entry_id. :param entry_id: Phase hypothesis entry id :type entry_id: int :param visible: whether to make feature labels visible or not :type visible: bool :param hypo: the hypothesis or None. If None, the hypothesis will be obtained from the project entry id. :type hypo: hypothesis.PhaseHypothesis """ markers = get_phase_markers() hypo = hypo or pt_hypothesis.get_hypothesis_from_project(entry_id) if not hypo: return markers.setFeatureLabelVisible(entry_id, visible) maestro.redraw() # Set property to show feature labels to the current state and update if hypo.property.get(constants.FEATURE_LABELS_VISIBLE, None) != visible: set_entry_property(entry_id, constants.FEATURE_LABELS_VISIBLE, visible)
[docs]def set_property_labels_visible(entry_id, visible, hypo=None): """ Toggles display of feature property labels for hypothesis with a given entry_id. :param entry_id: Phase hypothesis entry id :type entry_id: int :param visible: whether to make property labels visible or not :type visible: bool :param hypo: the hypothesis or None. If None, the hypothesis will be obtained from the project entry id. :type hypo: hypothesis.PhaseHypothesis """ markers = get_phase_markers() hypo = hypo or pt_hypothesis.get_hypothesis_from_project(entry_id) if not hypo: return markers.setPropertyLabelVisible(entry_id, visible) maestro.redraw() # Set property to show detailed labels to the current state and update if hypo.property.get(constants.PROP_LABELS_VISIBLE, None) != visible: set_entry_property(entry_id, constants.PROP_LABELS_VISIBLE, visible)
[docs]def show_phase_hover_markers(pick_id): """ Show hover effect when mouse is hovered on a phase marker. :param pick_id: Phase marker pick id :type pick_id: int """ markers = get_phase_markers() try: marker = markers.getFeatureFromId(pick_id) except ValueError: # The marker may have been deleted, but it is still emitting # phaseMarkerHoverEnter signals. This except block can hopefully be # removed when MAE-44134 is addressed. (This may also apply to # hide_phase_hover_markers().) return if marker.allow_picking: marker.setHovered(True)
[docs]def hide_phase_hover_markers(pick_id): """ Remove hover effect when mouse is moved away from a phase marker. :param pick_id: Phase marker pick id :type pick_id: int """ markers = get_phase_markers() try: marker = markers.getFeatureFromId(pick_id) if marker.allow_picking: marker.setHovered(False) except ValueError: # it is possible that feature was removed while mouse was still # hovering over it. This would result in a ValueError exception, # which we can ignore pass