def fillInUI(self, parent): import Tkinter, Pmw, Tix top = parent.winfo_toplevel() menubar = Tkinter.Menu(top, type="menubar", tearoff=False) top.config(menu=menubar) selectMenu = Tkinter.Menu(menubar) menubar.add_cascade(label="Select", menu=selectMenu) selectMenu.add_command(label="Current Rotamers", command=self._selCurCB) selectMenu.add_command(label="All Rotamers", command=self._selAllCB) self.columnMenu = Tkinter.Menu(menubar) menubar.add_cascade(label="Columns", menu=self.columnMenu) self.addColumnMenu = Tkinter.Menu(self.columnMenu) self.columnMenu.add_cascade(label="Add", menu=self.addColumnMenu) for label, ddn in self.dependentDialogInfo: self.addColumnMenu.add_command( label=label + "...", command=lambda name=ddn + "Dialog": self._showDialog(name)) self.columnMenu.add_separator() from chimera.tkgui import aquaMenuBar aquaMenuBar(menubar, parent, row=0) self.numChis = len(self.rotamers[0].chis) row = 1 Tkinter.Label(parent, text="%s %s rotamers" % (self.lib.displayName, self.resType)).grid(row=row, column=0) row += 1 self.buttonWidgets['OK'].config(state="disabled") self.buttonWidgets['Apply'].config(state="disabled") from CGLtk.Table import SortableTable self.rotTable = SortableTable(parent, menuInfo=(self.columnMenu, prefs, {}, True)) if not self.tableData: for i in range(self.numChis): self.rotTable.addColumn("Chi %d" % (i + 1), "lambda r: r.chis[%d]" % i, format="%6.1f") self.rotTable.addColumn("Probability", "rotamerProb", format="%.6f", font='TkFixedFont') self.rotTable.setData(self.rotamers) self.rotTable.launch(browseCmd=self._selRotamerCB, restoreInfo=self.tableData) delattr(self, 'tableData') parent.rowconfigure(row, weight=1) for tableColumn in range(1): parent.columnconfigure(tableColumn, weight=1) self.rotTable.grid(row=row, column=0, sticky="nsew") row += 1
def fillInUI(self, parent): from CGLtk.Table import SortableTable self.blastTable = SortableTable(parent) if not self.tableData: self._addColumn("GI", "lambda m: m.gi", format="%s ", anchor="w") self._addColumn("PDB", "lambda m: m.pdb or '-'", format="%s ", anchor="w") self._addColumn("Evalue", "lambda m: m.evalue", format="%s") self._addColumn("Score", "lambda m: m.score", format="%s") self._addColumn("Description", "lambda m: m.description", format=" %s", anchor="w") if self.parser: self.blastTable.setData(self.parser.matches) else: self.blastTable.setData([]) self.blastTable.launch(browseCmd=self._selectHitCB, restoreInfo=self.tableData) self.blastTable.pack(expand=True, fill="both") bw = self.buttonWidgets bw["Show in MAV"].config(state="disabled") bw["Load Structure"].config(state="disabled") bw["Quit"].config(state="disabled")
def fillInUI(self, parent): import Tkinter top = parent.winfo_toplevel() menubar = Tkinter.Menu(top, type="menubar", tearoff=False) top.config(menu=menubar) self.columnMenu = Tkinter.Menu(menubar) menubar.add_cascade(label="Columns", menu=self.columnMenu) from chimera.tkgui import aquaMenuBar aquaMenuBar(menubar, parent, pack = 'top') self._makeActionGroup(parent) from CGLtk.Table import SortableTable from prefs import colAttr, colOrder, prefs, defaults self.modBaseTable = SortableTable(parent, menuInfo=( self.columnMenu, prefs, defaults, False )) if not self.tableData: self._addColumn("Model", "lambda m: m.oslIdent()", format="%s", shown=True) for fieldName in colOrder: keyName, format = colAttr[fieldName] self._addColumn(fieldName, "lambda m: m.modbaseInfo['%s']" % keyName, format=format) self.modBaseTable.setData(self.molList) chimera.triggers.addHandler("post-frame", self._launchTable, None) self.modBaseTable.pack(expand=True, fill="both")
def fillInUI(self, parent): import Tkinter, Pmw, Tix top = parent.winfo_toplevel() menubar = Tkinter.Menu(top, type="menubar", tearoff=False) top.config(menu=menubar) selectMenu = Tkinter.Menu(menubar) menubar.add_cascade(label="Select", menu=selectMenu) selectMenu.add_command(label="Current Rotamers", command=self._selCurCB) selectMenu.add_command(label="All Rotamers", command=self._selAllCB) self.columnMenu = Tkinter.Menu(menubar) menubar.add_cascade(label="Columns", menu=self.columnMenu) self.addColumnMenu = Tkinter.Menu(self.columnMenu) self.columnMenu.add_cascade(label="Add", menu=self.addColumnMenu) for label, ddn in self.dependentDialogInfo: self.addColumnMenu.add_command(label=label + "...", command=lambda name=ddn+"Dialog": self._showDialog(name)) self.columnMenu.add_separator() from chimera.tkgui import aquaMenuBar aquaMenuBar(menubar, parent, row = 0) self.numChis = len(self.rotamers[0].chis) row = 1 Tkinter.Label(parent, text="%s %s rotamers" % (self.lib.displayName, self.resType)).grid( row=row, column=0) row += 1 self.buttonWidgets['OK'].config(state="disabled") self.buttonWidgets['Apply'].config(state="disabled") from CGLtk.Table import SortableTable self.rotTable = SortableTable(parent, menuInfo=(self.columnMenu, prefs, {}, True)) if not self.tableData: for i in range(self.numChis): self.rotTable.addColumn("Chi %d" % (i+1), "lambda r: r.chis[%d]" % i, format="%6.1f") self.rotTable.addColumn("Probability", "rotamerProb", format="%.6f", font='TkFixedFont') self.rotTable.setData(self.rotamers) self.rotTable.launch(browseCmd=self._selRotamerCB, restoreInfo=self.tableData) delattr(self, 'tableData') parent.rowconfigure(row, weight=1) for tableColumn in range(1): parent.columnconfigure(tableColumn, weight=1) self.rotTable.grid(row=row, column=0, sticky="nsew") row += 1
def fillInUI(self, parent): self.distances = [] self.angleInfo = [] self.numMolecules = len(chimera.openModels.list( modelTypes=[chimera.Molecule])) chimera.triggers.addHandler('Molecule', self._molChange, None) chimera.triggers.addHandler('PseudoBond', self._psbChange, None) chimera.triggers.addHandler('Atom', self._atomChange, None) chimera.triggers.addHandler('Model', self._modelChange, None) distanceMonitor.updateCallbacks.append(self._distUpdateCB) self.notebook = Pmw.NoteBook(parent, raisecommand=self._nbRaiseCB, lowercommand=self._nbLowerCB) self.notebook.pack(fill='both', expand=1) self.interfaces = {} for pn in pageNames: pageID = pn ## when more tabs shown, maybe do this... #if '/' in pn: # parts = pn.split('/') # pn = "/ ".join(parts) #if ' ' in pn: # parts = pn.split(' ') # pn = '\n'.join(parts) self.notebook.add(pageID, tab_text=pn) dp = self.notebook.page(DISTANCES) from CGLtk.Table import SortableTable self.distTable = SortableTable(dp) self.distTable.grid(row=0, column=0, sticky='nsew', rowspan=7) dp.columnconfigure(0, weight=1) dp.rowconfigure(4, weight=1) dp.rowconfigure(5, weight=1) self.distTable.addColumn("ID", "id", format="%d") self.distTable.addColumn("Atom 1", lambda d, s=self: s.atomLabel(d.atoms[0])) self.distTable.addColumn("Atom 2", lambda d, s=self: s.atomLabel(d.atoms[1])) self.distTable.addColumn("Distance", "distance", font="TkFixedFont") self.distTable.setData(self.distances) self.distTable.launch(browseCmd=self._distTableSelCB) self.distButtons = Pmw.ButtonBox(dp, padx=0) self.distButtons.add("Create", command=self._createDistance) self.distButtons.add("Remove", command=self._removeDistance) # remove the extra space around buttons allocated to indicate # which button is the 'default', so that buttons stack closely for but in range(self.distButtons.numbuttons()): self.distButtons.button(but).config(default='disabled') self.distButtons.alignbuttons() self.distButtons.grid(row=1, column=1) self.distLabelChoice = Pmw.RadioSelect(dp, pady=0, buttontype='radiobutton', command=self._distLabelModeChange, orient='vertical', labelpos='w', label_text="Labels") self.distLabelChoice.grid(row=2, column=1) self.distLabelChoice.add("None", highlightthickness=0) self.distLabelChoice.add("ID", highlightthickness=0) self.distLabelChoice.add("Distance", highlightthickness=0) self.distLabelChoice.invoke("Distance") self.distPrecisionChoice = Pmw.Counter(dp, datatype={ 'counter': self._distPrecisionChange}, labelpos='w', label_text="Decimal places", entry_width=1, entry_pyclass=PrecisionEntry, entryfield_value=str(precision())) self.distPrecisionChoice.grid(row=3, column=1) self.showUnitsVar = Tkinter.IntVar(dp) self.showUnitsVar.set(showUnits()) Tkinter.Checkbutton(dp, text="Show Angstrom symbol", variable=self.showUnitsVar, command=self._showUnitsChangeCB).grid(row=4, column=1) self.distSelectsAtomsVar = Tkinter.IntVar(dp) self.distSelectsAtomsVar.set(False) Tkinter.Checkbutton(dp, variable=self.distSelectsAtomsVar, text="Choosing in table\nselects atoms\n(and pseudobond)" ).grid(row=5, column=1) from chimera.pbgPanel import attributesCmd Tkinter.Button(dp, text="Display options...", command= lambda dm=distanceMonitor: attributesCmd([dm]) ).grid(row=6, column=1) for d in distanceMonitor.pseudoBonds: self.newDistance(d) atp = self.notebook.page(ANGLES) from CGLtk.Table import SortableTable self.angleTable = SortableTable(atp) self.angleTable.grid(row=0, column=0, sticky='nsew', rowspan=4) atp.columnconfigure(0, weight=1) atp.rowconfigure(2, weight=1) for i in range(4): self.angleTable.addColumn("Atom %d" % (i+1), lambda atoms, s=self, i=i: i >= len(atoms) and "N/A" or s.atomLabel(atoms[i])) self.angleTable.addColumn("Angle/Torsion", lambda atoms, s=self: s._angleLabel(atoms), font="TkFixedFont") self.angleTable.setData(self.angleInfo) self.angleTable.launch(browseCmd=self._angleTableSelCB) self._osHandler = None self.angleButtons = Pmw.ButtonBox(atp, padx=0) self.angleButtons.add("Create", command=self._createAngle) self.angleButtons.add("Remove", command=self._removeAngle, state='disabled') # remove the extra space around buttons allocated to indicate # which button is the 'default', so that buttons stack closely for but in range(self.angleButtons.numbuttons()): self.angleButtons.button(but).config(default='disabled') self.angleButtons.alignbuttons() self.angleButtons.grid(row=0, column=1) self.anglePrecisionChoice = Pmw.Counter(atp, datatype={ 'counter': self._anglePrecisionChange}, labelpos='w', label_text="Decimal places", entry_width=1, entry_pyclass=PrecisionEntry, entryfield_value=str(prefs[ANGLE_PRECISION])) self.anglePrecisionChoice.grid(row=1, column=1) self.angleSelectsComponentsVar = Tkinter.IntVar(atp) self.angleSelectsComponentsVar.set(True) Tkinter.Checkbutton(atp, variable=self.angleSelectsComponentsVar, text="Choosing in table selects\ncomponent atoms/bonds" ).grid(row=3, column=1) brp = self.notebook.page(BONDROTS) self.rotations = [] self.rotInfo = {} labeledButton = Pmw.LabeledWidget(brp, 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(brp, 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(parent) self.angleTitle.set("Torsion") self.rotTable.setColumnTitle(4, self.angleTitle, pyclass=Tkinter.Button, pady=0, command=self._toggleAngleType) brp.rowconfigure(1, weight=1) brp.columnconfigure(0, weight=1) brp.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(brp) 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(brp, 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]) self.torsionPrecisionChoice = Pmw.Counter(brp, 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(brp) self.showDegreeSymbolVar.set(prefs[SHOW_DEGREE_SYMBOL]) Tkinter.Checkbutton(brp, 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))) if GEOMETRIES in pageNames: gp = self.notebook.page(GEOMETRIES) from Geometry import GeometryInterface self.interfaces[GEOMETRIES] = GeometryInterface(gp, self.status) if METALS in pageNames: mp = self.notebook.page(METALS) from Metals import MetalsInterface self.interfaces[METALS] = MetalsInterface(mp, self.status) self.notebook.setnaturalsize()
class StructMeasure(ModelessDialog): title="Structure Measurements" buttons=("Close", "Save") name="structure measurements" provideStatus = True statusPosition = "above" dialSizes = [".1i", ".2i", ".3i"] def fillInUI(self, parent): self.distances = [] self.angleInfo = [] self.numMolecules = len(chimera.openModels.list( modelTypes=[chimera.Molecule])) chimera.triggers.addHandler('Molecule', self._molChange, None) chimera.triggers.addHandler('PseudoBond', self._psbChange, None) chimera.triggers.addHandler('Atom', self._atomChange, None) chimera.triggers.addHandler('Model', self._modelChange, None) distanceMonitor.updateCallbacks.append(self._distUpdateCB) self.notebook = Pmw.NoteBook(parent, raisecommand=self._nbRaiseCB, lowercommand=self._nbLowerCB) self.notebook.pack(fill='both', expand=1) self.interfaces = {} for pn in pageNames: pageID = pn ## when more tabs shown, maybe do this... #if '/' in pn: # parts = pn.split('/') # pn = "/ ".join(parts) #if ' ' in pn: # parts = pn.split(' ') # pn = '\n'.join(parts) self.notebook.add(pageID, tab_text=pn) dp = self.notebook.page(DISTANCES) from CGLtk.Table import SortableTable self.distTable = SortableTable(dp) self.distTable.grid(row=0, column=0, sticky='nsew', rowspan=7) dp.columnconfigure(0, weight=1) dp.rowconfigure(4, weight=1) dp.rowconfigure(5, weight=1) self.distTable.addColumn("ID", "id", format="%d") self.distTable.addColumn("Atom 1", lambda d, s=self: s.atomLabel(d.atoms[0])) self.distTable.addColumn("Atom 2", lambda d, s=self: s.atomLabel(d.atoms[1])) self.distTable.addColumn("Distance", "distance", font="TkFixedFont") self.distTable.setData(self.distances) self.distTable.launch(browseCmd=self._distTableSelCB) self.distButtons = Pmw.ButtonBox(dp, padx=0) self.distButtons.add("Create", command=self._createDistance) self.distButtons.add("Remove", command=self._removeDistance) # remove the extra space around buttons allocated to indicate # which button is the 'default', so that buttons stack closely for but in range(self.distButtons.numbuttons()): self.distButtons.button(but).config(default='disabled') self.distButtons.alignbuttons() self.distButtons.grid(row=1, column=1) self.distLabelChoice = Pmw.RadioSelect(dp, pady=0, buttontype='radiobutton', command=self._distLabelModeChange, orient='vertical', labelpos='w', label_text="Labels") self.distLabelChoice.grid(row=2, column=1) self.distLabelChoice.add("None", highlightthickness=0) self.distLabelChoice.add("ID", highlightthickness=0) self.distLabelChoice.add("Distance", highlightthickness=0) self.distLabelChoice.invoke("Distance") self.distPrecisionChoice = Pmw.Counter(dp, datatype={ 'counter': self._distPrecisionChange}, labelpos='w', label_text="Decimal places", entry_width=1, entry_pyclass=PrecisionEntry, entryfield_value=str(precision())) self.distPrecisionChoice.grid(row=3, column=1) self.showUnitsVar = Tkinter.IntVar(dp) self.showUnitsVar.set(showUnits()) Tkinter.Checkbutton(dp, text="Show Angstrom symbol", variable=self.showUnitsVar, command=self._showUnitsChangeCB).grid(row=4, column=1) self.distSelectsAtomsVar = Tkinter.IntVar(dp) self.distSelectsAtomsVar.set(False) Tkinter.Checkbutton(dp, variable=self.distSelectsAtomsVar, text="Choosing in table\nselects atoms\n(and pseudobond)" ).grid(row=5, column=1) from chimera.pbgPanel import attributesCmd Tkinter.Button(dp, text="Display options...", command= lambda dm=distanceMonitor: attributesCmd([dm]) ).grid(row=6, column=1) for d in distanceMonitor.pseudoBonds: self.newDistance(d) atp = self.notebook.page(ANGLES) from CGLtk.Table import SortableTable self.angleTable = SortableTable(atp) self.angleTable.grid(row=0, column=0, sticky='nsew', rowspan=4) atp.columnconfigure(0, weight=1) atp.rowconfigure(2, weight=1) for i in range(4): self.angleTable.addColumn("Atom %d" % (i+1), lambda atoms, s=self, i=i: i >= len(atoms) and "N/A" or s.atomLabel(atoms[i])) self.angleTable.addColumn("Angle/Torsion", lambda atoms, s=self: s._angleLabel(atoms), font="TkFixedFont") self.angleTable.setData(self.angleInfo) self.angleTable.launch(browseCmd=self._angleTableSelCB) self._osHandler = None self.angleButtons = Pmw.ButtonBox(atp, padx=0) self.angleButtons.add("Create", command=self._createAngle) self.angleButtons.add("Remove", command=self._removeAngle, state='disabled') # remove the extra space around buttons allocated to indicate # which button is the 'default', so that buttons stack closely for but in range(self.angleButtons.numbuttons()): self.angleButtons.button(but).config(default='disabled') self.angleButtons.alignbuttons() self.angleButtons.grid(row=0, column=1) self.anglePrecisionChoice = Pmw.Counter(atp, datatype={ 'counter': self._anglePrecisionChange}, labelpos='w', label_text="Decimal places", entry_width=1, entry_pyclass=PrecisionEntry, entryfield_value=str(prefs[ANGLE_PRECISION])) self.anglePrecisionChoice.grid(row=1, column=1) self.angleSelectsComponentsVar = Tkinter.IntVar(atp) self.angleSelectsComponentsVar.set(True) Tkinter.Checkbutton(atp, variable=self.angleSelectsComponentsVar, text="Choosing in table selects\ncomponent atoms/bonds" ).grid(row=3, column=1) brp = self.notebook.page(BONDROTS) self.rotations = [] self.rotInfo = {} labeledButton = Pmw.LabeledWidget(brp, 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(brp, 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(parent) self.angleTitle.set("Torsion") self.rotTable.setColumnTitle(4, self.angleTitle, pyclass=Tkinter.Button, pady=0, command=self._toggleAngleType) brp.rowconfigure(1, weight=1) brp.columnconfigure(0, weight=1) brp.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(brp) 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(brp, 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]) self.torsionPrecisionChoice = Pmw.Counter(brp, 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(brp) self.showDegreeSymbolVar.set(prefs[SHOW_DEGREE_SYMBOL]) Tkinter.Checkbutton(brp, 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))) if GEOMETRIES in pageNames: gp = self.notebook.page(GEOMETRIES) from Geometry import GeometryInterface self.interfaces[GEOMETRIES] = GeometryInterface(gp, self.status) if METALS in pageNames: mp = self.notebook.page(METALS) from Metals import MetalsInterface self.interfaces[METALS] = MetalsInterface(mp, self.status) self.notebook.setnaturalsize() def setCategoryMenu(self, category): # avoid unnecessary page raises; they interfere with # the bond rotation mouse mode (graphics window loses # focus) if self.notebook.getcurselection() != category: self.notebook.selectpage(category) 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 _angleLabel(self, atoms): pts = tuple([a.xformCoord() for a in atoms]) if len(pts) == 3: val = chimera.angle(*pts) else: val = chimera.dihedral(*pts) return "%.*f" % (prefs[ANGLE_PRECISION], val) def _anglePrecisionChange(self, *args): newPrecision = self._checkPrecision(*args) prefs[ANGLE_PRECISION] = newPrecision self.angleTable.refresh() return str(newPrecision) def _angleTableSelCB(self, selAngles): if self.angleSelectsComponentsVar.get(): select = [] for atoms in selAngles: select.extend(atoms) atomSet = set(atoms) for a in atoms: for b in a.bonds: if b.otherAtom(a) in atomSet: select.append(b) selection.setCurrent(select) def _atomChange(self, trigName, myData, trigData): if not trigData.deleted: return remove = [] for atoms in self.angleInfo: for a in atoms: if a.__destroyed__: remove.append(atoms) break if remove: self._removeAngle(remove=remove) 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 _createAngle(self, atoms=None): """'Create angle' callback""" if atoms is None: atoms = selection.currentAtoms(ordered=True) if len(atoms) not in [3,4]: replyobj.error("Either three or four atoms must be" " selected in graphics window\n") return if not self.angleInfo: from SimpleSession import SAVE_SESSION self._angleSesTrigID = chimera.triggers.addHandler(SAVE_SESSION, self._sessionAngleSaveCB, None) self.angleInfo.append(atoms) self.angleTable.setData(self.angleInfo) self.angleButtons.button("Remove").config(state='normal') if self._osHandler is None: models = set([a.molecule for a in atoms]) if len(models) > 1: self._osHandler = chimera.triggers.addHandler( 'OpenState', self._osChange, None) def _createDistance(self): """'Create distance' callback""" selAtoms = selection.currentAtoms() if len(selAtoms) != 2: replyobj.error("Exactly two atoms must be selected " "in graphics window\n") return addDistance(*tuple(selAtoms)) def _createRotation(self): selBonds = selection.currentBonds() if len(selBonds) == 1: addRotation(selBonds[0]) return replyobj.error("Exactly one bond must be selected " "in graphics window\n") def deadDistances(self): """Remove deleted distances from the table""" pre = len(self.distances) self.distances = [d for d in self.distances if not d.__destroyed__] if len(self.distances) != pre: self.distTable.setData(self.distances) return True return False 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 dihedEndAtoms(self, br): widgets, nearIndex, nearAtoms, farIndex, farAtoms = \ self.rotInfo[br] return nearAtoms[nearIndex], farAtoms[farIndex] def _distPrecisionChange(self, text, plusMinus, increment): setPrecision(int(text) + plusMinus) if precision() > 9: setPrecision(9) raise ValueError, "9 decimal places is enough" return str(precision()) def _distTableSelCB(self, selDists): if self.distSelectsAtomsVar.get(): select = [] select.extend(selDists) for sd in selDists: select.extend(sd.atoms) selection.setCurrent(select) else: selection.removeCurrent(self.distances) selection.addCurrent(selDists) def _distUpdateCB(self): """Distances just updated""" self.distTable.refresh() def _distLabelModeChange(self, mode): if mode == "None": distanceMonitor.fixedLabels = 1 for d in self.distances: d.label = "" elif mode == "ID": distanceMonitor.fixedLabels = 1 for d in self.distances: d.label = "%d" % d.id else: distanceMonitor.fixedLabels = 0 for d in self.distances: d.label = d.distance def Help(self): anchor = self.notebook.getcurselection().lower().split()[0] if "/" in anchor: anchor = anchor.split('/')[0] chimera.help.display("ContributedSoftware/structuremeas/" "structuremeas.html#" + anchor) 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 _modelChange(self, trigName, myData, trigData): if not trigData.modified: return # both Coord and CoordSet changes fire the Model trigger self._updateAngles() def _molChange(self, trigName, myData, trigData): n = len(chimera.openModels.list(modelTypes=[chimera.Molecule])) if n == 1 and self.numMolecules > 1 \ or n > 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) self.numMolecules = n 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 _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 newDistance(self, d): if self.distances: d.id = self.distances[-1].id + 1 else: d.id = 1 if not hasattr(d, 'distance'): d.distance = "" self.distances.append(d) self.distTable.setData(self.distances) def monitorCB(self, trigName, myData, trigData): from chimera.triggerSet import ONESHOT self.mcHandlerID = None self.remakeAtomLabels() return ONESHOT def _nbLowerCB(self, pageName): if pageName in self.interfaces: interface = self.interfaces[pageName] try: interface._lowerCmd() except AttributeError: self.status("") else: self.status("") def _nbRaiseCB(self, pageName): if pageName in self.interfaces: interface = self.interfaces[pageName] try: interface._raiseCmd() except AttributeError: pass def _osChange(self, trigName, myData, trigData): if 'transformation change' not in trigData.reasons: return self._updateAngles() def _psbChange(self, trigName, myData, trigData): """Callback from PseudoBond trigger""" change = False # clean up deleted distances if trigData.deleted: change = self.deadDistances() # insert new distances for psb in trigData.created: if psb in distanceMonitor.pseudoBonds: self.newDistance(psb) change = True if change: self.notebook.setnaturalsize() def remakeAtomLabels(self): self.distTable.refresh() self.angleTable.refresh() 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 _removeAngle(self, remove=None): if len(self.angleInfo) == 0: replyobj.error("No angles to remove\n") return if remove is None: if len(self.angleInfo) == 1: remove = self.angleInfo else: remove = self.angleTable.selected() if not remove: replyobj.error("Must select angle(s) in table\n") return for rm in remove: self.angleInfo.remove(rm) self.angleTable.setData(self.angleInfo) if len(self.angleInfo) == 0: self.angleButtons.button("Remove").config( state='disabled') from SimpleSession import SAVE_SESSION chimera.triggers.deleteHandler(SAVE_SESSION, self._angleSesTrigID) if self._osHandler: stillNeedHandler = False for info in self.angleInfo: models = dict.fromkeys([a.molecule for a in info[0]]) if len(models) > 1: stillNeedHandler = True break if not stillNeedHandler: chimera.triggers.deleteHandler('OpenState', self._osHandler) self._osHandler = None def _removeDistance(self): if len(self.distances) == 1: removeDistance(self.distances[0]) return if len(self.distances) == 0: replyobj.error("No distances to remove\n") return if not self.distTable.selected(): replyobj.error("Must select distance in table\n") return for d in self.distTable.selected(): removeDistance(d) def rotLabel(self, br): return "%s -> %s" % (self.atomLabel(br.atoms[0]), self.atomLabel(br.atoms[1], diffWith=br.atoms[0])) def _rotLabelModeChange(self, mode): prefs[ROT_LABEL] = mode for br in self.rotations: self._labelRot(br, mode) def Save(self): """Save the displayed info to file""" if not hasattr(self, '_saveDialog'): self._saveDialog = _SaveStructInfo(self, clientPos='s', title='Save Structure Measurements') self._saveDialog.enter() 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 _showUnitsChangeCB(self): showUnits(self.showUnitsVar.get()) def _syncUI(self, br, side): """Called from the Bond trigger callback, to correct the dihedral angle menus if possible""" start = 1+2*side index, atoms = self.rotInfo[br][start:start+2] newIndex, newNames, newAtoms = \ self.dihedChoices(br.atoms[side], br.atoms[1-side]) if not newAtoms: # when gnats 243 is fixed, this bond rotation will # already have been destroyed and we won't get here return None if atoms == newAtoms: return None atom = atoms[index] if atom in newAtoms: newIndex = newAtoms.index(atom) menu = self.rotInfo[br][0][1+2*side] menu.setitems(newNames, newNames[newIndex]) self.rotInfo[br][start:start+2] = [newIndex, newAtoms] return menu def _torsionPrecisionChange(self, *args): newPrecision = self._checkPrecision(*args) prefs[TORSION_PRECISION] = newPrecision for br in self.rotations: self._updateRot(br) return str(newPrecision) 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) self._updateAngles() if needResize: self.notebook.setnaturalsize() 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: default = 0 else: default = bonded.index(default) return default, names, bonded def dihedral(self, br): near, far = self.dihedEndAtoms(br) widgets, nearIndex, nearAtoms, farIndex, farAtoms = \ self.rotInfo[br] return dihedral(near.xformCoord(), br.atoms[0].xformCoord(), br.atoms[1].xformCoord(), far.xformCoord()) 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') self.rotations.insert(row, br) a1, a2 = br.atoms 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 = map(self.rotLabel, self.rotations) self.rotModeTorsMenu.setitems(items, index=modeTors) self._labelRot(br) 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 _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") def _dihedCB(self, br, degrees): # callback from dihedral AngleCounter curDihed = self.dihedral(br) br.set(br.get() + degrees - curDihed) def _counterAngle(self, text, updown, incr, **kw): angle = float(text) if updown > 0: angle = angle + incr else: angle = angle - incr while angle < -180.0: angle = angle + 360.0 while angle > 180.0: angle = angle - 360.0 self._deltaCB(kw['br'], angle=angle) return str(angle) def _deltaCB(self, br, degrees): # callback from delta AngleCounter br.set(degrees) 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) 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 _sessionAngleSaveCB(self, trigName, myData, sessionFile): from SimpleSession import sessionID, sesRepr sesData = [] for atoms in self.angleInfo: sesData.append([sessionID(a) for a in atoms]) print>>sessionFile, "angleInfo = %s" % sesRepr(sesData) print>>sessionFile, """ try: from StructMeasure.gui import restoreAngles restoreAngles(angleInfo) except: reportRestoreError("Error restoring angle monitors in session") """ 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 _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 _updateAngles(self): self.angleTable.refresh()
class CastpDialog(ModelessDialog): help = "UsersGuide/castp.html" buttons = ("Quit", "Hide") def __init__(self, name, cavities, sessionData=None): self.title = "CASTp: %s" % name self.cavities = cavities from weakref import proxy def wrapper(s=proxy(self)): s._cavityChangeCB() for cav in cavities: cav.pocketInfo["atoms"].selChangedCB = wrapper cav.mouthInfo["atoms"].selChangedCB = wrapper if sessionData: self.tableData = sessionData else: self.tableData = None ModelessDialog.__init__(self) chimera.extension.manager.registerInstance(self) def fillInUI(self, parent): from sys import getrefcount import Tkinter, Pmw top = parent.winfo_toplevel() menubar = Tkinter.Menu(top, type="menubar", tearoff=False) top.config(menu=menubar) self.columnMenu = Tkinter.Menu(menubar) menubar.add_cascade(label="Columns", menu=self.columnMenu) from chimera.tkgui import aquaMenuBar aquaMenuBar(menubar, parent, row = 0) row = 1 from CGLtk.Table import SortableTable self.cavityTable = SortableTable(parent, menuInfo= (self.columnMenu, prefs, dict.fromkeys( defaults[SHOWN_COLS], True), False)) if not self.tableData: from CASTp import mouthFieldNames, pocketFieldNames # add preferred columns first shownCols = prefs[SHOWN_COLS] for fn in ("ID", "MS volume", "SA volume", "pocket MS area", "pocket SA area", "# openings"): if fn[0] == '#' or fn == "ID": kw = {'format': "%d"} else: kw = {'format': "%6.1f", 'font': "TkFixedFont"} self.cavityTable.addColumn(fn, "lambda c: " "c.pocketInfo['%s']" % fn, **kw) for fn in ("mouth MS area", "mouth SA area", "MS circumference sum", "SA circumference sum"): self.cavityTable.addColumn(fn, "lambda c: " "c.mouthInfo['%s']" % fn, format="%6.1f", font='TkFixedFont') for fieldNames, attrName in [ (pocketFieldNames, "pocketInfo"), (mouthFieldNames, "mouthInfo")]: for fn in fieldNames: if fn[0] == '#' or fn == "ID": kw = {'format': "%d"} else: kw = {'format': "%6.1f", 'font': "TkFixedFont"} c = self.cavityTable.addColumn(fn, "lambda c: " "getattr(c, '%s')['%s']" % (attrName, fn), **kw) if fn == "ID": self.cavityTable.sortBy(c) self.cavityTable.sortBy(c) self.cavityTable.setData(self.cavities) self.cavityTable.launch(browseCmd=self._selCavityCB, restoreInfo=self.tableData) self.cavityTable.grid(row=row, column=0, sticky="nsew") parent.rowconfigure(row, weight=1) parent.columnconfigure(0, weight=1) row += 1 grp = Pmw.Group(parent, tag_text="Treatment of Chosen Pocket Atoms") grp.grid(row=row) checkFrame = grp.interior() from CGLtk import Hybrid def buttonCB(s=self): self._selCavityCB(self.cavityTable.selected()) self.doSelect = Hybrid.Checkbutton(checkFrame, "Select", prefs[DO_SELECT]) self.doSelect.variable.add_callback(buttonCB) self.doSelect.button.grid(row=0, sticky='w') f = Tkinter.Frame(checkFrame) f.grid(row=1, sticky='w') self.doColor = Hybrid.Checkbutton(f, "Color", prefs[DO_COLOR]) self.doColor.variable.add_callback(buttonCB) self.doColor.button.grid(row=0, column=0) from CGLtk.color.ColorWell import ColorWell self.pocketColor = ColorWell(f, color=prefs[POCKET_COLOR], noneOkay=True) self.pocketColor.grid(row=0, column=1) Tkinter.Label(f, text=" (and color all other atoms ").grid( row=0, column=2) self.nonpocketColor = ColorWell(f, color=prefs[NONPOCKET_COLOR], noneOkay=True) self.nonpocketColor.grid(row=0, column=3) Tkinter.Label(f, text=")").grid(row=0, column=4) self.doSurface = Hybrid.Checkbutton(checkFrame, "Surface", prefs[DO_SURFACE]) self.doSurface.variable.add_callback(buttonCB) self.doSurface.button.grid(row=2, sticky='w') self.doZoom = Hybrid.Checkbutton(checkFrame, "Zoom in on", prefs[DO_ZOOM]) self.doZoom.variable.add_callback(buttonCB) self.doZoom.button.grid(row=3, sticky='w') self.excludeMouth = Hybrid.Checkbutton(checkFrame, "Exclude mouth atoms", prefs[EXCLUDE_MOUTH]) self.excludeMouth.variable.add_callback(buttonCB) self.excludeMouth.button.grid(row=4, sticky='w') row += 1 def destroy(self): if self.cavities: cav1 = self.cavities[0] atoms = (cav1.mouthInfo["atoms"].atoms() or cav1.pocketInfo["atoms"].atoms()) if atoms: # close surface too... from chimera import openModels mol = atoms[0].molecule openModels.close(openModels.list( id=mol.id, subid=mol.subid)) for chk in [self.doSelect, self.doColor, self.doSurface, self.doZoom, self.excludeMouth]: chk.destroy() chimera.extension.manager.deregisterInstance(self) ModelessDialog.destroy(self) def emHide(self): """Extension manager method""" self.Close() Hide = emHide def emName(self): """Extension manager method""" return self.title def emQuit(self): """Extension manager method""" self.destroy() Quit = emQuit def emRaise(self): """Extension manager method""" self.enter() def _cavityChangeCB(self): newCavities = [cav for cav in self.cavities if len(cav.mouthInfo["atoms"]) > 0 or len(cav.pocketInfo["atoms"]) > 0] if len(newCavities) == len(self.cavities): return if not newCavities: self.destroy() return self.cavities = newCavities self.cavityTable.setData(self.cavities) def _colDispChange(self, col): self.cavityTable.columnUpdate(col, display=not col.display) prefs[SHOWN_COLS] = [c.title for c in self.cavityTable.columns if c.display] def _selCavityCB(self, tableSel): prefs[EXCLUDE_MOUTH] = self.excludeMouth.variable.get() from chimera.selection import ItemizedSelection, mergeCurrent, \ EXTEND, REMOVE cavitySel = ItemizedSelection() for cavity in tableSel: cavitySel.merge(EXTEND, cavity.pocketInfo["atoms"]) if prefs[EXCLUDE_MOUTH]: cavitySel.merge(REMOVE, cavity.mouthInfo["atoms"]) cavitySel.addImplied() # might be no cavities selected... cav1 = self.cavities[0] someA = (cav1.mouthInfo["atoms"].atoms() or cav1.pocketInfo["atoms"].atoms())[0] if someA.__destroyed__: return mol = someA.molecule cavitySet = set(cavitySel.atoms()) from chimera import selectionOperation doSelect = self.doSelect.variable.get() if doSelect != prefs[DO_SELECT]: #if not doSelect: # mergeCurrent(REMOVE, cavitySel) prefs[DO_SELECT] = doSelect doColor = self.doColor.variable.get() if doColor != prefs[DO_COLOR]: if not doColor and hasattr(self, '_prevColors'): for a, c, sc in self._prevColors: if a.__destroyed__: continue a.color = c a.surfaceColor = sc delattr(self, '_prevColors') prefs[DO_COLOR] = doColor if doColor: prefs[POCKET_COLOR] = self.pocketColor.rgba prefs[NONPOCKET_COLOR] = self.nonpocketColor.rgba doSurface = self.doSurface.variable.get() if doSurface != prefs[DO_SURFACE]: if not doSurface: for a in cavitySet: a.surfaceDisplay = False prefs[DO_SURFACE] = doSurface doZoom = self.doZoom.variable.get() if doZoom != prefs[DO_ZOOM]: if not doZoom: from Midas import focus, uncofr focus([mol]) uncofr() prefs[DO_ZOOM] = doZoom if doSelect: selectionOperation(cavitySel) if doColor: if prefs[POCKET_COLOR] == None: pocketColor = None else: pocketColor = chimera.MaterialColor( *prefs[POCKET_COLOR]) if prefs[NONPOCKET_COLOR] == None: nonpocketColor = None else: nonpocketColor = chimera.MaterialColor( *prefs[NONPOCKET_COLOR]) if not hasattr(self, '_prevColors'): self._prevColors = [(a, a.color, a.surfaceColor) for a in mol.atoms] for a in mol.atoms: if a in cavitySet: a.surfaceColor = a.color = pocketColor else: a.surfaceColor = a.color = nonpocketColor if doSurface: for a in mol.atoms: a.surfaceDisplay = a in cavitySet surfs = chimera.openModels.list(mol.id, mol.subid, modelTypes=[chimera.MSMSModel]) catsurfs = [s for s in surfs if s.category == "main"] if not catsurfs: from Midas import surfaceNew surfaceNew("main", models=[mol]) if doZoom and tableSel: from Midas import align, focus align(cavitySel, mol.atoms) focus(cavitySel)
def fillInUI(self, parent): from sys import getrefcount import Tkinter, Pmw top = parent.winfo_toplevel() menubar = Tkinter.Menu(top, type="menubar", tearoff=False) top.config(menu=menubar) self.columnMenu = Tkinter.Menu(menubar) menubar.add_cascade(label="Columns", menu=self.columnMenu) from chimera.tkgui import aquaMenuBar aquaMenuBar(menubar, parent, row = 0) row = 1 from CGLtk.Table import SortableTable self.cavityTable = SortableTable(parent, menuInfo= (self.columnMenu, prefs, dict.fromkeys( defaults[SHOWN_COLS], True), False)) if not self.tableData: from CASTp import mouthFieldNames, pocketFieldNames # add preferred columns first shownCols = prefs[SHOWN_COLS] for fn in ("ID", "MS volume", "SA volume", "pocket MS area", "pocket SA area", "# openings"): if fn[0] == '#' or fn == "ID": kw = {'format': "%d"} else: kw = {'format': "%6.1f", 'font': "TkFixedFont"} self.cavityTable.addColumn(fn, "lambda c: " "c.pocketInfo['%s']" % fn, **kw) for fn in ("mouth MS area", "mouth SA area", "MS circumference sum", "SA circumference sum"): self.cavityTable.addColumn(fn, "lambda c: " "c.mouthInfo['%s']" % fn, format="%6.1f", font='TkFixedFont') for fieldNames, attrName in [ (pocketFieldNames, "pocketInfo"), (mouthFieldNames, "mouthInfo")]: for fn in fieldNames: if fn[0] == '#' or fn == "ID": kw = {'format': "%d"} else: kw = {'format': "%6.1f", 'font': "TkFixedFont"} c = self.cavityTable.addColumn(fn, "lambda c: " "getattr(c, '%s')['%s']" % (attrName, fn), **kw) if fn == "ID": self.cavityTable.sortBy(c) self.cavityTable.sortBy(c) self.cavityTable.setData(self.cavities) self.cavityTable.launch(browseCmd=self._selCavityCB, restoreInfo=self.tableData) self.cavityTable.grid(row=row, column=0, sticky="nsew") parent.rowconfigure(row, weight=1) parent.columnconfigure(0, weight=1) row += 1 grp = Pmw.Group(parent, tag_text="Treatment of Chosen Pocket Atoms") grp.grid(row=row) checkFrame = grp.interior() from CGLtk import Hybrid def buttonCB(s=self): self._selCavityCB(self.cavityTable.selected()) self.doSelect = Hybrid.Checkbutton(checkFrame, "Select", prefs[DO_SELECT]) self.doSelect.variable.add_callback(buttonCB) self.doSelect.button.grid(row=0, sticky='w') f = Tkinter.Frame(checkFrame) f.grid(row=1, sticky='w') self.doColor = Hybrid.Checkbutton(f, "Color", prefs[DO_COLOR]) self.doColor.variable.add_callback(buttonCB) self.doColor.button.grid(row=0, column=0) from CGLtk.color.ColorWell import ColorWell self.pocketColor = ColorWell(f, color=prefs[POCKET_COLOR], noneOkay=True) self.pocketColor.grid(row=0, column=1) Tkinter.Label(f, text=" (and color all other atoms ").grid( row=0, column=2) self.nonpocketColor = ColorWell(f, color=prefs[NONPOCKET_COLOR], noneOkay=True) self.nonpocketColor.grid(row=0, column=3) Tkinter.Label(f, text=")").grid(row=0, column=4) self.doSurface = Hybrid.Checkbutton(checkFrame, "Surface", prefs[DO_SURFACE]) self.doSurface.variable.add_callback(buttonCB) self.doSurface.button.grid(row=2, sticky='w') self.doZoom = Hybrid.Checkbutton(checkFrame, "Zoom in on", prefs[DO_ZOOM]) self.doZoom.variable.add_callback(buttonCB) self.doZoom.button.grid(row=3, sticky='w') self.excludeMouth = Hybrid.Checkbutton(checkFrame, "Exclude mouth atoms", prefs[EXCLUDE_MOUTH]) self.excludeMouth.variable.add_callback(buttonCB) self.excludeMouth.button.grid(row=4, sticky='w') row += 1
def __init__(self, parent, status): self.status = status self.parent = parent import Pmw, Tkinter row = 0 parent.columnconfigure(0, weight=1) parent.columnconfigure(1, weight=1) Tkinter.Button(parent, text="Define axes...", pady=0, command=self._createAxesCB).grid(row=row, column=0) Tkinter.Button(parent, text="Define plane...", pady=0, command=self._createPlaneCB).grid(row=row, column=1) row += 1 from CGLtk.Table import SortableTable self.table = SortableTable(parent) self.table.grid(row=row, column=0, columnspan=2, sticky="nsew") parent.rowconfigure(row, weight=1) self.table.addColumn("Name", "name", format="%s ") self.table.addColumn(" ", "model.color", format=(False, True)) idCol = self.table.addColumn("ID", "id") self.table.addColumn("Shown", "model.display", format=bool) self.table.addColumn("Length", "length", format="%4.1f", font="TkFixedFont") self.table.addColumn("Radius", "radius", format="%4.1f", font="TkFixedFont") self.table.setData(geomManager.items) self.table.sortBy(idCol) self.table.launch(browseCmd=self._tableCB) row += 1 selGroup = Pmw.Group(parent, tag_text="Choosing in table...") selGroup.grid(row=row, column=0, columnspan=3, sticky='ew') row += 1 cbFrame = selGroup.interior() cbFrame.columnconfigure(0, weight=1) cbFrame.columnconfigure(1, weight=1) self.geomSelObjVar = Tkinter.IntVar(parent) self.geomSelObjVar.set(prefs[AXIS_SEL_OBJ]) Tkinter.Checkbutton(cbFrame, variable=self.geomSelObjVar, text="selects object").grid(row=0, column=0) self.geomSelObjVar.trace_variable("w", self._selModeChangeCB) self.geomSelAtomsVar = Tkinter.IntVar(parent) self.geomSelAtomsVar.set(prefs[AXIS_SEL_ATOMS]) Tkinter.Checkbutton(cbFrame, variable=self.geomSelAtomsVar, text="selects atoms").grid(row=0, column=1) self.geomSelAtomsVar.trace_variable("w", self._selModeChangeCB) self.buttons = [] self.buttons.append(Tkinter.Button(parent, text="Delete", pady=0, state='disabled', command=self._deleteItemsCB)) self.buttons[-1].grid(row=row, column=0, sticky='e') self.buttons.append(Tkinter.Button(parent, text="Rename", pady=0, state='disabled', command=self._renameItems)) self.buttons[-1].grid(row=row, column=1, sticky='w') self.renameDialogs = {} row += 1 f = Tkinter.Frame(parent) f.grid(row=row, column=0, columnspan=2) row += 1 self.buttons.append(Tkinter.Button(f, text="Report distance", pady=0, state='disabled', command=self._atomsDistanceCB)) self.buttons[-1].grid(row=0, column=0) Tkinter.Label(f, text="to selected atoms" ).grid(row=0, column=1) geomManager.registerInterface(self) if geomManager.items: self._selHandlerID = triggers.addHandler("selection changed", self._selChangeCB, None)
class RotamerDialog(ModelessDialog): buttons = ('OK', 'Apply', 'Close',) oneshot = True help = "ContributedSoftware/rotamers/rotamers.html#rotamerlist" def __init__(self, residue, resType, lib, sessionData=None): self._sesHandlerID = chimera.triggers.addHandler(SAVE_SESSION, self._sessionCB, None) self.residue = residue self.lib = lib self.resType = resType self.title = "%s Side-Chain Rotamers" % residue if sessionData: self.bbdep, self.rotamers, self.tableData = sessionData else: from Rotamers import getRotamers self.bbdep, self.rotamers = getRotamers(residue, log=True, lib=lib, resType=resType) from Midas import linewidth, color linewidth(2, self.rotamers) color('byhet', self.rotamers) chimera.openModels.add(self.rotamers, sameAs=residue.molecule, hidden=True, noprefs=True) registerAttribute(chimera.Molecule, "chis") registerAttribute(chimera.Molecule, "rotamerProb") self.tableData = None self.resHandlerID = chimera.triggers.addHandler( 'Residue', self._resChangeCB, None) self.dependentDialogInfo = [('Clashes', 'clash'), ('H-Bonds', 'hbond'), ('Density', 'volume')] for label, ddn in self.dependentDialogInfo: setattr(self, ddn + "Dialog", None) ModelessDialog.__init__(self) def fillInUI(self, parent): import Tkinter, Pmw, Tix top = parent.winfo_toplevel() menubar = Tkinter.Menu(top, type="menubar", tearoff=False) top.config(menu=menubar) selectMenu = Tkinter.Menu(menubar) menubar.add_cascade(label="Select", menu=selectMenu) selectMenu.add_command(label="Current Rotamers", command=self._selCurCB) selectMenu.add_command(label="All Rotamers", command=self._selAllCB) self.columnMenu = Tkinter.Menu(menubar) menubar.add_cascade(label="Columns", menu=self.columnMenu) self.addColumnMenu = Tkinter.Menu(self.columnMenu) self.columnMenu.add_cascade(label="Add", menu=self.addColumnMenu) for label, ddn in self.dependentDialogInfo: self.addColumnMenu.add_command(label=label + "...", command=lambda name=ddn+"Dialog": self._showDialog(name)) self.columnMenu.add_separator() from chimera.tkgui import aquaMenuBar aquaMenuBar(menubar, parent, row = 0) self.numChis = len(self.rotamers[0].chis) row = 1 Tkinter.Label(parent, text="%s %s rotamers" % (self.lib.displayName, self.resType)).grid( row=row, column=0) row += 1 self.buttonWidgets['OK'].config(state="disabled") self.buttonWidgets['Apply'].config(state="disabled") from CGLtk.Table import SortableTable self.rotTable = SortableTable(parent, menuInfo=(self.columnMenu, prefs, {}, True)) if not self.tableData: for i in range(self.numChis): self.rotTable.addColumn("Chi %d" % (i+1), "lambda r: r.chis[%d]" % i, format="%6.1f") self.rotTable.addColumn("Probability", "rotamerProb", format="%.6f", font='TkFixedFont') self.rotTable.setData(self.rotamers) self.rotTable.launch(browseCmd=self._selRotamerCB, restoreInfo=self.tableData) delattr(self, 'tableData') parent.rowconfigure(row, weight=1) for tableColumn in range(1): parent.columnconfigure(tableColumn, weight=1) self.rotTable.grid(row=row, column=0, sticky="nsew") row += 1 def addClashColumn(self, *args): from Rotamers import processClashes format = processClashes(self.residue, self.rotamers, *args) self.rotTable.addColumn("Clashes", "clashScore", format=format, display=True) registerAttribute(chimera.Molecule, "clashScore") def addHbondColumn(self, *args): from Rotamers import processHbonds processHbonds(self.residue, self.rotamers, *args) self.rotTable.addColumn("H-Bonds", "numHbonds", format="%d", display=True) registerAttribute(chimera.Molecule, "numHbonds") def addVolumeColumn(self, columnName, *args): from Rotamers import processVolume format = processVolume(self.rotamers, columnName, *args) self.rotTable.addColumn(columnName, "lambda r: r.volumeScores[%s]" % repr(columnName), format=format, display=True) registerAttribute(chimera.Molecule, "volumeScores") def Apply(self): from Rotamers import useRotamer useRotamer(self.residue, self.rotamerSel, log=True) def destroy(self): chimera.triggers.deleteHandler('Residue', self.resHandlerID) chimera.triggers.deleteHandler(SAVE_SESSION, self._sesHandlerID) chimera.openModels.close(self.rotamers) for label, ddn in self.dependentDialogInfo: dd = getattr(self, ddn + 'Dialog', None) if dd: dd.destroy() ModelessDialog.destroy(self) def selectRotamers(self, rotamers): from chimera import selectionOperation from chimera.selection import ItemizedSelection rotSel = ItemizedSelection() rotSel.add(rotamers) selectionOperation(rotSel) def _resChangeCB(self, trigName, myData, trigData): if self.residue in trigData.deleted: self.destroy() def _selAllCB(self): self.selectRotamers(self.rotamers) def _selCurCB(self): self.selectRotamers([r for r in self.rotamers if r.display]) def _selRotamerCB(self, tableSel): tableSet = set(tableSel) if tableSet == set([r for r in self.rotamers if r.display]): return self.rotamerSel = tableSel for rot in self.rotamers: rot.display = rot in tableSet if len(tableSel) > 0: state = 'normal' else: state = 'disabled' self.buttonWidgets['OK'].config(state=state) self.buttonWidgets['Apply'].config(state=state) def _sessionCB(self, trigName, myData, sesFile): from SimpleSession import sessionID data = (1, sessionID(self.residue), self.resType, [sessionID(rot) for rot in self.rotamers], self.bbdep, self.lib.displayName, self.rotTable.getRestoreInfo()) print>>sesFile, """ try: from Rotamers.gui import sessionRestore sessionRestore(%s) except: reportRestoreError('Error restoring Rotamers') """ % repr(data) def _showDialog(self, dialogName): d = getattr(self, dialogName, None) if d: d.enter() else: importName = dialogName[0].upper() + dialogName[1:] exec("from %s import %s" % (importName, importName)) exec("self.%s = %s(self)" % (dialogName, importName))
class IlabelDialog(ModelessDialog): name = "2D Labels/Color Key" provideStatus = True buttons = ("Delete", "Close") LABELS = "Labels" COLOR_KEY = "Color Key" MOUSE_LABEL_TEXT = "Use mouse for label placement" MOUSE_KEY_TEXT = "Use mouse for key placement" EmphasisColor = "forest green" def __init__(self): import os.path myDir, junk = os.path.split(__file__) addFunction('place text', (self._pickLabel, self._moveLabel, None), icon=chimage.get(Image.open(os.path.join(myDir, 'ilabel.png')), tkgui.app)) addFunction('place key', (self._startOrGrabKey, self._sizeOrMoveKey, None), icon=chimage.get( Image.open(os.path.join(myDir, 'key.png')), tkgui.app)) import Ilabel if not Ilabel._ilabelModel: Ilabel.IlabelModel() self.model = Ilabel._ilabelModel ModelessDialog.__init__(self) self._sessionHandlerID = chimera.triggers.addHandler( SAVE_SESSION, self._saveSession, None) self._closeHandlerID = chimera.triggers.addHandler( CLOSE_SESSION, self.destroy, None) self._beginRestoreHandlerID = chimera.triggers.addHandler( BEGIN_RESTORE_SESSION, self.destroy, None) def fillInUI(self, parent): top = parent.winfo_toplevel() menubar = Tkinter.Menu(top, type="menubar", tearoff=False) top.config(menu=menubar) self.fileMenu = Tkinter.Menu(menubar) menubar.add_cascade(label="File", menu=self.fileMenu) self.fileMenu.add_command(label="Write...", command=self._writeFileCB) self.fileMenu.add_command(label="Read...", command=self._readFileCB) from chimera.tkgui import aquaMenuBar aquaMenuBar(menubar, parent, row=0) from ColorKey import KeyModel parent.rowconfigure(1, weight=1) parent.columnconfigure(0, weight=1) # _pageRaisedCB uses mouseModeVar, so define first self.mouseLabelingVar = Tkinter.IntVar(parent) self.mouseLabelingVar.set(True) self.mlLabelVar = Tkinter.StringVar(parent) self.mouseModeButton = Tkinter.Checkbutton(parent, command=self._mouseFuncCB, variable=self.mouseLabelingVar, textvariable=self.mlLabelVar) self.mouseModeButton.grid(row=2, column=0) self.notebook = Pmw.NoteBook(parent, raisecommand=self._pageRaisedCB) self.notebook.add(self.LABELS, tab_text=self.LABELS) self.notebook.add(self.COLOR_KEY, tab_text=self.COLOR_KEY) self.notebook.grid(row=1, column=0, sticky="nsew") self._fillLabelsPage(self.notebook.page(self.LABELS)) self.keyModel = KeyModel(self) self.keyPosition = None self._fillColorKeyPage(self.notebook.page(self.COLOR_KEY)) def _fillLabelsPage(self, page): page.columnconfigure(0, weight=1) page.columnconfigure(1, weight=1) page.columnconfigure(2, weight=1) row = 0 from CGLtk.Table import SortableTable self.labelTable = SortableTable(page, automultilineHeaders=False) self.labelTable.addColumn("Label [(x, y) text]", self._labelListing, anchor='w') self.labelTable.addColumn("Shown", "shown", format=bool) self.labelTable.setData(self.model.labels) self.labelTable.launch(browseCmd=self._tableCB, selectMode="single") self.labelTable.grid(row=row, column=0, columnspan=3, sticky="nsew") page.rowconfigure(row, weight=1) row += 1 self.labelText = Pmw.ScrolledText(page, labelpos='w', label_text="Text", text_height=3, text_width=20, text_wrap='none', text_state='disabled', text_exportselection=False) text = self.labelText.component('text') text.bind("<<Modified>>", self._textCB) text.bind("<<Selection>>", self._updateTextAttrWidgets) self.labelText.grid(row=row, column=0, sticky='nsew', columnspan=3) page.rowconfigure(row, weight=1) row += 1 self.labelSymbolMenu = Pmw.OptionMenu(page, labelpos='w', label_text="Insert symbol:", command=self._insertSymbol, items=[ u'\N{GREEK SMALL LETTER ALPHA}', u'\N{GREEK SMALL LETTER BETA}', u'\N{GREEK SMALL LETTER GAMMA}', u'\N{GREEK SMALL LETTER DELTA}', u'\N{GREEK SMALL LETTER EPSILON}', u'\N{GREEK SMALL LETTER PI}', u'\N{GREEK SMALL LETTER PHI}', u'\N{GREEK SMALL LETTER CHI}', u'\N{GREEK SMALL LETTER PSI}', u'\N{GREEK SMALL LETTER OMEGA}', u'\N{LEFTWARDS ARROW}', u'\N{RIGHTWARDS ARROW}', u'\N{LEFT RIGHT ARROW}', u'\N{UPWARDS ARROW}', u'\N{DOWNWARDS ARROW}', u'\N{SUPERSCRIPT TWO}', u'\N{SUPERSCRIPT THREE}', u'\N{DEGREE SIGN}', u'\N{LATIN CAPITAL LETTER A WITH RING ABOVE}', "more..." ]) self.labelSymbolMenu.grid(row=row, column=0, columnspan=3) row += 1 colorHouse = Pmw.LabeledWidget(page, labelpos='w', label_text="Color") colorHouse.grid(row=row, column=0, rowspan=3) from CGLtk.color.ColorWell import ColorWell self.colorWell = ColorWell(colorHouse.interior(), color=self._contrastWithBG(), callback=self._colorCB) self.colorWell.grid() from chimera.tkoptions import IntOption self.labelFontSize = IntOption(page, row, "Font size", 24, self._labelChangeCB, startCol=1, min=1, attribute="size", width=3) row += 1 self.labelFontStyle = FontStyle(page, row, "Font style", oglFont.normal, self._labelChangeCB, startCol=1) row += 1 self.labelFontTypeface = FontTypeface(page, row, "Font typeface", FONT_TYPEFACE_VALUES[0], self._labelChangeCB, startCol=1) row += 1 if self.model.curLabel: self.changeToLabel(self.model.curLabel, force=True) def _fillColorKeyPage(self, page): from chimera.tkoptions import IntOption, EnumOption, \ BooleanOption, RGBAOption f = Tkinter.Frame(page) f.grid(row=0, columnspan=2) self.numComponents = IntOption(f, 0, "Number of colors/labels", 3, self._componentsCB, min=2, width=2) self.componentsFrame = Tkinter.Frame(page) self.componentsFrame.grid(row=1, column=0, sticky="nsew", columnspan=2) page.columnconfigure(0, weight=1) self.componentsFrame.columnconfigure(1, weight=1) class ColorTreatment(EnumOption): values = ("distinct", "blended") self.colorTreatment = ColorTreatment(page, 2, "Color range depiction", "blended", self._keyChangeCB, balloon="Should colors be shown as distinct rectangles" " or as a continuous range") class LabelPosition(EnumOption): values = ("left/top", "right/bottom") self.labelPos = LabelPosition(page, 3, "Label positions", "right/bottom", self._keyChangeCB, balloon="Position of" " labels relative to color key.\nLabels always" " positioned adjacent to long side.") self.labelColor = RGBAOption(page, 4, "Label color", self._contrastWithBG(), self._keyChangeCB, balloob= "Label color. If set to 'No color', use corresponding" " key color", noneOkay=True) class LabelJustification(EnumOption): values = ("left", "decimal point", "right") self.justification = LabelJustification(page, 5, "Label justification", "decimal point", self._keyChangeCB, balloon="Justification of label text" " in a vertical key layout.\nHorizontal key labels will" " always be center justified.") self.labelOffset = IntOption(page, 6, "Label offset", 0, self._keyChangeCB, width=3, balloon="Additional offset" " of labels from color bar, in pixels") self.keyFontSize = IntOption(page, 7, "Font size", 24, self._keyChangeCB, width=3) self.keyFontStyle = FontStyle(page, 8, "Font style", oglFont.normal, self._keyChangeCB) self.keyFontTypeface = FontTypeface(page, 9, "Font typeface", FONT_TYPEFACE_VALUES[0], self._keyChangeCB) self.borderColor = RGBAOption(page, 10, "Border color", None, self._keyChangeCB, balloon="Color of border" " around color key (not each individual color).\n" "If 'no color', then no border is drawn.") self.borderWidth = IntOption(page, 11, "Border width", 3, self._keyChangeCB, balloon="in pixels") self.tickMarks = BooleanOption(page, 12, "Show tick marks", False, self._keyChangeCB, balloon="Show tick marks" " pointing from key to labels") self._componentsCB(self.numComponents) def destroy(self, *args): self.mouseLabelingVar.set(True) self.mouseModeButton.invoke() chimera.triggers.deleteHandler(SAVE_SESSION, self._sessionHandlerID) chimera.triggers.deleteHandler(CLOSE_SESSION, self._closeHandlerID) chimera.triggers.deleteHandler(BEGIN_RESTORE_SESSION, self._beginRestoreHandlerID) chimera.openModels.close([self.model, self.keyModel]) ModelessDialog.destroy(self) def map(self, e=None): self._pageRaisedCB(self.notebook.getcurselection()) def unmap(self, e=None): self.mouseLabelingVar.set(True) self.mouseModeButton.invoke() def changeToLabel(self, nextLabel, force=False): if nextLabel == self.model.curLabel and not force: return if self.model.curLabel and not unicode(self.model.curLabel) \ and self.model.curLabel != nextLabel: # remove previous label if empty self.removeLabel(self.model.curLabel) self.model.changeToLabel(nextLabel) self.labelText.component('text').configure(state='normal') self.labelTable.select(nextLabel) self.labelText.settext(unicode(nextLabel)) text = self.labelText.component('text') lineIndex = 1 for line in self.model.curLabel.lines: charIndex = 0 for c in line: text.tag_add(id(c), "%d.%d" % (lineIndex, charIndex)) charIndex += 1 text.tag_add(id(line), "%d.%d" % (lineIndex, charIndex)) lineIndex += 1 def Delete(self): if self.notebook.getcurselection() == self.LABELS: self.labelText.clear() if not self.model.curLabel: self.status("No label to delete", color="red", blankAfter=10) return self.removeLabel(self.model.curLabel) self.labelText.component('text').configure( state='disabled') else: self.keyPosition = None self._keyChangeCB() def Help(self): helpLoc = "ContributedSoftware/2dlabels/2dlabels.html" if self.notebook.getcurselection() == self.COLOR_KEY: helpLoc += "#colorkey" chimera.help.display(helpLoc) def keyConfigure(self, data, pageChange=True): self._componentsCB(len(data), update=False) for datum, well, label in zip(data, self.wells, self.labels): color, text = datum well.showColor(color, doCallback=False) label.variable.set(text, invoke_callbacks=False) self._keyChangeCB() if pageChange: self.notebook.selectpage(self.COLOR_KEY) def makeChar(self, char, model): attrs = {} try: attrs['rgba'] = self.colorWell.rgba except AttributeError: # multi or None if model: attrs['rgba'] = model.rgba size = self.labelFontSize.get() if size is not None: attrs['size'] = size style = self.labelFontStyle.get() if style is not None: attrs['style'] = style fontName = self.labelFontTypeface.get() if fontName is not None: attrs['fontName'] = fontName return Character(char, **attrs) def newLabel(self, pos): label = self.model.newLabel(pos) self.labelTable.setData(self.model.labels) self.status("Mouse drag to reposition label", color=self.EmphasisColor) return label def removeLabel(self, label): self.model.removeLabel(label) self.labelTable.setData(self.model.labels) if self.model.curLabel is not None: self.labelTable.select(self.model.curLabel) def reverseKey(self): data = zip([w.rgba for w in self.wells], [l.variable.get() for l in self.labels]) data.reverse() self.keyConfigure(data) def setLabelFromText(self): curLabel = self.model.curLabel text = self.labelText.component('text') # delete parts of label not in text... # # newlines first... while len(curLabel.lines) > 1: for i, line in enumerate(curLabel.lines[:-1]): if not text.tag_ranges(id(line)): curLabel.lines[i+1][:0] = line del curLabel.lines[i] break else: break # characters... for line in curLabel.lines: for c in line[:]: if not text.tag_ranges(id(c)): line.remove(c) # get new parts of text into label model = None targets = [] lines = curLabel.lines for line in lines: targets.extend([id(c) for c in line]) if not model and line: model = line[0] if line is not lines[-1]: targets.append(id(line)) contents = self.labelText.get()[:-1] # drop trailing newline if targets: target = targets.pop(0) else: target = None textLine = 1 textIndex = -1 curLine = lines[0] for c in contents: textIndex += 1 if str(target) in text.tag_names("%d.%d" % (textLine, textIndex)): if targets: target = targets.pop(0) else: target = None if c == '\n': textLine += 1 textIndex = -1 curLine = lines[[id(l) for l in lines].index( id(curLine))+1] elif curLine: model = curLine[textIndex] elif c == '\n': insertLine = curLine[0:textIndex] lines.insert(textLine-1, insertLine) del curLine[0:textIndex] text.tag_add(id(insertLine), "%d.%d" % (textLine, textIndex)) textLine += 1 textIndex = -1 else: labelChar = self.makeChar(c, model) curLine.insert(textIndex, labelChar) text.tag_add(id(labelChar), "%d.%d" % (textLine, textIndex)) self.model.setMajorChange() def updateGUI(self, source="gui"): curLabel = self.model.curLabel if curLabel and source == "gui": self.setLabelFromText() self.labelTable.setData(self.model.labels) if curLabel: self.labelTable.select(curLabel) self._updateTextAttrWidgets() def _colorCB(self, color): curLabel = self.model.curLabel if not curLabel: self.status("No label to color", color='red') return self.model.setMajorChange() for c in self._selChars(): c.rgba = color def _componentsCB(self, opt, update=True): cf = self.componentsFrame if hasattr(self, 'wells'): for well in self.wells: well.grid_forget() well.destroy() for label in self.labels: label.frame.grid_forget() self.reverseButton.grid_forget() self.reverseButton.destroy() else: Tkinter.Label(cf, text="Colors").grid(row=0) Tkinter.Label(cf, text="Labels").grid(row=0, column=1) if isinstance(opt, int): numComponents = opt self.numComponents.set(opt) else: numComponents = opt.get() wellSize = min(38, int( (7 * 38) / numComponents )) from CGLtk.color.ColorWell import ColorWell self.wells = [] self.labels = [] from CGLtk import Hybrid for i in range(numComponents): well = ColorWell(cf, width=wellSize, height=wellSize, callback=self._keyChangeCB, color='white') well.grid(row=i+1) self.wells.append(well) label = Hybrid.Entry(cf, "", 10) label.variable.add_callback(self._keyTypingCB) label.frame.grid(row=i+1, column=1, sticky='ew') self.labels.append(label) self.reverseButton = Tkinter.Button(cf, command=self.reverseKey, text="Reverse ordering of above", pady=0) self.reverseButton.grid(row=numComponents+1, column=0, columnspan=2) self.notebook.setnaturalsize() if update: self._keyChangeCB() def _contrastWithBG(self): bg = chimera.viewer.background if bg: bgColor = bg.rgba() else: bgColor = (0, 0, 0) if bgColor[0]*2 + bgColor[1]*3 + bgColor[2] < 0.417: return (1, 1, 1) else: return (0, 0, 0) def _eventToPos(self, viewer, event, offset = (0, 0)): w, h = viewer.windowSize return (event.x - offset[0]) / float(w), \ (h - event.y - offset[1]) / float(h) def _handleTextChange(self): self.updateGUI() self.labelText.edit_modified(False) def _insertSymbol(self, item): if len(item) > 1: from chimera import help help.display("ContributedSoftware/2dlabels/symbols.html") return if not self.model.labels: self.status("No labels have been created yet", color="red") return if not self.labelTable.selected(): self.status("No labels active", color="red") self.labelText.insert("insert", item) self.setLabelFromText() def _keyChangeCB(self, *args): self.keyModel.setMajorChange() def _keyTypingCB(self, fromAfter=False): # wait for a pause in typing before updating key... if fromAfter: self._typingHandler = None self._keyChangeCB() return handle = getattr(self, '_typingHandler', None) if handle: self.componentsFrame.after_cancel(handle) self._typingHandler = self.componentsFrame.after(500, lambda: self._keyTypingCB(fromAfter=True)) def _labelChangeCB(self, option): curLabel = self.model.curLabel self.model.setMajorChange() val = option.get() attrName = option.attribute for c in self._selChars(): setattr(c, attrName, val) def _labelListing(self, label): text = unicode(label) if '\n' in text: newline = text.index('\n') text= text[:newline] + "..." if not text: text = "<empty>" return "(%.2f, %.2f) %s" % (label.pos[0], label.pos[1], text) def _mouseFuncCB(self): self.status("") if not self.mouseLabelingVar.get(): if hasattr(self, "_prevMouse"): setButtonFunction("1", (), self._prevMouse) delattr(self, "_prevMouse") elif self.mlLabelVar.get() == self.MOUSE_LABEL_TEXT: if not hasattr(self, "_prevMouse"): self._prevMouse = getFuncName("1", ()) setButtonFunction("1", (), "place text") else: if not hasattr(self, "_prevMouse"): self._prevMouse = getFuncName("1", ()) setButtonFunction("1", (), "place key") def _moveLabel(self, viewer, event): pos = self._eventToPos(viewer, event, self._moveOffset) self.model.moveLabel(pos) curLabel = self.model.curLabel self.labelTable.setData(self.model.labels) self.labelTable.select(curLabel) def _pageRaisedCB(self, pageName): if pageName == "Labels": pageItem = self.MOUSE_LABEL_TEXT if not self.model.labels: self.status("Click mouse button 1 in graphics\n" "window to place first label", color=self.EmphasisColor) for index in range(0, self.fileMenu.index('end')+1): self.fileMenu.entryconfigure(index, state='normal') else: pageItem = self.MOUSE_KEY_TEXT self.status("Drag mouse to position/size key", color=self.EmphasisColor) for index in range(0, self.fileMenu.index('end')+1): self.fileMenu.entryconfigure(index, state='disabled') self.mlLabelVar.set(pageItem) # just setting the var doesn't cause the callback, and # yet using invoke() toggles the var, so set it _opposite_ # to what's desired before calling invoke() self.mouseLabelingVar.set(False) self.mouseModeButton.invoke() def _pickLabel(self, viewer, event): w, h = viewer.windowSize pos = self._eventToPos(viewer, event) label, self._moveOffset = self.model.pickLabel(pos, w, h) if label is None: label = self.newLabel(pos) self._moveOffset = (0, 0) self.changeToLabel(label) self.labelText.component('text').focus_set() def _readFile(self, okayed, dialog): if not okayed: return from Ilabel import readFiles readFiles(dialog.getPaths(), clear=dialog.deleteExistingVar.get()) def _readFileCB(self): if not hasattr(self, "_readFileDialog"): self._readFileDialog = ReadFileDialog( command=self._readFile, clientPos='s') self._readFileDialog.enter() def _restoreSession(self, info): if info["sel ranges"]: self.labelText.tag_add("sel", *info["sel ranges"]) self._updateTextAttrWidgets() self._suppressMap = True self.labelText.edit_modified(False) if "key position" not in info: self.notebook.selectpage(self.LABELS) if info["mouse func"] == "normal": self.mouseLabelingVar.set(True) self.mouseModeButton.invoke() return self.keyPosition = info["key position"] self.colorTreatment.set(info["color depiction"]) self.labelPos.set(info["label positions"]) self.labelColor.set(info["label color"]) self.justification.set(info["label justification"]) self.labelOffset.set(info["label offset"]) self.keyFontSize.set(info["font size"]) self.keyFontStyle.set(info["font typeface"]) if "font name" in info: self.keyFontTypeface.set(info["font name"]) self.borderColor.set(info["border color"]) self.borderWidth.set(info["border width"]) self.tickMarks.set(info["show ticks"]) self.keyConfigure(info["colors/labels"]) if self.keyPosition: self.notebook.selectpage(self.COLOR_KEY) else: self.notebook.selectpage(self.LABELS) if info["mouse func"] == "normal": self.mouseLabelingVar.set(True) self.mouseModeButton.invoke() return info def _saveSession(self, triggerName, myData, sessionFile): print>>sessionFile, """ def restore2DLabelDialog(info): from chimera.dialogs import find, display from Ilabel.gui import IlabelDialog dlg = find(IlabelDialog.name) if dlg is not None: dlg.destroy() dlg = display(IlabelDialog.name) dlg._restoreSession(info) import SimpleSession SimpleSession.registerAfterModelsCB(restore2DLabelDialog, %s) """ % repr(self._sessionInfo()) def _selChars(self): chars = [] curLabel = self.model.curLabel if curLabel: sel = self.labelText.tag_ranges("sel") if sel: sline, schar = [int(x) for x in str(sel[0]).split('.')] eline, echar = [int(x) for x in str(sel[1]).split('.')] sline -= 1 eline -= 1 for li, line in enumerate(curLabel.lines): if li < sline: continue if li > eline: break if sline == eline: chars.extend(line[schar:echar]) elif li == sline: chars.extend(line[schar:]) elif li == eline: chars.extend(line[:echar]) else: chars.extend(line) else: for l in curLabel.lines: chars.extend(l) return chars def _sessionInfo(self): info = {} info["sel ranges"] = tuple([str(tr) for tr in self.labelText.tag_ranges("sel")]) if self.mouseLabelingVar.get(): info["mouse func"] = "labeling" else: info["mouse func"] = "normal" info["key position"] = self.keyPosition info["colors/labels"] = [(w.rgba, l.variable.get()) for w, l in zip(self.wells, self.labels)] info["color depiction"] = self.colorTreatment.get() info["label positions"] = self.labelPos.get() info["label color"] = self.labelColor.get() info["label justification"] = self.justification.get() info["label offset"] = self.labelOffset.get() info["font size"] = self.keyFontSize.get() info["font typeface"] = self.keyFontStyle.get() info["font name"] = self.keyFontTypeface.get() info["border color"] = self.borderColor.get() info["border width"] = self.borderWidth.get() info["show ticks"] = self.tickMarks.get() return info def _sizeOrMoveKey(self, viewer, event): pos = self._eventToPos(viewer, event) if self.grabPos: deltas = [pos[axis] - self.grabPos[axis] for axis in [0, 1]] for posIndex in [0, 1]: old = self.keyPosition[posIndex] self.keyPosition[posIndex] = ( old[0] + deltas[0], old[1] + deltas[1]) self.grabPos = pos elif len(self.keyPosition) == 1: self.keyPosition.append(pos) else: self.keyPosition[1] = pos self._keyChangeCB() def _startOrGrabKey(self, viewer, event): pos = self._eventToPos(viewer, event) if self.keyPosition and len(self.keyPosition) == 2: # possible grab; # see if in middle third of long side... p1, p2 = self.keyPosition x1, y1 = p1 x2, y2 = p2 if abs(x2 - x1) < abs(y2 - y1): longAxis = 1 ymin = min(y2, y1) ymax = max(y2, y1) b1 = (2* ymin + ymax) / 3.0 b2 = (2* ymax + ymin) / 3.0 o1 = min(x1, x2) o2 = max(x1, x2) else: longAxis = 0 xmin = min(x2, x1) xmax = max(x2, x1) b1 = (2* xmin + xmax) / 3.0 b2 = (2* xmax + xmin) / 3.0 o1 = min(y1, y2) o2 = max(y1, y2) if b1 < pos[longAxis] < b2 \ and o1 < pos[1-longAxis] < o2: self.grabPos = pos return self.grabPos = None self.keyPosition = [pos] self._keyChangeCB() self.status("Grab middle of key to reposition", color=self.EmphasisColor) def _tableCB(self, sel): if not sel: return self.changeToLabel(sel) def _textCB(self, e): text = self.labelText.component('text') if not text.tk.call((text._w, 'edit', 'modified')): #if not text.edit_modified(): return # this callback can happen _before_ the change # actually occurs, so do the processing after idle text.after_idle(self._handleTextChange) def _updateTextAttrWidgets(self, e=None): rgba = None multiple = False for c in self._selChars(): if rgba is None: rgba = c.rgba elif rgba != c.rgba: multiple = True break if rgba is not None: self.colorWell.showColor(rgba, multiple=multiple, doCallback=False) self.labelFontSize.display(self._selChars()) self.labelFontStyle.display(self._selChars()) self.labelFontTypeface.display(self._selChars()) def _writeFile(self, okayed, dialog): if not okayed: return from Ilabel import writeFile writeFile(dialog.getPaths()[0]) def _writeFileCB(self): if not hasattr(self, "_writeFileDialog"): from OpenSave import SaveModeless self._writeFileDialog = SaveModeless(command=self._writeFile, title="Write 2D Labels Info") self._writeFileDialog.enter()
def _fillLabelsPage(self, page): page.columnconfigure(0, weight=1) page.columnconfigure(1, weight=1) page.columnconfigure(2, weight=1) row = 0 from CGLtk.Table import SortableTable self.labelTable = SortableTable(page, automultilineHeaders=False) self.labelTable.addColumn("Label [(x, y) text]", self._labelListing, anchor='w') self.labelTable.addColumn("Shown", "shown", format=bool) self.labelTable.setData(self.model.labels) self.labelTable.launch(browseCmd=self._tableCB, selectMode="single") self.labelTable.grid(row=row, column=0, columnspan=3, sticky="nsew") page.rowconfigure(row, weight=1) row += 1 self.labelText = Pmw.ScrolledText(page, labelpos='w', label_text="Text", text_height=3, text_width=20, text_wrap='none', text_state='disabled', text_exportselection=False) text = self.labelText.component('text') text.bind("<<Modified>>", self._textCB) text.bind("<<Selection>>", self._updateTextAttrWidgets) self.labelText.grid(row=row, column=0, sticky='nsew', columnspan=3) page.rowconfigure(row, weight=1) row += 1 self.labelSymbolMenu = Pmw.OptionMenu( page, labelpos='w', label_text="Insert symbol:", command=self._insertSymbol, items=[ u'\N{GREEK SMALL LETTER ALPHA}', u'\N{GREEK SMALL LETTER BETA}', u'\N{GREEK SMALL LETTER GAMMA}', u'\N{GREEK SMALL LETTER DELTA}', u'\N{GREEK SMALL LETTER EPSILON}', u'\N{GREEK SMALL LETTER PI}', u'\N{GREEK SMALL LETTER PHI}', u'\N{GREEK SMALL LETTER CHI}', u'\N{GREEK SMALL LETTER PSI}', u'\N{GREEK SMALL LETTER OMEGA}', u'\N{LEFTWARDS ARROW}', u'\N{RIGHTWARDS ARROW}', u'\N{LEFT RIGHT ARROW}', u'\N{UPWARDS ARROW}', u'\N{DOWNWARDS ARROW}', u'\N{SUPERSCRIPT TWO}', u'\N{SUPERSCRIPT THREE}', u'\N{DEGREE SIGN}', u'\N{LATIN CAPITAL LETTER A WITH RING ABOVE}', "more..." ]) self.labelSymbolMenu.grid(row=row, column=0, columnspan=3) row += 1 colorHouse = Pmw.LabeledWidget(page, labelpos='w', label_text="Color") colorHouse.grid(row=row, column=0, rowspan=3) from CGLtk.color.ColorWell import ColorWell self.colorWell = ColorWell(colorHouse.interior(), color=self._contrastWithBG(), callback=self._colorCB) self.colorWell.grid() from chimera.tkoptions import IntOption self.labelFontSize = IntOption(page, row, "Font size", 24, self._labelChangeCB, startCol=1, min=1, attribute="size", width=3) row += 1 self.labelFontStyle = FontStyle(page, row, "Font style", oglFont.normal, self._labelChangeCB, startCol=1) row += 1 self.labelFontTypeface = FontTypeface(page, row, "Font typeface", FONT_TYPEFACE_VALUES[0], self._labelChangeCB, startCol=1) row += 1 if self.model.curLabel: self.changeToLabel(self.model.curLabel, force=True)
def fillInUI(self, parent): # scaleWidget allows user to scale the displacement # vector for display purposes. # addColumn arguments are: (column title, attribute associated # with column, layout format). format=None means use default # display string. format=bool means display using checkbutton # that can be used to change the value of the variable. self.modesTable = t = SortableTable(parent, automultilineHeaders=False) t.pack(expand=True, fill="both") t.addColumn("#", "index", format="%d") t.addColumn("Active", "active", format=bool) t.addColumn(u"Frequency (cm\u207B\u00b9)", "frequency", format=None) t.setData(self.modeData) t.launch() #self.hydro = False #self.resid = False self.representation = 1 # self.scaleWidget = Tkinter.Scale(parent, # label="Scale Factor", # from_=1.0, to=50.0, orient="horizontal", resolution = 1.0, # command = self.__scaleCB) # self.scaleWidget.set(0.1) # self.scaleWidget.pack(expand=True, fill="both") self.scaleWidget = Pmw.Counter(parent, entryfield_command=self.__scaleCB, autorepeat=True, entryfield_value=1, entryfield_validate=self._validate) self.scaleWidget.pack(expand=True, fill="both") self.optionVector = Pmw.RadioSelect( parent, buttontype='checkbutton', labelpos='w', label_text='Show displacement Vectors:', command=self._show) self.optionVector.add("") self.optionVector.pack(expand=False, fill='both') self.vectorGroup = Pmw.Group(parent, tag_text='Displacement Vectors') self.vectorGroup.pack(expand=False, fill='both') self.vectorGroup.pack_forget() # test for drawing vectors self.__vecTypeDef = Tkinter.StringVar() self.__vecTypeDef.set('all atoms') self.vecType = Pmw.OptionMenu( self.vectorGroup.interior(), labelpos='w', label_text='Representation :', menubutton_textvariable=self.__vecTypeDef, items=('all atoms', 'no hydrogens', 'by residues'), command=self.__vecType) self.vecType.pack(anchor='w', fill="both") self.vectorType = Pmw.RadioSelect(self.vectorGroup.interior(), buttontype='radiobutton', orient='vertical', command=self.__scaleCB2) self.vectorType.add("Positive Displacement") self.vectorType.add("Negative Displacement") self.vectorType.setvalue("Positive Displacement") self.vectorType.pack(expand=False, fill="both") # defining the arrows self.sf = Pmw.ScrolledFrame(self.vectorGroup.interior(), labelpos='n', label_text='Modifying Arrows', usehullsize=1, hull_width=400, hull_height=220) self.arrow = Pmw.RadioSelect(self.vectorGroup.interior(), buttontype='checkbutton', labelpos='w', label_text='Configuration arrows', command=self._arrowShow) self.arrow.add("") self.arrow.pack(expand=False, fill='both') # function per entrar infos sobre les amplituds de les flexes self.__vectorCyl = Pmw.Counter(self.sf.interior(), entryfield_command=self.__cylCB, autorepeat=True, label_text="Radius of the cylinder", labelpos="w", entryfield_value=0.03, datatype={ 'counter': 'real', 'separator': '.' }, increment=0.01, entryfield_validate=self._validateCyl) self.__vectorCyl.pack(expand=False, fill="both") self.__vectorCon = Pmw.Counter( self.sf.interior(), entryfield_command=self.__conCB, autorepeat=True, label_text="Radius of the base of the cone", labelpos="w", entryfield_value=0.3, datatype={ 'counter': 'real', 'separator': '.' }, increment=0.01, entryfield_validate=self._validateCon) self.__vectorCon.pack(expand=False, fill="both") self.__vectorRel = Pmw.Counter( self.sf.interior(), entryfield_command=self.__relCB, autorepeat=True, label_text="Relative size of the cylinder", labelpos="w", entryfield_value=0.8, datatype={ 'counter': 'real', 'separator': '.' }, increment=0.01, entryfield_validate=self._validateRel) self.__vectorRel.pack(expand=False, fill="both") self.__var = Tkinter.StringVar() self.__var.set('medium purple') self.__vectorColor = Pmw.OptionMenu( self.sf.interior(), labelpos='w', label_text='Choose Color:', menubutton_textvariable=self.__var, items=('red', 'blue', 'cyan', 'medium purple', 'yellow', 'grey', 'green'), command=self.__colorCB, ) self.__vectorColor.pack(anchor='w', fill="both") self.__vectorScale = Pmw.Counter(self.sf.interior(), entryfield_command=self.__scaleCB, autorepeat=True, label_text="Vector Scale Factor", labelpos="w", entryfield_value=50, datatype={'counter': 'numeric'}, increment=1, entryfield_validate=self._validateSf) self.__vectorScale.pack(expand=False, fill="both") self.saveOptionVector = Pmw.RadioSelect( parent, buttontype='checkbutton', labelpos='w', label_text='Save Modes in Session:', command=self._showsave) self.saveOptionVector.add("") self.saveOptionVector.pack(expand=False, fill='both') self.vecSaveGroup = Pmw.Group(parent, tag_text='Modes to save in session') self.vecSaveGroup.pack(expand=False, fill='both') self.modeText = Pmw.EntryField(self.vecSaveGroup.interior(), label_text=" modes", labelpos='e', validate={"validator": "real"}) self.modeText.pack(expand=False, fill="both") self.vecSaveGroup.pack_forget()
class BlastResultsDialog(ModelessDialog): buttons = ( "Show in MAV", "Load Structure", "Hide", "Quit" ) help = "UsersGuide/blast.html#results" def __init__(self, mol=None, seq=None, blastData=None, sessionData=None): self.loaded = {} self.reference = mol if isinstance(self.reference, StructureSequence): self.molecule = self.reference.molecule elif isinstance(self.reference, Molecule): self.molecule = self.reference self.sequence = seq # for session data only if seq is None: self.seq, self.refResList = self._makeSeq(mol) else: self.seq = seq if self.reference: seq, resList = self._makeSeq(mol) self.refResList = self._getResidues(self.seq, seq, resList) if blastData: self.initBlast(*blastData) else: self.initSession(*sessionData) self.title = "Blast: %s" % self.basename ModelessDialog.__init__(self) if not blastData: self._updateLoadButton() if self.molecule: self.closeHandler = chimera.openModels.addRemoveHandler( self._modelClosedCB, None) else: self.closeHandler = None self.sesHandler = chimera.triggers.addHandler( SAVE_SESSION, self._sessionCB, None) chimera.extension.manager.registerInstance(self) def _makeSeq(self, mol): if isinstance(mol, StructureSequence): if not mol.hasProtein(): raise UserError("No protein sequence found " "in %s %s\n" % ( mol.molecule.name, mol.name)) return ''.join(mol.sequence), mol.residues elif isinstance(mol, Molecule): from chimera import resCode seq = [] refResList = [] for r in mol.residues: try: seq.append(resCode.protein3to1[r.type]) except KeyError: pass else: refResList.append(r) if len(seq) == 0: raise UserError("No protein sequence " "found in %s\n" % mol.name) return ''.join(seq), refResList else: raise ValueError("Blast Protein not called with " "molecule or chain\n") def initBlast(self, prog, db, evalue, matrix, passes, showOne): import os.path self.program = prog if self.molecule is not None: base = os.path.basename(self.molecule.name) else: try: base = self.seq.name except AttributeError: base = "query" self.basename = base.replace(' ', '_') self.showOne = showOne self.parser = None from Parser import BlastproteinService self.service = BlastproteinService(self._finish, params=( prog, db, self.basename, self.seq, evalue, matrix, passes)) self.tableData = None def initSession(self, program, name, showOne, parserData, serviceData, tableData): self.basename = name self.program = program self.showOne = showOne if parserData: from Parser import restoreParser self.parser = restoreParser(parserData) else: self.parser = None if serviceData: from Parser import BlastproteinService self.service = BlastproteinService(self._finish, sessionData=serviceData) else: self.service = None self.tableData = tableData def fillInUI(self, parent): from CGLtk.Table import SortableTable self.blastTable = SortableTable(parent) if not self.tableData: self._addColumn("GI", "lambda m: m.gi", format="%s ", anchor="w") self._addColumn("PDB", "lambda m: m.pdb or '-'", format="%s ", anchor="w") self._addColumn("Evalue", "lambda m: m.evalue", format="%s") self._addColumn("Score", "lambda m: m.score", format="%s") self._addColumn("Description", "lambda m: m.description", format=" %s", anchor="w") if self.parser: self.blastTable.setData(self.parser.matches) else: self.blastTable.setData([]) self.blastTable.launch(browseCmd=self._selectHitCB, restoreInfo=self.tableData) self.blastTable.pack(expand=True, fill="both") bw = self.buttonWidgets bw["Show in MAV"].config(state="disabled") bw["Load Structure"].config(state="disabled") bw["Quit"].config(state="disabled") def _finish(self, results): if self.blastTable is None: # We already quit, so UI is gone return from Parser import Parser import os.path try: self.parser = Parser(self.basename, results, self.program == "psiblast") except SyntaxError, s: replyobj.error("BLAST error: %s\n" % s) return except:
class RotamerDialog(ModelessDialog): buttons = ( 'OK', 'Apply', 'Close', ) oneshot = True help = "ContributedSoftware/rotamers/rotamers.html#rotamerlist" def __init__(self, residue, resType, lib, sessionData=None): self._sesHandlerID = chimera.triggers.addHandler( SAVE_SESSION, self._sessionCB, None) self.residue = residue self.lib = lib self.resType = resType self.title = "%s Side-Chain Rotamers" % residue if sessionData: self.bbdep, self.rotamers, self.tableData = sessionData else: from Rotamers import getRotamers self.bbdep, self.rotamers = getRotamers(residue, log=True, lib=lib, resType=resType) from Midas import linewidth, color linewidth(2, self.rotamers) color('byhet', self.rotamers) chimera.openModels.add(self.rotamers, sameAs=residue.molecule, hidden=True, noprefs=True) registerAttribute(chimera.Molecule, "chis") registerAttribute(chimera.Molecule, "rotamerProb") self.tableData = None self.resHandlerID = chimera.triggers.addHandler( 'Residue', self._resChangeCB, None) self.dependentDialogInfo = [('Clashes', 'clash'), ('H-Bonds', 'hbond'), ('Density', 'volume')] for label, ddn in self.dependentDialogInfo: setattr(self, ddn + "Dialog", None) ModelessDialog.__init__(self) def fillInUI(self, parent): import Tkinter, Pmw, Tix top = parent.winfo_toplevel() menubar = Tkinter.Menu(top, type="menubar", tearoff=False) top.config(menu=menubar) selectMenu = Tkinter.Menu(menubar) menubar.add_cascade(label="Select", menu=selectMenu) selectMenu.add_command(label="Current Rotamers", command=self._selCurCB) selectMenu.add_command(label="All Rotamers", command=self._selAllCB) self.columnMenu = Tkinter.Menu(menubar) menubar.add_cascade(label="Columns", menu=self.columnMenu) self.addColumnMenu = Tkinter.Menu(self.columnMenu) self.columnMenu.add_cascade(label="Add", menu=self.addColumnMenu) for label, ddn in self.dependentDialogInfo: self.addColumnMenu.add_command( label=label + "...", command=lambda name=ddn + "Dialog": self._showDialog(name)) self.columnMenu.add_separator() from chimera.tkgui import aquaMenuBar aquaMenuBar(menubar, parent, row=0) self.numChis = len(self.rotamers[0].chis) row = 1 Tkinter.Label(parent, text="%s %s rotamers" % (self.lib.displayName, self.resType)).grid(row=row, column=0) row += 1 self.buttonWidgets['OK'].config(state="disabled") self.buttonWidgets['Apply'].config(state="disabled") from CGLtk.Table import SortableTable self.rotTable = SortableTable(parent, menuInfo=(self.columnMenu, prefs, {}, True)) if not self.tableData: for i in range(self.numChis): self.rotTable.addColumn("Chi %d" % (i + 1), "lambda r: r.chis[%d]" % i, format="%6.1f") self.rotTable.addColumn("Probability", "rotamerProb", format="%.6f", font='TkFixedFont') self.rotTable.setData(self.rotamers) self.rotTable.launch(browseCmd=self._selRotamerCB, restoreInfo=self.tableData) delattr(self, 'tableData') parent.rowconfigure(row, weight=1) for tableColumn in range(1): parent.columnconfigure(tableColumn, weight=1) self.rotTable.grid(row=row, column=0, sticky="nsew") row += 1 def addClashColumn(self, *args): from Rotamers import processClashes format = processClashes(self.residue, self.rotamers, *args) self.rotTable.addColumn("Clashes", "clashScore", format=format, display=True) registerAttribute(chimera.Molecule, "clashScore") def addHbondColumn(self, *args): from Rotamers import processHbonds processHbonds(self.residue, self.rotamers, *args) self.rotTable.addColumn("H-Bonds", "numHbonds", format="%d", display=True) registerAttribute(chimera.Molecule, "numHbonds") def addVolumeColumn(self, columnName, *args): from Rotamers import processVolume format = processVolume(self.rotamers, columnName, *args) self.rotTable.addColumn(columnName, "lambda r: r.volumeScores[%s]" % repr(columnName), format=format, display=True) registerAttribute(chimera.Molecule, "volumeScores") def Apply(self): from Rotamers import useRotamer useRotamer(self.residue, self.rotamerSel, log=True) def destroy(self): chimera.triggers.deleteHandler('Residue', self.resHandlerID) chimera.triggers.deleteHandler(SAVE_SESSION, self._sesHandlerID) chimera.openModels.close(self.rotamers) for label, ddn in self.dependentDialogInfo: dd = getattr(self, ddn + 'Dialog', None) if dd: dd.destroy() ModelessDialog.destroy(self) def selectRotamers(self, rotamers): from chimera import selectionOperation from chimera.selection import ItemizedSelection rotSel = ItemizedSelection() rotSel.add(rotamers) selectionOperation(rotSel) def _resChangeCB(self, trigName, myData, trigData): if self.residue in trigData.deleted: self.destroy() def _selAllCB(self): self.selectRotamers(self.rotamers) def _selCurCB(self): self.selectRotamers([r for r in self.rotamers if r.display]) def _selRotamerCB(self, tableSel): tableSet = set(tableSel) if tableSet == set([r for r in self.rotamers if r.display]): return self.rotamerSel = tableSel for rot in self.rotamers: rot.display = rot in tableSet if len(tableSel) > 0: state = 'normal' else: state = 'disabled' self.buttonWidgets['OK'].config(state=state) self.buttonWidgets['Apply'].config(state=state) def _sessionCB(self, trigName, myData, sesFile): from SimpleSession import sessionID data = (1, sessionID(self.residue), self.resType, [sessionID(rot) for rot in self.rotamers], self.bbdep, self.lib.displayName, self.rotTable.getRestoreInfo()) print >> sesFile, """ try: from Rotamers.gui import sessionRestore sessionRestore(%s) except: reportRestoreError('Error restoring Rotamers') """ % repr(data) def _showDialog(self, dialogName): d = getattr(self, dialogName, None) if d: d.enter() else: importName = dialogName[0].upper() + dialogName[1:] exec("from %s import %s" % (importName, importName)) exec("self.%s = %s(self)" % (dialogName, importName))
def fillInUI(self, parent): from chimera.match import matchPositions from chimera import numpyArrayFromAtoms from chimera import selection, UserError self._computing = False # load needed coord sets... frameNums = range(self.startFrame, self.endFrame+1, self.stride) for frameNum in frameNums: if not self.movie.findCoordSet(frameNum): self.status("loading frame %d" % frameNum) self.movie._LoadFrame(frameNum, makeCurrent=False) # compute RMSDs from analysis import analysisAtoms try: atoms = analysisAtoms(self.movie, self.useSel, self.ignoreBulk, self.ignoreHyds) except UserError: self.Close() raise self.buttonWidgets['Close'].configure(text="Abort") numFrames = len(frameNums) self.status("Fetching %d coordinate arrays" % numFrames) numpyArrays = {} from time import time t0 = time() for i, fn in enumerate(frameNums): numpyArrays[fn] = numpyArrayFromAtoms(atoms, self.movie.findCoordSet(fn)) self._computing = True self._abort = False parent.update() # allow Abort button to function self._computing = False if self._abort: parent.after_idle(self.Close) return if i == numFrames - 1: self.status("Fetched %d coordinate arrays" % (i+1)) else: elapsed = time() - t0 perSec = elapsed / (i+1) remaining = perSec * (numFrames - (i+1)) self.status("Fetched %d of %d coordinate arrays" "\nAbout %.1f minutes remaining" % (i+1, numFrames, remaining / 60.0)) t0 = time() totalRMSDs = numFrames * (numFrames - 1) / 2 self.status("Computing %d RMSDs" % totalRMSDs) from EnsembleMatch.distmat import DistanceMatrix fullDM = DistanceMatrix(numFrames) sameAs = {} for i, frame1 in enumerate(frameNums): na1 = numpyArrays[frame1] for j, frame2 in enumerate(frameNums[i+1:]): na2 = numpyArrays[frame2] rmsd = matchPositions(na1, na2)[1] fullDM.set(i, i+j+1, rmsd) if rmsd == 0.0: sameAs[frame2] = frame1 self._computing = True self._abort = False parent.update() # allow Abort button to function self._computing = False if self._abort: parent.after_idle(self.Close) return numComputed = totalRMSDs - ((numFrames - (i+1)) * (numFrames - (i+2))) / 2 if numComputed == totalRMSDs: self.status("Computed %d RMSDs" % totalRMSDs) else: elapsed = time() - t0 perSec = elapsed / numComputed remaining = perSec * (totalRMSDs - numComputed) if remaining < 50: timeEst = "%d seconds" % int( remaining + 0.5) else: timeEst = "%.1f minutes" % ( remaining / 60.0) self.status("Computed %d of %d RMSDs\n" "About %s remaining" % (numComputed, totalRMSDs, timeEst)) self.status("Generating clusters") self.buttonWidgets['Close'].configure(text="Close") if not sameAs: dm = fullDM reducedFrameNums = frameNums indexMap = range(len(frameNums)) elif len(sameAs) == numFrames - 1: raise UserError("All frames to cluster are identical!") self.Close() else: dm = DistanceMatrix(numFrames - len(sameAs)) reducedFrameNums = [] indexMap = [] for i, fn in enumerate(frameNums): if fn in sameAs: continue reducedFrameNums.append(fn) indexMap.append(i) for i in range(len(reducedFrameNums)): mapi = indexMap[i] for j in range(i+1, len(reducedFrameNums)): mapj = indexMap[j] dm.set(i, j, fulldm.get(mapi, mapj)) from EnsembleMatch.nmrclust import NMRClust clustering = NMRClust(dm) self.clusterMap = {} self.representatives = [] self.clusters = [] unsortedClusters = [(c, clustering.representative(c)) for c in clustering.clusters] # sort the clusters so that the coloring is reproducible # while trying to avoid using adjacent colors for # adjacent clusters clusters = [unsortedClusters.pop()] while unsortedClusters: bestCluster = bestVal = None for uc in unsortedClusters: val = abs(uc[-1] - clusters[-1][-1]) if len(clusters) > 1: val += 0.5 * abs(uc[-1] - clusters[-2][-1]) if len(clusters) > 2: val += 0.25 * abs(uc[-1] - clusters[-3][-1]) if bestVal == None or val > bestVal: bestCluster = uc bestVal = val unsortedClusters.remove(bestCluster) clusters.append(bestCluster) colors = colorRange(len(clusters)) for c, rep in clusters: cluster = Cluster() cluster.color = colors.pop() self.clusters.append(cluster) cluster.representative = reducedFrameNums[rep] cluster.members = [] for m in c.members(): f = reducedFrameNums[m] self.clusterMap[f] = cluster cluster.members.append(f) for dup, base in sameAs.items(): c = self.clusterMap[base] self.clusterMap[dup] = c c.members.append(dup) self.status("%d clusters" % len(self.clusters)) from CGLtk.Table import SortableTable self.table = SortableTable(parent) self.table.addColumn("Color", "color", format=(False, False), titleDisplay=False) membersCol = self.table.addColumn("Members", "lambda c: len(c.members)", format="%d") self.table.addColumn("Representative Frame", "representative", format="%d") self.table.setData(self.clusters) self.table.launch(browseCmd=self.showRep) self.table.sortBy(membersCol) self.table.sortBy(membersCol) # to get descending order self.table.grid(sticky="nsew") self.timeLine = Pmw.ScrolledCanvas(parent, canvas_height="0.5i") self.timeLine.grid(row=1, column=0, sticky="nsew") parent.rowconfigure(0, weight=1) parent.columnconfigure(0, weight=1) self.zoom = 2.0 self.drawTimeLine()
def fillInUI(self, parent): self.sel_handler = None self.last_selected = set() import Tkinter from CGLtk import Hybrid top = parent.winfo_toplevel() menubar = Tkinter.Menu(top, type="menubar", tearoff=False) top.config(menu=menubar) file_menu_entries = (('Export...', self.export_cb), ) fmenu = Hybrid.cascade_menu(menubar, 'File', file_menu_entries) self.columnMenu = Tkinter.Menu(menubar) menubar.add_cascade(label="Columns", menu=self.columnMenu) from chimera.tkgui import aquaMenuBar aquaMenuBar(menubar, parent, row=0) row = 1 columns = ( ('region', lambda r: r.rid), ('grid points', lambda r: r.point_count()), ('grouped', lambda r: len(r.children())), ('has surface', lambda r: 1 if r.has_surface() else 0), ('contacts', lambda r: len(r.contacting_regions())), ('edge distance', lambda r: r.edge_distance()), ('bounds', lambda r: tuple(r.bounds()[0]) + tuple(r.bounds()[1]), '%d,%d,%d,%d,%d,%d', False), ) self.computed_attributes = dict([c[:2] for c in columns]) self.keys = set([c[0] for c in columns]) defaults = dict([(c[0], True) for c in columns]) prefs = {} mi = (self.columnMenu, prefs, defaults, True) from CGLtk.Table import SortableTable self.attribute_table = t = SortableTable(parent, menuInfo=mi) for cspec in columns: name, func = cspec[:2] format = cspec[2] if len(cspec) >= 3 else format_item display = cspec[3] if len(cspec) >= 4 else True t.addColumn(name, func, format=format, display=display, font='TkFixedFont', refresh=False) self.add_columns() # Additional region attributes. t.setData([]) t.launch(browseCmd=self.table_line_selected) t.grid(row=row, column=0, sticky="nsew") parent.rowconfigure(row, weight=1) parent.columnconfigure(0, weight=1) row += 1 naf = Tkinter.Frame(parent) naf.grid(row=row, column=0, sticky='new') naf.columnconfigure(1, weight=1) row += 1 ke = Hybrid.Entry(naf, 'Set attribute ', 15) ke.frame.grid(row=0, column=0, sticky='w') self.key_name = ke.variable ke.entry.bind('<KeyPress-Return>', self.new_value_cb) ve = Hybrid.Entry(naf, ' to value ', 15) ve.frame.grid(row=0, column=1, sticky='ew') self.value = ve.variable ve.entry.bind('<KeyPress-Return>', self.new_value_cb) ol = Tkinter.Label(naf, text=' or ') ol.grid(row=0, column=2, sticky='e') sb = Tkinter.Button(naf, text='snapshot', command=self.snapshot_cb) sb.grid(row=0, column=3, sticky='e') fl = Hybrid.Checkbutton_Entries(parent, False, 'Filter list ', (50, 'grid_points > 1000')) fl.frame.grid(row=row, column=0, sticky='new') row += 1 self.use_filter, self.filter_text = fl.variables self.use_filter.add_callback(self.filter_cb) e = fl.entries[0] fl.frame.columnconfigure(1, weight=1) e.grid(sticky='ew') e.bind('<KeyPress-Return>', self.filter_cb) op = Hybrid.Popup_Panel(parent, resize_dialog=False) opf = op.frame opf.grid(row=row, column=0, sticky='news') opf.grid_remove() opf.columnconfigure(0, weight=1) self.optionsPanel = op.panel_shown_variable row += 1 orow = 0 cb = op.make_close_button(opf) cb.grid(row=orow, column=0, sticky='e') l = Tkinter.Label(opf, text='Options', font='TkCaptionFont') l.grid(column=0, row=orow, sticky='w', pady=5) orow += 1 ss = Hybrid.Checkbutton(opf, 'Show selected region surface', False) ss.button.grid(row=orow, column=0, sticky='w') orow += 1 self.show_selected = ss.variable msg = Tkinter.Label(parent, width=60, anchor='w', justify='left') msg.grid(column=0, row=row, sticky='ew') self.msg = msg self.Update()
class IlabelDialog(ModelessDialog): name = "2D Labels/Color Key" provideStatus = True buttons = ("Delete", "Close") LABELS = "Labels" COLOR_KEY = "Color Key" MOUSE_LABEL_TEXT = "Use mouse for label placement" MOUSE_KEY_TEXT = "Use mouse for key placement" EmphasisColor = "forest green" def __init__(self): import os.path myDir, junk = os.path.split(__file__) addFunction('place text', (self._pickLabel, self._moveLabel, None), icon=chimage.get( Image.open(os.path.join(myDir, 'ilabel.png')), tkgui.app)) addFunction('place key', (self._startOrGrabKey, self._sizeOrMoveKey, None), icon=chimage.get( Image.open(os.path.join(myDir, 'key.png')), tkgui.app)) import Ilabel if not Ilabel._ilabelModel: Ilabel.IlabelModel() self.model = Ilabel._ilabelModel ModelessDialog.__init__(self) self._sessionHandlerID = chimera.triggers.addHandler( SAVE_SESSION, self._saveSession, None) self._closeHandlerID = chimera.triggers.addHandler( CLOSE_SESSION, self.destroy, None) self._beginRestoreHandlerID = chimera.triggers.addHandler( BEGIN_RESTORE_SESSION, self.destroy, None) def fillInUI(self, parent): top = parent.winfo_toplevel() menubar = Tkinter.Menu(top, type="menubar", tearoff=False) top.config(menu=menubar) self.fileMenu = Tkinter.Menu(menubar) menubar.add_cascade(label="File", menu=self.fileMenu) self.fileMenu.add_command(label="Write...", command=self._writeFileCB) self.fileMenu.add_command(label="Read...", command=self._readFileCB) from chimera.tkgui import aquaMenuBar aquaMenuBar(menubar, parent, row=0) from ColorKey import KeyModel parent.rowconfigure(1, weight=1) parent.columnconfigure(0, weight=1) # _pageRaisedCB uses mouseModeVar, so define first self.mouseLabelingVar = Tkinter.IntVar(parent) self.mouseLabelingVar.set(True) self.mlLabelVar = Tkinter.StringVar(parent) self.mouseModeButton = Tkinter.Checkbutton( parent, command=self._mouseFuncCB, variable=self.mouseLabelingVar, textvariable=self.mlLabelVar) self.mouseModeButton.grid(row=2, column=0) self.notebook = Pmw.NoteBook(parent, raisecommand=self._pageRaisedCB) self.notebook.add(self.LABELS, tab_text=self.LABELS) self.notebook.add(self.COLOR_KEY, tab_text=self.COLOR_KEY) self.notebook.grid(row=1, column=0, sticky="nsew") self._fillLabelsPage(self.notebook.page(self.LABELS)) self.keyModel = KeyModel(self) self.keyPosition = None self._fillColorKeyPage(self.notebook.page(self.COLOR_KEY)) def _fillLabelsPage(self, page): page.columnconfigure(0, weight=1) page.columnconfigure(1, weight=1) page.columnconfigure(2, weight=1) row = 0 from CGLtk.Table import SortableTable self.labelTable = SortableTable(page, automultilineHeaders=False) self.labelTable.addColumn("Label [(x, y) text]", self._labelListing, anchor='w') self.labelTable.addColumn("Shown", "shown", format=bool) self.labelTable.setData(self.model.labels) self.labelTable.launch(browseCmd=self._tableCB, selectMode="single") self.labelTable.grid(row=row, column=0, columnspan=3, sticky="nsew") page.rowconfigure(row, weight=1) row += 1 self.labelText = Pmw.ScrolledText(page, labelpos='w', label_text="Text", text_height=3, text_width=20, text_wrap='none', text_state='disabled', text_exportselection=False) text = self.labelText.component('text') text.bind("<<Modified>>", self._textCB) text.bind("<<Selection>>", self._updateTextAttrWidgets) self.labelText.grid(row=row, column=0, sticky='nsew', columnspan=3) page.rowconfigure(row, weight=1) row += 1 self.labelSymbolMenu = Pmw.OptionMenu( page, labelpos='w', label_text="Insert symbol:", command=self._insertSymbol, items=[ u'\N{GREEK SMALL LETTER ALPHA}', u'\N{GREEK SMALL LETTER BETA}', u'\N{GREEK SMALL LETTER GAMMA}', u'\N{GREEK SMALL LETTER DELTA}', u'\N{GREEK SMALL LETTER EPSILON}', u'\N{GREEK SMALL LETTER PI}', u'\N{GREEK SMALL LETTER PHI}', u'\N{GREEK SMALL LETTER CHI}', u'\N{GREEK SMALL LETTER PSI}', u'\N{GREEK SMALL LETTER OMEGA}', u'\N{LEFTWARDS ARROW}', u'\N{RIGHTWARDS ARROW}', u'\N{LEFT RIGHT ARROW}', u'\N{UPWARDS ARROW}', u'\N{DOWNWARDS ARROW}', u'\N{SUPERSCRIPT TWO}', u'\N{SUPERSCRIPT THREE}', u'\N{DEGREE SIGN}', u'\N{LATIN CAPITAL LETTER A WITH RING ABOVE}', "more..." ]) self.labelSymbolMenu.grid(row=row, column=0, columnspan=3) row += 1 colorHouse = Pmw.LabeledWidget(page, labelpos='w', label_text="Color") colorHouse.grid(row=row, column=0, rowspan=3) from CGLtk.color.ColorWell import ColorWell self.colorWell = ColorWell(colorHouse.interior(), color=self._contrastWithBG(), callback=self._colorCB) self.colorWell.grid() from chimera.tkoptions import IntOption self.labelFontSize = IntOption(page, row, "Font size", 24, self._labelChangeCB, startCol=1, min=1, attribute="size", width=3) row += 1 self.labelFontStyle = FontStyle(page, row, "Font style", oglFont.normal, self._labelChangeCB, startCol=1) row += 1 self.labelFontTypeface = FontTypeface(page, row, "Font typeface", FONT_TYPEFACE_VALUES[0], self._labelChangeCB, startCol=1) row += 1 if self.model.curLabel: self.changeToLabel(self.model.curLabel, force=True) def _fillColorKeyPage(self, page): from chimera.tkoptions import IntOption, EnumOption, \ BooleanOption, RGBAOption f = Tkinter.Frame(page) f.grid(row=0, columnspan=2) self.numComponents = IntOption(f, 0, "Number of colors/labels", 3, self._componentsCB, min=2, width=2) self.componentsFrame = Tkinter.Frame(page) self.componentsFrame.grid(row=1, column=0, sticky="nsew", columnspan=2) page.columnconfigure(0, weight=1) self.componentsFrame.columnconfigure(1, weight=1) class ColorTreatment(EnumOption): values = ("distinct", "blended") self.colorTreatment = ColorTreatment( page, 2, "Color range depiction", "blended", self._keyChangeCB, balloon="Should colors be shown as distinct rectangles" " or as a continuous range") class LabelPosition(EnumOption): values = ("left/top", "right/bottom") self.labelPos = LabelPosition( page, 3, "Label positions", "right/bottom", self._keyChangeCB, balloon="Position of" " labels relative to color key.\nLabels always" " positioned adjacent to long side.") self.labelColor = RGBAOption( page, 4, "Label color", self._contrastWithBG(), self._keyChangeCB, balloob="Label color. If set to 'No color', use corresponding" " key color", noneOkay=True) class LabelJustification(EnumOption): values = ("left", "decimal point", "right") self.justification = LabelJustification( page, 5, "Label justification", "decimal point", self._keyChangeCB, balloon="Justification of label text" " in a vertical key layout.\nHorizontal key labels will" " always be center justified.") self.labelOffset = IntOption(page, 6, "Label offset", 0, self._keyChangeCB, width=3, balloon="Additional offset" " of labels from color bar, in pixels") self.keyFontSize = IntOption(page, 7, "Font size", 24, self._keyChangeCB, width=3) self.keyFontStyle = FontStyle(page, 8, "Font style", oglFont.normal, self._keyChangeCB) self.keyFontTypeface = FontTypeface(page, 9, "Font typeface", FONT_TYPEFACE_VALUES[0], self._keyChangeCB) self.borderColor = RGBAOption( page, 10, "Border color", None, self._keyChangeCB, balloon="Color of border" " around color key (not each individual color).\n" "If 'no color', then no border is drawn.") self.borderWidth = IntOption(page, 11, "Border width", 3, self._keyChangeCB, balloon="in pixels") self.tickMarks = BooleanOption(page, 12, "Show tick marks", False, self._keyChangeCB, balloon="Show tick marks" " pointing from key to labels") self._componentsCB(self.numComponents) def destroy(self, *args): self.mouseLabelingVar.set(True) self.mouseModeButton.invoke() chimera.triggers.deleteHandler(SAVE_SESSION, self._sessionHandlerID) chimera.triggers.deleteHandler(CLOSE_SESSION, self._closeHandlerID) chimera.triggers.deleteHandler(BEGIN_RESTORE_SESSION, self._beginRestoreHandlerID) chimera.openModels.close([self.model, self.keyModel]) ModelessDialog.destroy(self) def map(self, e=None): self._pageRaisedCB(self.notebook.getcurselection()) def unmap(self, e=None): self.mouseLabelingVar.set(True) self.mouseModeButton.invoke() def changeToLabel(self, nextLabel, force=False): if nextLabel == self.model.curLabel and not force: return if self.model.curLabel and not unicode(self.model.curLabel) \ and self.model.curLabel != nextLabel: # remove previous label if empty self.removeLabel(self.model.curLabel) self.model.changeToLabel(nextLabel) self.labelText.component('text').configure(state='normal') self.labelTable.select(nextLabel) self.labelText.settext(unicode(nextLabel)) text = self.labelText.component('text') lineIndex = 1 for line in self.model.curLabel.lines: charIndex = 0 for c in line: text.tag_add(id(c), "%d.%d" % (lineIndex, charIndex)) charIndex += 1 text.tag_add(id(line), "%d.%d" % (lineIndex, charIndex)) lineIndex += 1 def Delete(self): if self.notebook.getcurselection() == self.LABELS: self.labelText.clear() if not self.model.curLabel: self.status("No label to delete", color="red", blankAfter=10) return self.removeLabel(self.model.curLabel) self.labelText.component('text').configure(state='disabled') else: self.keyPosition = None self._keyChangeCB() def Help(self): helpLoc = "ContributedSoftware/2dlabels/2dlabels.html" if self.notebook.getcurselection() == self.COLOR_KEY: helpLoc += "#colorkey" chimera.help.display(helpLoc) def keyConfigure(self, data, pageChange=True): self._componentsCB(len(data), update=False) for datum, well, label in zip(data, self.wells, self.labels): color, text = datum well.showColor(color, doCallback=False) label.variable.set(text, invoke_callbacks=False) self._keyChangeCB() if pageChange: self.notebook.selectpage(self.COLOR_KEY) def makeChar(self, char, model): attrs = {} try: attrs['rgba'] = self.colorWell.rgba except AttributeError: # multi or None if model: attrs['rgba'] = model.rgba size = self.labelFontSize.get() if size is not None: attrs['size'] = size style = self.labelFontStyle.get() if style is not None: attrs['style'] = style fontName = self.labelFontTypeface.get() if fontName is not None: attrs['fontName'] = fontName return Character(char, **attrs) def newLabel(self, pos): label = self.model.newLabel(pos) self.labelTable.setData(self.model.labels) self.status("Mouse drag to reposition label", color=self.EmphasisColor) return label def removeLabel(self, label): self.model.removeLabel(label) self.labelTable.setData(self.model.labels) if self.model.curLabel is not None: self.labelTable.select(self.model.curLabel) def reverseKey(self): data = zip([w.rgba for w in self.wells], [l.variable.get() for l in self.labels]) data.reverse() self.keyConfigure(data) def setLabelFromText(self): curLabel = self.model.curLabel text = self.labelText.component('text') # delete parts of label not in text... # # newlines first... while len(curLabel.lines) > 1: for i, line in enumerate(curLabel.lines[:-1]): if not text.tag_ranges(id(line)): curLabel.lines[i + 1][:0] = line del curLabel.lines[i] break else: break # characters... for line in curLabel.lines: for c in line[:]: if not text.tag_ranges(id(c)): line.remove(c) # get new parts of text into label model = None targets = [] lines = curLabel.lines for line in lines: targets.extend([id(c) for c in line]) if not model and line: model = line[0] if line is not lines[-1]: targets.append(id(line)) contents = self.labelText.get()[:-1] # drop trailing newline if targets: target = targets.pop(0) else: target = None textLine = 1 textIndex = -1 curLine = lines[0] for c in contents: textIndex += 1 if str(target) in text.tag_names("%d.%d" % (textLine, textIndex)): if targets: target = targets.pop(0) else: target = None if c == '\n': textLine += 1 textIndex = -1 curLine = lines[[id(l) for l in lines].index(id(curLine)) + 1] elif curLine: model = curLine[textIndex] elif c == '\n': insertLine = curLine[0:textIndex] lines.insert(textLine - 1, insertLine) del curLine[0:textIndex] text.tag_add(id(insertLine), "%d.%d" % (textLine, textIndex)) textLine += 1 textIndex = -1 else: labelChar = self.makeChar(c, model) curLine.insert(textIndex, labelChar) text.tag_add(id(labelChar), "%d.%d" % (textLine, textIndex)) self.model.setMajorChange() def updateGUI(self, source="gui"): curLabel = self.model.curLabel if curLabel and source == "gui": self.setLabelFromText() self.labelTable.setData(self.model.labels) if curLabel: self.labelTable.select(curLabel) self._updateTextAttrWidgets() def _colorCB(self, color): curLabel = self.model.curLabel if not curLabel: self.status("No label to color", color='red') return self.model.setMajorChange() for c in self._selChars(): c.rgba = color def _componentsCB(self, opt, update=True): cf = self.componentsFrame if hasattr(self, 'wells'): for well in self.wells: well.grid_forget() well.destroy() for label in self.labels: label.frame.grid_forget() self.reverseButton.grid_forget() self.reverseButton.destroy() else: Tkinter.Label(cf, text="Colors").grid(row=0) Tkinter.Label(cf, text="Labels").grid(row=0, column=1) if isinstance(opt, int): numComponents = opt self.numComponents.set(opt) else: numComponents = opt.get() wellSize = min(38, int((7 * 38) / numComponents)) from CGLtk.color.ColorWell import ColorWell self.wells = [] self.labels = [] from CGLtk import Hybrid for i in range(numComponents): well = ColorWell(cf, width=wellSize, height=wellSize, callback=self._keyChangeCB, color='white') well.grid(row=i + 1) self.wells.append(well) label = Hybrid.Entry(cf, "", 10) label.variable.add_callback(self._keyTypingCB) label.frame.grid(row=i + 1, column=1, sticky='ew') self.labels.append(label) self.reverseButton = Tkinter.Button(cf, command=self.reverseKey, text="Reverse ordering of above", pady=0) self.reverseButton.grid(row=numComponents + 1, column=0, columnspan=2) self.notebook.setnaturalsize() if update: self._keyChangeCB() def _contrastWithBG(self): bg = chimera.viewer.background if bg: bgColor = bg.rgba() else: bgColor = (0, 0, 0) if bgColor[0] * 2 + bgColor[1] * 3 + bgColor[2] < 0.417: return (1, 1, 1) else: return (0, 0, 0) def _eventToPos(self, viewer, event, offset=(0, 0)): w, h = viewer.windowSize return (event.x - offset[0]) / float(w), \ (h - event.y - offset[1]) / float(h) def _handleTextChange(self): self.updateGUI() self.labelText.edit_modified(False) def _insertSymbol(self, item): if len(item) > 1: from chimera import help help.display("ContributedSoftware/2dlabels/symbols.html") return if not self.model.labels: self.status("No labels have been created yet", color="red") return if not self.labelTable.selected(): self.status("No labels active", color="red") self.labelText.insert("insert", item) self.setLabelFromText() def _keyChangeCB(self, *args): self.keyModel.setMajorChange() def _keyTypingCB(self, fromAfter=False): # wait for a pause in typing before updating key... if fromAfter: self._typingHandler = None self._keyChangeCB() return handle = getattr(self, '_typingHandler', None) if handle: self.componentsFrame.after_cancel(handle) self._typingHandler = self.componentsFrame.after( 500, lambda: self._keyTypingCB(fromAfter=True)) def _labelChangeCB(self, option): curLabel = self.model.curLabel self.model.setMajorChange() val = option.get() attrName = option.attribute for c in self._selChars(): setattr(c, attrName, val) def _labelListing(self, label): text = unicode(label) if '\n' in text: newline = text.index('\n') text = text[:newline] + "..." if not text: text = "<empty>" return "(%.2f, %.2f) %s" % (label.pos[0], label.pos[1], text) def _mouseFuncCB(self): self.status("") if not self.mouseLabelingVar.get(): if hasattr(self, "_prevMouse"): setButtonFunction("1", (), self._prevMouse) delattr(self, "_prevMouse") elif self.mlLabelVar.get() == self.MOUSE_LABEL_TEXT: if not hasattr(self, "_prevMouse"): self._prevMouse = getFuncName("1", ()) setButtonFunction("1", (), "place text") else: if not hasattr(self, "_prevMouse"): self._prevMouse = getFuncName("1", ()) setButtonFunction("1", (), "place key") def _moveLabel(self, viewer, event): pos = self._eventToPos(viewer, event, self._moveOffset) self.model.moveLabel(pos) curLabel = self.model.curLabel self.labelTable.setData(self.model.labels) self.labelTable.select(curLabel) def _pageRaisedCB(self, pageName): if pageName == "Labels": pageItem = self.MOUSE_LABEL_TEXT if not self.model.labels: self.status( "Click mouse button 1 in graphics\n" "window to place first label", color=self.EmphasisColor) for index in range(0, self.fileMenu.index('end') + 1): self.fileMenu.entryconfigure(index, state='normal') else: pageItem = self.MOUSE_KEY_TEXT self.status("Drag mouse to position/size key", color=self.EmphasisColor) for index in range(0, self.fileMenu.index('end') + 1): self.fileMenu.entryconfigure(index, state='disabled') self.mlLabelVar.set(pageItem) # just setting the var doesn't cause the callback, and # yet using invoke() toggles the var, so set it _opposite_ # to what's desired before calling invoke() self.mouseLabelingVar.set(False) self.mouseModeButton.invoke() def _pickLabel(self, viewer, event): w, h = viewer.windowSize pos = self._eventToPos(viewer, event) label, self._moveOffset = self.model.pickLabel(pos, w, h) if label is None: label = self.newLabel(pos) self._moveOffset = (0, 0) self.changeToLabel(label) self.labelText.component('text').focus_set() def _readFile(self, okayed, dialog): if not okayed: return from Ilabel import readFiles readFiles(dialog.getPaths(), clear=dialog.deleteExistingVar.get()) def _readFileCB(self): if not hasattr(self, "_readFileDialog"): self._readFileDialog = ReadFileDialog(command=self._readFile, clientPos='s') self._readFileDialog.enter() def _restoreSession(self, info): if info["sel ranges"]: self.labelText.tag_add("sel", *info["sel ranges"]) self._updateTextAttrWidgets() self._suppressMap = True self.labelText.edit_modified(False) if "key position" not in info: self.notebook.selectpage(self.LABELS) if info["mouse func"] == "normal": self.mouseLabelingVar.set(True) self.mouseModeButton.invoke() return self.keyPosition = info["key position"] self.colorTreatment.set(info["color depiction"]) self.labelPos.set(info["label positions"]) self.labelColor.set(info["label color"]) self.justification.set(info["label justification"]) self.labelOffset.set(info["label offset"]) self.keyFontSize.set(info["font size"]) self.keyFontStyle.set(info["font typeface"]) if "font name" in info: self.keyFontTypeface.set(info["font name"]) self.borderColor.set(info["border color"]) self.borderWidth.set(info["border width"]) self.tickMarks.set(info["show ticks"]) self.keyConfigure(info["colors/labels"]) if self.keyPosition: self.notebook.selectpage(self.COLOR_KEY) else: self.notebook.selectpage(self.LABELS) if info["mouse func"] == "normal": self.mouseLabelingVar.set(True) self.mouseModeButton.invoke() return info def _saveSession(self, triggerName, myData, sessionFile): print >> sessionFile, """ def restore2DLabelDialog(info): from chimera.dialogs import find, display from Ilabel.gui import IlabelDialog dlg = find(IlabelDialog.name) if dlg is not None: dlg.destroy() dlg = display(IlabelDialog.name) dlg._restoreSession(info) import SimpleSession SimpleSession.registerAfterModelsCB(restore2DLabelDialog, %s) """ % repr(self._sessionInfo()) def _selChars(self): chars = [] curLabel = self.model.curLabel if curLabel: sel = self.labelText.tag_ranges("sel") if sel: sline, schar = [int(x) for x in str(sel[0]).split('.')] eline, echar = [int(x) for x in str(sel[1]).split('.')] sline -= 1 eline -= 1 for li, line in enumerate(curLabel.lines): if li < sline: continue if li > eline: break if sline == eline: chars.extend(line[schar:echar]) elif li == sline: chars.extend(line[schar:]) elif li == eline: chars.extend(line[:echar]) else: chars.extend(line) else: for l in curLabel.lines: chars.extend(l) return chars def _sessionInfo(self): info = {} info["sel ranges"] = tuple( [str(tr) for tr in self.labelText.tag_ranges("sel")]) if self.mouseLabelingVar.get(): info["mouse func"] = "labeling" else: info["mouse func"] = "normal" info["key position"] = self.keyPosition info["colors/labels"] = [(w.rgba, l.variable.get()) for w, l in zip(self.wells, self.labels)] info["color depiction"] = self.colorTreatment.get() info["label positions"] = self.labelPos.get() info["label color"] = self.labelColor.get() info["label justification"] = self.justification.get() info["label offset"] = self.labelOffset.get() info["font size"] = self.keyFontSize.get() info["font typeface"] = self.keyFontStyle.get() info["font name"] = self.keyFontTypeface.get() info["border color"] = self.borderColor.get() info["border width"] = self.borderWidth.get() info["show ticks"] = self.tickMarks.get() return info def _sizeOrMoveKey(self, viewer, event): pos = self._eventToPos(viewer, event) if self.grabPos: deltas = [pos[axis] - self.grabPos[axis] for axis in [0, 1]] for posIndex in [0, 1]: old = self.keyPosition[posIndex] self.keyPosition[posIndex] = (old[0] + deltas[0], old[1] + deltas[1]) self.grabPos = pos elif len(self.keyPosition) == 1: self.keyPosition.append(pos) else: self.keyPosition[1] = pos self._keyChangeCB() def _startOrGrabKey(self, viewer, event): pos = self._eventToPos(viewer, event) if self.keyPosition and len(self.keyPosition) == 2: # possible grab; # see if in middle third of long side... p1, p2 = self.keyPosition x1, y1 = p1 x2, y2 = p2 if abs(x2 - x1) < abs(y2 - y1): longAxis = 1 ymin = min(y2, y1) ymax = max(y2, y1) b1 = (2 * ymin + ymax) / 3.0 b2 = (2 * ymax + ymin) / 3.0 o1 = min(x1, x2) o2 = max(x1, x2) else: longAxis = 0 xmin = min(x2, x1) xmax = max(x2, x1) b1 = (2 * xmin + xmax) / 3.0 b2 = (2 * xmax + xmin) / 3.0 o1 = min(y1, y2) o2 = max(y1, y2) if b1 < pos[longAxis] < b2 \ and o1 < pos[1-longAxis] < o2: self.grabPos = pos return self.grabPos = None self.keyPosition = [pos] self._keyChangeCB() self.status("Grab middle of key to reposition", color=self.EmphasisColor) def _tableCB(self, sel): if not sel: return self.changeToLabel(sel) def _textCB(self, e): text = self.labelText.component('text') if not text.tk.call((text._w, 'edit', 'modified')): #if not text.edit_modified(): return # this callback can happen _before_ the change # actually occurs, so do the processing after idle text.after_idle(self._handleTextChange) def _updateTextAttrWidgets(self, e=None): rgba = None multiple = False for c in self._selChars(): if rgba is None: rgba = c.rgba elif rgba != c.rgba: multiple = True break if rgba is not None: self.colorWell.showColor(rgba, multiple=multiple, doCallback=False) self.labelFontSize.display(self._selChars()) self.labelFontStyle.display(self._selChars()) self.labelFontTypeface.display(self._selChars()) def _writeFile(self, okayed, dialog): if not okayed: return from Ilabel import writeFile writeFile(dialog.getPaths()[0]) def _writeFileCB(self): if not hasattr(self, "_writeFileDialog"): from OpenSave import SaveModeless self._writeFileDialog = SaveModeless(command=self._writeFile, title="Write 2D Labels Info") self._writeFileDialog.enter()
class ModBaseDialog(ModelessDialog): buttons = ( "Hide", "Quit" ) help = "UsersGuide/modbase.html" def __init__(self, name, molList, tableData=None): self.title = "ModBase: %s" % name self.molList = molList self.tableData = tableData ModelessDialog.__init__(self) self.closeHandler = chimera.openModels.addRemoveHandler( self._modelClosedCB, None) self.selHandler = chimera.triggers.addHandler( "selection changed", self._selectionChangedCB, None) self.sesHandler = chimera.triggers.addHandler( SAVE_SESSION, self._sessionCB, None) chimera.extension.manager.registerInstance(self) def fillInUI(self, parent): import Tkinter top = parent.winfo_toplevel() menubar = Tkinter.Menu(top, type="menubar", tearoff=False) top.config(menu=menubar) self.columnMenu = Tkinter.Menu(menubar) menubar.add_cascade(label="Columns", menu=self.columnMenu) from chimera.tkgui import aquaMenuBar aquaMenuBar(menubar, parent, pack = 'top') self._makeActionGroup(parent) from CGLtk.Table import SortableTable from prefs import colAttr, colOrder, prefs, defaults self.modBaseTable = SortableTable(parent, menuInfo=( self.columnMenu, prefs, defaults, False )) if not self.tableData: self._addColumn("Model", "lambda m: m.oslIdent()", format="%s", shown=True) for fieldName in colOrder: keyName, format = colAttr[fieldName] self._addColumn(fieldName, "lambda m: m.modbaseInfo['%s']" % keyName, format=format) self.modBaseTable.setData(self.molList) chimera.triggers.addHandler("post-frame", self._launchTable, None) self.modBaseTable.pack(expand=True, fill="both") def _makeActionGroup(self, parent): from prefs import prefs from chimera import chimage import Tkinter, Pmw d = prefs.get("treatment", {}) self.treatmentShow = d.get("show", 0) selAtoms = d.get("selectAtoms", 0) selModels = d.get("selectModels", 0) hideOthers = d.get("hideOthers", 1) self.rightArrow = chimage.get("rightarrow.png", parent) self.downArrow = chimage.get("downarrow.png", parent) if self.treatmentShow: relief = "groove" image = self.downArrow else: relief = "flat" image = self.rightArrow self.treatmentGroup = Pmw.Group(parent, collapsedsize=0, tagindent=0, ring_relief=relief, tag_pyclass=Tkinter.Button, tag_text=" Treatment of Chosen Models", tag_relief="flat", tag_compound="left", tag_image=image, tag_command=self._treatmentCB) if not self.treatmentShow: self.treatmentGroup.collapse() self.treatmentGroup.pack(side="top", fill="x", padx=3) interior = self.treatmentGroup.interior() self.treatmentSelAtom = Tkinter.IntVar(parent) self.treatmentSelAtom.set(selAtoms) b = Tkinter.Checkbutton(interior, text="Select atoms", onvalue=1, offvalue=0, variable=self.treatmentSelAtom, command=self._treatmentChangedCB) b.pack(side="left") self.treatmentSelModel = Tkinter.IntVar(parent) self.treatmentSelModel.set(selModels) b = Tkinter.Checkbutton(interior, text="Choose in Model Panel", onvalue=1, offvalue=0, variable=self.treatmentSelModel, command=self._treatmentChangedCB) b.pack(side="left") self.treatmentHideOthers = Tkinter.IntVar(parent) self.treatmentHideOthers.set(hideOthers) b = Tkinter.Checkbutton(interior, text="Hide others", onvalue=1, offvalue=0, variable=self.treatmentHideOthers, command=self._treatmentChangedCB) b.pack(side="left") def _treatmentCB(self): self.treatmentShow = not self.treatmentShow if self.treatmentShow: self.treatmentGroup.configure(ring_relief="groove", tag_image=self.downArrow) self.treatmentGroup.expand() else: self.treatmentGroup.configure(ring_relief="flat", tag_image=self.rightArrow) self.treatmentGroup.collapse() self._savePrefs() def _addColumn(self, title, attrFetch, format="%s", shown=None): if title in [c.title for c in self.modBaseTable.columns]: return if title in ["Model Score", "Sequence Identity"]: # try to align decimal points kw = {'font': 'TkFixedFont'} else: kw = {} c = self.modBaseTable.addColumn(title, attrFetch, format=format, display=shown, **kw) self.modBaseTable.columnUpdate(c) def _launchTable(self, trigger, closure, mols): self.modBaseTable.launch(browseCmd=self._selectModelCB, restoreInfo=self.tableData) return chimera.triggerSet.ONESHOT def _modelClosedCB(self, trigger, closure, mols): remainder = [ m for m in self.molList if m not in mols ] if len(remainder) == 0: self.molList = [] self.exit() elif len(remainder) != len(self.molList): self.molList = remainder self.modBaseTable.setData(self.molList) self.modBaseTable.refresh(rebuild=True) def _selectionChangedCB(self, trigger, closure, ignore): from chimera import selection mols = selection.currentMolecules() selected = [ m for m in mols if m in self.molList ] self.modBaseTable.highlight(selected) def _selectModelCB(self, tableSel): if self.treatmentSelAtom.get(): from chimera import selection selection.clearCurrent() selection.addCurrent(tableSel) selection.addImpliedCurrent() if self.treatmentSelModel.get(): from ModelPanel import ModelPanel from chimera import dialogs d = dialogs.display(ModelPanel.name) d.selectionChange(tableSel) shown = {} if self.treatmentHideOthers.get(): for m in self.molList: key = (m.id, m.subid) shown[key] = m in tableSel or not tableSel else: for m in tableSel: key = (m.id, m.subid) shown[key] = True for m in chimera.openModels.list(): key = (m.id, m.subid) try: m.display = shown[key] except KeyError: pass def _treatmentChangedCB(self): self._selectModelCB(self.modBaseTable.selected()) self._savePrefs() def _savePrefs(self): from prefs import prefs prefs["treatment"] = { "show": self.treatmentShow, "selectAtoms": self.treatmentSelAtom.get(), "selectModels": self.treatmentSelModel.get(), "hideOthers": self.treatmentHideOthers.get(), } prefs.save() def _sessionCB(self, trigger, myData, sesFile): from SimpleSession import sessionID data = (1, # version self.title, # title [ sessionID(m) for m in self.molList ], # molecules [ m.modbaseInfo for m in self.molList ],# stats self.modBaseTable.getRestoreInfo()) # GUI print >> sesFile, """ try: from ModBase.gui import sessionRestore sessionRestore(%s) except: reportRestoreError("Error restoring ModBase") """ % repr(data) def exit(self): if self.molList: chimera.openModels.close(self.molList) if self.closeHandler: chimera.openModels.deleteRemoveHandler( self.closeHandler) self.closeHandler = None if self.selHandler: chimera.triggers.deleteHandler("selection changed", self.selHandler) self.selHandler = None if self.sesHandler: chimera.triggers.deleteHandler(SAVE_SESSION, self.sesHandler) self.sesHandler = None chimera.extension.manager.deregisterInstance(self) self.destroy() def emName(self): return self.title def emRaise(self): self.enter() def emHide(self): self.Close() Hide = emHide def emQuit(self): self.exit() Quit = emQuit
def fillInUI(self, parent): top = parent.winfo_toplevel() row = 0 from CGLtk.Table import SortableTable rt = SortableTable(parent, automultilineHeaders = False) self.resultsTable = rt columns = {} fval = "lambda e: float(e['%s'])" sval = "lambda e: ' '.join(e['%s'].split())" lval = "lambda e: ', '.join(e['%s'])" val= "lambda e: e.get('%s','')" def cite(e): return ('%s\n%s\n%s %s (%s) %s-%s' % (' '.join(e['title'].split()), e.get('citationAuthorList',''), e.get('citationJournalName',''), e.get('citationJournalYear',''), e.get('citationJournalVolume',''), e.get('citationJournalFirstPage',''), e.get('citationJournalLastPage',''))) headings = (('ID', sval % 'accessionCode', 'nw', 0), ('Sample Name', sval % 'sampleName', 'nw', '6c'), ('Reference', cite, 'nw', '10c'), ('Resolution', fval % 'resolution', 'n', 0), ('Release Date', val % 'mapReleaseDate', 'n', 0), ('Fit PDBs', val % 'fittedPDBidList', 'nw', '6c'), ) for h, vfunc, a, w in headings: c = rt.addColumn(h, vfunc, format="%s", wrapLength = w, anchor = a, entryPadX = 10, entryPadY = 5, headerPadX = 10, headerPadY = 5, font = 'TkTextFont') columns[h] = c rt.sortBy(columns["ID"]) rt.setData(self.results) rt.grid(row=row, column=0, sticky="nsew") rt.launch() parent.rowconfigure(row, weight=1) parent.columnconfigure(0, weight=1) row += 1
class GeometryInterface: crossingPrompt = "Show angles/distances by choosing two objects" \ " from table" def __init__(self, parent, status): self.status = status self.parent = parent import Pmw, Tkinter row = 0 parent.columnconfigure(0, weight=1) parent.columnconfigure(1, weight=1) Tkinter.Button(parent, text="Define axes...", pady=0, command=self._createAxesCB).grid(row=row, column=0) Tkinter.Button(parent, text="Define plane...", pady=0, command=self._createPlaneCB).grid(row=row, column=1) row += 1 from CGLtk.Table import SortableTable self.table = SortableTable(parent) self.table.grid(row=row, column=0, columnspan=2, sticky="nsew") parent.rowconfigure(row, weight=1) self.table.addColumn("Name", "name", format="%s ") self.table.addColumn(" ", "model.color", format=(False, True)) idCol = self.table.addColumn("ID", "id") self.table.addColumn("Shown", "model.display", format=bool) self.table.addColumn("Length", "length", format="%4.1f", font="TkFixedFont") self.table.addColumn("Radius", "radius", format="%4.1f", font="TkFixedFont") self.table.setData(geomManager.items) self.table.sortBy(idCol) self.table.launch(browseCmd=self._tableCB) row += 1 selGroup = Pmw.Group(parent, tag_text="Choosing in table...") selGroup.grid(row=row, column=0, columnspan=3, sticky='ew') row += 1 cbFrame = selGroup.interior() cbFrame.columnconfigure(0, weight=1) cbFrame.columnconfigure(1, weight=1) self.geomSelObjVar = Tkinter.IntVar(parent) self.geomSelObjVar.set(prefs[AXIS_SEL_OBJ]) Tkinter.Checkbutton(cbFrame, variable=self.geomSelObjVar, text="selects object").grid(row=0, column=0) self.geomSelObjVar.trace_variable("w", self._selModeChangeCB) self.geomSelAtomsVar = Tkinter.IntVar(parent) self.geomSelAtomsVar.set(prefs[AXIS_SEL_ATOMS]) Tkinter.Checkbutton(cbFrame, variable=self.geomSelAtomsVar, text="selects atoms").grid(row=0, column=1) self.geomSelAtomsVar.trace_variable("w", self._selModeChangeCB) self.buttons = [] self.buttons.append(Tkinter.Button(parent, text="Delete", pady=0, state='disabled', command=self._deleteItemsCB)) self.buttons[-1].grid(row=row, column=0, sticky='e') self.buttons.append(Tkinter.Button(parent, text="Rename", pady=0, state='disabled', command=self._renameItems)) self.buttons[-1].grid(row=row, column=1, sticky='w') self.renameDialogs = {} row += 1 f = Tkinter.Frame(parent) f.grid(row=row, column=0, columnspan=2) row += 1 self.buttons.append(Tkinter.Button(f, text="Report distance", pady=0, state='disabled', command=self._atomsDistanceCB)) self.buttons[-1].grid(row=0, column=0) Tkinter.Label(f, text="to selected atoms" ).grid(row=0, column=1) geomManager.registerInterface(self) if geomManager.items: self._selHandlerID = triggers.addHandler("selection changed", self._selChangeCB, None) def addItems(self, items): if len(items) == len(geomManager.items): self._selHandlerID = triggers.addHandler("selection changed", self._selChangeCB, None) self.table.setData(geomManager.items) def feedback(self, sels): if not self.parent.winfo_viewable(): return if len(sels) == 2: words1 = sels[0].name.split() words2 = sels[1].name.split() if len(words2) < len(words1): words1, words2 = words2, words1 for i, word in enumerate(words1): if word != words2[i]: break diff = " ".join(words2[i:]) infos = geomManager.infoTexts(sels) message = "%s/%s %s\n" % (" ".join(words1), diff, ", ".join(infos)) self.status(message, log=True) else: self.status(self.crossingPrompt) def removeItems(self, items): self.table.setData(geomManager.items) for item in items: if item in self.renameDialogs: self.renameDialogs[item].destroy() del self.renameDialogs[item] if items and not geomManager.items: triggers.deleteHandler("selection changed", self._selHandlerID) def _atomsDistanceCB(self): atoms = selection.currentAtoms() if not atoms: self.status("No atoms selected", color="red") return items = self.table.selected() if not items: self.status("No objects chosen", color="red") return for item in items: points = [] dists = item.pointDistances([a.xformCoord() for a in atoms]) if len(dists) == 1: self.status("Distance from %s to %s: %.1f\n" % (atoms[0], item, dists[0]), log=True) continue import numpy dists = numpy.array(dists) imin = dists.argmin() imax = dists.argmax() self.status("Distance from %d atoms to %s: " "min: %.1f (%s), mean: %.1f, max: %.1f (%s)\n" % (len(atoms), item, dists[imin], atoms[imin], dists.mean(), dists[imax], atoms[imax]), log=True) def _createAxesCB(self): if not hasattr(self, 'createAxesDialog'): from Axes import CreateAxesDialog self.createAxesDialog = CreateAxesDialog() self.createAxesDialog.enter() def _createPlaneCB(self): if not hasattr(self, 'createPlaneDialog'): from Planes import CreatePlaneDialog self.createPlaneDialog = CreatePlaneDialog() self.createPlaneDialog.enter() def _deleteItemsCB(self): items = self.table.selected() if not items: self.status("No objects chosen", color="red") return geomManager.removeItems(items) def _itemsSelectAtoms(self, items, add=False): atoms = set() for item in items: atoms.update(item.atoms) if self.geomSelAtomsVar.get(): if add: selection.addCurrent(atoms) else: selection.setCurrent(atoms) elif set(atoms) == set(selection.currentAtoms()): selection.removeCurrent(atoms) def _lowerCmd(self): for rd in self.renameDialogs.values(): rd.destroy() self.renameDialogs.clear() self.status("") def _raiseCmd(self): self.status(self.crossingPrompt) def _renameItems(self): for item in self.table.selected(): if item in self.renameDialogs: self.renameDialogs[item].enter() else: self.renameDialogs[item] = RenameDialog(self, item) def _renameItemCB(self, item, name): item.name = name self.table.refresh() self.renameDialogs[item].destroy() del self.renameDialogs[item] def _selChangeCB(self, *args): selGraphs = set(selection.currentGraphs()) itemModels = set([item.model for item in geomManager.items]) selItems = selGraphs & itemModels chosenItems = set([item.model for item in self.table.selected()]) if chosenItems != selItems: items = [item for item in geomManager.items if item.model in selItems] self.table.select(items) if self.geomSelAtomsVar.get(): self._itemsSelectAtoms(items, add=True) self.feedback(items) def _selModeChangeCB(self, *args): self._tableCB(self.table.selected(), modeChange=True) def _tableCB(self, sels, modeChange=False): if self.geomSelAtomsVar.get() or modeChange: self._itemsSelectAtoms(sels) if self.geomSelObjVar.get(): items = set(geomManager.items) # avoid affecting the selection if models are # being destroyed... removable = [a.model for a in items.difference(sels) if not a.model.__destroyed__] if removable: selection.removeCurrent(removable) addable = [a.model for a in sels if not a.model.__destroyed__] if addable: selection.addCurrent(addable) if modeChange: return butState = 'normal' if not sels: butState = 'disabled' for but in self.buttons: but.configure(state=butState) self.feedback(sels)
def _fillLabelsPage(self, page): page.columnconfigure(0, weight=1) page.columnconfigure(1, weight=1) page.columnconfigure(2, weight=1) row = 0 from CGLtk.Table import SortableTable self.labelTable = SortableTable(page, automultilineHeaders=False) self.labelTable.addColumn("Label [(x, y) text]", self._labelListing, anchor='w') self.labelTable.addColumn("Shown", "shown", format=bool) self.labelTable.setData(self.model.labels) self.labelTable.launch(browseCmd=self._tableCB, selectMode="single") self.labelTable.grid(row=row, column=0, columnspan=3, sticky="nsew") page.rowconfigure(row, weight=1) row += 1 self.labelText = Pmw.ScrolledText(page, labelpos='w', label_text="Text", text_height=3, text_width=20, text_wrap='none', text_state='disabled', text_exportselection=False) text = self.labelText.component('text') text.bind("<<Modified>>", self._textCB) text.bind("<<Selection>>", self._updateTextAttrWidgets) self.labelText.grid(row=row, column=0, sticky='nsew', columnspan=3) page.rowconfigure(row, weight=1) row += 1 self.labelSymbolMenu = Pmw.OptionMenu(page, labelpos='w', label_text="Insert symbol:", command=self._insertSymbol, items=[ u'\N{GREEK SMALL LETTER ALPHA}', u'\N{GREEK SMALL LETTER BETA}', u'\N{GREEK SMALL LETTER GAMMA}', u'\N{GREEK SMALL LETTER DELTA}', u'\N{GREEK SMALL LETTER EPSILON}', u'\N{GREEK SMALL LETTER PI}', u'\N{GREEK SMALL LETTER PHI}', u'\N{GREEK SMALL LETTER CHI}', u'\N{GREEK SMALL LETTER PSI}', u'\N{GREEK SMALL LETTER OMEGA}', u'\N{LEFTWARDS ARROW}', u'\N{RIGHTWARDS ARROW}', u'\N{LEFT RIGHT ARROW}', u'\N{UPWARDS ARROW}', u'\N{DOWNWARDS ARROW}', u'\N{SUPERSCRIPT TWO}', u'\N{SUPERSCRIPT THREE}', u'\N{DEGREE SIGN}', u'\N{LATIN CAPITAL LETTER A WITH RING ABOVE}', "more..." ]) self.labelSymbolMenu.grid(row=row, column=0, columnspan=3) row += 1 colorHouse = Pmw.LabeledWidget(page, labelpos='w', label_text="Color") colorHouse.grid(row=row, column=0, rowspan=3) from CGLtk.color.ColorWell import ColorWell self.colorWell = ColorWell(colorHouse.interior(), color=self._contrastWithBG(), callback=self._colorCB) self.colorWell.grid() from chimera.tkoptions import IntOption self.labelFontSize = IntOption(page, row, "Font size", 24, self._labelChangeCB, startCol=1, min=1, attribute="size", width=3) row += 1 self.labelFontStyle = FontStyle(page, row, "Font style", oglFont.normal, self._labelChangeCB, startCol=1) row += 1 self.labelFontTypeface = FontTypeface(page, row, "Font typeface", FONT_TYPEFACE_VALUES[0], self._labelChangeCB, startCol=1) row += 1 if self.model.curLabel: self.changeToLabel(self.model.curLabel, force=True)
class ClusterDialog(ModelessDialog): oneshot = True provideStatus = True buttons = ("Close", ) title = "Clustering" def __init__(self, movie, startFrame, stride, endFrame, useSel, ignoreBulk, ignoreHyds): self.movie = movie self.movie.subdialogs.append(self) self.startFrame = startFrame self.stride = stride self.endFrame = endFrame self.useSel = useSel self.ignoreBulk = ignoreBulk self.ignoreHyds = ignoreHyds ModelessDialog.__init__(self) def Close(self): if self._computing: self._abort = True return self.movie.subdialogs.remove(self) ModelessDialog.Close(self) def destroy(self): # calling ModelessDialog.Close causes recursion ModelessDialog.destroy(self) def fillInUI(self, parent): from chimera.match import matchPositions from chimera import numpyArrayFromAtoms from chimera import selection, UserError self._computing = False # load needed coord sets... frameNums = range(self.startFrame, self.endFrame + 1, self.stride) for frameNum in frameNums: if not self.movie.findCoordSet(frameNum): self.status("loading frame %d" % frameNum) self.movie._LoadFrame(frameNum, makeCurrent=False) # compute RMSDs from analysis import analysisAtoms try: atoms = analysisAtoms(self.movie, self.useSel, self.ignoreBulk, self.ignoreHyds) except UserError: self.Close() raise self.buttonWidgets['Close'].configure(text="Abort") numFrames = len(frameNums) self.status("Fetching %d coordinate arrays" % numFrames) numpyArrays = {} from time import time t0 = time() for i, fn in enumerate(frameNums): numpyArrays[fn] = numpyArrayFromAtoms(atoms, self.movie.findCoordSet(fn)) self._computing = True self._abort = False parent.update() # allow Abort button to function self._computing = False if self._abort: parent.after_idle(self.Close) return if i == numFrames - 1: self.status("Fetched %d coordinate arrays" % (i + 1)) else: elapsed = time() - t0 perSec = elapsed / (i + 1) remaining = perSec * (numFrames - (i + 1)) self.status("Fetched %d of %d coordinate arrays" "\nAbout %.1f minutes remaining" % (i + 1, numFrames, remaining / 60.0)) t0 = time() totalRMSDs = numFrames * (numFrames - 1) / 2 self.status("Computing %d RMSDs" % totalRMSDs) from EnsembleMatch.distmat import DistanceMatrix fullDM = DistanceMatrix(numFrames) sameAs = {} for i, frame1 in enumerate(frameNums): na1 = numpyArrays[frame1] for j, frame2 in enumerate(frameNums[i + 1:]): na2 = numpyArrays[frame2] rmsd = matchPositions(na1, na2)[1] fullDM.set(i, i + j + 1, rmsd) if rmsd == 0.0: sameAs[frame2] = frame1 self._computing = True self._abort = False parent.update() # allow Abort button to function self._computing = False if self._abort: parent.after_idle(self.Close) return numComputed = totalRMSDs - ((numFrames - (i + 1)) * (numFrames - (i + 2))) / 2 if numComputed == totalRMSDs: self.status("Computed %d RMSDs" % totalRMSDs) else: elapsed = time() - t0 perSec = elapsed / numComputed remaining = perSec * (totalRMSDs - numComputed) if remaining < 50: timeEst = "%d seconds" % int(remaining + 0.5) else: timeEst = "%.1f minutes" % (remaining / 60.0) self.status("Computed %d of %d RMSDs\n" "About %s remaining" % (numComputed, totalRMSDs, timeEst)) self.status("Generating clusters") self.buttonWidgets['Close'].configure(text="Close") if not sameAs: dm = fullDM reducedFrameNums = frameNums indexMap = range(len(frameNums)) elif len(sameAs) == numFrames - 1: raise UserError("All frames to cluster are identical!") self.Close() else: dm = DistanceMatrix(numFrames - len(sameAs)) reducedFrameNums = [] indexMap = [] for i, fn in enumerate(frameNums): if fn in sameAs: continue reducedFrameNums.append(fn) indexMap.append(i) for i in range(len(reducedFrameNums)): mapi = indexMap[i] for j in range(i + 1, len(reducedFrameNums)): mapj = indexMap[j] dm.set(i, j, fulldm.get(mapi, mapj)) from EnsembleMatch.nmrclust import NMRClust clustering = NMRClust(dm) self.clusterMap = {} self.representatives = [] self.clusters = [] unsortedClusters = [(c, clustering.representative(c)) for c in clustering.clusters] # sort the clusters so that the coloring is reproducible # while trying to avoid using adjacent colors for # adjacent clusters clusters = [unsortedClusters.pop()] while unsortedClusters: bestCluster = bestVal = None for uc in unsortedClusters: val = abs(uc[-1] - clusters[-1][-1]) if len(clusters) > 1: val += 0.5 * abs(uc[-1] - clusters[-2][-1]) if len(clusters) > 2: val += 0.25 * abs(uc[-1] - clusters[-3][-1]) if bestVal == None or val > bestVal: bestCluster = uc bestVal = val unsortedClusters.remove(bestCluster) clusters.append(bestCluster) colors = colorRange(len(clusters)) for c, rep in clusters: cluster = Cluster() cluster.color = colors.pop() self.clusters.append(cluster) cluster.representative = reducedFrameNums[rep] cluster.members = [] for m in c.members(): f = reducedFrameNums[m] self.clusterMap[f] = cluster cluster.members.append(f) for dup, base in sameAs.items(): c = self.clusterMap[base] self.clusterMap[dup] = c c.members.append(dup) self.status("%d clusters" % len(self.clusters)) from CGLtk.Table import SortableTable self.table = SortableTable(parent) self.table.addColumn("Color", "color", format=(False, False), titleDisplay=False) membersCol = self.table.addColumn("Members", "lambda c: len(c.members)", format="%d") self.table.addColumn("Representative Frame", "representative", format="%d") self.table.setData(self.clusters) self.table.launch(browseCmd=self.showRep) self.table.sortBy(membersCol) self.table.sortBy(membersCol) # to get descending order self.table.grid(sticky="nsew") self.timeLine = Pmw.ScrolledCanvas(parent, canvas_height="0.5i") self.timeLine.grid(row=1, column=0, sticky="nsew") parent.rowconfigure(0, weight=1) parent.columnconfigure(0, weight=1) self.zoom = 2.0 self.drawTimeLine() def drawTimeLine(self, width=None): if width == None: width = 3.0 tl = self.timeLine for item in tl.find_all(): tl.delete(item) for c in self.clusters: c.canvasItems = [] c.canvas = tl frameNums = range(self.startFrame, self.endFrame + 1, self.stride) cluster = startFrame = None f2x = self._frame2x height = 22 for fn in frameNums: c = self.clusterMap[fn] if c == cluster: continue if cluster != None: tkColor = rgba2tk(cluster.color) cluster.canvasItems.append( tl.create_rectangle(f2x(startFrame), 0, f2x(fn) - 1, height, fill=tkColor, outline=tkColor)) cluster = c startFrame = fn tkColor = rgba2tk(cluster.color) cluster.canvasItems.append( tl.create_rectangle(f2x(startFrame), 0, f2x(frameNums[-1] + self.stride) - 1, height, fill=tkColor, outline=tkColor)) for c in self.clusters: rep = c.representative tkColor = rgba2tk(c.color) c.canvasItems.append( tl.create_rectangle(f2x(rep), 0, f2x(rep + self.stride) - 1, -0.5 * height, fill=tkColor, outline=tkColor)) tl.resizescrollregion() def showRep(self, clusters): self.movie.currentFrame.set(clusters[0].representative) self.movie.LoadFrame() def _frame2x(self, fn): return int(self.zoom * (fn - self.startFrame) / self.stride + 0.5)