def __init__(self, element, *args, single_state=False, **kwargs): super().__init__(element, *args, **kwargs) self._tristate = False self._single_state = single_state self.setMinimumWidth(int(1.4*self.fontMetrics().boundingRect("QQ").width())) self.setMaximumWidth(int(1.4*self.fontMetrics().boundingRect("QQ").width())) self.setMinimumHeight(int(1.5*self.fontMetrics().boundingRect("QQ").height())) self.setMaximumHeight(int(1.5*self.fontMetrics().boundingRect("QQ").height())) self.ele_color = tuple(list(element_color(ELEMENTS.index(element)))[:-1]) if not single_state: self.state = 0 self.setStyleSheet("QPushButton { background: whitesmoke; color: DarkSlateGray; font-weight: normal; }") self.clicked.connect(self._changeState) else: self.state = 1 #weird function to decide if text color is white or black based on jmol color #it's harder to see white on green, but easier to see white on blue style = "{ background: rgb(%i, %i, %i); " % self.ele_color + \ "alternate-background-color: rgb(%i, %i, %i); " % self.ele_color + \ "color: %s; font-weight: bold; }" % contrast_bw(self.ele_color) self.setStyleSheet("QPushButton, QPushButton:down %s" % style)
def place_helium(structure, res_name, position=None): '''If position is None, place in the center of view''' max_existing = 0 for r in structure.residues: if r.chain_id == "het" and r.number > max_existing: max_existing = r.number res = structure.new_residue(res_name, "het", max_existing + 1) if position is None: if len(structure.session.models) == 0: position = (0.0, 0.0, 0.0) else: #view = structure.session.view #n, f = view.near_far_distances(view.camera, None) #position = view.camera.position.origin() + (n+f) * view.camera.view_direction() / 2 # apparently the commented-out code above is equivalent to... position = structure.session.main_view.center_of_rotation from numpy import array position = array(position) from chimerax.atomic.struct_edit import add_atom helium = Element.get_element("He") a = add_atom("He", helium, res, position) from chimerax.atomic.colors import element_color a.color = element_color(helium.number) a.draw_mode = a.BALL_STYLE return a
def seqcrow_bse(session, models=None, atoms=None): """non-H atoms are displayed with B&S H atoms are displayed with S atoms colored by Jmol colors""" from AaronTools.const import RADII from chimerax.atomic import AtomicStructure, Atom, Bond from chimerax.atomic.colors import element_color if models is None or atoms is None: apply_seqcrow_bse_lighting(session) if models is None: if atoms is None: models = session.models.list(type=AtomicStructure) else: models = list(set([atom.structure for atom in atoms])) elif isinstance(models, AtomicStructure): models = [models] for m in models: if atoms is None: m.ball_scale = 0.4 if atoms is None: atom_list = m.atoms else: atom_list = [ atom for atom in atoms if (atom.structure is m or atom.residue is m) ] for bond in m.bonds: if any(a in atom_list for a in bond.atoms): bond.halfbond = True bond.radius = 0.16 bond.hide = False for atom in atom_list: ele = atom.element.name color = element_color(atom.element.number) atom.color = color atom.display = True if ele in RADII: #AaronTools has bonding radii, maybe I should use vdw? #check to see how necessary this is atom.radius = RADII[ele] if ele != 'H': atom.draw_mode = Atom.BALL_STYLE elif len(atom.neighbors) > 1: atom.radius = 0.625 atom.draw_mode = Atom.BALL_STYLE else: atom.draw_mode = Atom.STICK_STYLE
def complete_terminal_carboxylate(session, cter): from chimerax.atomic.bond_geom import bond_positions from chimerax.atomic.struct_edit import add_atom from chimerax.atomic import Element if cter.find_atom("OXT"): return c = cter.find_atom("C") if c: if c.num_bonds != 2: return loc = bond_positions(c.coord, 3, 1.229, [n.coord for n in c.neighbors])[0] oxt = add_atom("OXT", Element.get_element("O"), cter, loc, bonded_to=c) from chimerax.atomic.colors import element_color if c.color == element_color(c.element.number): oxt.color = element_color(oxt.element.number) else: oxt.color = c.color session.logger.info("Missing OXT added to C-terminal residue %s" % str(cter))
def determine_h_color(parent_atom): global _h_coloring, _solvent_atoms res = parent_atom.residue struct = parent_atom.structure if struct not in _solvent_atoms: from weakref import WeakSet solvent_set = WeakSet() struct_atoms = struct.atoms solvent_set.update([ a for a in struct_atoms.filter( struct_atoms.structure_categories == "solvent") ]) _solvent_atoms[struct] = solvent_set else: solvent_set = _solvent_atoms[struct] if res.name in res.water_res_names or parent_atom in solvent_set: return element_color(1) if parent_atom.structure in _h_coloring: color_scheme = _h_coloring[parent_atom.structure] else: num_match_elements = 0 for a in parent_atom.structure.atoms: if a.residue.name in res.water_res_names or struct in solvent_set: continue if a.element.name == "C": continue if a.color == element_color(a.element.number): num_match_elements += 1 if num_match_elements > 1: color_scheme = "element" break else: color_scheme = "parent" break else: color_scheme = "element" _h_coloring[parent_atom.structure] = color_scheme return parent_atom.color if color_scheme == "parent" else element_color(1)
def cmd_centroid(session, atoms=None, *, mass_weighting=False, name="centroid", color=None, radius=2.0): """Wrapper to be called by command line. Use chimerax.centroids.centroid for other programming applications. """ from chimerax.core.errors import UserError from chimerax.atomic import AtomicStructure, concatenate, Structure if atoms is None: structures_atoms = [m.atoms for m in session.models if isinstance(m, AtomicStructure)] if structures_atoms: atoms = concatenate(structures_atoms) else: raise UserError("Atom specifier selects no atoms") structures = atoms.unique_structures if len(structures) > 1: crds = atoms.scene_coords else: crds = atoms.coords if mass_weighting: masses = atoms.elements.masses avg_mass = masses.sum() / len(masses) import numpy weights = masses[:, numpy.newaxis] / avg_mass else: weights = None xyz = centroid(crds, weights=weights) s = Structure(session, name=name) r = s.new_residue('centroid', 'centroid', 1) from chimerax.atomic.struct_edit import add_atom a = add_atom('cent', 'C', r, xyz) if color: a.color = color.uint8x4() else: from chimerax.atomic.colors import element_color, predominant_color color = predominant_color(atoms) if color is None: a.color = element_color(a.element.number) else: a.color = color a.radius = radius if len(structures) > 1: session.models.add([s]) else: structures[0].add([s]) session.logger.info("Centroid '%s' placed at %s" % (name, xyz)) return a
def element_changed(self, *args): elements = self.ptable.selectedElements() if len(elements) == 1: element = elements[0] self.button.setText(element) ele_color = tuple(list(element_color(ELEMENTS.index(element)))[:-1]) self.button.setStyleSheet( "QPushButton { background: rgb(%i, %i, %i); color: %s; font-weight: bold; }" % ( *ele_color, 'white' if sum( int(x < 130) - int(x > 225) for x in ele_color ) - int(ele_color[1] > 225) + int(ele_color[2] > 200) >= 2 else 'black' ) )
def seqcrow_vdw(session, models=None, atoms=None): """atoms are displayed as B atoms colored by Jmol colors""" from AaronTools.const import VDW_RADII from chimerax.atomic import AtomicStructure, Atom from chimerax.atomic.colors import element_color if models is None or atoms is None: apply_seqcrow_bse_lighting(session) if models is None: if atoms is None: models = session.models.list(type=AtomicStructure) else: models = list(set([atom.structure for atom in atoms])) elif isinstance(models, AtomicStructure): models = [models] for m in models: if atoms is None: m.ball_scale = 1.0 if atoms is None: atom_list = m.atoms else: atom_list = [atom for atom in atoms if atom.structure is m] for bond in m.bonds: if any(a in atom_list for a in bond.atoms): bond.halfbond = True bond.radius = 0.16 bond.hide = False for atom in atom_list: ele = atom.element.name color = element_color(atom.element.number) atom.color = color atom.display = True if ele in VDW_RADII: atom.radius = VDW_RADII[ele] atom.draw_mode = Atom.BALL_STYLE
def __init__(self, element, *args, **kwargs): super().__init__(element, *args, **kwargs) self._tristate = False self.state = 0 self.setMinimumWidth( int(1.3 * self.fontMetrics().boundingRect("QQ").width())) self.setMaximumWidth( int(1.3 * self.fontMetrics().boundingRect("QQ").width())) self.setMinimumHeight( int(1.5 * self.fontMetrics().boundingRect("QQ").height())) self.setMaximumHeight( int(1.5 * self.fontMetrics().boundingRect("QQ").height())) self.ele_color = tuple( list(element_color(ELEMENTS.index(element)))[:-1]) self.setStyleSheet( "QPushButton { background: ghostwhite; color: lightgray; font-weight: normal; }" ) self.clicked.connect(self._changeState)
def seqcrow_s(session, models=None, atoms=None): """atoms are represented with sticks atoms colored by Jmol colors""" from AaronTools.const import RADII from chimerax.atomic import AtomicStructure, Atom from chimerax.atomic.colors import element_color from SEQCROW.selectors import get_fragment if models is None or atoms is None: apply_seqcrow_s_lighting(session) if models is None: if atoms is None: models = session.models.list(type=AtomicStructure) else: models = list(set([atom.structure for atom in atoms])) elif isinstance(models, AtomicStructure): models = [models] for m in models: if atoms is None: m.ball_scale = 0.625 if atoms is None: atom_list = m.atoms else: atom_list = [atom for atom in atoms if atom.structure is m] for bond in m.bonds: if any(a in atom_list for a in bond.atoms): bond.halfbond = True bond.radius = 0.25 bond.hide = False tm_bonds = m.pseudobond_group(m.PBG_METAL_COORDINATION, create_type=None) ts_bonds = m.pseudobond_group("TS bonds", create_type=None) h_bonds = m.pseudobond_group("hydrogen bonds", create_type=None) for atom in atom_list: ele = atom.element.name color = element_color(atom.element.number) atom.color = color if not atom.neighbors: atom.draw_mode = Atom.BALL_STYLE if ele in RADII: atom.radius = RADII[ele] else: atom.draw_mode = Atom.STICK_STYLE if atom.element.name == "H": display = len(atom.neighbors) != 1 if tm_bonds: if any(atom in bond.atoms for bond in tm_bonds.pseudobonds): display = True if h_bonds: if any(atom in bond.atoms for bond in h_bonds.pseudobonds): display = True if ts_bonds: if any(atom in bond.atoms for bond in ts_bonds.pseudobonds): display = True if not display: for bonded_atom in atom.neighbors: if "C" != bonded_atom.element.name or (( bonded_atom.element.name == "C" and ( # show H's on terminal carbons that aren't RCH3's sum( len(a.neighbors) == 1 for a in bonded_atom.neighbors ) >= bonded_atom.num_bonds - 1 and not (sum( int(a.element.name == "H") for a in bonded_atom.neighbors) == 3 and len(bonded_atom.neighbors) == 4) # show H's in TS bonds or on atoms that are coordinated to a metal or (ts_bonds and any(bonded_atom in bond.atoms for bond in ts_bonds.pseudobonds)) or (tm_bonds and any(bonded_atom in bond.atoms for bond in tm_bonds.pseudobonds)))) # show H's that are on chiral carbons # this is a really lazy check and doesn't get everything or (sum( a.element. name == "H" for a in bonded_atom. neighbors ) == 1 and (len( set( sum( get_fragment( a, bonded_atom ). elements .masses ) for a in bonded_atom .neighbors ) ) == 4 # or any(a.element.name not in "CH" for a in bonded_atom.neighbors) ) and bonded_atom. num_bonds == 4)): display = True break # show H's on trigonal carbons adjacent to terminal trigonal carbons if "C" == bonded_atom.element.name and bonded_atom.num_bonds < 4: for bonded_atom2 in bonded_atom.neighbors: if bonded_atom2.element.name == "C" and (sum( len(a.neighbors) == 1 for a in bonded_atom2.neighbors ) >= bonded_atom2.num_bonds - 1 and not (sum( int(a.element.name == "H") for a in bonded_atom2.neighbors ) == 3 and len(bonded_atom2.neighbors) == 4)): display = True break atom.display = display
def changeElement(self, element): self.ele_color = tuple(list(element_color(ELEMENTS.index(element)))[:-1]) self.setText(element) self._changeState(state=self.state)
def seqcrow_s(session, models=None, atoms=None): """atoms are represented with sticks atoms colored by Jmol colors""" from AaronTools.const import RADII, VDW_RADII, TMETAL from chimerax.atomic import AtomicStructure, Atom, Bond from chimerax.atomic.colors import element_color if models is None or atoms is None: apply_seqcrow_s_lighting(session) if models is None: if atoms is None: models = session.models.list(type=AtomicStructure) else: models = list(set([atom.structure for atom in atoms])) elif isinstance(models, AtomicStructure): models = [models] for m in models: if atoms is None: m.ball_scale = 0.625 if atoms is None: atom_list = m.atoms else: atom_list = [atom for atom in atoms if atom.structure is m] for bond in m.bonds: if any(a in atom_list for a in bond.atoms): bond.halfbond = True bond.radius = 0.25 bond.hide = False tm_bonds = m.pseudobond_group(m.PBG_METAL_COORDINATION, create_type=None) ts_bonds = m.pseudobond_group("TS bonds", create_type=None) h_bonds = m.pseudobond_group("hydrogen bonds", create_type=None) for atom in atom_list: ele = atom.element.name color = element_color(atom.element.number) atom.color = color if not atom.neighbors: atom.draw_mode = Atom.BALL_STYLE if ele in RADII: atom.radius = RADII[ele] else: atom.draw_mode = Atom.STICK_STYLE if atom.element.name == "H": display = len(atom.neighbors) != 1 if tm_bonds: if any(atom in bond.atoms for bond in tm_bonds.pseudobonds): display = True if h_bonds: if any(atom in bond.atoms for bond in h_bonds.pseudobonds): display = True if ts_bonds: if any(atom in bond.atoms for bond in ts_bonds.pseudobonds): display = True if not display: for bonded_atom in atom.neighbors: # do not display H's on C unless it's a terminus if "C" != bonded_atom.element.name or ( (bonded_atom.element.name == "C" and (sum( len(a.neighbors) == 1 for a in bonded_atom.neighbors) >= bonded_atom.num_bonds - 1 and not (sum( int(a.element.name == "H") for a in bonded_atom.neighbors) == 3 and len( bonded_atom.neighbors) == 4) or (ts_bonds and any(bonded_atom in bond.atoms for bond in ts_bonds.pseudobonds)) or (tm_bonds and any(bonded_atom in bond.atoms for bond in tm_bonds.pseudobonds))))): display = True break atom.display = display
def _build_ui(self): layout = QGridLayout() tabs = QTabWidget() layout.addWidget(tabs) ts_bond_tab = QWidget() ts_options = QFormLayout(ts_bond_tab) self.tsbond_color = ColorButton(has_alpha_channel=False, max_size=(16, 16)) self.tsbond_color.set_color(self.settings.tsbond_color) ts_options.addRow("color:", self.tsbond_color) self.tsbond_transparency = QSpinBox() self.tsbond_transparency.setRange(1, 99) self.tsbond_transparency.setValue(self.settings.tsbond_transparency) self.tsbond_transparency.setSuffix("%") ts_options.addRow("transparency:", self.tsbond_transparency) self.tsbond_radius = QDoubleSpinBox() self.tsbond_radius.setRange(0.01, 1) self.tsbond_radius.setDecimals(3) self.tsbond_radius.setSingleStep(0.005) self.tsbond_radius.setSuffix(" \u212B") self.tsbond_radius.setValue(self.settings.tsbond_radius) ts_options.addRow("radius:", self.tsbond_radius) draw_tsbonds = QPushButton("draw TS bonds on selected atoms/bonds") draw_tsbonds.clicked.connect(self.run_tsbond) ts_options.addRow(draw_tsbonds) self.draw_tsbonds = draw_tsbonds erase_tsbonds = QPushButton("erase selected TS bonds") erase_tsbonds.clicked.connect(self.run_erase_tsbond) ts_options.addRow(erase_tsbonds) self.erase_tsbonds = erase_tsbonds bond_tab = QWidget() bond_options = QFormLayout(bond_tab) self.bond_halfbond = QCheckBox() self.bond_halfbond.setChecked(self.settings.bond_halfbond) self.bond_halfbond.setToolTip( "each half of the bond will be colored according to the atom's color" ) bond_options.addRow("half-bond:", self.bond_halfbond) self.bond_color = ColorButton(has_alpha_channel=True, max_size=(16, 16)) self.bond_color.set_color(self.settings.bond_color) self.bond_color.setEnabled( self.bond_halfbond.checkState() != Qt.Checked) self.bond_halfbond.stateChanged.connect( lambda state, widget=self.bond_color: self.bond_color.setEnabled( state != Qt.Checked)) bond_options.addRow("color:", self.bond_color) self.bond_radius = QDoubleSpinBox() self.bond_radius.setRange(0.01, 1) self.bond_radius.setDecimals(3) self.bond_radius.setSingleStep(0.005) self.bond_radius.setSuffix(" \u212B") self.bond_radius.setValue(self.settings.bond_radius) bond_options.addRow("radius:", self.bond_radius) draw_tsbonds = QPushButton("draw bond between selected atoms") draw_tsbonds.clicked.connect(self.run_bond) bond_options.addRow(draw_tsbonds) self.draw_tsbonds = draw_tsbonds erase_bonds = QPushButton("erase selected bonds") erase_bonds.clicked.connect( lambda *, ses=self.session: run(ses, "delete bonds sel")) bond_options.addRow(erase_bonds) self.erase_bonds = erase_bonds hbond_tab = QWidget() hbond_options = QFormLayout(hbond_tab) self.hbond_color = ColorButton(has_alpha_channel=True, max_size=(16, 16)) self.hbond_color.set_color(self.settings.hbond_color) hbond_options.addRow("color:", self.hbond_color) self.hbond_radius = QDoubleSpinBox() self.hbond_radius.setDecimals(3) self.hbond_radius.setSuffix(" \u212B") self.hbond_radius.setValue(self.settings.hbond_radius) hbond_options.addRow("radius:", self.hbond_radius) self.hbond_dashes = QSpinBox() self.hbond_dashes.setRange(0, 28) self.hbond_dashes.setSingleStep(2) self.hbond_radius.setSingleStep(0.005) self.hbond_dashes.setValue(self.settings.hbond_dashes) hbond_options.addRow("dashes:", self.hbond_dashes) draw_hbonds = QPushButton("draw H-bonds") draw_hbonds.clicked.connect(self.run_hbond) hbond_options.addRow(draw_hbonds) self.draw_hbonds = draw_hbonds erase_hbonds = QPushButton("erase all H-bonds") erase_hbonds.clicked.connect( lambda *, ses=self.session: run(ses, "~hbonds")) hbond_options.addRow(erase_hbonds) self.erase_hbonds = erase_hbonds tm_bond_tab = QWidget() tm_bond_options = QFormLayout(tm_bond_tab) self.tm_bond_color = ColorButton(has_alpha_channel=True, max_size=(16, 16)) self.tm_bond_color.set_color(self.settings.tm_bond_color) tm_bond_options.addRow("color:", self.tm_bond_color) self.tm_bond_radius = QDoubleSpinBox() self.tm_bond_radius.setDecimals(3) self.tm_bond_radius.setSuffix(" \u212B") self.tm_bond_radius.setValue(self.settings.tm_bond_radius) tm_bond_options.addRow("radius:", self.tm_bond_radius) self.tm_bond_dashes = QSpinBox() self.tm_bond_dashes.setRange(0, 28) self.tm_bond_dashes.setSingleStep(2) self.tm_bond_radius.setSingleStep(0.005) self.tm_bond_dashes.setValue(self.settings.tm_bond_dashes) tm_bond_options.addRow("dashes:", self.tm_bond_dashes) draw_tm_bonds = QPushButton("draw metal coordination bonds") draw_tm_bonds.clicked.connect(self.run_tm_bond) tm_bond_options.addRow(draw_tm_bonds) self.draw_tm_bonds = draw_tm_bonds erase_tm_bonds = QPushButton("erase all metal coordination bonds") erase_tm_bonds.clicked.connect(self.del_tm_bond) tm_bond_options.addRow(erase_tm_bonds) self.erase_tm_bonds = erase_tm_bonds bond_length_tab = QWidget() bond_length_layout = QFormLayout(bond_length_tab) self.bond_distance = QDoubleSpinBox() self.bond_distance.setRange(0.5, 10.0) self.bond_distance.setSingleStep(0.05) self.bond_distance.setValue(1.51) self.bond_distance.setSuffix(" \u212B") bond_length_layout.addRow("bond length:", self.bond_distance) self.move_fragment = QComboBox() self.move_fragment.addItems(["both", "smaller", "larger"]) bond_length_layout.addRow("move side:", self.move_fragment) bond_lookup = QGroupBox("bond length lookup:") bond_lookup_layout = QGridLayout(bond_lookup) bond_lookup_layout.addWidget( QLabel("elements:"), 0, 0, ) self.ele1 = QPushButton("C") self.ele1.setMinimumWidth( int(1.3 * self.ele1.fontMetrics().boundingRect("QQ").width())) self.ele1.setMaximumWidth( int(1.3 * self.ele1.fontMetrics().boundingRect("QQ").width())) self.ele1.setMinimumHeight( int(1.5 * self.ele1.fontMetrics().boundingRect("QQ").height())) self.ele1.setMaximumHeight( int(1.5 * self.ele1.fontMetrics().boundingRect("QQ").height())) self.ele1.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) ele_color = tuple(list(element_color(ELEMENTS.index("C")))[:-1]) self.ele1.setStyleSheet( "QPushButton { background: rgb(%i, %i, %i); color: %s; font-weight: bold; }" % (*ele_color, 'white' if sum(int(x < 130) - int(x > 225) for x in ele_color) - int(ele_color[1] > 225) + int(ele_color[2] > 200) >= 2 else 'black')) self.ele1.clicked.connect( lambda *args, button=self.ele1: self.open_ptable(button)) bond_lookup_layout.addWidget(self.ele1, 0, 1, Qt.AlignRight | Qt.AlignTop) bond_lookup_layout.addWidget(QLabel("-"), 0, 2, Qt.AlignHCenter | Qt.AlignVCenter) self.ele2 = QPushButton("C") self.ele2.setMinimumWidth( int(1.3 * self.ele2.fontMetrics().boundingRect("QQ").width())) self.ele2.setMaximumWidth( int(1.3 * self.ele2.fontMetrics().boundingRect("QQ").width())) self.ele2.setMinimumHeight( int(1.5 * self.ele2.fontMetrics().boundingRect("QQ").height())) self.ele2.setMaximumHeight( int(1.5 * self.ele2.fontMetrics().boundingRect("QQ").height())) self.ele2.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) ele_color = tuple(list(element_color(ELEMENTS.index("C")))[:-1]) self.ele2.setStyleSheet( "QPushButton { background: rgb(%i, %i, %i); color: %s; font-weight: bold; }" % (*ele_color, 'white' if sum(int(x < 130) - int(x > 225) for x in ele_color) - int(ele_color[1] > 225) + int(ele_color[2] > 200) >= 2 else 'black')) self.ele2.clicked.connect( lambda *args, button=self.ele2: self.open_ptable(button)) bond_lookup_layout.addWidget(self.ele2, 0, 3, Qt.AlignLeft | Qt.AlignTop) bond_lookup_layout.addWidget(QLabel("bond order:"), 1, 0) self.bond_order = BondOrderSpinBox() self.bond_order.setRange(1., 3.) self.bond_order.setValue(1) self.bond_order.setSingleStep(0.5) self.bond_order.setDecimals(1) self.bond_order.valueChanged.connect(self.check_bond_lengths) bond_lookup_layout.addWidget(self.bond_order, 1, 1, 1, 3) bond_lookup_layout.setColumnStretch(0, 0) bond_lookup_layout.setColumnStretch(1, 0) bond_lookup_layout.setColumnStretch(2, 0) bond_lookup_layout.setColumnStretch(3, 1) bond_length_layout.addRow(bond_lookup) self.status = QStatusBar() self.status.setSizeGripEnabled(False) bond_lookup_layout.addWidget(self.status, 2, 0, 1, 4) self.do_bond_change = QPushButton("change selected bond lengths") self.do_bond_change.clicked.connect(self.change_bond_length) bond_length_layout.addRow(self.do_bond_change) tabs.addTab(bond_tab, "covalent bonds") tabs.addTab(ts_bond_tab, "TS bonds") tabs.addTab(hbond_tab, "H-bonds") tabs.addTab(tm_bond_tab, "coordination bonds") tabs.addTab(bond_length_tab, "bond length") self.tool_window.ui_area.setLayout(layout) self.tool_window.manage(None)
def use_rotamer(session, res, rots, retain=False, log=False, bfactor=None): """Takes a Residue instance and either a list or dictionary of rotamers (as returned by get_rotamers, i.e. with backbone already matched) and swaps the Residue's side chain with the given rotamers. If the rotamers are a dictionary, then the keys should match the alt locs of the CA atom, and the corresponding rotamer will be used for that alt loc. If the alt locs are a list, if the list has only one rotamer then that rotamer will be used for each CA alt loc. If the list has multiple rotamers, then the CA must have only one alt loc (namely ' ') and all the rotamers will be attached, using different alt loc characters for each. If 'retain' is True, existing side chains will be retained. If 'bfactor' is None, then the current highest existing bfactor in the residue will be used. """ N = res.find_atom("N") CA = res.find_atom("CA") C = res.find_atom("C") if not N or not C or not CA: raise LimitationError( "N, CA, or C missing from %s: needed for side-chain pruning algorithm" % res) import string alt_locs = string.ascii_uppercase + string.ascii_lowercase + string.digits + string.punctuation if retain and CA.alt_locs: raise LimitationError( "Cannot retain side chains if multiple CA alt locs") ca_alt_locs = [' '] if not CA.alt_locs else CA.alt_locs if not isinstance(rots, dict): # reformat as dictionary if CA.alt_locs and len(rots) > 1: raise LimitationError( "Cannot add multiple rotamers to multi-position backbone") retained_alt_locs = side_chain_locs(res) if retain else [] num_retained = len(retained_alt_locs) if len(rots) + num_retained > len(alt_locs): raise LimitationError("Don't have enough unique alternate " "location characters to place %d rotamers." % len(rots)) if len(rots) + num_retained > 1: rots = { loc: rot for loc, rot in zip( [c for c in alt_locs if c not in retained_alt_locs][:len(rots)], rots) } else: rots = {alt_loc: rots[0] for alt_loc in ca_alt_locs} swap_type = list(rots.values())[0].residues[0].name if retain and res.name != swap_type: raise LimitationError( "Cannot retain side chains if rotamers are a different residue type" ) rot_anchors = {} for rot in rots.values(): rot_res = rot.residues[0] rot_N, rot_CA = rot_res.find_atom("N"), rot_res.find_atom("CA") if not rot_N or not rot_CA: raise LimitationError( "N or CA missing from rotamer: cannot matchup with original residue" ) rot_anchors[rot] = (rot_N, rot_CA) color_by_element = N.color != CA.color if color_by_element: carbon_color = CA.color else: uniform_color = N.color # prune old side chain bfactor = bfactor_for_res(res, bfactor) if not retain: res_atoms = res.atoms side_atoms = res_atoms.filter(res_atoms.is_side_onlys) serials = {a.name: a.serial_number for a in side_atoms} side_atoms.delete() else: serials = {} # for proline, also prune amide hydrogens if swap_type == "PRO": for nnb in N.neighbors[:]: if nnb.element.number == 1: N.structure.delete_atom(nnb) tot_prob = sum([r.rotamer_prob for r in rots.values()]) with CA.suppress_alt_loc_change_notifications(): res.name = swap_type from chimerax.atomic.struct_edit import add_atom, add_bond for alt_loc, rot in rots.items(): if CA.alt_locs: CA.alt_loc = alt_loc if log: extra = " using alt loc %s" % alt_loc if alt_loc != ' ' else "" session.logger.info( "Applying %s rotamer (chi angles: %s) to %s%s" % (rot_res.name, " ".join(["%.1f" % c for c in rot.chis]), res, extra)) # add new side chain rot_N, rot_CA = rot_anchors[rot] visited = set([N, CA, C]) sprouts = [rot_CA] while sprouts: sprout = sprouts.pop() built_sprout = res.find_atom(sprout.name) for nb in sprout.neighbors: built_nb = res.find_atom(nb.name) if tot_prob == 0.0: # some rotamers in Dunbrack are zero prob! occupancy = 1.0 / len(rots) else: occupancy = rot.rotamer_prob / tot_prob if not built_nb: serial = serials.get(nb.name, None) built_nb = add_atom(nb.name, nb.element, res, nb.coord, serial_number=serial, bonded_to=built_sprout, alt_loc=alt_loc) built_nb.occupancy = occupancy built_nb.bfactor = bfactor if color_by_element: if built_nb.element.name == "C": built_nb.color = carbon_color else: from chimerax.atomic.colors import element_color built_nb.color = element_color( built_nb.element.number) else: built_nb.color = uniform_color elif built_nb not in visited: built_nb.set_alt_loc(alt_loc, True) built_nb.coord = nb.coord built_nb.occupancy = occupancy built_nb.bfactor = bfactor if built_nb not in visited: sprouts.append(nb) visited.add(built_nb) if built_nb not in built_sprout.neighbors: add_bond(built_sprout, built_nb)
def template_swap_res(res, res_type, *, preserve=False, bfactor=None): """change 'res' into type 'res_type'""" fixed, buds, start, end = get_res_info(res) if res_type == "HIS": res_type = "HIP" if res_type in ["A", "C", "G", "T" ] and res.name in ["DA", "DC", "DT", "DG"]: res_type = "D" + res_type from chimerax.atomic import TmplResidue, Atom tmpl_res = TmplResidue.get_template(res_type, start=start, end=end) if not tmpl_res: raise TemplateError("No connectivity template for residue '%s'" % res_type) # sanity check: does the template have the bud atoms? for bud in buds: if tmpl_res.find_atom(bud) is None: raise TemplateError("New residue type (%s) not compatible with" " starting residue type (%s)" % (res_type, res.name)) color_by_element = False uniform_color = res.find_atom(buds[0]).color het = res.find_atom("N") or res.find_atom("O4'") if het: carbon = res.find_atom("CA") or res.find_atom("C4'") if carbon: color_by_element = het.color != carbon.color if color_by_element: carbon_color = carbon.color else: uniform_color = het.color bfactor = bfactor_for_res(res, bfactor) if preserve: if "CA" in fixed and res_type not in ['GLY', 'ALA']: raise TemplateSwapError( "'preserve' keyword not yet implemented for amino acids") a1 = res.find_atom("O4'") a2 = res.find_atom("C1'") if not a1 or not a2: preserve_pos = None else: dihed_names = {"N9": ["C4", "C8"], "N1": ["C2", "C6"]} a3 = res.find_atom("N9") or res.find_atom("N1") if a3: if a2 not in a3.neighbors: preserve_pos = None else: preserve_pos = a3.coord else: preserve_pos = None if preserve_pos: p1, p2, p3 = [a.coord for a in (a1, a2, a3)] preserved_pos = False prev_name, alt_name = dihed_names[a3.name] a4 = res.find_atom(prev_name) if a4 and a3 in a4.neighbors: p4 = a4.coord from chimerax.geometry import dihedral preserve_dihed = dihedral(p1, p2, p3, p4) else: preserve_dihed = None else: preserve_dihed = None # prune non-backbone atoms for a in res.atoms: if a.name not in fixed: a.structure.delete_atom(a) # add new sidechain new_atoms = [] xf = None from chimerax.atomic.struct_edit import add_bond while len(buds) > 0: bud = buds.pop() tmpl_bud = tmpl_res.find_atom(bud) res_bud = res.find_atom(bud) try: info = Atom.idatm_info_map[tmpl_bud.idatm_type] geom = info.geometry subs = info.substituents except KeyError: raise AssertionError( "Can't determine atom type information for atom %s of residue %s" % (bud, res)) # use .coord rather than .scene_coord: we want to set the new atom's coord, # to which the proper xform will then be applied for a, b in zip(tmpl_bud.neighbors, tmpl_bud.bonds): if a.element.number == 1: # don't add hydrogens continue if res.find_atom(a.name): res_bonder = res.find_atom(a.name) if res_bonder not in res_bud.neighbors: add_bond(a, res_bonder) continue new_atom = None num_bonded = len(res_bud.bonds) if num_bonded >= subs: raise AssertionError( "Too many atoms bonded to %s of residue %s" % (bud, res)) if num_bonded == 0: raise AssertionError( "Atom %s of residue %s has no neighbors after pruning?!?" % (bud, res)) # since fused ring systems may have distorted bond angles, always use dihedral placement real1 = res_bud.neighbors[0] kw = {} if preserve: if preserve_pos and not preserved_pos: kw['pos'] = preserve_pos preserved_pos = True preserved_name = a.name elif preserve_dihed is not None: prev_name, alt_name = dihed_names[preserved_name] if a.name == prev_name: kw['dihed'] = preserve_dihed elif a.name == alt_name: kw['dihed'] = preserve_dihed + 180.0 if not kw and xf is not None: kw['pos'] = xf * a.coord new_atom = form_dihedral(res_bud, real1, tmpl_res, a, b, **kw) new_atom.draw_mode = res_bud.draw_mode new_atom.bfactor = bfactor if color_by_element: if new_atom.element.name == "C": new_atom.color = carbon_color else: from chimerax.atomic.colors import element_color new_atom.color = element_color(new_atom.element.number) else: new_atom.color = uniform_color new_atoms.append(new_atom) for bonded in a.neighbors: bond_atom = res.find_atom(bonded.name) if not bond_atom: continue add_bond(new_atom, bond_atom) buds.append(new_atom.name) # once we've placed 3 side chain atoms, we use superpositioning to # place the remainder of the side chain, since dihedrals will # likely distort ring closures if 'preserve' is true if buds and not xf and len(new_atoms) >= 3: placed_positions = [] tmpl_positions = [] for na in new_atoms: placed_positions.append(na.coord) tmpl_positions.append(tmpl_res.find_atom(na.name).coord) import numpy from chimerax.geometry import align_points xf = align_points(numpy.array(tmpl_positions), numpy.array(placed_positions))[0] res.name = res_type
def cmd_define_plane(session, atoms, *, thickness=defaults["plane_thickness"], padding=0.0, color=None, radius=None, name="plane"): """Wrapper to be called by command line. Use chimerax.axes_planes.plane for other programming applications. """ from chimerax.core.errors import UserError from chimerax.atomic import AtomicStructure, concatenate, Structure if atoms is None: structures_atoms = [ m.atoms for m in session.models if isinstance(m, AtomicStructure) ] if structures_atoms: atoms = concatenate(structures_atoms) else: raise UserError("Atom specifier selects no atoms") if len(atoms) < 3: raise UserError("Must specify at least 3 atoms to define a plane") structures = atoms.unique_structures if len(structures) > 1: crds = atoms.scene_coords else: crds = atoms.coords from chimerax.geometry import Plane, distance_squared plane = Plane(crds) if radius is None: max_sq_dist = None origin = plane.origin for crd in crds: projected = plane.nearest(crd) sq_dist = distance_squared(origin, projected) if max_sq_dist is None or sq_dist > max_sq_dist: max_sq_dist = sq_dist from math import sqrt radius = sqrt(max_sq_dist) if color is None: from chimerax.atomic.colors import element_color, predominant_color color = predominant_color(atoms) if color is None: color = element_color(a.element.number) else: color = color.uint8x4() plane_model = PlaneModel(session, name, plane, thickness, radius + padding, color) if len(structures) > 1: session.models.add([plane_model]) else: structures[0].add([plane_model]) session.logger.info("Plane %s' placed at %s with normal %s" % (name, plane.origin, plane.normal)) return plane_model
def _build_ui(self): layout = QGridLayout() self.alchemy_tabs = QTabWidget() #substitute substitute_tab = QWidget() substitute_layout = QGridLayout(substitute_tab) sublabel = QLabel("substituent name:") substitute_layout.addWidget(sublabel, 0, 0, Qt.AlignVCenter) self.subname = QLineEdit() # self.subname.setText("Et") sub_completer = NameCompleter(Substituent.list(), self.subname) self.subname.setCompleter(sub_completer) self.subname.setToolTip("name of substituent in the AaronTools library or your personal library\nseparate names with commas and uncheck 'modify selected structure' to create several structures") substitute_layout.addWidget(self.subname, 0, 1, Qt.AlignVCenter) open_sub_lib = QPushButton("from library...") open_sub_lib.clicked.connect(self.open_sub_selector) substitute_layout.addWidget(open_sub_lib, 0, 2, Qt.AlignTop) substitute_layout.addWidget(QLabel("modify selected structure:"), 1, 0, 1, 1, Qt.AlignVCenter) self.close_previous_sub = QCheckBox() self.close_previous_sub.setToolTip("checked: selected structure will be modified\nunchecked: new model will be created for the modified structure") self.close_previous_sub.setChecked(self.settings.modify) self.close_previous_sub.stateChanged.connect(self.close_previous_change) substitute_layout.addWidget(self.close_previous_sub, 1, 1, 1, 2, Qt.AlignTop) substitute_layout.addWidget(QLabel("relax substituent:"), 2, 0, 1, 1, Qt.AlignVCenter) self.minimize = QCheckBox() self.minimize.setToolTip("spin the added substituents to try to minimize the LJ potential energy") self.minimize.setChecked(self.settings.minimize) substitute_layout.addWidget(self.minimize, 2, 1, 1, 1, Qt.AlignTop) substitute_layout.addWidget(QLabel("guess previous substituent:"), 3, 0, 1, 1, Qt.AlignVCenter) self.guess_old = QCheckBox() self.guess_old.setToolTip("checked: leave the longest connected fragment in the residue\nunchecked: previous substituent must be selected") self.guess_old.setChecked(self.settings.guess) self.guess_old.stateChanged.connect(lambda state, settings=self.settings: settings.__setattr__("guess", True if state == Qt.Checked else False)) substitute_layout.addWidget(self.guess_old, 3, 1, 1, 2, Qt.AlignTop) substitute_layout.addWidget(QLabel("new residue:"), 5, 0, 1, 1, Qt.AlignVCenter) self.new_residue = QCheckBox() self.new_residue.setToolTip("put the new substituent in its own residue instead\nof adding it to the residue of the old substituent") self.new_residue.setChecked(self.settings.new_residue) self.new_residue.stateChanged.connect(lambda state, settings=self.settings: settings.__setattr__("new_residue", True if state == Qt.Checked else False)) substitute_layout.addWidget(self.new_residue, 5, 1, 1, 2, Qt.AlignTop) substitute_layout.addWidget(QLabel("use distance names:"), 4, 0, 1, 1, Qt.AlignVCenter) self.use_greek = QCheckBox() self.use_greek.setChecked(self.settings.use_greek) self.use_greek.setToolTip("indicate distance from point of attachment with atom name") substitute_layout.addWidget(self.use_greek, 4, 1, 1, 1, Qt.AlignTop) substitute_layout.addWidget(QLabel("change residue name:"), 6, 0, 1, 1, Qt.AlignVCenter) self.new_sub_name = QLineEdit() self.new_sub_name.setToolTip("change name of modified residues") self.new_sub_name.setPlaceholderText("leave blank to keep current") substitute_layout.addWidget(self.new_sub_name, 6, 1, 1, 2, Qt.AlignTop) substitute_button = QPushButton("substitute current selection") substitute_button.clicked.connect(self.do_substitute) substitute_layout.addWidget(substitute_button, 7, 0, 1, 3, Qt.AlignTop) self.substitute_button = substitute_button substitute_layout.setRowStretch(0, 0) substitute_layout.setRowStretch(1, 0) substitute_layout.setRowStretch(2, 0) substitute_layout.setRowStretch(3, 0) substitute_layout.setRowStretch(4, 0) substitute_layout.setRowStretch(5, 0) substitute_layout.setRowStretch(6, 0) substitute_layout.setRowStretch(7, 1) #map ligand maplig_tab = QWidget() maplig_layout = QGridLayout(maplig_tab) liglabel = QLabel("ligand name:") maplig_layout.addWidget(liglabel, 0, 0, Qt.AlignVCenter) self.ligname = QLineEdit() lig_completer = NameCompleter(Component.list(), self.ligname) self.ligname.setCompleter(lig_completer) self.ligname.setToolTip("name of ligand in the AaronTools library or your personal library\nseparate names with commas and uncheck 'modify selected structure' to create several structures") maplig_layout.addWidget(self.ligname, 0, 1, Qt.AlignVCenter) open_lig_lib = QPushButton("from library...") open_lig_lib.clicked.connect(self.open_lig_selector) maplig_layout.addWidget(open_lig_lib, 0, 2, Qt.AlignTop) maplig_layout.addWidget(QLabel("modify selected structure:"), 1, 0, 1, 1, Qt.AlignVCenter) self.close_previous_lig = QCheckBox() self.close_previous_lig.setToolTip("checked: selected structure will be modified\nunchecked: new model will be created for the modified structure") self.close_previous_lig.setChecked(self.settings.modify) self.close_previous_lig.stateChanged.connect(self.close_previous_change) maplig_layout.addWidget(self.close_previous_lig, 1, 1, 1, 2, Qt.AlignTop) maplig_button = QPushButton("swap ligand with selected coordinating atoms") maplig_button.clicked.connect(self.do_maplig) maplig_layout.addWidget(maplig_button, 2, 0, 1, 3, Qt.AlignTop) self.maplig_button = maplig_button start_structure_button = QPushButton("place in:") self.lig_model_selector = ModelComboBox(self.session, addNew=True) start_structure_button.clicked.connect(self.do_new_lig) maplig_layout.addWidget(start_structure_button, 3, 0, 1, 1, Qt.AlignTop) maplig_layout.addWidget(self.lig_model_selector, 3, 1, 1, 2, Qt.AlignTop) maplig_layout.setRowStretch(0, 0) maplig_layout.setRowStretch(1, 0) maplig_layout.setRowStretch(2, 0) maplig_layout.setRowStretch(3, 1) #close ring closering_tab = QWidget() closering_layout = QGridLayout(closering_tab) ringlabel = QLabel("ring name:") closering_layout.addWidget(ringlabel, 0, 0, Qt.AlignVCenter) self.ringname = QLineEdit() ring_completer = NameCompleter(Ring.list(), self.ringname) self.ringname.setCompleter(ring_completer) self.ringname.setToolTip("name of ring in the AaronTools library or your personal library\nseparate names with commas and uncheck 'modify selected structure' to create several structures") closering_layout.addWidget(self.ringname, 0, 1, Qt.AlignVCenter) open_ring_lib = QPushButton("from library...") open_ring_lib.clicked.connect(self.open_ring_selector) closering_layout.addWidget(open_ring_lib, 0, 2, Qt.AlignTop) closering_layout.addWidget(QLabel("modify selected structure:"), 1, 0, 1, 1, Qt.AlignVCenter) self.close_previous_ring = QCheckBox() self.close_previous_ring.setToolTip("checked: selected structure will be modified\nunchecked: new model will be created for the modified structure") self.close_previous_ring.setChecked(self.settings.modify) self.close_previous_ring.stateChanged.connect(self.close_previous_change) closering_layout.addWidget(self.close_previous_ring, 1, 1, 1, 2, Qt.AlignTop) closering_layout.addWidget(QLabel("try multiple:"), 2, 0, 1, 1, Qt.AlignVCenter) self.minimize_ring = QCheckBox() self.minimize_ring.setToolTip("try to use other versions of this ring in the library to find the one that fits best") self.minimize_ring.setChecked(self.settings.minimize_ring) closering_layout.addWidget(self.minimize_ring, 2, 1, 1, 2, Qt.AlignTop) closering_layout.addWidget(QLabel("new residue name:"), 3, 0, 1, 1, Qt.AlignVCenter) self.new_ring_name = QLineEdit() self.new_ring_name.setToolTip("change name of modified residues") self.new_ring_name.setPlaceholderText("leave blank to keep current") closering_layout.addWidget(self.new_ring_name, 3, 1, 1, 2, Qt.AlignTop) closering_button = QPushButton("put a ring on current selection") closering_button.clicked.connect(self.do_fusering) closering_layout.addWidget(closering_button, 4, 0, 1, 3, Qt.AlignTop) self.closering_button = closering_button start_structure_button = QPushButton("place in:") self.ring_model_selector = ModelComboBox(self.session, addNew=True) start_structure_button.clicked.connect(self.do_new_ring) closering_layout.addWidget(start_structure_button, 5, 0, 1, 1, Qt.AlignTop) closering_layout.addWidget(self.ring_model_selector, 5, 1, 1, 2, Qt.AlignTop) closering_layout.setRowStretch(0, 0) closering_layout.setRowStretch(1, 0) closering_layout.setRowStretch(2, 0) closering_layout.setRowStretch(3, 0) closering_layout.setRowStretch(4, 0) closering_layout.setRowStretch(5, 1) #change element changeelement_tab = QWidget() changeelement_layout = QFormLayout(changeelement_tab) self.element = QPushButton("C") self.element.setMinimumWidth(int(1.3*self.element.fontMetrics().boundingRect("QQ").width())) self.element.setMaximumWidth(int(1.3*self.element.fontMetrics().boundingRect("QQ").width())) self.element.setMinimumHeight(int(1.5*self.element.fontMetrics().boundingRect("QQ").height())) self.element.setMaximumHeight(int(1.5*self.element.fontMetrics().boundingRect("QQ").height())) ele_color = tuple(list(element_color(ELEMENTS.index("C")))[:-1]) self.element.setStyleSheet( "QPushButton { background: rgb(%i, %i, %i); color: %s; font-weight: bold; }" % ( *ele_color, 'white' if sum( int(x < 130) - int(x > 225) for x in ele_color ) - int(ele_color[1] > 225) + int(ele_color[2] > 200) >= 2 else 'black' ) ) self.element.clicked.connect(self.open_ptable) changeelement_layout.addRow("element:", self.element) self.vsepr = QComboBox() self.vsepr.addItems([ "do not change", # 0 "linear (1 bond)", # 1 "linear (2 bonds)", # 2 "trigonal planar (2 bonds)", # 3 "tetrahedral (2 bonds)", # 4 "trigonal planar", # 5 "tetrahedral (3 bonds)", # 6 "T-shaped", # 7 "trigonal pyramidal", # 8 "tetrahedral", # 9 "sawhorse", #10 "seesaw", #11 "square planar", #12 "trigonal bipyramidal", #13 "square pyramidal", #14 "pentagonal", #15 "octahedral", #16 "hexagonal", #17 "trigonal prismatic", #18 "pentagonal pyramidal", #19 "capped octahedral", #20 "capped trigonal prismatic", #21 "heptagonal", #22 "hexagonal pyramidal", #23 "pentagonal bipyramidal", #24 "biaugmented trigonal prismatic", #25 "cubic", #26 "elongated trigonal bipyramidal", #27 "hexagonal bipyramidal", #28 "heptagonal pyramidal", #29 "octagonal", #30 "square antiprismatic", #31 "trigonal dodecahedral", #32 "capped cube", #33 "capped square antiprismatic", #34 "enneagonal", #35 "heptagonal bipyramidal", #36 "hula-hoop", #37 "triangular cupola", #38 "tridiminished icosahedral", #39 "muffin", #40 "octagonal pyramidal", #41 "tricapped trigonal prismatic", #42 ]) self.vsepr.setCurrentIndex(9) self.vsepr.insertSeparator(33) self.vsepr.insertSeparator(25) self.vsepr.insertSeparator(20) self.vsepr.insertSeparator(16) self.vsepr.insertSeparator(13) self.vsepr.insertSeparator(8) self.vsepr.insertSeparator(5) self.vsepr.insertSeparator(2) self.vsepr.insertSeparator(1) self.vsepr.insertSeparator(0) changeelement_layout.addRow("geometry:", self.vsepr) self.change_bonds = QCheckBox() self.change_bonds.setChecked(self.settings.change_bonds) changeelement_layout.addRow("adjust bond lengths:", self.change_bonds) change_element_button = QPushButton("change selected elements") change_element_button.clicked.connect(self.do_change_element) changeelement_layout.addRow(change_element_button) self.change_element_button = change_element_button start_structure_button = QPushButton("place in:") self.model_selector = ModelComboBox(self.session, addNew=True) start_structure_button.clicked.connect(self.do_new_atom) changeelement_layout.addRow(start_structure_button, self.model_selector) delete_atoms_button = QPushButton("delete selected atoms") delete_atoms_button.clicked.connect(self.delete_atoms) changeelement_layout.addRow(delete_atoms_button) self.alchemy_tabs.addTab(substitute_tab, "substitute") self.alchemy_tabs.addTab(maplig_tab, "swap ligand") self.alchemy_tabs.addTab(closering_tab, "fuse ring") self.alchemy_tabs.addTab(changeelement_tab, "change element") layout.addWidget(self.alchemy_tabs) self.tool_window.ui_area.setLayout(layout) self.tool_window.manage(None)