class ImageSaveDialog(ModelessDialog):

	name = "Save Image"
	buttons = ['Tips', 'Save As', Cancel]
	default = 'Save As'
	help = 'UsersGuide/print.html'
	provideStatus = True

	def fillInUI(self, master):
		self._ModelTrigger = None
		#self._SetupTrigger = None
		self._SizeTrigger = None
		self.raytrace = None
		# Image Size
		imageSetup = Tix.LabelFrame(master, label="Image Size")
		imageSetup.pack(fill=Tk.X, ipadx=2, ipady=2)
		subframe = imageSetup.frame
		#subframe = Tk.Frame(imageSetup.frame)
		#subframe.pack(fill=Tk.BOTH, expand=1)

		# add in conversion factor for pixels and graphics screen
		from chimera import tkgui
		win = tkgui.app.graphics
		res = tkgui.getScreenMMWidth() / win.winfo_screenwidth()
		convert['pixels'] = mm2pt(res)

		self.matchAspect = Tk.BooleanVar(master)
		self.matchAspect.set(1)

		self.usePrint = Tk.BooleanVar(master)
		self.usePrint.set(preferences.get(IMAGE_SETUP, USE_PRINT_UNITS))
		import itertools
		row = itertools.count()

		self.showUsePrint = Tk.Checkbutton(subframe, indicatoron=1,
				variable=self.usePrint, highlightthickness=0,
				text=USE_PRINT_UNITS,
				command=self._updateUsePrint)
		self.showUsePrint.grid(columnspan=2, row=row.next(), sticky=Tk.W)

		w, h = chimera.viewer.windowSize
		self.units = ImageUnitsOption(subframe, row.next(), UNITS,
						'pixels', self.updateImageUnits)
		self.iWidth = FloatOption(subframe, row.next(), 'Image width',
					w, self.updateImageWidth, min=1e-10)
		self.iHeight = FloatOption(subframe, row.next(), 'Image height',
					h, self.updateImageHeight, min=1e-10)

		matchAspect = Tk.Checkbutton(subframe, indicatoron=1,
				variable=self.matchAspect, highlightthickness=0,
				text="Maintain current aspect ratio",
				command=self.updateMatchAspect)
		matchAspect.grid(columnspan=2, row=row.next(), sticky=Tk.W)
		self.grow = Tk.Button(imageSetup.frame, text="Grow to Fit",
					command=self.Resize, state=Tk.DISABLED)
		fitrow = row.next()
		self.grow.grid(row=fitrow, column=0, padx=2, pady=2,
								sticky=Tk.NSEW)
		self.shrink = Tk.Button(imageSetup.frame, text="Shrink to Fit",
				command=lambda f=self.Resize: f(False),
				state=Tk.DISABLED)
		self.shrink.grid(row=fitrow, column=1, padx=2, pady=2,
								sticky=Tk.NSEW)

		#fetch = Tk.Button(imageSetup.frame, text="Get Pixels",
		#				command=self.fetchWindowSize)
		#fetch.grid(row=row.next(), column=0, padx=2, pady=2, sticky=Tk.NSEW)

		#calc = Tk.Button(imageSetup.frame, text="Image Setup",
		#			command=self.showImageSetupDialog)
		#calc.grid(row=row.next(), column=1, padx=2, pady=2, sticky=Tk.NSEW)

		self.printRes = FloatOption(subframe, row.next(), DPI,
				preferences.get(IMAGE_SETUP, DPI),
				self._updatePrint, min=1)

		# Image Information
		info = Tix.LabelFrame(master, label="Image Information")
		info.pack(fill=Tk.X)
		d = Tk.Label(info.frame, text="Description:")
		d.grid(columnspan=2, row=0, column=0, sticky=Tk.W, padx=2,
									pady=1)
		self.description = Tk.Entry(info.frame)
		info.frame.grid_columnconfigure(0, weight=1)
		info.frame.grid_columnconfigure(1, weight=1)
		self.description.grid(columnspan=2, row=1, column=0,
						sticky=Tk.EW, padx=2, pady=2)
		imageCredits = Tk.Button(info.frame,
					text="Image Credits",
					command=self.showImageCreditsDialog)
		imageCredits.grid(row=2, column=0, padx=2, pady=2)
		credit = Tk.Button(info.frame, text="Citing Chimera",
				command=lambda: help.display("credits.html"))
		credit.grid(row=2, column=1, padx=2, pady=2)

		# Image camera
		self.raytrace = BooleanOption(master, -1,
				'Raytrace with POV-Ray', False,
				self._updateRaytrace)
		self.raytraceOptions = Tk.Button(master, text=POVRAY_SETUP,
					command=self.showPOVRayOptions)
		self.raytraceOptions.pack()
		self.raytraceOptions.pack_forget()
		self.supersample = SupersampleOption(master, -1, SUPERSAMPLE,
				preferences.get(IMAGE_SETUP, SUPERSAMPLE),
				self._updateSS)
		self.adjustFOV = AdjustFOVOption(master, -1, ADJUST_FOV,
				preferences.get(IMAGE_SETUP, ADJUST_FOV),
				self._updateAdjustFOV)
		self.printMode = _PrintModeOption(master, -1,
				'Image camera mode', _PrintModeOption.SAME,
				self._updatePrintMode)
		self.lenticular = IntOption(master, -1,
				'Number of lenticular images',
				chimera.Camera.lenticularImageCount(),
				self._updateLenticular, min=2, width=4)
		self.lenticular.forget()

		# switch to user's prefered units
		self.adjust = convert['pixels']
		units = preferences.get(IMAGE_SETUP, UNITS)
		self._updateUsePrint()
		self.units.set(units)
		self.updateImageUnits()

	def map(self, event=None):
		self._ModelTrigger = chimera.triggers.addHandler('Model',
						self._computeMaxLineWidth, None)
		#self._SetupTrigger = chimera.triggers.addHandler(IMAGE_SETUP,
		#				self._computeMaxLineWidth, None)
		self._SizeTrigger = chimera.triggers.addHandler(
				'graphics window size', self._resetSize, None)
		self._computeMaxLineWidth()

	def unmap(self, event=None):
		if self._ModelTrigger:
			chimera.triggers.deleteHandler('Model',
							self._ModelTrigger)
			self._ModelTrigger = None
		#if self._SetupTrigger:
		#	chimera.triggers.deleteHandler(IMAGE_SETUP,
		#					self._SetupTrigger)
		#	self._SetupTrigger = None

	def _computeMaxLineWidth(self, *args):
		if self.raytrace and self.raytrace.get():
			# not relevant if raytracing
			self.status('', blankAfter=0)
			return
		# this should be called models are modified, when the
		# image size changes and when image setup parameters
		# change (DPI, supersampling)
		opengl_max = min(
				chimera.opengl_getFloat("max point size"),
				chimera.opengl_getFloat("max line width"))
		max_lw = max([m.lineWidth
				for m in chimera.openModels.list(all=True)
					if hasattr(m, 'lineWidth')])
		# compute tile scaling factor like C++ code does
		width = self.iWidth.get()
		height = self.iHeight.get()
		horizPixels, vertPixels, supersample = \
				imageArgs(self.units.get(), width, height)
		width, height = chimera.viewer.windowSize
		tileScale = float(horizPixels) / float(width)
		tmp = float(vertPixels) / float(height)
		if tmp > tileScale:
			tileScale = tmp
		tileScale *= supersample

		opengl_max /= tileScale
		if max_lw > opengl_max:
			color = 'red'
		else:
			color = 'black'
		self.status('effective maximum line width is %g' % opengl_max,
				color=color, blankAfter=0)

	#def Print(self):
	#	self.Cancel()
	#	image, options = self.getImage()
	#	filename = tempfile.mktemp()
	#	image.save(filename, **options)
	#	cmd = preferences.get(PAGE_SETUP, PRINTER_CMD)
	#	cmd = re.sub('%s', "'" + filename + "'", cmd)
	#	os.system(cmd)
	#	os.unlink(filename)

	def SaveAs(self):
		# now save the image
		self.Cancel()
		printMode = self.printMode.get()
		raytrace = self.raytrace.get()
		if printMode == _PrintModeOption.SAME:
			printMode = None
		if not chimera.nogui and raytrace and chimera.viewer.clipping:
			dialog = ClipWarning()
			if not dialog.run(chimera.tkgui.app):
				return
		saveImage(None, self.iWidth.get(), self.iHeight.get(), 
				units=self.units.get(), master=self.uiMaster(),
				description=self.description.get().strip(),
				printMode=printMode, raytrace=raytrace)

	def Tips(self):
		import help
		help.display(self.help + "#tips")

	def Resize(self, larger=True):
		vw, vh = chimera.viewer.windowSize
		iw = self.iWidth.get()
		ih = self.iHeight.get()
		vaspect = vw / float(vh)
		iaspect = iw / float(ih)
		from chimera import tkgui
		top = tkgui.app.winfo_toplevel()
		if larger:
			if vaspect < iaspect:
				w = int(vw * iaspect / vaspect + 0.5)
				if w != vw:
					top.wm_geometry('')
					tkgui.app.graphics.config(width=w)
			elif vaspect > iaspect:
				h = int(vh * vaspect / iaspect + 0.5)
				if h != vh:
					top.wm_geometry('')
					tkgui.app.graphics.config(height=h)
		else:
			if vaspect < iaspect:
				h = int(vh * vaspect / iaspect + 0.5)
				if h != vh:
					top.wm_geometry('')
					tkgui.app.graphics.config(height=h)
			elif vaspect > iaspect:
				w = int(vw * iaspect / vaspect + 0.5)
				if w != vw:
					top.wm_geometry('')
					tkgui.app.graphics.config(width=w)

	def fetchWindowSize(self):
		w, h = chimera.viewer.windowSize
		self.units.set('pixels')
		self.updateImageUnits()
		self.iWidth.set(w)
		self.iHeight.set(h)

	def _updateRaytrace(self, option):
		raytrace = option.get()
		if raytrace:
			self.printMode.forget()
			self.lenticular.forget()
			self.supersample.forget()
			self.raytraceOptions.pack()
		else:
			self.raytraceOptions.pack_forget()
			self.printMode.manage()
			self.supersample.manage()
			self._updatePrintMode(self.printMode)
		self._computeMaxLineWidth()

	def _updatePrintMode(self, option):
		printMode = option.get()
		if printMode == 'lenticular':
			self.lenticular.manage()
		else:
			self.lenticular.forget()

	def _updateLenticular(self, option):
		count = option.get()
		chimera.Camera.setLenticularImageCount(count)

	def updateMatchAspect(self, *args):
		if self.matchAspect.get():
			self.grow.config(state=Tk.DISABLED)
			self.shrink.config(state=Tk.DISABLED)
			self.Resize()
		else:
			self.grow.config(state=Tk.NORMAL)
			self.shrink.config(state=Tk.NORMAL)

	def updateImageUnits(self, *args):
		units = self.units.get()
		if units == 'pixels':
			self.printRes.disable()
			self.adjustFOV.disable()
		else:
			self.printRes.enable()
			self.adjustFOV.enable()
		if units != preferences.get(IMAGE_SETUP, UNITS):
			preferences.set(IMAGE_SETUP, UNITS, units)
		try:
			adjust = convert[units]
		except KeyError:
			adjust = -1
		if adjust == self.adjust:
			return
		if self.adjust != -1 and adjust != -1:
			factor = self.adjust / adjust
			w = self.iWidth.get() * factor
			self.iWidth.set(w)
			h = self.iHeight.get() * factor
			self.iHeight.set(h)
		if adjust == -1:
			# entering pixel mode
			w, h = chimera.viewer.windowSize
			self.iWidth.set(w)
			self.iHeight.set(h)
		elif self.adjust == -1:
			pass
			# leaving pixel mode, sanity check
			#w, h = paper_types[preferences.get(PAGE_SETUP, PAPER_TYPE)]
			#if self.iWidth.get() > w:
			#	self.iWidth.set(w)
			#if self.iHeight.get() > h:
			#	self.iHeight.set(h)
		self.adjust = adjust
		self._computeMaxLineWidth()

	def updateImageWidth(self, option):
		# adjust height to compensate for new width
		if self.matchAspect.get():
			iWidth = option.get()
			w, h = chimera.viewer.windowSize
			self.iHeight.set(iWidth * h / w)
		self._computeMaxLineWidth()

	def updateImageHeight(self, option):
		# adjust width to compensate for new height
		if self.matchAspect.get():
			iHeight = option.get()
			w, h = chimera.viewer.windowSize
			self.iWidth.set(iHeight * w / h)
		self._computeMaxLineWidth()

	def _resetSize(self, triggerName, myData, sizes):
		width, height, mmwidth, mmheight = sizes
		units = self.units.get()
		if units == 'pixels':
			self.iWidth.set(width)
			self.iHeight.set(height)
		else:
			adjust = convert['millimeters'] / convert[units]
			self.iWidth.set(mmwidth * adjust)
			self.iHeight.set(mmheight * adjust)
		self._computeMaxLineWidth()

	def _updateUsePrint(self):
		usePrint = self.usePrint.get()
		preferences.set(IMAGE_SETUP, USE_PRINT_UNITS, usePrint)
		if not usePrint:
			values = ['pixels']
		else:
			values = convert.keys()
			try:
				values.remove('pixels')
			except ValueError:
				pass
			values.sort()
		self.units.values = values
		self.units.remakeMenu()
		if usePrint:
			self.units.set('inches')
		else:
			self.units.set('pixels')
		self.updateImageUnits()
		if not usePrint:
			self.fetchWindowSize()

	def _updatePrint(self, option):
		res = option.get()
		preferences.set(IMAGE_SETUP, DPI, res)

	def _updateAdjustFOV(self, option):
		adjust = option.get()
		preferences.set(IMAGE_SETUP, ADJUST_FOV, adjust)

	def _updateSS(self, option):
		ss = option.get()
		preferences.set(IMAGE_SETUP, SUPERSAMPLE, ss)
		self._computeMaxLineWidth()

	#def showImageSetupDialog(self, *args):
	#	import dialogs
	#	d = dialogs.display("preferences")
	#	d.setCategoryMenu(IMAGE_SETUP)

	def showImageCreditsDialog(self, *args):
		import dialogs
		d = dialogs.display("preferences")
		d.setCategoryMenu(IMAGE_CREDITS)

	def showPOVRayOptions(self, *args):
		import dialogs
		d = dialogs.display("preferences")
		d.setCategoryMenu(POVRAY_SETUP)
