# --- UCSF Chimera Copyright ---
# Copyright (c) 2000 Regents of the University of California.
# All rights reserved. This software provided pursuant to a
# license agreement containing restrictions on its disclosure,
# duplication and use. This notice must be embedded in or
# attached to all copies, including partial copies, of the
# software or any revisions or derivations thereof.
# --- UCSF Chimera Copyright ---
#
# $Id: gui.py 40949 2016-04-12 00:20:03Z pett $
ADD_ATOMS = "Start Structure"
CHANGE_ATOM = "Modify Structure"
ADJUST_BONDS = "Adjust Bonds"
BOND_ROTS = "Adjust Torsions"
JOIN_MODELS = "Join Models"
CHIRALITY = "Invert"
BOND_ANGLES = "Adjust Bond Angles"
pageNames = [ADD_ATOMS, CHANGE_ATOM, ADJUST_BONDS, BOND_ROTS,
JOIN_MODELS, CHIRALITY, BOND_ANGLES]
from StructMeasure.prefs import prefs, ROT_LABEL, ROT_DIAL_SIZE, \
TORSION_PRECISION, SHOW_DEGREE_SYMBOL
import chimera
from chimera.baseDialog import ModelessDialog
from chimera import UserError, selection, mousemodes
from BuildStructure import setBondLength, placeHelium, changeAtom, ParamError, \
placeFragment, placePeptide, elementRadius, bind, cnPeptideBond, placeNucleotide, \
PeptideError, NucleotideError
from BondRotMgr import bondRotMgr
import Tkinter, Pmw
class BuildStructureDialog(ModelessDialog):
title = "Build Structure"
name = "build structure"
help = "ContributedSoftware/editing/editing.html"
buttons = ("Close",)
provideStatus = True
statusPosition = "left"
RES_NOOP = 0
RES_RENAME = 1
RES_MAKENEW = 2
PLACE_ATOM = "atom"
PLACE_FRAGMENT = "fragment"
PLACE_SMILES = "SMILES string"
PLACE_PUBCHEM = "PubChem CID"
PLACE_PEPTIDE = "peptide"
PLACE_NUCLEIC = "helical DNA/RNA"
PLACE_RNA = "more RNA..."
dialSizes = [".1i", ".2i", ".3i"]
def fillInUI(self, parent):
self._mapped = False
self.notebookMenu = Pmw.OptionMenu(parent, items=pageNames,
initialitem=pageNames[0],
command=lambda pn: self.notebook.selectpage(pn))
self.notebookMenu.grid(row=0, column=0)
self.notebook = Pmw.NoteBook(parent, tabpos=None,
raisecommand=self._raiseCB, lowercommand=self._lowerCB)
self.notebook.grid(sticky="nsew")
parent.rowconfigure(1, weight=1)
parent.columnconfigure(0, weight=1)
for pn in pageNames:
self.notebook.add(pn)
self.resNameVar = Tkinter.StringVar(parent)
self.resNameVar.set("UNK")
self.chainNameVar = Tkinter.StringVar(parent)
self.colorByElementVar = Tkinter.IntVar(parent)
self.colorByElementVar.set(True)
self._countMolecules()
chimera.openModels.addAddHandler(self._countMolecules, None)
chimera.openModels.addRemoveHandler(self._countMolecules, None)
self._fillPlaceAtomPage()
self._fillChangeAtomPage()
self._fillAdjustBondsPage()
self._fillJoinModelsPage()
self._fillChiralityPage()
self._fillBondAnglesPage()
self._fillBondRotsPage()
self.notebook.setnaturalsize()
self.buttonWidgets['Help'].config(state='normal',
command=self.Help)
from SimpleSession import SAVE_SESSION
chimera.triggers.addHandler(SAVE_SESSION, self._sessionSave, None)
chimera.triggers.addHandler('Atom', self._atomChange, None)
def atomLabel(self, atom, diffWith=None):
if self.numMolecules > 1:
showModel = 1
else:
showModel = 0
from chimera.misc import chimeraLabel
lab = chimeraLabel(atom, showModel=showModel,
diffWith=diffWith)
if lab == "":
lab = atom.name
return lab
def dihedChoices(self, baseAtom, otherAtom):
# sort choices so they are always in the same order
default = None
info = []
for a in baseAtom.neighbors:
if a is otherAtom:
continue
name = self.atomLabel(a, diffWith=baseAtom)
info.append((name, a))
if a.element.number > 1:
default = a
info.sort()
names = map(lambda items: items[0], info)
bonded = map(lambda items: items[1], info)
if default is None:
if len(names) > 0:
default = 0
else:
default = bonded.index(default)
return default, names, bonded
def dihedEndAtoms(self, br):
widgets, nearIndex, nearAtoms, farIndex, farAtoms = \
self.rotInfo[br]
if nearIndex is None or farIndex is None:
return None, None
return nearAtoms[nearIndex], farAtoms[farIndex]
def dihedral(self, br):
near, far = self.dihedEndAtoms(br)
if not near or not far:
return br.get()
widgets, nearIndex, nearAtoms, farIndex, farAtoms = \
self.rotInfo[br]
return chimera.dihedral(near.xformCoord(), br.atoms[0].xformCoord(),
br.atoms[1].xformCoord(), far.xformCoord())
def map(self):
self._mapped = True
pageName = self.notebook.getcurselection()
if pageName == ADJUST_BONDS:
self._abSelChange()
from chimera import triggers
self._abSelChangeHandler = triggers.addHandler("selection changed",
self._abSelChange, None)
elif pageName == BOND_ANGLES:
self._baSelChange()
from chimera import triggers
self._baSelChangeHandler = triggers.addHandler("selection changed",
self._baSelChange, None)
elif pageName == CHANGE_ATOM:
self._genAtomNameCB()
from chimera import triggers
self._caSelChangeHandler = triggers.addHandler('selection changed',
lambda *args: self._genAtomNameCB(), None)
elif pageName == JOIN_MODELS:
from chimera import triggers
self._jmSelChangeHandler = triggers.addHandler("selection changed",
self._jmConfig, None)
def remakeAtomLabels(self):
newLabels = []
for br in self.rotations:
newLabels.append(self.rotLabel(br))
rotMenu = self.rotInfo[br][0][2]
rotMenu.configure(text=newLabels[-1])
if newLabels:
modeTors = self.rotModeTorsMenu.index(Pmw.SELECT)
if not modeTors:
modeTors = None
else:
modeTors = None
self.rotModeTorsMenu.setitems(newLabels, index=modeTors)
def rotChange(self, trigger, brInfo):
needResize = False
if trigger == bondRotMgr.DELETED:
self._delRots(brInfo)
needResize = True
elif trigger == bondRotMgr.CREATED:
self._addRot(brInfo)
needResize = True
elif trigger == bondRotMgr.REVERSED:
# since the bond has reversed, need to switch
# near/far labels as well
widgets, nearIndex, nearAtoms, farIndex, farAtoms = \
self.rotInfo[brInfo]
row = self.rotations.index(brInfo)
self.rotInfo[brInfo] = [widgets, farIndex, farAtoms,
nearIndex, nearAtoms]
# swap torsion-end menus
widgets[1], widgets[3] = widgets[3], widgets[1]
widgets[2].configure(text=self.rotLabel(brInfo))
if self.angleTitle.get() == "Torsion":
widgets[1].grid_forget()
widgets[3].grid_forget()
widgets[1].grid(row=row, column=1, sticky='ew')
widgets[3].grid(row=row, column=3, sticky='ew')
self.rotModeTorsMenu.setitems([self.rotLabel(r)
for r in self.rotations],
index=self.rotModeTorsMenu.index(Pmw.SELECT))
else:
self._updateRot(brInfo)
if needResize:
self.notebook.setnaturalsize()
def rotLabel(self, br):
return "%s -> %s" % (self.atomLabel(br.atoms[0]),
self.atomLabel(br.atoms[1], diffWith=br.atoms[0]))
def setCategory(self, category):
# avoid unnecessary page raises; they interfere with
# the bond rotation mouse mode (graphics window loses
# focus)
if self.notebook.getcurselection() != category:
self.notebookMenu.invoke(category)
def unmap(self):
self._mapped = False
pageName = self.notebook.getcurselection()
if pageName == ADJUST_BONDS:
from chimera import triggers
triggers.deleteHandler("selection changed", self._abSelChangeHandler)
delattr(self, "_abSelChangeHandler")
elif pageName == BOND_ANGLES:
from chimera import triggers
triggers.deleteHandler("selection changed", self._baSelChangeHandler)
delattr(self, "_baSelChangeHandler")
self.baMenuOrder = None # don't hold reference to bonds
elif pageName == CHANGE_ATOM:
from chimera import triggers
triggers.deleteHandler("selection changed", self._caSelChangeHandler)
delattr(self, "_caSelChangeHandler")
elif pageName == JOIN_MODELS:
from chimera import triggers
triggers.deleteHandler("selection changed", self._jmSelChangeHandler)
delattr(self, "_jmSelChangeHandler")
self.status("")
def _addBonds(self):
atoms = selection.currentAtoms()
if len(atoms) < 2:
raise UserError("Must have at least 2 atoms selected")
doAll = self.addBondsMenu.getvalue() != "reasonable"
bondLength = chimera.Element.bondLength
from chimera.molEdit import addBond
for i, a1 in enumerate(atoms):
for a2 in atoms[i+1:]:
if a1.molecule != a2.molecule:
continue
if a2 in a1.bondsMap:
continue
if a1.altLoc and a2.altLoc \
and a1.altLoc != a2.altLoc:
continue
if not doAll:
if a1.xformCoord().distance(
a2.xformCoord()) - bondLength(
a1.element, a2.element) > 0.4:
continue
addBond(a1, a2)
def _abSelChange(self, *args):
bonds = selection.currentBonds()
if len(bonds) == 1:
self.blEntry.set_value(bonds[0].length())
self.blSideLabel.config(text="%s)" % self._blSideText(bonds[0]))
else:
self.blSideLabel.config(text=")")
from weakref import WeakKeyDictionary
self.blStartCache = WeakKeyDictionary(dict([
(b, b.length()) for b in bonds
]))
def _addParamBond(self):
a1, a2 = selection.currentAtoms()
if self._jmBondTypeSelect.getvalue() == "other":
if a1.oslIdent() > a2.oslIdent():
a1, a2 = a2, a1
side = self._apobSideMenu.getvalue()
from chimera.misc import chimeraLabel
sides = [chimeraLabel(a) for a in (a1, a2)]
movingSide = sides.index(side)
moving = (a1, a2)[movingSide]
nonMoving = (a1, a2)[1-movingSide]
args = [nonMoving, moving, self._apobLength.get()]
choice = self._apobDihedralMenu.getvalue()
for name, atoms in zip(*self._enumerateDihedrals(a1, a2)):
if name == choice:
args.append((atoms, self._apobDihedral.get()))
break
else:
args.append(None)
bind(*tuple(args))
else:
if a1.element.name == "C":
c, n = a1, a2
else:
c, n = a2, a1
if self._appbSideMenu.getvalue() == "C":
moving = c
else:
moving = n
try:
cn = cnPeptideBond(c, n, moving, self._appbLength.get(),
self._appbDihedral.get(), phi=self._appbPhi.get())
except AssertionError, v:
self.status(unicode(v), color="red", blankAfter=15)
raise UserError(unicode(v))
from chimera.selection import setCurrent
setCurrent(cn)
def _addRot(self, br, row=-1):
self._setTorWidgetsState("normal")
if row == -1:
# by default, append bond rotation
row = len(self.rotInfo)
else:
# insert bond rotation at given row, so make room
for i in range(len(self.rotations), row, -1):
nbr = self.rotations[i - 1]
widgets = self.rotInfo[nbr][0]
for w in widgets:
gi = w.grid_info()
if gi.has_key('column'):
# otherwise presumably unmapped
column = gi['column']
w.grid_forget()
w.grid(row=i, column=column,
sticky='ew')
# if adding a rotation where no torsion angle exists (no bonds off
# one end), then switch to Delta if this is the only rotation
a1, a2 = br.atoms
useDelta = len(a1.neighbors) < 2 or len(a2.neighbors) < 2
if not self.rotations and self.angleTitle.get() == "Torsion" and useDelta:
self._toggleAngleType()
self.rotations.insert(row, br)
nearIndex, nearNames, nearAtoms = self.dihedChoices(a1, a2)
farIndex, farNames, farAtoms = self.dihedChoices(a2, a1)
# need to do this here so that self.dihedral(br) works
widgets = []
self.rotInfo[br] = [widgets,
nearIndex, nearAtoms, farIndex, farAtoms]
ID = Tkinter.Label(self.rotTable.interior(), text=str(br.id))
widgets.append(ID)
ID.grid(row=row, column=0)
near = Pmw.OptionMenu(self.rotTable.interior(),
menubutton_highlightthickness=0,
initialitem=nearIndex, items=nearNames)
near.configure(command=lambda x, br=br, n=near, s=self:
s._setDihedEnd(br, n))
widgets.append(near)
if self.angleTitle.get() == "Torsion":
near.grid(row=row, column=1, sticky='ew')
actions = PmwableMenuButton(self.rotTable.interior(),
text=self.rotLabel(br), indicatoron=1,
relief='raised', bd=2, justify='right',
states=['normal', 'normal', 'normal', 'normal'],
items=['Revert', 'Reverse', 'Deactivate', 'Select'],
command=lambda c, s=self, br=br: s._menuCB(c, br))
widgets.append(actions)
actions.grid(row=row, column=2, sticky='ew')
far = Pmw.OptionMenu(self.rotTable.interior(),
menubutton_highlightthickness=0,
initialitem=farIndex, items=farNames)
far.configure(command=lambda x, br=br, n=far, s=self:
s._setDihedEnd(br, n))
widgets.append(far)
if self.angleTitle.get() == "Torsion":
far.grid(row=row, column=3, sticky='ew')
from CGLtk.AngleCounter import AngleCounter
delta = AngleCounter(self.rotTable.interior(), dialpos='e',
angle=float("%.*f" % (prefs[TORSION_PRECISION],
br.get())),
dial_zeroAxis='y', dial_radius=self.dialSizes[
prefs[ROT_DIAL_SIZE]], dial_rotDir='clockwise',
command=lambda d, s=self, br=br: s._deltaCB(br, d))
dihed = AngleCounter(self.rotTable.interior(), dialpos='e',
angle=float("%.*f" % (prefs[TORSION_PRECISION],
self.dihedral(br))),
dial_zeroAxis='y', dial_radius=self.dialSizes[
prefs[ROT_DIAL_SIZE]], dial_rotDir='clockwise',
command=lambda d, s=self, br=br: s._dihedCB(br, d))
if self.angleTitle.get() == "Delta":
delta.grid(row=row, column=4, sticky='ew')
widgets.extend([delta, dihed])
else:
dihed.grid(row=row, column=4, sticky='ew')
widgets.extend([dihed, delta])
modeTors = self.rotModeTorsMenu.getvalue()
if not modeTors:
modeTors = None
items = [self.rotLabel(r) for r in self.rotations]
self.rotModeTorsMenu.setitems(items, index=modeTors)
self._labelRot(br)
if useDelta:
self._menuCB("Reverse", br)
def _atomChange(self, trigName, myData, trigData):
# torsions
for br, info in self.rotInfo.items():
if br.bond.__destroyed__:
continue
widgets, ni, nearAtoms, fi, farAtoms = info
for menu, index, atoms, brAtom, infoIndex in (
(widgets[1], ni, nearAtoms, br.atoms[0], 2),
(widgets[3], fi, farAtoms, br.atoms[1], 4)):
dead = [a.__destroyed__ for a in atoms]
if dead.count(True) == 0:
continue
if dead.count(False) == 0:
br.destroy()
break
survivors = [a for a in atoms if not a.__destroyed__]
if atoms[index] in survivors:
si = survivors.index(atoms[index])
menu.setitems([self.atomLabel(a, diffWith=brAtom)
for a in survivors], index=si)
info[infoIndex] = survivors
info[infoIndex-1] = si
else:
menu.setitems([self.atomLabel(a, diffWith=brAtom)
for a in survivors])
info[infoIndex] = survivors
info[infoIndex-1] = 0
menu.invoke(0)
self.status("Dihedral endpoint atom deleted; new"
" endpoint randomly chosen")
def _baAngleChange(self, degrees):
if not self.baMenuOrder:
return
moving = self.baMenuOrder[self.baMover.index(Pmw.SELECT)]
from BuildStructure import setBondAngle, BondInCycleError
fixed = self.baMenuOrder[0] if moving == self.baMenuOrder[1] else self.baMenuOrder[1]
try:
setBondAngle(moving, fixed, degrees)
except BondInCycleError:
self.baStatus.config(text="The %s bond is part of a cycle.\n"
"To adjust the bond angle you must first delete some other bond\n"
"that forms the cycle and then recreate it afterwards. You can\n"
"do both with the %s tab." % (self.baMover.getvalue(), ADJUST_BONDS),
foreground="red")
junction = (set(moving.atoms) & set(fixed.atoms)).pop()
self.baAngleWidget.configure(angle=chimera.angle(
fixed.otherAtom(junction).coord() - junction.coord(),
moving.otherAtom(junction).coord() - junction.coord()))
return
def _baMoverChange(self, *args):
movingRef, fixedRef, angleVal, moverIndex = getattr(self, 'baStartingInfo',
(None, None, None, None))
if angleVal is None:
return
if moverIndex == self.baMover.index(Pmw.SELECT):
return
self.baStartingInfo = (fixedRef, movingRef,
float(self.baAngleWidget.getvalue()), self.baMover.index(Pmw.SELECT))
def _baRevert(self):
movingRef, fixedRef, angleVal, moverIndex = getattr(self, 'baStartingInfo',
(None, None, None, None))
def _statusClear(s=self):
s.baStatus.config(text="")
if angleVal is None:
self.baStatus.config(text="Nothing to revert", foreground="red")
self.baStatus.after(5000, _statusClear)
return
if (movingRef() is None or fixedRef() is None) \
or (movingRef().__destroyed__ or fixedRef().__destroyed__):
self.baStatus.config(text="Parts of the bond angle no longer exist",
foreground="red")
self.baStatus.after(5000, _statusClear)
return
from BuildStructure import setBondAngle
setBondAngle(movingRef(), fixedRef(), angleVal)
self.baAngleWidget.configure(angle=angleVal)
def _baSelChange(self, *args):
self.baStatus.config(text="")
prevOrder = self.baMenuOrder
self.baMenuOrder = None
bonds = selection.currentBonds()
if len(bonds) != 2:
if not bonds:
self.baStatus.config(text="No bonds selected", foreground="black")
else:
if len(bonds) == 1:
bondText = "bond"
else:
bondText = "bonds"
self.baStatus.config(text="%s %s selected" % (len(bonds), bondText),
foreground="red")
self.baHelp.config(text=self.baHelpText)
self.baTitle.config(text="")
self.baMover.setitems([])
self.baAngleWidget.configure(angle=0)
return
from BuildStructure import smallerBranch, BondsNotAdjacentError
try:
moving = smallerBranch(bonds)
except BondsNotAdjacentError:
self.baStatus.config(text="Selected bonds are not adjacent", foreground="red")
self.baHelp.config(text=self.baHelpText)
self.baTitle.config(text="")
self.baMover.setitems([])
self.baAngleWidget.configure(angle=0)
return
fixed = bonds[0] if bonds[1] == moving else bonds[1]
self.baHelp.config(text="")
junction = (set(moving.atoms) & set(fixed.atoms)).pop()
from chimera.misc import chimeraLabel as cl
otherFixed = fixed.otherAtom(junction)
otherMoving = moving.otherAtom(junction)
self.baTitle.config(text=u"%s \N{EM DASH} %s \N{EM DASH} %s" % (cl(otherFixed),
cl(junction, diffWith=otherFixed), cl(otherMoving, diffWith=junction)))
self.baMenuOrder = (moving, fixed)
self.baMover.setitems([cl(b, showModel=False) for b in self.baMenuOrder], index=0)
curAngle = chimera.angle(otherFixed.coord() - junction.coord(),
otherMoving.coord() - junction.coord())
if prevOrder != self.baMenuOrder:
from weakref import ref
self.baStartingInfo = (ref(moving), ref(fixed), curAngle,
self.baMover.index(Pmw.SELECT))
self.baAngleWidget.configure(angle=curAngle)
def _blRevert(self):
from BuildStructure import setBondLength
side = self.blSideMenu.getvalue()
widgetVal = None
changeWidget = False
for b, bl in self.blStartCache.items():
if b.__destroyed__:
continue
setBondLength(b, bl, movingSide=side, status=self.status)
if widgetVal is None:
widgetVal = bl
changeWidget = True
elif widgetVal != bl:
changeWidget = False
if changeWidget:
self.blEntry.set_value(widgetVal, invoke_callbacks=False)
def _blSideText(self, bond):
a1, a2 = bond.atoms
a1done = set([a1])
a2done = set([a2])
a1todo = a1.neighbors
a2todo = a2.neighbors
a1todo.remove(a2)
a2todo.remove(a1)
while a1todo and a2todo:
a1a = a1todo.pop(0)
a1done.add(a1a)
for a1nb in a1a.neighbors:
if a1nb == a2:
break
if a1nb in a1done:
continue
a1todo.append(a1nb)
a2a = a2todo.pop(0)
a2done.add(a2a)
for a2nb in a2a.neighbors:
if a2nb == a1:
break
if a2nb in a2done:
continue
a2todo.append(a2nb)
if (a1todo and a2todo) or (not a1todo and not a2todo):
return ""
if a1todo:
bigger = a1
else:
bigger = a2
if self.blSideMenu.getvalue() == "smaller side":
labeled = bond.otherAtom(bigger)
else:
labeled = bigger
return chimera.misc.chimeraLabel(labeled)
def _changeAtom(self):
selAtoms = selection.currentAtoms()
if len(selAtoms) > 4:
from chimera.baseDialog import AskYesNoDialog
if AskYesNoDialog("Really change %d atoms?" %
len(selAtoms)).run(self.uiMaster()) == 'no':
self.enter()
return
if self.retainAtomNamesVar.get():
for atom in selAtoms:
if not self._changeSingleAtom(atom, atom.name):
break
elif self.atomNameInfo.get():
assert(len(self._atomNameCache) == len(selAtoms))
for atom, name in zip(selAtoms, self._atomNameCache):
if not self._changeSingleAtom(atom, name):
break
else:
for atom in selAtoms:
if not self._changeSingleAtom(atom):
break
def _checkPrecision(self, text, plusMinus, increment):
newPrecision = int(text) + plusMinus
if newPrecision < 0:
raise ValueError("decimal places must be non-negative")
if newPrecision > 9:
raise ValueError("9 decimal places is enough")
return newPrecision
def _countMolecules(self, *args):
prevNum = getattr(self, 'numMolecules', None)
self.numMolecules = len(chimera.openModels.list(modelTypes=[chimera.Molecule]))
if prevNum != None:
if prevNum == 1 and self.numMolecules > 1 \
or prevNum > 1 and self.numMolecules == 1:
# don't want to remake atom labels right away since
# some of the distances may have gone away, and that
# won't get cleaned up until the Pseudobond trigger
# fires, so register for the monitorChanges trigger and
# update the labels there
chimera.triggers.addHandler('monitor changes', self._monitorCB, None)
def _createRotation(self):
selBonds = selection.currentBonds()
if len(selBonds) == 1:
addRotation(selBonds[0])
return
raise UserError("Exactly one bond must be selected "
"in graphics window")
def _changeSingleAtom(self, atom, name=None):
resMode = self.newresVar.get()
if resMode == self.RES_MAKENEW:
newResName = self._getResName()
if newResName == None:
return False
oldRes = atom.residue
oldRes.removeAtom(atom)
chain = self.chainNameVar.get()
oldID = oldRes.id
if len(atom.bonds) > 0 and oldID == chain:
pos = oldID.position + 1
else:
pos = 1
while True:
mid = chimera.MolResId(chain, pos)
if not atom.molecule.findResidue(mid):
break
pos += 1
newRes = atom.molecule.newResidue(newResName,
chain, pos, ' ')
newRes.addAtom(atom)
if not oldRes.atoms:
atom.molecule.deleteResidue(oldRes)
chimera.selection._currentSelection._cache = {}
elif resMode == self.RES_RENAME:
newResName = self._getResName()
if newResName == None:
return False
from BuildStructure import changeResidueType
changeResidueType(atom.residue, newResName)
numBonds = int(self.bondsMenu.getvalue())
elementVal = self.elementMenu.getvalue()
if type(elementVal) == str:
element = chimera.Element(elementVal)
else:
element = chimera.Element(elementVal[-1])
if numBonds < 2:
if element.number > 2:
geom = 4
else:
geom = 1
else:
geom = self.geometryMenu.index(Pmw.SELECT) + numBonds
if name == None:
name=self.atomNameOption.get()
try:
changed = changeAtom(atom, element, geom, numBonds,
autoClose=self.autocloseVar.get(), name=name)
except ParamError, v:
raise UserError(str(v))
if self.autoFocusVar.get():
from Midas import focus
focus(atom.residue.atoms)
if self.colorByElementVar.get():
from Midas import color
color("byelement", changed)
return True
def _delBonds(self):
bonds = selection.currentBonds()
if not bonds:
raise UserError("Select at least one bond")
for bond in bonds:
bond.molecule.deleteBond(bond)
def _delRots(self, brs):
for br in brs:
row = self.rotations.index(br)
widgets = self.rotInfo[br][0]
for w in widgets:
w.grid_forget()
w.destroy()
del self.rotInfo[br]
for i in range(row + 1, len(self.rotations)):
nbr = self.rotations[i]
widgets = self.rotInfo[nbr][0]
for w in widgets:
gi = w.grid_info()
if gi.has_key('column'):
# otherwise presumably unmapped
column = gi['column']
w.grid_forget()
w.grid(row=i-1, column=column,
sticky='ew')
self.rotations.remove(br)
modeTors = self.rotModeTorsMenu.getvalue()
items = [self.rotLabel(br) for br in self.rotations]
if modeTors in items:
self.rotModeTorsMenu.setitems(items, index=modeTors)
else:
self.rotModeTorsMenu.setitems(items)
if self.mouseModeVar.get():
self.mouseModeVar.set(False)
self._mouseModeCB()
if not items:
self._setTorWidgetsState("disabled")
def _delSelAtomsBonds(self):
selAtoms = selection.currentAtoms()
selBonds = selection.currentBonds()
if not selAtoms and not selBonds:
self.status("No atoms or bonds selected", color="red")
else:
from Midas import deleteAtomsBonds
deleteAtomsBonds(selAtoms, selBonds)
def _deltaCB(self, br, degrees):
# callback from delta AngleCounter
br.set(degrees)
def _dialSizeChangeCB(self, dialSizeLabel):
dialSize = self.dialSizeLabels.index(dialSizeLabel)
prefs[ROT_DIAL_SIZE] = dialSize
for info in self.rotInfo.values():
for angleCounter in info[0][-2:]:
angleCounter.configure(dial_radius=
self.dialSizes[dialSize])
self.notebook.setnaturalsize()
def _dihedCB(self, br, degrees):
# callback from dihedral AngleCounter
curDihed = self.dihedral(br)
br.set(br.get() + degrees - curDihed)
def _drawFragment(self, frag):
self.fragCanvas.delete("all")
self.fragmentLookup[frag[-1]].depict(self.fragCanvas, scale=15)
left, top, right, bottom = self.fragCanvas.bbox("all")
self.fragCanvas.configure(width=right-left, height=bottom-top,
scrollregion=(left, top, right, bottom))
self.notebook.setnaturalsize()
def _enumerateDihedrals(self, a1, a2):
names, atoms = [], []
from chimera.misc import chimeraLabel
b1, b2 = a1.neighbors + a2.neighbors
for nb1 in b1.neighbors:
if nb1 == a1:
continue
for nb2 in b2.neighbors:
if nb2 == a2:
continue
names.append(u"%s\u2194%s\u2194%s\u2194%s" % (
chimeraLabel(nb1),
chimeraLabel(b1, diffWith=nb1),
chimeraLabel(b2, diffWith=b1),
chimeraLabel(nb2, diffWith=b2)))
atoms.append((nb1, b1, b2, nb2))
return names, atoms
def _fillPlaceAtomPage(self):
atomPage = self.notebook.page(ADD_ATOMS)
self.placeTypeVar = Tkinter.StringVar(atomPage)
self.apGroups = {}
for row, val in enumerate([self.PLACE_ATOM, self.PLACE_FRAGMENT,
self.PLACE_SMILES, self.PLACE_PUBCHEM,
self.PLACE_PEPTIDE, self.PLACE_NUCLEIC, self.PLACE_RNA]):
rb = Tkinter.Radiobutton(atomPage, text=val,
value=val, variable=self.placeTypeVar,
command=lambda val=val:
self._showPlaceGroup(val, 0))
rb.grid(row=row, column=1, sticky="w")
if val == self.PLACE_RNA:
addText = ""
else:
addText = " parameters"
self.apGroups[val] = Pmw.Group(atomPage, tag_text=
paramTitle(val + addText))
Tkinter.Label(atomPage, text="Add"
).grid(row=0, column=0, rowspan=len(self.apGroups))
self.apModelNameFrame = f = Tkinter.Frame(atomPage)
f.grid(row=len(self.apGroups), column=0, columnspan=3)
from chimera.widgets import NewMoleculeOptionMenu
self.molMenu = NewMoleculeOptionMenu(f, labelpos='w',
label_text="Put atoms in", command=self._molMenuCB)
self.molMenu.grid(row=0, column=0, sticky='e')
from chimera.tkoptions import StringOption
self.molName = StringOption(f, 0, "named", "scratch", None,
startCol=2)
self._molMenuCB()
self.apColorButton = Tkinter.Checkbutton(atomPage,
variable=self.colorByElementVar,
text="Color new atoms by element")
self.apColorButton.grid(row=len(self.apGroups)+1,
column=0, columnspan=3)
Tkinter.Button(atomPage, text="Apply", command=self._placeAtoms
).grid(row=len(self.apGroups)+2, column=0, columnspan=3)
atomGroup = self.apGroups[self.PLACE_ATOM]
Tkinter.Label(atomGroup.interior(), text= "Place helium atom at:"
).grid(row=0, column=0)
self.atomPosVar = Tkinter.StringVar(atomGroup.interior())
self.atomPosVar.set("view")
f = Tkinter.Frame(atomGroup.interior())
f.grid(row=1, column=0)
Tkinter.Radiobutton(f, variable=self.atomPosVar, value="view",
text="Center of view").grid(row=0, column=0, sticky='w')
f2 = Tkinter.Frame(f)
f2.grid(row=1, column=0, sticky='w')
Tkinter.Radiobutton(f2, variable=self.atomPosVar, value="xyz",
).grid(row=0, column=0)
mod = lambda s=self: s.atomPosVar.set("xyz")
self.xEntry = Pmw.EntryField(f2, command=self._placeAtoms, labelpos='w',
value="0", modifiedcommand=mod, label_text='x:', validate='real',
entry_width=5)
self.xEntry.grid(row=0, column=1)
self.yEntry = Pmw.EntryField(f2, command=self._placeAtoms, labelpos='w',
value="0", modifiedcommand=mod, label_text='y:', validate='real',
entry_width=5)
self.yEntry.grid(row=0, column=2)
self.zEntry = Pmw.EntryField(f2, command=self._placeAtoms, labelpos='w',
value="0", modifiedcommand=mod, label_text='z:', validate='real',
entry_width=5)
self.zEntry.grid(row=0, column=3)
Tkinter.Label(atomGroup.interior(), text="Use '%s' tab to change\nelement"
" type and add bonded atoms." % CHANGE_ATOM).grid(row=2, column=0)
self.placeTypeVar.set("atom")
lw = Pmw.LabeledWidget(atomGroup.interior(), labelpos='w',
label_text="Residue name:")
lw.grid(row=3, column=0)
Tkinter.Entry(lw.interior(), width=4,
textvariable=self.resNameVar).grid()
self.autoselAtomVar = Tkinter.IntVar(atomGroup.interior())
self.autoselAtomVar.set(True)
Tkinter.Checkbutton(atomGroup.interior(),
variable=self.autoselAtomVar, text="Select placed atom"
).grid(row=4, column=0)
self._showPlaceGroup("atom", 0)
fragmentGroup = self.apGroups[self.PLACE_FRAGMENT]
from Fragment import fragments, RING6
menuItems, self.fragmentLookup = self._processFragments(
fragments)
from CGLtk.optCascade import CascadeOptionMenu
self.fragMenu = CascadeOptionMenu(fragmentGroup.interior(),
labelpos="w", label_text="Fragment", items=menuItems,
buttonStyle="final", command=self._drawFragment)
self.fragMenu.grid(row=1, column=0)
self.fragCanvas = Tkinter.Canvas(fragmentGroup.interior())
self.fragCanvas.grid(row=1, column=1)
self.fragMenu.invoke([RING6, "benzene"])
lw = Pmw.LabeledWidget(fragmentGroup.interior(), labelpos='w',
label_text="Residue name:")
lw.grid(row=2, columnspan=2)
Tkinter.Entry(lw.interior(), width=4,
textvariable=self.resNameVar).grid()
smilesGroup = self.apGroups[self.PLACE_SMILES]
from chimera.tkoptions import StringOption
self.smilesEntry = StringOption(smilesGroup.interior(), 0,
"SMILES string", "", None, width=25)
smilesGroup.interior().columnconfigure(1, weight=1)
StringOption(smilesGroup.interior(), 1, "Residue name",
self.resNameVar.get(), None,
textvariable=self.resNameVar)
from chimera.HtmlText import HtmlText
plainTexts = [ "SMILES support courtesy of ", "CICC@iu", " or ", "NCI CADD Group",
"web services" ]
urls = [ '', '',
'', '', ""]
html = HtmlText(smilesGroup.interior(),
width=len("".join(plainTexts)), height=1, bd=0)
for plain, url in zip(plainTexts, urls):
html.insert("end", plain+url)
html.tag_add("center", "0.0", "end")
html.tag_configure("center", justify="center")
html.configure(state="disabled")
html.grid(row=2, column=0, columnspan=2)
pubChemGroup = self.apGroups[self.PLACE_PUBCHEM]
from chimera.tkoptions import StringOption
self.pubChemEntry = StringOption(pubChemGroup.interior(), 0,
"PubChem CID", "", None, width=6)
pubChemGroup.interior().columnconfigure(1, weight=1)
StringOption(pubChemGroup.interior(), 1, "Residue name",
self.resNameVar.get(), None,
textvariable=self.resNameVar)
from chimera.HtmlText import HtmlText
plainTexts = [ "PubChem CID support courtesy of ",
"PubChem Power User Gateway (PUG) web services" ]
urls = ['', ""]
html = HtmlText(pubChemGroup.interior(),
width=len("".join(plainTexts)), height=1, bd=0)
for plain, url in zip(plainTexts, urls):
html.insert("end", plain+url)
html.tag_add("center", "0.0", "end")
html.tag_configure("center", justify="center")
html.configure(state="disabled")
html.grid(row=2, column=0, columnspan=2)
peptideGroup = self.apGroups[self.PLACE_PEPTIDE]
pgi = peptideGroup.interior()
# below widget must be at least 3 high or else
# the scroller gets all infinite-loopy on OS X
self.peptideSequence = Pmw.ScrolledText(pgi,
text_height=3, text_width=30, text_wrap='char',
labelpos='n', label_text="Peptide Sequence")
self.peptideSequence.grid(row=1, sticky='nsew')
Tkinter.Label(pgi, text=u"'Apply' button will bring up dialog"
u" for setting \u03A6/\u03A8 angles").grid(row=2,
column=0)
nucleicGroup = self.apGroups[self.PLACE_NUCLEIC]
ngi = nucleicGroup.interior()
# below widget must be at least 3 high or else
# the scroller gets all infinite-loopy on OS X
self.nucleicSequence = Pmw.ScrolledText(ngi,
text_height=3, text_width=30, text_wrap='char',
labelpos='n', label_text="Sequence")
self.nucleicSequence.grid(row=1, sticky='nsew', columnspan=2)
lab = Tkinter.Label(ngi, text="Enter single strand;"
" double helix will be generated")
lab.grid(row=2, column=0, columnspan=2)
from CGLtk.Font import shrinkFont
shrinkFont(lab)
self.nucType = Pmw.RadioSelect(ngi, buttontype='radiobutton',
orient='vertical', pady=0)
self.nucType.add("DNA", justify="left")
self.nucType.add("RNA", justify="left")
self.nucType.add("Hybrid DNA/RNA (enter DNA)", justify="left")
self.nucType.setvalue("DNA")
self.nucType.grid(row=3, column=0, sticky='ew')
self.nucForm = Pmw.RadioSelect(ngi, buttontype='radiobutton',
orient='vertical', pady=0)
self.nucForm.add("A-form", justify="left")
self.nucForm.add("B-form", justify="left")
self.nucForm.setvalue("B-form")
self.nucForm.grid(row=3, column=1, sticky='ew')
# below widget must be at least 3 high or else
# the scroller gets all infinite-loopy on OS X
self.peptideSequence = Pmw.ScrolledText(pgi,
text_height=3, text_width=30, text_wrap='char',
labelpos='n', label_text="Peptide Sequence")
self.peptideSequence.grid(row=1, sticky='nsew')
Tkinter.Label(pgi, text=u"'Apply' button will bring up dialog"
u" for setting \u03A6/\u03A8 angles").grid(row=2,
column=0)
rnaGroup = self.apGroups[self.PLACE_RNA]
rgi = rnaGroup.interior()
from CGLtk.WrappingLabel import WrappingLabel
WrappingLabel(rgi, text="The Assemble2 plugin to Chimera allows"
" designing RNA in 2D and predicting corresponding"
" 3D structures. Assemble2 is developed by"
" Dr. Fabrice Jossinet, University of Strasbourg.").grid(
row=0, column=0, sticky='ew')
from chimera.help import display
Tkinter.Button(rgi, text="Get Assemble2",
command=lambda disp=display: disp("http://bioinformatics.org/assemble/index.html")
).grid(row=1, column=0, padx="1i")
def _fillChangeAtomPage(self):
caPage = self.notebook.page(CHANGE_ATOM)
row = 0
Tkinter.Label(caPage, text="Change selected atoms to...").grid(row=0,
column=0)
row += 1
paramFrame = Tkinter.Frame(caPage, bd=1, relief='solid')
paramFrame.grid(row=row, column=0)
row += 1
from chimera.selection.element \
import frequentElements, elementRanges
elementNames = chimera.elements.name[:]
elementNames.remove("LP")
elementNames.sort()
menuItems = frequentElements[:]
subItems = []
for start, end in elementRanges:
subItems.append(("%s-%s" % (start, end),
elementNames[elementNames.index(start):
elementNames.index(end)+1]))
menuItems.append(("other", subItems))
from CGLtk.optCascade import CascadeOptionMenu
self.elementMenu = CascadeOptionMenu(paramFrame, labelpos="n",
label_text="Element", items=menuItems,
command=lambda *args: self._genAtomNameCB(),
initialitem=["C"], buttonStyle="final")
self.elementMenu.grid(row=0, column=0)
self.bondsJustChanged = False
self.bondsMenu = Pmw.OptionMenu(paramFrame, labelpos="n",
label_text="Bonds", command=self._newBonds,
items=[str(x) for x in range(5)], initialitem=4)
self.bondsMenu.grid(row=0, column=1)
from chimera.bondGeom import geometryName
self.geometryMenu = Pmw.OptionMenu(paramFrame, labelpos="n",
label_text="Geometry", command=self._geomChangeCB,
items=geometryName[4:], initialitem=geometryName[4])
self.geometryMenu.grid(row=0, column=2)
nameFrame = Tkinter.Frame(paramFrame)
nameFrame.grid(row=1, column=0, columnspan=3)
self.retainAtomNamesVar = Tkinter.IntVar(nameFrame)
self.retainAtomNamesVar.set(True)
self.retainAtomNameButton = Tkinter.Radiobutton(nameFrame,
text="Retain current atom names",
variable=self.retainAtomNamesVar, value=True)
self.retainAtomNameButton.grid(row=0, sticky='w')
customNameFrame = Tkinter.Frame(nameFrame)
customNameFrame.grid(row=1, sticky='w')
self.customAtomNameButton = Tkinter.Radiobutton(customNameFrame,
text="Set atom names to:",
variable=self.retainAtomNamesVar, value=False)
self.customAtomNameButton.grid(row=0, sticky='w')
from chimera.tkoptions import StringOption
self.atomNameOption = StringOption(customNameFrame, 0, "", "",
lambda opt: self._genAtomNameCB(infoOnly=True),
startCol=1, width=4)
self.atomNameInfo = Tkinter.StringVar(paramFrame)
self.atomNameInfo.set("")
Tkinter.Label(customNameFrame, textvariable=self.atomNameInfo
).grid(row=0, column=3)
self.newresVar = Tkinter.IntVar(caPage)
self.newresVar.set(self.RES_RENAME)
self._genAtomNameCB()
paramFrame.columnconfigure(0, pad="0.1i")
paramFrame.columnconfigure(1, pad="0.1i")
paramFrame.columnconfigure(2, pad="0.1i")
cbFrame = Tkinter.Frame(caPage)
cbFrame.grid(row=row, column=0)
row += 1
self.autocloseVar = Tkinter.IntVar(caPage)
self.autocloseVar.set(True)
Tkinter.Checkbutton(cbFrame, variable=self.autocloseVar,
text="Connect to pre-existing atoms if appropriate"
).grid(row=0, column=0, sticky='w')
self.autoFocusVar = Tkinter.IntVar(caPage)
self.autoFocusVar.set(False)
Tkinter.Checkbutton(cbFrame, variable=self.autoFocusVar,
text="Focus view on modified residue"
).grid(row=1, column=0, sticky='w')
Tkinter.Checkbutton(cbFrame, variable=self.colorByElementVar,
text="Color new atoms by element"
).grid(row=2, column=0, sticky='w')
resGroup = Pmw.Group(cbFrame, tag_text="Residue Name")
resGroup.grid(row=3, column=0)
rgFrame = resGroup.interior()
Tkinter.Radiobutton(rgFrame, variable=self.newresVar,
text="Leave unchanged", value=self.RES_NOOP).grid(
row=0, sticky='w')
f1 = Tkinter.Frame(rgFrame)
f1.grid(row=1, sticky='w')
Tkinter.Radiobutton(f1, variable=self.newresVar,
text="Change modified residue's name to",
command=self._genAtomNameCB, value=self.RES_RENAME
).grid(row=0, column=0, sticky='w')
Tkinter.Entry(f1, width=4, textvariable=self.resNameVar
).grid(row=0, column=1, sticky='w')
f2 = Tkinter.Frame(rgFrame)
f2.grid(row=2, sticky='w')
Tkinter.Radiobutton(f2, variable=self.newresVar,
text="Put just changed atoms in new residue named",
command=self._genAtomNameCB, value=self.RES_MAKENEW
).grid(row=0, column=0, sticky='w')
Tkinter.Entry(f2, width=4, textvariable=self.resNameVar
).grid(row=0, column=1, sticky='w')
Tkinter.Label(f2, text="in chain").grid(row=0, column=2)
Tkinter.Entry(f2, width=3, textvariable=self.chainNameVar
).grid(row=0, column=4)
Tkinter.Button(caPage, text="Apply", command=self._changeAtom
).grid(row=row, column=0)
row += 1
# horizontal separator (Frame in Frame)
f = Tkinter.Frame(caPage, bd=2, relief="raised")
Tkinter.Frame(f).grid()
f.grid(row=row, column=0, sticky='ew')
row += 1
f = Tkinter.Frame(caPage)
f.grid(row=row, column=0)
Tkinter.Button(f, text="Delete", command=self._delSelAtomsBonds).grid(
row=0, column=0)
Tkinter.Label(f, text="selected atoms/bonds").grid(row=0, column=1)
row += 1
def _fillAdjustBondsPage(self):
abPage = self.notebook.page(ADJUST_BONDS)
row = 0
addDelGroup = Pmw.Group(abPage, tag_text="Add/Delete")
addDelGroup.grid(row=row, sticky="ew")
inside = addDelGroup.interior()
f = Tkinter.Frame(inside)
f.grid(row=0, sticky='w')
Tkinter.Button(f, text="Delete", command=self._delBonds, pady=0
).grid(row=0, column=0)
Tkinter.Label(f, text="selected bonds").grid(row=0, column=1)
f = Tkinter.Frame(inside)
f.grid(row=1, sticky='w')
Tkinter.Button(f, text="Add", command=self._addBonds, pady=0
).grid(row=0, column=0)
self.addBondsMenu = Pmw.OptionMenu(f, initialitem=
"reasonable", items=["reasonable", "all possible"],
labelpos='e', label_text="bonds between selected atoms")
self.addBondsMenu.grid(row=0, column=1)
row += 1
setGroup = Pmw.Group(abPage, tag_text="Set Length")
setGroup.grid(row=row, sticky="ew")
inside = setGroup.interior()
f = Tkinter.Frame(inside)
f.grid(row=0, sticky='w')
from CGLtk.Hybrid import Scale
self.blEntry = Scale(f, "Set length of selected bonds to:", 0.5, 4.5, 0.01, 1.5)
self.blEntry.scale.config(tickinterval=1.0, length='3i')
self.blEntry.callback(self._setBondLength)
self.blEntry.frame.grid(row=0, column=0, columnspan=2)
self.blSideMenu = Pmw.OptionMenu(f, labelpos='w', label_text="(move atoms on",
items= ["smaller side", "larger side"], initialitem="smaller side")
self.blSideMenu.grid(row=1, column=0, sticky='e')
self.blSideLabel = Tkinter.Label(f, text=")")
self.blSideLabel.grid(row=1, column=1, sticky='w')
f = Tkinter.Frame(inside)
f.grid(row=1)
Tkinter.Button(f, text="Revert", pady=0, command=self._blRevert).grid(row=0, column=0)
Tkinter.Label(f, text="bond lengths to their original values").grid(row=0, column=1)
row += 1
def _fillBondAnglesPage(self):
baPage = self.notebook.page(BOND_ANGLES)
row = 0
self.baHelpText = "Select two adjacent bonds to adjust their bond angle"
self.baHelp = Tkinter.Label(baPage, text=self.baHelpText)
self.baHelp.grid(row=row, column=0)
row += 1
self.baTitle = Tkinter.Label(baPage)
self.baTitle.grid(row=row, column=0)
row += 1
f = Tkinter.Frame(baPage)
f.grid(row=row, column=0)
self.baMenuOrder = None
self.baMover = Pmw.OptionMenu(f, labelpos='w', label_text="Move",
command=self._baMoverChange)
self.baMover.grid(row=0, column=0)
from CGLtk.AngleCounter import AngleCounter
self.baAngleWidget = AngleCounter(f, labelpos='w', label_text="side ", minangle=0,
dial_zeroAxis='y', dial_rotDir='clockwise', command=self._baAngleChange)
self.baAngleWidget.grid(row=0, column=1)
row += 1
f = Tkinter.Frame(baPage)
f.grid(row=row, column=0)
Tkinter.Button(f, text="Revert", command=self._baRevert).grid(row=0, column=0)
Tkinter.Label(f, text="bond angle to starting value").grid(row=0, column=1)
row += 1
# add a little buffer to the status
Tkinter.Label(baPage).grid(row=row, column=0)
row += 1
self.baStatus = Tkinter.Label(baPage)
self.baStatus.grid(row=row, column=0)
row += 1
def _fillJoinModelsPage(self):
fmPage = self.notebook.page(JOIN_MODELS)
bts = self._jmBondTypeSelect = Pmw.RadioSelect(fmPage, labelpos='w',
buttontype="radiobutton", command=self._jmChangeBondType,
label_text="Form", orient="vertical", pady=0)
bts.add("peptide", text="C-N peptide bond", justify='left')
bts.add("other", text="other bond", justify='left')
bts.grid(row=0, column=0, sticky='w')
obf = self.jmGenBondFrame = Tkinter.Frame(fmPage, bd=3, relief="raised")
obf.grid(row=1, column=0)
row = 0
Tkinter.Label(obf, text="Delete selected atoms and add bond as"
" follows:").grid(row=row)
row += 1
interior = Tkinter.Frame(obf)
interior.grid(row=row)
from chimera.tkoptions import FloatOption
Tkinter.Label(interior, text="length:").grid(row=0, column=0,
sticky='e')
self._apobLength = FloatOption(interior, 0, "", 1.54, None,
min=0.0, startCol=1)
f = Tkinter.Frame(interior)
f.grid(row=1, column=0, sticky='e')
self._apobDihedralMenu = Pmw.OptionMenu(f)
self._apobDihedralMenu.grid(row=0, column=0)
Tkinter.Label(f, text=" dihedral:").grid(row=0, column=1)
self._apobDihedral = FloatOption(interior, 1, "", 180.0, None,
startCol=1)
f = Tkinter.Frame(interior)
f.grid(row=2, column=0, columnspan=3)
Tkinter.Label(f, text="Move atoms on").grid(row=0, column=0)
self._apobSideMenu = Pmw.OptionMenu(f)
self._apobSideMenu.grid(row=0, column=1)
Tkinter.Label(f, text="side").grid(row=0, column=2)
row += 1
reminder = Tkinter.Label(obf, text="(Selected atoms must be in"
" different models and bonded to at most one atom each)")
from CGLtk.Font import shrinkFont
shrinkFont(reminder)
reminder.grid(row=row)
pbf = self.jmPeptideBondFrame = Tkinter.Frame(fmPage,
bd=3, relief="raised")
pbf.grid(row=1, column=0)
row = 0
Tkinter.Label(pbf, text="Form bond between selected C-terminal carbon\n"
"and N-terminal nitrogen as follows:").grid(row=row)
row += 1
interior = Tkinter.Frame(pbf)
interior.grid(row=row)
from chimera.tkoptions import FloatOption
self._appbLength = FloatOption(interior, 0, "C-N length", 1.33, None,
min=0.0)
self._appbDihedral = FloatOption(interior, 1,
u"C\N{GREEK SMALL LETTER ALPHA}-C-N-C\N{GREEK SMALL LETTER ALPHA}"
u" dihedral (\N{GREEK SMALL LETTER OMEGA} angle)", 180.0, None)
self._appbPhi = FloatOption(interior, 2,
u"C-N-C\N{GREEK SMALL LETTER ALPHA}-C"
u" dihedral (\N{GREEK SMALL LETTER PHI} angle)", -120.0, None)
self._appbPsi = Tkinter.Label(interior)
self._appbPsi.grid(row=3, column=0, columnspan=2)
f = Tkinter.Frame(interior)
f.grid(row=4, column=0, columnspan=2)
Tkinter.Label(f, text="Move atoms on").grid(row=0, column=0)
self._appbSideMenu = Pmw.OptionMenu(f, items=["N", "C"])
self._appbSideMenu.grid(row=0, column=1)
Tkinter.Label(f, text="side").grid(row=0, column=2)
row += 1
reminder = Tkinter.Label(pbf, text="(Selected atoms must be in"
' different models and each bonded to exactly one carbon atom)\n'
"[exception: N-terminal proline nitrogen can be bonded to"
" two carbons]")
from CGLtk.Font import shrinkFont
shrinkFont(reminder)
reminder.grid(row=row, column=0)
self._addParamBondButton = Tkinter.Button(fmPage, text="Apply",
command=self._addParamBond)
self._addParamBondButton.grid(row=2, column=0)
bts.invoke("other")
self._jmConfig()
def _fillBondRotsPage(self):
brPage = self.notebook.page(BOND_ROTS)
self.rotations = []
self.rotInfo = {}
labeledButton = Pmw.LabeledWidget(brPage, labelpos="e",
label_text="selected bond as torsion")
labeledButton.grid(row=0, column=0, columnspan=2)
self.createRotButton = Tkinter.Button(labeledButton.interior(),
text="Activate", command=self._createRotation, pady=0)
self.createRotButton.grid()
tableFrame = Tkinter.Frame(brPage, pady="0.1i")
tableFrame.grid(row=1, column=0, columnspan=2, sticky='ns')
from CGLtk.Table import ScrolledTable
self.rotTable = ScrolledTable(tableFrame, hscrollmode='none')
self.rotTable.setColumnTitle(0, "ID")
self.rotTable.setColumnTitle(1, "Near")
self.rotTable.setColumnTitle(2, "Bond")
self.rotTable.setColumnTitle(3, "Far")
self.angleTitle = Tkinter.StringVar(brPage)
self.angleTitle.set("Torsion")
self.rotTable.setColumnTitle(4, self.angleTitle,
pyclass=Tkinter.Button, pady=0,
command=self._toggleAngleType)
brPage.rowconfigure(1, weight=1)
brPage.columnconfigure(0, weight=1)
brPage.columnconfigure(1, weight=1)
tableFrame.rowconfigure(0, weight=1)
tableFrame.columnconfigure(0, weight=1)
self.rotTable.columnconfigure(4, weight=1)
self.rotTable.grid(row=0, column=0, sticky='news')
self.dialSizeLabels = ["small", "medium", "large"]
Pmw.OptionMenu(tableFrame,
items=self.dialSizeLabels, labelpos='w',
initialitem=self.dialSizeLabels[prefs[ROT_DIAL_SIZE]],
label_text="Dial size:", command=self._dialSizeChangeCB,
).grid(row=1, column=0, sticky='e')
f = Tkinter.Frame(brPage)
f.grid(row=2, column=0, columnspan=2)
self.mouseModeVar = Tkinter.IntVar(f)
self.mouseModeVar.set(False)
self.needTorWidgets = []
self.needTorWidgets.append(Tkinter.Checkbutton(f, text="Rotate",
variable=self.mouseModeVar, command=self._mouseModeCB))
self.needTorWidgets[-1].grid(row=0, column=0)
self.rotModeTorsMenu = Pmw.OptionMenu(f)
self.rotModeTorsMenu.grid(row=0, column=1)
self.needTorWidgets.append(self.rotModeTorsMenu)
self.buttonLabels = []
self.labelValues = {}
for mod in ("",) + mousemodes.usedMods:
for but in mousemodes.usedButtons:
if mod:
self.buttonLabels.append(
mod.lower() + " button " + but)
self.labelValues[self.buttonLabels[-1]]\
= (but, (mod,))
else:
self.buttonLabels.append("button "+but)
self.labelValues[self.buttonLabels[-1]]\
= (but, ())
self._modeButton = self.buttonLabels[0]
self.rotModeButMenu = Pmw.OptionMenu(f, labelpos='w',
command=self._modeButtonCB,
label_text="using", items=self.buttonLabels)
self.rotModeButMenu.grid(row=0, column=2)
self.needTorWidgets.append(self.rotModeButMenu)
self.rotLabelChoice = Pmw.RadioSelect(brPage, pady=0,
buttontype='radiobutton', hull_pady=".1i",
command=self._rotLabelModeChange, orient='vertical',
labelpos='w', label_text="Labels")
self.rotLabelChoice.grid(row=3, rowspan=2, column=0)
self.rotLabelChoice.add("None", highlightthickness=0)
self.rotLabelChoice.add("ID", highlightthickness=0)
self.rotLabelChoice.add("Name", highlightthickness=0)
self.rotLabelChoice.add("Angle", highlightthickness=0)
self.rotLabelChoice.invoke(prefs[ROT_LABEL])
from StructMeasure.gui import PrecisionEntry
self.torsionPrecisionChoice = Pmw.Counter(brPage, datatype={
'counter': self._torsionPrecisionChange}, labelpos='w',
label_text="Decimal places", entry_width=1,
entry_pyclass=PrecisionEntry,
entryfield_value=str(prefs[TORSION_PRECISION]))
self.torsionPrecisionChoice.grid(row=3, column=1)
self.showDegreeSymbolVar = Tkinter.IntVar(brPage)
self.showDegreeSymbolVar.set(prefs[SHOW_DEGREE_SYMBOL])
Tkinter.Checkbutton(brPage, text="Show degree symbol",
variable=self.showDegreeSymbolVar,
command=self._showDegreeSymbolChangeCB).grid(
row=4, column=1)
self._setTorWidgetsState("disabled")
mousemodes.addFunction("rotate bond", (lambda v, e:
v.recordPosition(e.time, e.x, e.y, "rotate"),
self._mouseSphere,
lambda v, e: v.setCursor(None)))
row = 0
def _fillChiralityPage(self):
cPage = self.notebook.page(CHIRALITY)
row = 0
from CGLtk.WrappingLabel import WrappingLabel
WrappingLabel(cPage, text="Select one atom to swap the two smallest"
" substituents bonded to that atom, or select two atoms to swap"
" those specific substituents").grid(row=row, column=0, sticky='ew')
cPage.columnconfigure(0, weight=1)
row += 1
Tkinter.Button(cPage, text="Swap", command=self._swapSubstituents
).grid(row=row, column=0)
def _finishPlace(self, atoms):
for a in atoms:
a.drawMode = chimera.Atom.EndCap
for b in a.bonds:
b.drawMode = chimera.Bond.Stick
if self.colorByElementVar.get():
from Midas import color
color("byelement", atoms)
def _genAtomNameCB(self, infoOnly=False):
self.bondsJustChanged = False
selResidues = chimera.selection.currentResidues()
selAtoms = chimera.selection.currentAtoms()
atomName = element = self.elementMenu.getvalue()[-1].upper()
if not infoOnly:
for a in selAtoms:
if a.element.name != element:
self.retainAtomNamesVar.set(False)
self.retainAtomNameButton.configure(
state="disabled")
break
else:
self.retainAtomNameButton.configure(
state="normal")
self.retainAtomNamesVar.set(True)
self.atomNameInfo.set("")
if self.newresVar.get() == self.RES_MAKENEW:
atomName += "1"
elif len(selAtoms) == 1 \
and selAtoms[0].name.startswith(atomName):
atomName = selAtoms[0].name
elif len(selResidues) == 1:
from chimera.molEdit import genAtomName
if infoOnly:
atomName = self.atomNameOption.get()
else:
atomName = genAtomName(element, selResidues[0])
if len(selAtoms) > 1:
if atomName == element:
atomName += "1"
self._atomNameCache = [atomName]
rem = atomName[len(element):]
try:
num = int(rem)
except ValueError:
pass
else:
needed = len(selAtoms) - 1
selSet = set(selAtoms)
remResAtoms = set(selResidues[0].atoms
) - selSet
remNames = set([a.name
for a in remResAtoms])
while needed:
num += 1
while element + str(num) \
in remNames:
num += 1
self._atomNameCache.append(
"%s%d" % (element, num))
needed -= 1
self.atomNameInfo.set("through %s%d"
% (element, num))
if infoOnly:
return
self.atomNameOption.set(atomName)
chains = set([r.id.chainId for r in selResidues])
if len(chains) != 1:
self.chainNameVar.set("het")
else:
chain = chains.pop()
if chain == "water":
self.chainNameVar.set("het")
else:
self.chainNameVar.set(chain)
def _getMol(self):
m = self.molMenu.getvalue()
if type(m) == str:
m = self.molName.get()
return m
def _getResName(self):
rn = self.resNameVar.get().strip()
if not rn:
self.enter()
raise UserError("Must specify a residue name")
if len(rn) > 4 or not rn.isalnum():
from chimera.baseDialog import AskYesNoDialog
if AskYesNoDialog(
"Residue names longer than 4 characters\n"
"or containing non-alphanumeric characters\n"
"can be problematic when saving to certain\n"
"file formats (e.g. PDB).\n"
"\n"
"Really use residue name '%s'?" % rn).run(
self.uiMaster()) == 'no':
self.enter()
return None
return rn
def Help(self):
chimera.help.display("ContributedSoftware/editing/editing.html#"
+ "setbond")
def _geomChangeCB(self, val):
if not self.bondsJustChanged:
from chimera.bondGeom import geometryName
maxBonds = geometryName.index(val)
self.bondsMenu.setvalue(str(maxBonds))
def _jmChangeBondType(self, btype):
if btype == "other":
self.jmGenBondFrame.grid()
self.jmPeptideBondFrame.grid_remove()
else:
self.jmGenBondFrame.grid_remove()
self.jmPeptideBondFrame.grid()
self._jmConfig()
def _jmConfig(self, *args):
selAtoms = selection.currentAtoms()
twoOkayAtoms = False
if not hasattr(self, "_jmBondHandler"):
self._jmBondHandler = None
if len(selAtoms) == 2:
if not self._jmBondHandler:
self._jmBondHandler = \
chimera.triggers.addHandler(
"Bond", self._jmConfig, None)
a1, a2 = selAtoms
if a1.oslIdent() > a2.oslIdent():
a1, a2 = a2, a1
if a1.molecule != a2.molecule:
if self._jmBondTypeSelect.getvalue() == "peptide":
if set([a1.element.name,a2.element.name]) == set(["N","C"]):
ca1 = [nb for nb in a1.primaryNeighbors()
if nb.element.name == "C"]
ca2 = [nb for nb in a2.primaryNeighbors()
if nb.element.name == "C"]
if a1.residue.type in ["PRO", "HYP"]:
testValues1 = [1,2]
else:
testValues1 = [1]
if a2.residue.type in ["PRO", "HYP"]:
testValues2 = [1,2]
else:
testValues2 = [1]
if len(ca1) in testValues1 and len(ca2) in testValues2:
twoOkayAtoms = True
else:
rootA1 = a1.molecule.rootForAtom(a1, True)
rootA2 = a2.molecule.rootForAtom(a2, True)
if rootA1 != rootA2 \
and len(a1.neighbors) == len(a2.neighbors) == 1:
twoOkayAtoms = True
elif self._jmBondHandler:
chimera.triggers.deleteHandler("Bond",
self._jmBondHandler)
self._jmBondHandler = None
if twoOkayAtoms:
self._addParamBondButton.config(state="normal")
if self._jmBondTypeSelect.getvalue() != "other":
self._appbPsi.grid()
if a1.element.name == "N":
psi = a1.residue.psi
else:
psi = a2.residue.psi
if psi is None:
psiText = "N/A"
else:
psiText = "%.1f" % psi
self._appbPsi.configure(text=u"(Existing "
u"N-C\N{GREEK SMALL LETTER ALPHA}-C-N"
u" dihedral (\N{GREEK SMALL LETTER PSI}"
u" angle): %s)" % psiText)
return
osls = [a1.oslIdent(), a2.oslIdent()]
if not hasattr(self, "_jmPrevAtoms"):
self._jmPrevAtoms = None
self._jmPrevAtoms = osls
if self._jmPrevAtoms != osls:
self._apobLength.set(elementRadius[a1.element] +
elementRadius[a2.element])
names, atoms = self._enumerateDihedrals(a1, a2)
prevName = self._apobDihedralMenu.getvalue()
kw = {}
if prevName in names:
kw['index'] = prevName
self._apobDihedralMenu.setitems(names, **kw)
kw = {}
prevSide = self._apobSideMenu.getvalue()
from chimera.misc import chimeraLabel
sides = [chimeraLabel(a) for a in (a1, a2)]
if prevSide in sides:
kw['index'] = prevSide
else:
if rootA1.size.numAtoms < rootA2.size.numAtoms:
kw['index'] = sides[0]
else:
kw['index'] = sides[1]
self._apobSideMenu.setitems(sides, **kw)
else:
self._addParamBondButton.config(state="disabled")
if self._jmBondTypeSelect.getvalue() != "other":
self._appbPsi.grid_remove()
return
self._apobDihedralMenu.setitems([])
self._apobSideMenu.setitems([])
if self._addParamBondButton.winfo_ismapped():
self.notebook.setnaturalsize()
def _labelRot(self, br, mode=None):
if mode is None:
mode = self.rotLabelChoice.getvalue()
if mode == "None":
for br in self.rotations:
br.bond.label = ""
elif mode == "Name":
for br in self.rotations:
br.bond.label = self.rotLabel(br)
elif mode == "Angle":
isDihed = self.angleTitle.get() == "Torsion"
if prefs[SHOW_DEGREE_SYMBOL]:
suffix = "\260"
else:
suffix = ""
for br in self.rotations:
if isDihed:
val = self.dihedral(br)
else:
val = br.get()
while val < -180.0:
val += 180.0
while val > 180.0:
val -= 180.0
br.bond.label = "%.*f%s" % (
prefs[TORSION_PRECISION], val, suffix)
elif mode == "ID":
br.bond.label = str(br.id)
def _lowerCB(self, pageName):
if not self._mapped:
return
if pageName == ADJUST_BONDS:
from chimera import triggers
triggers.deleteHandler("selection changed", self._abSelChangeHandler)
delattr(self, "_abSelChangeHandler")
elif pageName == BOND_ANGLES:
from chimera import triggers
triggers.deleteHandler("selection changed", self._baSelChangeHandler)
delattr(self, "_baSelChangeHandler")
self.baMenuOrder = None # don't hold reference to bonds
elif pageName == CHANGE_ATOM:
from chimera import triggers
triggers.deleteHandler("selection changed", self._caSelChangeHandler)
delattr(self, "_caSelChangeHandler")
elif pageName == JOIN_MODELS:
from chimera import triggers
triggers.deleteHandler("selection changed", self._jmSelChangeHandler)
delattr(self, "_jmSelChangeHandler")
self.status("") # can fire pending map/unmap trigger (some platforms), so do last
def _menuCB(self, cmdText, br):
# callback from angle pull-down menu
if cmdText == "Revert":
br.set(0)
elif cmdText == "Deactivate":
br.bond.label = ""
br.destroy()
elif cmdText == "Select":
selectables = []
selectables.extend(br.atoms)
selectables.append(br.bond)
if self.angleTitle.get() == "Torsion":
near, far = self.dihedEndAtoms(br)
if near and far:
selectables.extend([near, far])
selectables.append(br.atoms[0].bondsMap[near])
selectables.append(br.atoms[1].bondsMap[far])
from chimera.tkgui import selectionOperation
sel = selection.ItemizedSelection()
sel.add(selectables)
selectionOperation(sel)
elif cmdText == "Reverse":
br.anchorSide = br.bond.otherAtom(br.anchorSide)
def _modeButtonCB(self, but):
if self.mouseModeVar.get():
# "manually" turn off, then on with new value
self.mouseModeVar.set(False)
self._mouseModeCB()
self._modeButton = but
self.mouseModeVar.set(True)
self._mouseModeCB()
else:
self._modeButton = but
def _molMenuCB(self, val=None):
val = self.molMenu.getvalue()
if type(val) == str:
self.molName.gridManage()
else:
self.molName.gridUnmanage()
def _monitorCB(self, trigName, myData, trigData):
from chimera.triggerSet import ONESHOT
self.remakeAtomLabels()
return ONESHOT
def _mouseModeCB(self):
but, mods = self.labelValues[self._modeButton]
if self.mouseModeVar.get():
self._formerMouseMode = mousemodes.getFuncName(but,mods)
mousemodes.setButtonFunction(but, mods, "rotate bond")
else:
mousemodes.setButtonFunction(but, mods, self._formerMouseMode)
def _mouseSphere(self, viewer, event):
xf = viewer.vsphere(event.time, event.x, event.y,
event.state % 2 == 1)
if xf.isIdentity():
return
axis, angle = xf.getRotation()
br = self.rotations[self.rotModeTorsMenu.index(Pmw.SELECT)]
rotVec = br.atoms[1].xformCoord() - br.atoms[0].xformCoord()
axis.normalize()
rotVec.normalize()
turn = angle * (axis * rotVec)
br.set(turn + br.get())
def _newBonds(self, val):
self.bondsJustChanged = True
numBonds = int(val)
mb = self.geometryMenu.component('menubutton')
if numBonds < 2:
mb.configure(state="normal")
self.geometryMenu.setvalue("N/A")
mb.configure(state="disabled")
return
mb.configure(state="normal")
from chimera.bondGeom import geometryName
availGeoms = geometryName[numBonds:]
self.geometryMenu.setitems(availGeoms, index=availGeoms[0])
def _placeAtoms(self):
placeType = self.placeTypeVar.get()
if placeType in [self.PLACE_ATOM, self.PLACE_FRAGMENT,
self.PLACE_SMILES, self.PLACE_PUBCHEM]:
resName = self._getResName()
if resName is None:
return
m = self._getMol()
if placeType == self.PLACE_ATOM:
if self.atomPosVar.get() == "view":
h = placeHelium(resName, model=m)
else:
xyz = []
for lab, entry in [("x", self.xEntry), ("y", self.yEntry),
("z", self.zEntry)]:
if not entry.valid():
self.enter()
raise UserError(lab + " entry invalid")
xyz.append(float(entry.getvalue()))
h = placeHelium(resName, model=m, position=chimera.Point(*xyz))
if self.autoselAtomVar.get():
chimera.selection.setCurrent(h)
atoms = [h]
elif placeType in [self.PLACE_FRAGMENT, self.PLACE_SMILES,
self.PLACE_PUBCHEM]:
if placeType == self.PLACE_FRAGMENT:
frag = self.fragmentLookup[
self.fragMenu.getvalue()[-1]]
r = placeFragment(frag, resName, model=m)
elif placeType == self.PLACE_SMILES:
from Smiles import openSmiles, SmilesTranslationError
try:
m = openSmiles(self.smilesEntry.get(),
resName=resName)
except SmilesTranslationError, v:
raise UserError(unicode(v))
r = m.residues[0]
else:
from PubChem import openPubChem, InvalidPub3dID
try:
m = openPubChem(self.pubChemEntry.get(),
resName=resName)
except InvalidPub3dID, v:
raise UserError(unicode(v))
r = m.residues[0]
atoms = r.atoms
elif placeType == self.PLACE_NUCLEIC:
nucType = self.nucType.getvalue()
if nucType.startswith("Hybrid"):
nucType = "hybrid"
else:
nucType = nucType.lower()
try:
residues = placeNucleotide(self.nucleicSequence.getvalue().strip(),
self.nucForm.getvalue()[0], type=nucType, model=self._getMol())
except NucleotideError, v:
raise UserError(v)
atoms = []
for r in residues:
atoms.extend(r.atoms)
else:
seq = self.peptideSequence.get().strip()
PeptideDialog(seq, self._placePeptideCB)
return
self._finishPlace(atoms)
def _placePeptideCB(self, seq, phiPsis, chainID, libName):
try:
residues = placePeptide(seq, phiPsis, rotlib=libName,
model=self._getMol(), chainID=chainID)
except PeptideError, v:
raise UserError(v)
atoms = []
for r in residues:
atoms.extend(r.atoms)
self._finishPlace(atoms)
def _processFragments(self, fragments):
menuItems = []
lookup = {}
for item in fragments:
if type(item) == list:
# submenu
subitems, sublookup = self._processFragments(
item[1])
menuItems.append([item[0], subitems])
lookup.update(sublookup)
else:
# Fragment
menuItems.append(item.name)
lookup[item.name] = item
return menuItems, lookup
def _raiseCB(self, pageName):
if not self._mapped:
return
self.bondsJustChanged = False
if pageName == ADJUST_BONDS:
self._abSelChange()
from chimera import triggers
self._abSelChangeHandler = triggers.addHandler("selection changed",
self._abSelChange, None)
elif pageName == BOND_ANGLES:
self._baSelChange()
from chimera import triggers
self._baSelChangeHandler = triggers.addHandler("selection changed",
self._baSelChange, None)
elif pageName == CHANGE_ATOM:
self._genAtomNameCB()
self._caSelChangeHandler = chimera.triggers.addHandler('selection changed',
lambda *args: self._genAtomNameCB(), None)
elif pageName == JOIN_MODELS:
from chimera import triggers
self._jmSelChangeHandler = triggers.addHandler("selection changed",
self._jmConfig, None)
def _rotLabelModeChange(self, mode):
prefs[ROT_LABEL] = mode
for br in self.rotations:
self._labelRot(br, mode)
def _sessionSave(self, trigName, myData, sessionFile):
info = {'mapped': self.uiMaster().winfo_ismapped()}
if bondRotMgr.rotations:
info['adjust torsions'] = {
'labels': self.rotLabelChoice.getvalue(),
'decimal places': int(self.torsionPrecisionChoice.getvalue()),
'show degree symbol': self.showDegreeSymbolVar.get()
}
print>>sessionFile, """
try:
from BuildStructure.gui import _sessionRestore
_sessionRestore(%s)
except:
reportRestoreError("Failure restoring Build Structure")
""" % repr(info)
def _sessionRestore(self, info):
if info['mapped']:
self.enter()
else:
self.Close()
if 'adjust torsions' in info:
atInfo = info['adjust torsions']
showSymbol = atInfo['show degree symbol']
if showSymbol != self.showDegreeSymbolVar.get():
self.showDegreeSymbolVar.set(showSymbol)
precision = atInfo['decimal places']
diff = precision - int(self.torsionPrecisionChoice.getvalue())
while diff > 0:
diff -= 1
self.torsionPrecisionChoice.decrement()
while diff < 0:
diff += 1
self.torsionPrecisionChoice.increment()
labels = atInfo['labels']
if labels != self.rotLabelChoice.getvalue():
self.rotLabelChoice.invoke(labels)
def _setBondLength(self):
self.status("")
bondLength = self.blEntry.value()
if bondLength < 0 or bondLength is None:
if not self.blEntry.variable.get():
# unfortunately, selecting the entry text and typing a valid
# length goes through an intervening blank, so ignore the
# call if the entry is empty instead of complaining
return
raise UserError("Invalid bond length specified")
cbs = selection.currentBonds()
if not cbs:
raise UserError("No bonds selected")
side = self.blSideMenu.getvalue()
for bond in cbs:
setBondLength(bond, bondLength, movingSide=side,
status=self.status)
def _setDihedEnd(self, br, dihedMenu):
"""callback when a 'near atoms' menu is set"""
widgets, nearIndex, nearAtoms, farIndex, farAtoms = \
self.rotInfo[br]
index = dihedMenu.index(Pmw.SELECT)
if dihedMenu is widgets[1]:
nearIndex = index
else:
farIndex = index
self.rotInfo[br] = [widgets, nearIndex, nearAtoms,
farIndex, farAtoms]
widgets[4].configure(angle=float("%.*f" %
(prefs[TORSION_PRECISION], self.dihedral(br))))
self._labelRot(br)
def _setTorWidgetsState(self, state):
for w in self.needTorWidgets:
if isinstance(w, Pmw.OptionMenu):
if w['labelpos']:
w.configure(label_state=state)
w.configure(menubutton_state=state)
else:
w.configure(state=state)
def _showDegreeSymbolChangeCB(self):
prefs[SHOW_DEGREE_SYMBOL] = self.showDegreeSymbolVar.get()
if self.rotLabelChoice.getvalue() == "Angle":
for br in self.rotations:
self._labelRot(br)
def _showPlaceGroup(self, val, row):
for group in self.apGroups.values():
group.grid_forget()
self.apGroups[val].grid(row=row, column=2,
rowspan=len(self.apGroups))
if val in [self.PLACE_SMILES, self.PLACE_PUBCHEM]:
self.apModelNameFrame.grid_remove()
self.apColorButton.grid_remove()
else:
self.apModelNameFrame.grid()
self.apColorButton.grid()
self.notebook.setnaturalsize()
def _swapSubstituents(self):
selAtoms = selection.currentAtoms()
if len(selAtoms) == 1:
center = selAtoms[0]
kw = {}
elif len(selAtoms) == 2:
centers = set(selAtoms[0].neighbors) & set(selAtoms[1].neighbors)
if not centers:
raise UserError("Selected atoms have no neighbor atom in common!")
elif len(centers) > 1:
raise UserError("Selected atoms have more than one neighbor atom"
" in common!")
center = centers.pop()
kw = {'swapees': selAtoms}
else:
raise UserError("Please select either one or two atoms.")
from BuildStructure import invertChirality, InvertChiralityError
try:
invertChirality(center, **kw)
except InvertChiralityError, v:
raise UserError(unicode(v))
def _toggleAngleType(self):
if self.angleTitle.get() == "Torsion":
self.angleTitle.set("Delta")
self.rotTable.setColumnTitle(1, None)
self.rotTable.setColumnTitle(3, None)
for i in range(len(self.rotations)):
br = self.rotations[i]
ID, near, menu, far, dihed, delta = \
self.rotInfo[br][0]
dihed.grid_forget()
near.grid_forget()
far.grid_forget()
delta.grid(row=i, column=4, sticky='ew')
self.rotInfo[br][0][-2:] = delta, dihed
else:
self.angleTitle.set("Torsion")
self.rotTable.setColumnTitle(1, "Near")
self.rotTable.setColumnTitle(3, "Far")
for i in range(len(self.rotations)):
br = self.rotations[i]
ID, near, menu, far, delta, dihed = \
self.rotInfo[br][0]
delta.grid_forget()
dihed.grid(row=i, column=4, sticky='ew')
near.grid(row=i, column=1, sticky='ew')
far.grid(row=i, column=3, sticky='ew')
self.rotInfo[br][0][-2:] = dihed, delta
if self.rotLabelChoice.getvalue() == "Angle":
for br in self.rotations:
self._labelRot(br, "Angle")
def _torsionPrecisionChange(self, *args):
newPrecision = self._checkPrecision(*args)
prefs[TORSION_PRECISION] = newPrecision
for br in self.rotations:
self._updateRot(br)
return str(newPrecision)
def _updateRot(self, br):
if self.angleTitle.get() == "Torsion":
dihed, delta = self.rotInfo[br][0][-2:]
else:
delta, dihed = self.rotInfo[br][0][-2:]
delta.configure(angle = float("%.*f" %
(prefs[TORSION_PRECISION], br.get())))
dihed.configure(angle = float("%.*f" %
(prefs[TORSION_PRECISION], self.dihedral(br))))
if self.rotLabelChoice.getvalue() == "Angle":
self._labelRot(br, "Angle")
class PmwableMenuButton(Tkinter.Menubutton):
def __init__(self, *args, **kw):
try:
items = kw['items']
del kw['items']
except KeyError:
items = []
try:
command = kw['command']
del kw['command']
except KeyError:
command = None
try:
states = kw['states']
del kw['states']
except KeyError:
states = ['normal'] * len(items)
Tkinter.Menubutton.__init__(self, *args, **kw)
self.menu = Tkinter.Menu(self)
for i in range(len(items)):
item = items[i]
state = states[i]
if command:
self.menu.add_command(label=item, state=state,
command=lambda c=command, i=item: c(i))
else:
self.menu.add_command(label=item, state=state)
self.configure(menu=self.menu)
def paramTitle(startText):
if '(' in startText:
parened = startText.split('(')
for i, trailer in enumerate(parened[1:]):
parenthetical, rem = trailer.split(')')
parts = parenthetical.split()
parened[i+1] = "-".join([part.capitalize()
for part in parts]) + " " + rem
text = " ".join(parened)
else:
text = startText
return " ".join([x[1:].islower() and x.capitalize()
or x for x in text.split()])
class PeptideDialog(ModelessDialog):
oneshot = True
title = "Add Peptide Sequence"
help = "ContributedSoftware/editing/editing.html#peptide-angles"
def __init__(self, seq, cb):
self.seq = seq
self.cb = cb
ModelessDialog.__init__(self)
def fillInUI(self, parent):
from CGLtk.Table import SortableTable
table = self.table = SortableTable(parent,
allowUserSorting=False)
table.addColumn("Res", "code")
table.addColumn(u"\u03A6", "phi", format="%g")
table.addColumn(u"\u03A8", "psi", format="%g")
class ResData:
pass
self.data = data = []
for c in self.seq:
rd = ResData()
rd.code = c
rd.phi, rd.psi = PhiPsiOption.values[0]
data.append(rd)
table.setData(data)
table.launch()
table.grid(row=0, column=0, sticky="nsew", columnspan=6)
parent.rowconfigure(0, weight=1)
parent.columnconfigure(5, weight=1)
from chimera import tkgui
if tkgui.windowSystem == "aqua":
padx = None
else:
padx = 0
Tkinter.Button(parent, text="Set", command=self._setPhiPsi,
pady=0, padx=padx).grid(row=1, column=0)
Tkinter.Label(parent, text="selected rows to").grid(row=1,
column=1)
from chimera.tkoptions import FloatOption
self.phi = FloatOption(parent, 1, u"\u03A6", 0.0, None, width=6,
startCol=2)
self.psi = FloatOption(parent, 1, u"\u03A8", 0.0, None, width=6,
startCol=4)
f = Tkinter.Frame(parent)
f.grid(row=2, columnspan=6)
self._seedPhiPsi(
PhiPsiOption(f, 0, u"Seed above \u03A6/\u03A8 with"
u" values for", PhiPsiOption.values[0],
self._seedPhiPsi))
from Rotamers.gui import RotLibOption, defaultLib
f2 = Tkinter.Frame(parent)
f2.grid(row=3, columnspan=6)
self.rotlib = RotLibOption(f2, 0, "Rotamer library",
defaultLib(), None)
from chimera.tkoptions import StringOption
self.chainID = StringOption(f2, 0, "chain ID", "A", None,
width=1, startCol=2)
def Apply(self):
self.cb(self.seq, [(d.phi, d.psi) for d in self.data],
self.chainID.get(), self.rotlib.get().importName)
def _seedPhiPsi(self, opt):
phi, psi = opt.get()
self.phi.set(phi)
self.psi.set(psi)
def _setPhiPsi(self):
residues = self.table.selected()
if not residues:
raise UserError("No table rows selected")
phi = self.phi.get()
psi = self.psi.get()
for r in residues:
r.phi = phi
r.psi = psi
self.table.refresh()
from chimera.tkoptions import SymbolicEnumOption
class PhiPsiOption(SymbolicEnumOption):
values = ((-57, -47),
(-139, 135),
(-119, 113),
(-49, -26),
(-57, -70))
## Tk 8.4 rendering of Unicode pretty yucky
#labels = ("alpha helix", "3/10 helix",
# "pi helix", "parallel beta strand",
# "anti-parallel beta strand")
# but Tk 8.5 rendering is okay
labels = (u"\u03B1 helix",
u"antiparallel \u03B2 strand",
u"parallel \u03B2 strand",
# subscripts look shitty and subscript zero doesn't even work,
# so instead of using:
#u"3\u2081\u2080 helix",
# use:
"3/10 helix",
u"\u03C0 helix")
from chimera import dialogs
dialogs.register(BuildStructureDialog.name, BuildStructureDialog)
def addRotation(bond):
d = dialogs.find(BuildStructureDialog.name)
import types
br = bondRotMgr.rotationForBond(bond, create=False)
if br == None:
br = bondRotMgr.rotationForBond(bond)
else:
d.enter()
d.setCategory(BOND_ROTS)
return br
def _showBondRotUI(trigger, myData, br):
if chimera.nogui:
return
if trigger == bondRotMgr.CREATED:
# some window managers are slow to raise windows
# only auto-raise the dialog if a new rotation is created
d = dialogs.display(BuildStructureDialog.name)
d.setCategory(BOND_ROTS)
d = dialogs.find(BuildStructureDialog.name)
d.rotChange(trigger, br)
for trigger in bondRotMgr.triggerNames:
bondRotMgr.triggers.addHandler(trigger, _showBondRotUI, None)
if bondRotMgr.rotations:
d = dialogs.display(BuildStructureDialog.name)
d.setCategory(BOND_ROTS)
for br in bondRotMgr.rotations.values():
d.rotChange(bondRotMgr.CREATED, br)
def _sessionRestore(info):
d = dialogs.display(BuildStructureDialog.name)
d._sessionRestore(info)