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
Exemple #2
0
	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")
Exemple #3
0
	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")
Exemple #4
0
	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
Exemple #5
0
	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()
Exemple #6
0
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()
Exemple #7
0
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)
Exemple #8
0
	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
Exemple #9
0
	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)
Exemple #10
0
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))
Exemple #11
0
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()
Exemple #12
0
    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)
Exemple #13
0
    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()
Exemple #14
0
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))
Exemple #16
0
	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()
Exemple #17
0
    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()
Exemple #18
0
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()
Exemple #19
0
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
Exemple #20
0
	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
Exemple #21
0
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)
Exemple #22
0
	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)