class Match2Align(ModelessDialog):
	title = "Create Alignment from Superposition"
	oneshot = True
	help = "ContributedSoftware/matchalign/matchalign.html"

	def fillInUI(self, parent):
		row = 0
		parent.columnconfigure(0, weight=1)
		parent.rowconfigure(row, weight=1)
		self.chainList = MoleculeChainScrolledListBox(parent,
					selectioncommand=self._updateIterRef,
					listbox_selectmode='multiple')
		self.chainList.grid(row=row, column=0, sticky='nsew')
		row += 1
		mols = {}
		for chain in self.chainList.get():
			mol = chain.molecule
			if mol in mols:
				continue
			mols[mol] = chain
		self.chainList.setvalue(mols.values())
	
		f = Tkinter.Frame(parent)
		f.grid(row=row, column=0, sticky='w')
		row += 1
		self.distCutoff = FloatOption(f, 0,
			"Residue-residue distance cutoff (angstroms)",
			prefs[DIST_CUTOFF], None, balloon="""\
residues whose principal atoms are further apart
than this distance will not be aligned in the
generated sequence alignment""")
		self.distCutoff.min = 0.0

		class MatchTypeOption(SymbolicEnumOption):
			values = ["any", "all"]
			labels = ["at least one other", "all others"]
		f = Tkinter.Frame(parent)
		f.grid(row=row, column=0, sticky='w')
		row += 1
		self.matchType = MatchTypeOption(f, 0,
			"Residue aligned in column if within cutoff of",
			prefs[ANYALL], None, balloon="""\
whether a residue needs to match the distance cutoff to all other
residues in its column, or just to one residue in the column""")

		class GapCharOption(SymbolicEnumOption):
			values = [".", "-", "~"]
			labels = [". (period)", "- (dash)", "~ (tilde)"]
		f = Tkinter.Frame(parent)
		f.grid(row=row, column=0, sticky='w')
		row += 1
		self.gapChar = GapCharOption(f, 0, "Gap character",
			prefs[GAPCHAR], None, balloon="""\
character used to depict gaps in alignment""")

		self.circularVar = Tkinter.IntVar(parent)
		self.circularVar.set(prefs[CIRCULAR])
		Tkinter.Checkbutton(parent, variable=self.circularVar, text=
				"Allow for circular permutation").grid(row=row,
				column=0, sticky='w')
		row += 1

		self.iterateVar = Tkinter.IntVar(parent)
		self.iterateVar.set(prefs[ITERATE])
		Tkinter.Checkbutton(parent, command=self._iterParamsDisplay,
				text="Iterate superposition/alignment...",
				variable=self.iterateVar).grid(
				row=row, column=0, columnspan=2, sticky='w')
		row += 1
		self.iterParams = Pmw.Group(parent, hull_padx=2,
					tag_text="Iteration Parameters")
		self.iterParams.grid(row=row, column=0, columnspan=2)
		row += 1
		inside = self.iterParams.interior()
		Tkinter.Label(inside, text="Iterate alignment:").grid(
					row=0, column=0, rowspan=2, sticky='e')
		self.iterConvergeVar = Tkinter.IntVar(parent)
		self.iterConvergeVar.set(prefs[ITER_CONVERGE])
		f = Tkinter.Frame(inside)
		f.grid(row=0, column=1, sticky='w')
		Tkinter.Radiobutton(f, value=False, text="at most",
			variable=self.iterConvergeVar).grid(row=0, column=0)
		self.iterLimit = Pmw.EntryField(f, labelpos='e',
			label_text="times", validate={'min': 1,
			'validator': 'numeric'}, value=str(prefs[ITER_AMOUNT]),
			entry_width=2, entry_justify="center")
		self.iterLimit.grid(row=0, column=1)
		Tkinter.Radiobutton(inside, text="until convergence",
			value=True, variable=self.iterConvergeVar).grid(
			row=1, column=1, sticky='w')
		inside.rowconfigure(2, minsize="0.1i")
		Tkinter.Label(inside, text="Superimpose full columns:"
			).grid(row=3, rowspan=2, column=0, sticky='e')
		self.iterAllColsVar = Tkinter.IntVar(parent)
		self.iterAllColsVar.set(prefs[ITER_ALL_COLS])
		Tkinter.Radiobutton(inside, text="across entire alignment",
			value=True, variable=self.iterAllColsVar).grid(
			row=3, column=1, sticky='w')
		f = Tkinter.Frame(inside)
		f.grid(row=4, column=1, sticky='w')
		Tkinter.Radiobutton(f, text="in stretches of at least",
					variable=self.iterAllColsVar,
					value=False).grid(row=0, column=0)
		self.stretchLen = Pmw.EntryField(f, labelpos='e',
				label_text="consecutive columns",
				validate={'min': 2, 'validator': 'numeric'},
				value=str(prefs[ITER_CONSECUTIVE_COLS]),
				entry_width=1, entry_justify="center")
		self.stretchLen.grid(row=0, column=1)
		self.referenceMenu = Pmw.OptionMenu(inside, labelpos='w',
			items=Pmw.ScrolledListBox.get(self.chainList),
			label_text="Reference chain for matching:")
		self.referenceMenu.grid(row=5, column=0, columnspan=2)

		self._iterParamsDisplay()

		f = Tkinter.Frame(parent)
		f.grid(row=row, column=0, columnspan=2, sticky='ew')
		row += 1
		from chimera import help
		b = Tkinter.Button(f, text="Save settings", pady=0,
					command=self._saveSettings)
		b.grid(row=0, column=0)
		help.register(b, balloon="Save current settings")
		b = Tkinter.Button(f, text="Reset to defaults", pady=0,
					command=self._restoreSettings)
		b.grid(row=0, column=1)
		help.register(b, balloon="Reset dialog to factory defaults")
		f.columnconfigure(0, weight=1)
		f.columnconfigure(1, weight=1)

	def Apply(self):
		chains = self.chainList.getvalue()
		if len(chains) < 2:
			replyobj.error("Must choose at least two chains\n")
			self.enter()
			return

		mols = {}
		for chain in chains:
			if chain.molecule in mols:
				replyobj.error(
				  "Please choose only one chain per model\n")
				self.enter()
				return
			mols[chain.molecule] = 1

		cutoff = float(self.distCutoff.get())
		matchType = self.matchType.get()
		gapChar = self.gapChar.get()
		circular = self.circularVar.get()

		from align import match2align
		from Midas import match, rmsd
		seqs = match2align(chains, cutoff, matchType, gapChar, circular)
		cols = alignedCols(seqs)
		replyobj.info("%d fully populated columns\n" % (len(cols)))
		if self.iterateVar.get():
			best = None
			iteration = 1
			refChain = self.chainList.itemMap[
					self.referenceMenu.getvalue()]
			if self.iterConvergeVar.get():
				iterLimit = None
			else:
				self.iterLimit.invoke()
				iterLimit = int(self.iterLimit.getvalue())
			if self.iterAllColsVar.get():
				stretchLen = 1
			else:
				self.stretchLen.invoke()
				stretchLen = int(self.stretchLen.getvalue())
			while True:
				refSeq = [s for s in seqs
					if s.molecule == refChain.molecule][0]
				# cull columns based on stretch len criteria
				stretch = []
				culled = []
				for col in cols:
					if not stretch or stretch[-1]+1 == col:
						stretch.append(col)
						continue
					if len(stretch) >= stretchLen:
						culled.extend(stretch)
					stretch = [col]
				if len(stretch) >= stretchLen:
					culled.extend(stretch)
				if stretchLen > 1:
					replyobj.info("%d fully populated"
						" columns in at least %d"
						" column stretches\n" % (
						len(culled), stretchLen))
				if not culled:
					break
					
				# match
				refAtoms = columnAtoms(refSeq, culled)
				for seq in seqs:
					if seq.molecule == refSeq.molecule:
						continue
					seqAtoms = columnAtoms(seq, culled)
					replyobj.info("Matching %s onto %s\n"
						% (seq.name, refSeq.name))
					match(seqAtoms, refAtoms)
				seqs = match2align(chains, cutoff, matchType,
					gapChar, circular, statusPrefix=
					"Iteration %d: " % iteration)
				cols = alignedCols(seqs)
				replyobj.info("Iteration %d: %d fully populated"
					" columns\n" % (iteration, len(cols)))
				if best == None or len(cols) > len(best):
					best = cols
				else:
					break
				if iterLimit and iteration >= iterLimit:
					break
				iteration += 1
		if len(seqs) == 2:
			mav = showAlignment(seqs, "Match of %s and %s" %
						(seqs[0].name, seqs[1].name))
		else:
			mav = showAlignment(seqs, "Match -> Align (%d models)"
								% len(seqs))
		from MultAlignViewer.MAViewer import MATCHED_REGION_INFO
		name, fill, outline = MATCHED_REGION_INFO
		mav.newRegion(name="Fully populated columns", columns=cols,
						fill=fill, outline=outline)

		if cols:
			# show pairwise RMSD matrix in fully populated columns
			matchAtoms = {}
			for seq in seqs:
				matchAtoms[seq] = columnAtoms(seq, cols)
			replyobj.info("\nEvaluating superpositions across all %d fully"
				" populated columns in the final alignment:\n"
				% len(cols))
			dsqSum = 0
			for i, s1 in enumerate(seqs):
				for s2 in seqs[i+1:]:
					v = rmsd(matchAtoms[s1], matchAtoms[s2],
									log=False)
					dsqSum += v * v
					replyobj.info("RMSD of %s with %s: %.3f\n" %
							(s1.name, s2.name, v))
			from math import sqrt
			replyobj.info("Overall RMSD: %.3f\n\n" % sqrt(2 * dsqSum /
							(len(seqs) * (len(seqs)-1))))
			mav.status("RMSDs reported in Reply Log", color="purple")
			replyobj.status("RMSDs reported in Reply Log", color="purple")
		else:
			mav.status("No fully populated columns in alignment", color="blue")

	def _iterParamsDisplay(self):
		if self.iterateVar.get():
			self.iterParams.grid()
		else:
			self.iterParams.grid_remove()

	def _restoreSettings(self):
		from prefs import defaults
		self.distCutoff.set(defaults[DIST_CUTOFF])
		self.matchType.set(defaults[ANYALL])
		self.gapChar.set(defaults[GAPCHAR])
		self.circularVar.set(defaults[CIRCULAR])
		self.iterateVar.set(defaults[ITERATE])
		self._iterParamsDisplay()
		self.iterConvergeVar.set(defaults[ITER_CONVERGE])
		self.iterLimit.setvalue(str(defaults[ITER_AMOUNT]))
		self.iterAllColsVar.set(defaults[ITER_ALL_COLS])
		self.stretchLen.setvalue(str(defaults[ITER_CONSECUTIVE_COLS]))

	def _saveSettings(self):
		prefs[DIST_CUTOFF] = float(self.distCutoff.get())
		prefs[ANYALL] = self.matchType.get()
		prefs[GAPCHAR] = self.gapChar.get()
		prefs[CIRCULAR] = self.circularVar.get()
		prefs[ITERATE] = self.iterateVar.get()
		prefs[ITER_CONVERGE] = self.iterConvergeVar.get()
		self.iterLimit.invoke()
		prefs[ITER_AMOUNT] = int(self.iterLimit.getvalue())
		prefs[ITER_ALL_COLS] = self.iterAllColsVar.get()
		self.stretchLen.invoke()
		prefs[ITER_CONSECUTIVE_COLS] = int(self.stretchLen.getvalue())

	def _updateIterRef(self, *args):
		self.referenceMenu.setitems([self.chainList.valueMap[chain]
				for chain in self.chainList.getvalue()])
