# Copyright (c) 2000 by the Regents of the University of California. # All rights reserved. See http://www.cgl.ucsf.edu/chimera/ for # license details. # # $Id: bgprefs.py 39151 2013-10-11 23:01:06Z pett $ """ background preferences """ import chimera from chimera import preferences from chimera import tkoptions from chimera.paletteoptions import GradientOption from chimera.imageoptions import ImageOpacityOption, PrefImageOpacityOption BACKGROUND = "Background" BG_METHOD = "Background method" BG_COLOR = "Background color" BG_GRADIENT = "Background gradient" BG_IMAGE = "Background image" #LENS_PROFILE = "Draw lens borders" #LENS_COLOR = "Lens border color" class MethodOption(tkoptions.SymbolicEnumOption): values = (chimera.LensViewer.Solid, chimera.LensViewer.Gradient, chimera.LensViewer.Image) labels = ('solid', 'gradient', 'image') def _backgroundMethodCB(option): method = option.get() try: chimera.viewer.backgroundMethod = method except ValueError, e: from chimera import replyobj replyobj.error(str(e)) def _backgroundColorCB(option): rgba = option.get() if not rgba: color = None else: color = chimera.MaterialColor(*rgba) import Midas Midas.background(color=color, setMethod=False) def _backgroundGradientCB(option): palette, opacity = option.get() chimera.viewer.backgroundGradient = palette, opacity, 0, 0 def _backgroundImageCB(option): image, scale, tiling, opacity = option.get() chimera.viewer.backgroundImage = image, scale, tiling, opacity, 0, 0 #def _drawBorderCB(option): # border = option.get() # chimera.viewer.lensBorder = border #def _borderColorCB(option): # rgba = option.get() # if not rgba: # color = None # else: # color = chimera.MaterialColor(*rgba) # chimera.viewer.lensBorderColor = color _bgImageCache = None # need to get back Image that was saved def _saveBGImage((image, scale, tiling, opacity)): global _bgImageCache import os filename = 'bgimage.png' pf = os.path.split(preferences.preferences.filename()) path = os.path.join(*(pf[0:-1] + (filename,))) if image is not None: image.save(path) else: try: os.remove(filename) except OSError: pass filename = None _bgImageCache = image return (filename, scale, tiling, opacity) def _restoreBGImage((filename, scale, tiling, opacity)): global _bgImageCache if filename is None: image = None _bgImageCache = None else: import os pf = os.path.split(preferences.preferences.filename()) path = os.path.join(*(pf[0:-1] + (filename,))) if _bgImageCache is not None: image = _bgImageCache else: try: from PIL import Image image = Image.open(path) except IOError: image = None _bgImageCache = image return (image, scale, tiling, opacity) default_label_font = None def initialize(): backgroundPreferences = [{ BG_COLOR: ( tkoptions.RGBAOption, None, _backgroundColorCB ), }] backgroundPreferencesOrder = [ BG_COLOR ] if isinstance(chimera.viewer, chimera.LensViewer): # change initial default gradient from None from chimera import palettes default_gradient = palettes.getPaletteByName("Chimera default") chimera.viewer.backgroundGradient = default_gradient, 1, 0, 0 backgroundPreferences.extend([{ BG_METHOD: ( MethodOption, chimera.viewer.Solid, _backgroundMethodCB ), BG_GRADIENT: ( GradientOption, (default_gradient, 1), _backgroundGradientCB, { "noneOkay": True } ), #LENS_PROFILE: ( # tkoptions.BooleanOption, chimera.viewer.lensBorder, # _drawBorderCB #), #LENS_COLOR: ( # tkoptions.RGBAOption, chimera.viewer.lensBorderColor.rgba(), # _borderColorCB, { "noneOkay": False } #) }, PrefImageOpacityOption, { BG_IMAGE: ( ImageOpacityOption, (None, 1, 0, 1), _backgroundImageCB, { 'noneOkay': True, }, { 'imageFilename': 'bgimage.png' } ) }]) backgroundPreferencesOrder[0:0] = [BG_METHOD] backgroundPreferencesOrder[2:2] = [BG_GRADIENT, BG_IMAGE] preferences.register(BACKGROUND, backgroundPreferences, onDisplay=_bgPrefDisp, onHide=_bgPrefHide) preferences.setOrder(BACKGROUND, backgroundPreferencesOrder) if not chimera.nogui: # Fix initialize resize background color import Midas Midas.background(color=chimera.viewer.background, setMethod=False) _bgPrefHandler = None def _bgPrefDisp(event=None): global _bgPrefHandler if _bgPrefHandler is None: _bgPrefHandler = chimera.triggers.addHandler("Viewer", _bgPrefUpdate, None) _bgPrefUpdate("Viewer", None, None) def _bgPrefHide(event=None): global _bgPrefHandler if _bgPrefHandler is not None: chimera.triggers.deleteHandler("Viewer", _bgPrefHandler) _bgPrefHandler = None def _bgPrefUpdate(trigger, closure, changed): if changed is not None and chimera.viewer not in changed.modified: return from initprefs import PREF_SELECTION, HMETHOD, HCOLOR _bgCheckOpt(BACKGROUND, BG_METHOD, chimera.viewer.backgroundMethod) _bgCheckColor(BACKGROUND, BG_COLOR, chimera.viewer.background) _bgCheckGradient(BACKGROUND, BG_GRADIENT, chimera.viewer.backgroundGradient), _bgCheckImage(BACKGROUND, BG_IMAGE, chimera.viewer.backgroundImage), _bgCheckOpt(PREF_SELECTION, HMETHOD, chimera.viewer.highlight) _bgCheckColor(PREF_SELECTION, HCOLOR, chimera.viewer.highlightColor) #_bgCheckOpt(BACKGROUND, LENS_PROFILE, chimera.viewer.lensBorder) #_bgCheckColor(BACKGROUND, LENS_COLOR, chimera.viewer.lensBorderColor) def _bgCheckColor(cat, opt, c): oc = preferences.get(cat, opt) if c is None: if oc is not None: preferences.set(cat, opt, None) else: nc = c.rgba() if oc != nc: preferences.set(cat, opt, nc) def _bgCheckOpt(cat, opt, b): ob = preferences.get(cat, opt) if ob != b: preferences.set(cat, opt, b) def _bgCheckGradient(cat, opt, bg): from chimera import palettes palette, opacity, angle, offset = bg ob = preferences.get(cat, opt) if ob != (palette, opacity): preferences.set(cat, opt, (palette, opacity)) def _bgCheckImage(cat, opt, bi): image, scale, tiling, opacity, angle, offset = bi ob = preferences.get(cat, opt) if ob != (image, scale, tiling, opacity): preferences.set(cat, opt, (image, scale, tiling, opacity)) # monkey patch LensViewer to change backgroundGradient to take a Palette # and backgroundImage to take an image def power2(i): """return smallest power of 2 greater than or equal to argument""" if i < 0: raise ValueError("need non-negative integer") p2 = 1 while p2 < i: p2 <<= 1 # assume two's complement arithmetic return p2 def _setTextureOpacity(texture, opacity): mat = texture.baseMaterial if mat.opacity == opacity: return default = chimera.Material.lookup("default") if mat is default: mat = chimera.Material('') texture.baseMaterial = mat mat.opacity = opacity def _backgroundGradient(self): texture, width, angle, offset = chimera.viewer._backgroundGradient if texture: opacity = texture.baseMaterial.opacity else: opacity = 1 return self._bgGradient, opacity, angle, offset def _setBackgroundGradient(self, (palette, opacity, angle, offset)): if palette == None: chimera.viewer._backgroundGradient = None, 0, angle, offset self._bgGradient = palette return texture, width = palette.texture() chimera.viewer._backgroundGradient = texture, width, angle, offset self._bgGradient = palette _setTextureOpacity(texture, opacity) chimera.LensViewer._bgGradient = None chimera.LensViewer._backgroundGradient = chimera.LensViewer.backgroundGradient chimera.LensViewer.backgroundGradient = property(_backgroundGradient, _setBackgroundGradient, None, "(Palette, float opacity, float angle, float offset)") def _backgroundImage(self): texture, xscale, yscale, tiling, angle, offset = chimera.viewer._backgroundImage if texture: opacity = texture.baseMaterial.opacity else: opacity = 1 return self._bgImage, self._bgScale, tiling, opacity, angle, offset def _setBackgroundImage(self, (image, scale, tiling, opacity, angle, offset)): if image == self._bgImage and scale == self._bgScale: texture = chimera.viewer._backgroundImage[0] texture, xscale, yscale, _, _, _ = chimera.viewer._backgroundImage chimera.viewer._backgroundImage = texture, xscale, yscale, tiling, angle, offset if not texture: return _setTextureOpacity(texture, opacity) return if image == None: chimera.viewer._backgroundImage = None, 1, 1, tiling, angle, offset self._bgImage = None self._bgScale = 1 return from PIL import Image try: # PIL image origin is top-left, OpenGL origin is bottom-left teximage = image.transpose(Image.FLIP_TOP_BOTTOM) except: # On any error, act as if the image is missing chimera.viewer._backgroundImage = None, 1, 1, tiling, angle, offset self._bgImage = None self._bgScale = 1 return # make RGBA for consistency if teximage.mode != 'RGBA': teximage = teximage.convert('RGBA') max_tex_size = chimera.opengl_getInt("maximum texture size") width, height = teximage.size p2width = power2(width) if p2width > max_tex_size: p2width = max_tex_size p2height = power2(height) if p2height > max_tex_size: p2height = max_tex_size if width == p2width and height == p2height: xscale = yscale = 1 else: xscale = float(width) / p2width yscale = float(height) / p2height teximage = teximage.resize((p2width, p2height), Image.BICUBIC) texture = chimera.Texture('', chimera.Texture.RGBA, chimera.Texture.UnsignedByte, p2width, p2height) size = p2width * p2height * 4 memmap = chimera.memoryMap(texture.startEditing(), size, texture.type()) memmap[:] = [ord(c) for c in teximage.tostring()] texture.finishEditing() xscale *= scale yscale *= scale chimera.viewer._backgroundImage = texture, xscale, yscale, tiling, angle, offset self._bgImage = image self._bgScale = scale _setTextureOpacity(texture, opacity) chimera.LensViewer._bgImage = None chimera.LensViewer._bgScale = 1.0 chimera.LensViewer._backgroundImage = chimera.LensViewer.backgroundImage chimera.LensViewer.backgroundImage = property(_backgroundImage, _setBackgroundImage, None, "(Image, float scale, tiling, float opacity, float angle, float offset)")