Esempio n. 3
0
class AnisoDialog(ModelessDialog):
    title = "Thermal Ellipsoids"
    name = "anisotropic ellipsoids"
    buttons = ("Show Ellipsoids", "Hide Ellipsoids", "Close")
    help = "ContributedSoftware/thermal/thermal.html"

    builtinPresets = [("Simple ellipsoid", {}),
                      ("Principal axes", {
                          "ellipsoid": False,
                          "axisFactor": 1.0
                      }),
                      ("Principal ellipses", {
                          "ellipsoid": False,
                          "ellipseFactor": 1.0
                      }), ("Ellipsoid and principal axes", {
                          "axisFactor": 1.5
                      }),
                      ("Octant lines", {
                          "ellipseFactor": 1.01,
                          "ellipseColor": (0, 0, 0, 1)
                      }),
                      ("Snow globe axes", {
                          "ellipsoidColor": (1, 1, 1, 1),
                          "transparency": 50.0,
                          "axisFactor": 0.99
                      }),
                      ("Snow globe ellipses", {
                          "ellipsoidColor": (1, 1, 1, 1),
                          "transparency": 50.0,
                          "ellipseFactor": 0.99
                      })]

    def fillInUI(self, parent):
        import Pmw, Tkinter
        top = parent.winfo_toplevel()
        menubar = Tkinter.Menu(top, type="menubar", tearoff=False)
        top.config(menu=menubar)

        self.presetsMenu = Tkinter.Menu(menubar)
        menubar.add_cascade(label="Presets", menu=self.presetsMenu)
        presets = self.builtinPresets + prefs[PRESETS]
        for label, kw in presets:
            self.presetsMenu.add_command(
                label=label, command=lambda kw=kw: self.preset(**kw))
        self.presetsMenu.add_separator()
        self.presetsMenu.add_command(label="Preset from current settings...",
                                     command=self.startDefinePreset)
        self.presetsMenu.add_command(label="Delete user preset...",
                                     command=self.startDeletePreset)
        from chimera.tkgui import aquaMenuBar
        row = aquaMenuBar(menubar, parent, row=0, columnspan=2)

        parent.columnconfigure(0, weight=1)
        parent.columnconfigure(1, weight=1)

        self._modFromCalc = False
        self.scaling = Pmw.EntryField(parent,
                                      labelpos='w',
                                      value='1',
                                      label_text="Scale factor:",
                                      entry_width=5,
                                      validate={
                                          'validator': 'real',
                                          'min': 0.0
                                      },
                                      modifiedcommand=self._scaleTypingCB,
                                      command=self.ShowEllipsoids)
        self.scaling.grid(row=row, column=0)

        self.smoothing = Pmw.EntryField(parent,
                                        labelpos='w',
                                        label_text="Smoothing level:",
                                        validate={
                                            'validator': 'numeric',
                                            'min': 1,
                                            'max': 100
                                        },
                                        value='3',
                                        entry_width=2,
                                        command=self.ShowEllipsoids)
        self.smoothing.grid(row=row, column=1)
        row += 1

        self.calculator = Pmw.EntryField(
            parent,
            labelpos='w',
            label_text="Set scale factor for probability (%):",
            entry_width=4,
            command=self._prob2scaling)
        self.calculator.grid(row=row, column=0, columnspan=2)
        row += 1

        from chimera.tkoptions import SymbolicEnumOption, \
                FloatOption, RGBAOption
        self.showEllipsoidVar = Tkinter.IntVar(parent)
        self.showEllipsoidVar.set(True)
        ellipsoidGroup = Pmw.Group(parent,
                                   tag_pyclass=Tkinter.Checkbutton,
                                   tag_text="Depict ellipsoids",
                                   tag_variable=self.showEllipsoidVar)
        ellipsoidGroup.grid(row=row, column=0, columnspan=2, sticky="ew")
        row += 1
        inside = ellipsoidGroup.interior()
        inside.columnconfigure(0, weight=1, uniform=1)
        inside.columnconfigure(1, weight=1, uniform=1)
        self.ellipsoidColorOpt = RGBAOption(
            inside,
            0,
            'Color',
            None,
            None,
            noneOkay=True,
            balloon='"No color" means use atom colors')

        class TransparencyOption(SymbolicEnumOption):
            values = [
                None, 0.0, 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0,
                90.0, 100.0
            ]
            labels = [
                "same as color", "0%", "10%", "20%", "30%", "40%", "50%",
                "60%", "70%", "80%", "90%", "100%"
            ]

        self.transparencyOpt = TransparencyOption(inside, 1, "Transparency",
                                                  None, None)

        self.showAxesVar = Tkinter.IntVar(parent)
        self.showAxesVar.set(False)
        axesGroup = Pmw.Group(parent,
                              tag_pyclass=Tkinter.Checkbutton,
                              tag_text="Depict principal axes",
                              tag_variable=self.showAxesVar)
        axesGroup.grid(row=row, column=0, columnspan=2, sticky="ew")
        row += 1
        inside = axesGroup.interior()
        inside.columnconfigure(0, weight=1, uniform=1)
        inside.columnconfigure(1, weight=1, uniform=1)
        self.axisColorOpt = RGBAOption(
            inside,
            0,
            'Color',
            None,
            None,
            noneOkay=True,
            balloon='"No color" means use atom colors')
        self.axisFactorOpt = FloatOption(inside,
                                         1,
                                         "Length factor",
                                         1.5,
                                         self.ShowEllipsoids,
                                         balloon="relative to ellipsoids",
                                         sticky="w")
        self.axisThicknessOpt = FloatOption(inside,
                                            2,
                                            "Thickness",
                                            0.01,
                                            self.ShowEllipsoids,
                                            sticky="w")

        self.showEllipsesVar = Tkinter.IntVar(parent)
        self.showEllipsesVar.set(False)
        ellipsesGroup = Pmw.Group(parent,
                                  tag_pyclass=Tkinter.Checkbutton,
                                  tag_text="Depict principal ellipses",
                                  tag_variable=self.showEllipsesVar)
        ellipsesGroup.grid(row=row, column=0, columnspan=2, sticky="ew")
        row += 1
        inside = ellipsesGroup.interior()
        inside.columnconfigure(0, weight=1, uniform=1)
        inside.columnconfigure(1, weight=1, uniform=1)
        self.ellipseColorOpt = RGBAOption(
            inside,
            0,
            'Color',
            None,
            None,
            noneOkay=True,
            balloon='"No color" means use atom colors')
        self.ellipseFactorOpt = FloatOption(inside,
                                            1,
                                            "Size factor",
                                            1.0,
                                            self.ShowEllipsoids,
                                            balloon="relative to ellipsoids",
                                            sticky="w")
        self.ellipseThicknessOpt = FloatOption(inside,
                                               2,
                                               "Thickness",
                                               0.02,
                                               self.ShowEllipsoids,
                                               sticky="w")

        self.selRestrictVar = Tkinter.IntVar(parent)
        self.selRestrictVar.set(False)
        Tkinter.Checkbutton(parent,
                            text="Restrict Show/Hide to current"
                            " selection, if any",
                            variable=self.selRestrictVar).grid(row=row,
                                                               column=0,
                                                               columnspan=2)
        row += 1

    def ShowEllipsoids(self, *args):
        if self.showAxesVar.get():
            axisFactor = self.axisFactorOpt.get()
        else:
            axisFactor = None
        if self.showEllipsesVar.get():
            ellipseFactor = self.ellipseFactorOpt.get()
        else:
            ellipseFactor = None
        kw = {
            'color': self.ellipsoidColorOpt.get(),
            'smoothing': int(self.smoothing.getvalue()),
            'scale': float(self.scaling.getvalue()),
            'showEllipsoid': self.showEllipsoidVar.get(),
            'transparency': self.transparencyOpt.get(),
            'axisFactor': axisFactor,
            'axisColor': self.axisColorOpt.get(),
            'axisThickness': self.axisThicknessOpt.get(),
            'ellipseFactor': ellipseFactor,
            'ellipseColor': self.ellipseColorOpt.get(),
            'ellipseThickness': self.ellipseThicknessOpt.get()
        }
        if self.selRestrictVar.get():
            from chimera.selection import currentAtoms
            selAtoms = currentAtoms()
            if selAtoms:
                kw['targets'] = selAtoms
        from Aniso import aniso
        from Midas import MidasError
        try:
            aniso(**kw)
        except MidasError:
            from chimera import UserError
            raise UserError("No atoms chosen had anisotropic" " information")

    def HideEllipsoids(self):
        kw = {}
        if self.selRestrictVar.get():
            from chimera.selection import currentAtoms
            selAtoms = currentAtoms()
            if selAtoms:
                kw['targets'] = selAtoms
        from Aniso import unaniso
        unaniso(**kw)

    def preset(self,
               ellipsoid=True,
               ellipsoidColor=None,
               transparency=None,
               axisFactor=None,
               axisColor=None,
               axisThickness=0.01,
               ellipseFactor=None,
               ellipseColor=None,
               ellipseThickness=0.02):
        self.showEllipsoidVar.set(ellipsoid)
        if ellipsoid:
            self.ellipsoidColorOpt.set(ellipsoidColor)
            self.transparencyOpt.set(transparency)
        if axisFactor:
            self.showAxesVar.set(True)
            self.axisFactorOpt.set(axisFactor)
            self.axisColorOpt.set(axisColor)
            self.axisThicknessOpt.set(axisThickness)
        else:
            self.showAxesVar.set(False)
        if ellipseFactor:
            self.showEllipsesVar.set(True)
            self.ellipseFactorOpt.set(ellipseFactor)
            self.ellipseColorOpt.set(ellipseColor)
            self.ellipseThicknessOpt.set(ellipseThickness)
        else:
            self.showEllipsesVar.set(False)
        self.ShowEllipsoids()

    def startDefinePreset(self):
        if not hasattr(self, "_presetNameDialog"):
            import Pmw
            self._presetNameDialog = Pmw.PromptDialog(
                self.uiMaster(),
                title="Preset Name",
                label_text="Preset name:",
                entryfield_labelpos='w',
                defaultbutton=0,
                buttons=('OK', 'Cancel'),
                command=self._definePresetCB)
        self._presetNameDialog.show()

    def startDeletePreset(self):
        if not prefs[PRESETS]:
            from chimera import UserError
            raise UserError("No user presets have been defined")
        if not hasattr(self, "_presetDeleteDialog"):
            import Pmw
            self._presetDeleteDialog = Pmw.SelectionDialog(
                self.uiMaster(),
                title="Delete Preset",
                label_text="Preset name:",
                scrolledlist_labelpos='n',
                defaultbutton=0,
                scrolledlist_items=[ps[0] for ps in prefs[PRESETS]],
                buttons=('OK', 'Cancel'),
                command=self._deletePresetCB)
        self._presetDeleteDialog.show()

    def _definePresetCB(self, button):
        self._presetNameDialog.withdraw()
        if button is None or button == 'Cancel':
            return
        name = self._presetNameDialog.get().strip()
        if not name:
            self._presetNameDialog.show()
            from chimera import UserError
            UserError("Must provide preset name")
        if name in [x[0] for x in self.builtinPresets]:
            self._presetNameDialog.show()
            from chimera import UserError
            UserError("Cannot redefine builtin preset")

        anisoKw = {}
        anisoKw['ellipsoid'] = self.showEllipsoidVar.get()
        if anisoKw['ellipsoid']:
            anisoKw['ellipsoidColor'] = self.ellipsoidColorOpt.get()
            anisoKw['transparency'] = self.transparencyOpt.get()
        if self.showAxesVar.get():
            anisoKw['axisFactor'] = self.axisFactorOpt.get()
            anisoKw['axisColor'] = self.axisColorOpt.get()
            anisoKw['axisThickness'] = self.axisThicknessOpt.get()
        if self.showEllipsesVar.get():
            anisoKw['ellipseFactor'] = self.ellipseFactorOpt.get()
            anisoKw['ellipseColor'] = self.ellipseColorOpt.get()
            anisoKw['ellipseThickness'] = self.ellipseThicknessOpt.get()

        newPreset = (name, anisoKw)
        customPresets = prefs[PRESETS][:]
        for info in customPresets:
            if info[0] == name:
                # changing _contents_ of kw dictionary means we don't
                # have to redefine the menu command action
                info[1].clear()
                info[1].update(anisoKw)
                break
        else:
            customPresets.append(newPreset)
            self.presetsMenu.insert_command(
                len(self.builtinPresets) + len(customPresets),
                label=name,
                command=lambda kw=anisoKw: self.preset(**kw))
        prefs[PRESETS] = customPresets

    def _deletePresetCB(self, result):
        self._presetDeleteDialog.withdraw()
        if result is None or result == 'Cancel':
            self._presetDeleteDialog.destroy()
            delattr(self, '_presetDeleteDialog')
            return
        deletions = self._presetDeleteDialog.getvalue()
        remaining = [ps for ps in prefs[PRESETS] if ps[0] not in deletions]
        prefs[PRESETS] = remaining
        for name in deletions:
            self.presetsMenu.delete(name)
        self._presetDeleteDialog.destroy()
        delattr(self, '_presetDeleteDialog')

    def _prob2scaling(self):
        try:
            prob = float(self.calculator.getvalue()) / 100.0
            if prob < 0.0 or prob >= 1.0:
                raise ValueError("bad prob")
        except ValueError:
            from chimera import UserError
            raise UserError("Probability must be >= 0 and < 100")
        from Aniso import prob2scale
        self._modFromCalc = True
        self.scaling.setvalue("%g" % prob2scale(prob))
        self._modFromCalc = False

    def _scaleTypingCB(self):
        if not self._modFromCalc:
            self.calculator.setvalue("")