Source code for choose_palette


# Tkinter was renamed to tkinter Py2->Py3,
# make sure the correct module is loaded.
import sys
if sys.version_info.major < 3:
    from Tkinter import *
else:
    from tkinter import *


# -------------------------------------------------------------------
# -------------------------------------------------------------------
# -------------------------------------------------------------------
[docs]class Slider(object): """Slider(x, y, width, height, active, type_, from_, to, \ resolution, **kwargs) Initializes a new Slider object for the graphical user interface :py:class:`choose_palette.gui`. A Slider is a combination of a ``Tk.Frame`` including a ``Tk.Label``, ``Tk.Slider``, and a ``Tk.Entry`` element with all necessary interactions. Parameters ---------- x : int x-position on the Tk interface y : int y-position on the Tk interface width : int width of the Slider object (``Tk.Frame`` taking up ``Tk.Scale``, ``Tk.Label``, and ``Tk.Entry``) height : int height of the Slider object (``Tk.Frame`` taking up ``Tk.Scale``, ``Tk.Label``, and ``Tk.Entry``) type_ : str name of the Slider from_ : numeric lower value of the Slider (see :py:func:`isValidInt`, :py:func:`isValidFloat`) to : numeric upper value of the Slider (see :py:func:`isValidInt`, :py:func:`isValidFloat`) resolution : numeric resolution of the slider, the increments when moving the Slider kwargs : ... Unused """ _Frame = None # Used to store the Tk.Frame object _Scale = None # Used to store the Tk.Scale object _Label = None # Used to store the Tk.Label object _Entry = None # Used to store the Tk.Entry object _Value = None # Used to store/trac the current value of the Slider _name = None # Name of the slider _is_active = True # Bool, used to store the Slider state FGACTIVE = "#b0b0b0" BGACTIVE = "#b0b0b0" FGDISABLED = "#dadada" BGDISABLED = "#efefef" DISABLED = "#b0b0b0" BGDEFAULT = "#d9d9d9" def __init__(self, master, name, x, y, width, height, active, type_, from_, to, resolution, **kwargs): if type_ == "int": self._Value = IntVar(master) vcmd = getattr(self, "isValidInt") elif type_ == "float": self._Value = DoubleVar(master) vcmd = getattr(self, "isValidFloat") else: raise Exception("unknown type_ when initializing {:s}".format(self.__class__.__name__)) self._name = name # Frame around the slider objects self._Frame = Frame(master) self._Frame.place(x = x, y = y, width = width, height = height) # Object handling slider actions/callbacks self._Scale = Scale(self._Frame, variable = self._Value, orient = HORIZONTAL, showvalue = 0, length = width - 100, width = 15, from_ = from_, to = to, resolution = resolution) self._Scale.place(x = 50) # Plading the label self._Label = Label(self._Frame, text = name.upper()) self._Label.config(anchor = CENTER) self._Label.place(x = 0) # Adding text element self._Entry = Entry(self._Frame, bd = 0, width = 4) self._Entry.insert(INSERT, 0) # Register a function which checks if the user input is valid or not. vcmd = self._Entry.register(vcmd) self._Entry.config(justify = RIGHT, validate = "key", validatecommand = (vcmd, "%P", from_, to)) self._Entry.place(x = width - 40) # Changing the Tk.Value triggers the GUI update def fun(event, parent): val = event.widget.get() # Empty? Use existing value if len(val) == 0: event.widget.insert(0, self._Value.get()) # Else change value else: # Just to double-check: must be a number try: val = float(val) self._Value.set(event.widget.get()) # This exception should never happen! except: pass self._Entry.bind("<Return>", lambda event: fun(event, self)) self._Entry.bind("<FocusOut>", lambda event: fun(event, self)) # Tracing the _Value self._Value.trace(mode = "w", callback = self.OnTrace) # Disbale if necessary if not active: self.disable()
[docs] def isValidInt(self, x, from_ = -999, to = 999): """isValidInt(x, from_ = -999, to = 999) Helper function to check whether ``x`` is a valid integer in the range ``[from_,to]``. Parameters ---------- x : int Value to be validated from_ : int Lower limit of the valid range to : int Upper limit of the valid range Returns ------- bool Returns ``True`` if ``x`` is a valid float within ``[from_, to]`` and ``False`` otherwise. """ # If empty if len(x) == 0: return True import re # If not matching signed integer: return False if not re.match("^-?(0|[1-9]|[1-9][0-9]{1,2})?$", x): return False # Only a "-": that's Ok if re.match("^-$", x): return True # Outside range? Return False if float(x) < float(from_) or float(x) > float(to): return False # Else True return True
[docs] def isValidFloat(self, x, from_ = -999., to = 999.): """isValidFloat(x, from_ = -999., to = 999.) Helper function to check whether ``x`` is a valid float in the range ``[from_,to]``. Parameters ---------- x : float Value to be validated from_ : float Lower limit of the valid range to : float Upper limit of the valid range Returns ------- bool Returns ``True`` if ``x`` is a valid float within ``[from_, to]`` and ``False`` otherwise. """ # If empty if len(x) == 0: return True # If no valid float: return False try: x = float(x) from_ = float(from_) to = float(to) except Exception as e: return False # If more than one digits: import re if from_ >= 0: if not re.match("[0-9]+(\\.|\\.[0-9])?$", str(x)): return False else: if not re.match("-?[0-9]+(\\.|\\.[0-9])?$", str(x)): return False return True
[docs] def OnTrace(self, *args, **kwargs): """OnTrace(*args, **kwargs) Triggered when :py:func:`Slider.trace` is triggered. The method is loading the current value and sets the ``Tk.Scale`` and ``Tk.Entry`` element to the new value. """ val = self._Value.get() self.set(self._Value.get())
[docs] def name(self): """name() Returns ------- str Returns the name of the :py:class:`Slider`. """ return self._name
def set(self, val): # Reading text value and adjust slider self._Scale.set(val) # Setting Text self._Entry.delete(0, END) self._Entry.insert(0, val)
[docs] def get(self): """get() Returns ------- int or float Returns the current value of the slider. The return value depends on the slider config (`int` or `float`). """ return self._Value.get()
[docs] def trace(self, mode, *args, **kwargs): """trace(mode, *args, **kwargs) Trace method of the :py:class:`Slider` object. Parameters ---------- mode : str default is ``w`` (call observer when variable is written) args : ... arguments passed to ``Tkinter.<vartype>.trace()``, at least one argument (a callback function) should be provided kwargs : ... arguments passed to ``Tkinter.<vartype>.trace()``, unused """ self._Value.trace(mode, *args, **kwargs)
[docs] def disable(self): """disable() Disables the :py:class:`Slider`. """ self._Scale.configure(state = "disabled", relief = FLAT, bg = self.FGDISABLED, activebackground = self.FGDISABLED, troughcolor = self.BGDISABLED) self._Label.configure(fg = self.DISABLED) self._Entry.configure(state = "disabled", relief = FLAT, fg = self.DISABLED, bg = self.BGDISABLED) self._is_active = False
[docs] def enable(self): """enable() Enables the :py:class:`Slider`. """ self._Scale.configure(state = "active", bg = self.FGACTIVE, activebackground = self.FGACTIVE, troughcolor = self.BGACTIVE) self._Label.configure(fg = "black") self._Entry.configure(state = "normal", relief = FLAT, fg = "#000000", bg = "white") self._is_active = True
[docs] def is_active(self): """is_active() Returns ``True`` if the :py:class:`Slider` is active and ``False`` if currently inactive. """ ##return not self._Scale.config()["state"][4] == "disabled" return self._is_active
# ------------------------------------------------------------------- # ------------------------------------------------------------------- # -------------------------------------------------------------------
[docs]class defaultpalettecanvas(object): """defaultpalettecanvas(palframe, sliders, pal, n, xpos, figwidth, figheight) Sets up a ``Tk.Canvas`` element containing the colors of the default HCL color palettes which will be placed in the top part of the GUI. Parameters ---------- palframe : ``Tk.Frame`` the bounding ``Tk.Frame`` which takes up the palettes. sliders : list list of :py:class:`Slider` objects. When a user selets a new default palette the sliders will be set to the specification given the selected palette (and enabled/disabled corresponding to the palette specification) pal : :py:class:`defaultpalette` the default color palette n : int number of colors to be drawn xpos : numeric x position within ``Tk.Canvas`` (palframe input) figwidth : numeric width of the ``Tk.Canvas`` element (palframe input) figheight : numeric width of the ``Tk.Canvas`` element (palframe input) """ def __init__(self, palframe, sliders, pal, n, xpos, figwidth, figheight): self._palframe = palframe self._sliders = sliders self._pal = pal colors = pal.colors(n) self._draw_canvas(colors, xpos, figwidth, figheight) def _draw_canvas(self, colors, xpos, figwidth, figheight): # Compute width and height of the color map offset = 0 # White frame around the palettes n = len(colors) h = (figheight - 2. * offset) / len(colors) w = figwidth - 2. * offset canvas = Canvas(self._palframe, width = figwidth, height = figheight, bg = "#ffffff") canvas.place(x = xpos, y = 0) # Binding for interaction canvas.bind("<Button-1>", lambda event: \ self._activate(event, self._pal, self._sliders)) for i in range(0, n): canvas.create_rectangle(offset, i*h, w, (i+1)*h, width = 0, fill = colors[i]) def _activate(self, event, pal, sliders): # Loading settings of the current palette settings = pal.get_settings() for elem in sliders: # Setting value, ensable slider if elem.name() == "n": continue elif elem.name() in settings.keys(): elem.set(settings[elem.name()]) elem.enable() # Disable slider else: elem.disable()
# ------------------------------------------------------------------- # ------------------------------------------------------------------- # -------------------------------------------------------------------
[docs]class currentpalettecanvas(object): """currentpalettecanvas(parent, x, y, width, height) Draws the current palette (the palette as specified on the GUI), will be displayed in the lower part of the GUI. Parameters ---------- parent : ``Tk`` the ``Tk`` object (interface) x : numeric x position on the interface y : numeric y position on the interface width : numeric width of the palette on the interface height : numeric height of the palette on the interface """ def __init__(self, parent, x, y, width, height): self.parent = parent self.x = x self.y = y self.width = width self.height = height self.canvas = Canvas(self.parent, width = self.width, height = self.height) self.canvas.config(borderwidth = 1, bg = "#000000") self.canvas.place(x = self.x, y = self.y + 20) def _draw_canvas(self, colors): from numpy import floor n = len(colors) w = floor(float(self.width) / float(len(colors))) h = self.height # Overwrite everything with a white box self.canvas.create_rectangle(0, 0, self.width, self.height + 1, width = 0, fill = "white") for i in range(0, n): # Dropping Nan's if len(str(colors[i])) < 7: continue # Last box to self.width x1 = (i+1) * w if i < (n-1) else self.width self.canvas.create_rectangle(i*w, 0, x1, h+1, width = 0, fill = colors[i])
# ------------------------------------------------------------------- # ------------------------------------------------------------------- # -------------------------------------------------------------------
[docs]def choose_palette(**kwargs): """choose_palette(**kwargs) Graphical user interface to choose HCL based color palettes. Returns an object of :py:class:`palettes.diverging_hcl`, :py:class:`palettes.qualitative_hcl`, or :py:class:`palettes.sequential_hcl` with user-defined default settings. Parameters ---------- kwargs : ... See :py:class:`choose_palette.gui`. Returns ------- `palettes.hclpalette` object The object allows to get colors in different ways, the default is a list with hex colors. See :py:class:`palettes.hclpalette` or, more specifically, the manual of the depending palette (:py:class:`palettes.diverging_hcl`, :py:class:`palettes.qualitative_hcl`, or :py:class:`palettes.sequential_hcl`). """ obj = gui(**kwargs) from . import palettes method = getattr(palettes, obj.method()) # Overwrite __init__ method, add new defaults import sys if sys.version_info.major < 3: varnames = list(method.__init__.im_func.func_code.co_varnames) defaults = list(method.__init__.im_func.func_defaults) else: varnames = list(method.__init__.__code__.co_varnames) defaults = list(method.__init__.__defaults__) # Getting current parameters from slider object settings = {} for s in obj.sliders(): # Only read active sliders if not s.is_active(): continue # If active, store current value settings[s.name()] = s.get() # Prepare new default arguments for key in ["h","c","l","p"]: k1 = "{:s}1".format(key) k2 = "{:s}2".format(key) key = "power" if key == "p" else key if k1 in settings.keys() and k2 in settings.keys(): settings[key] = [settings[k1], settings[k2]] del settings[k1] del settings[k2] elif k1 in settings.keys(): settings[key] = settings[k1] del settings[k1] elif k2 in settings.keys(): settings[key] = settings[k2] del settings[k2] for idx,key in enumerate(varnames[1:(len(defaults)+1)]): if key in settings.keys(): defaults[idx] = settings[key] # Ugly but functional # Possibly nicer with functools.partial import types method.__init__ = types.FunctionType(method.__init__.__code__, method.__init__.__globals__, method.__init__.__name__, tuple(defaults), method.__init__.__closure__) return method()
# ------------------------------------------------------------------- # This is the GUI itself (called by choose_palette which is handling # the return). # -------------------------------------------------------------------
[docs]class gui(object): """choose_palette(\\**kwargs) Graphical user interface to choose custom HCL-basec color palettes. Parameters ---------- kwargs : ... Optional, can be used to change the defaults when starting the GUI. Currently a parameter called ``palette`` is allowed to specify the initial color palette. If not set, ``palette = "Blue-Red"`` is used. Example ------- >>> colorspace.choose_palette() """ WIDTH = 400 HEIGHT = 700 FRAMEHEIGHT = 100 FRAMEWIDTH = WIDTH - 20 # Slider settings _slider_settings = { "h1" : {"type": "int", "from": -360, "to": 360, "resolution": 1}, "h2" : {"type": "int", "from": -360, "to": 360, "resolution": 1}, "c1" : {"type": "int", "from": 0, "to": 100, "resolution": 1}, "cmax" : {"type": "int", "from": 0, "to": 180, "resolution": 1}, "c2" : {"type": "int", "from": 0, "to": 100, "resolution": 1}, "l1" : {"type": "int", "from": 0, "to": 100, "resolution": 1}, "l2" : {"type": "int", "from": 0, "to": 100, "resolution": 1}, "p1" : {"type": "float", "from": 0, "to": 3, "resolution": .1}, "p2" : {"type": "float", "from": 0, "to": 3, "resolution": .1}, "n" : {"type": "int", "from": 2, "to": 30, "resolution": 1} } _sliders = [] # Canvas for the current palette _currentpalette = None # Dropdown menu (Dropdown) _Dropdown = None # Frame taking up the default palettes _palframe = None # Tkiter object for the demo _demoTk = None # Used to store the control buttons (desaturate, reversed, ...) _control = None # Initialize defaults _setting_names = ["h1","h2","c1","cmax","c2","l1","l2","p1","p2","n"] _settings = {} for key in _setting_names: _settings[key] = 7 if key == "n" else None def __init__(self, **kwargs): # Initialization arguments, if any init_args = {} # Default if no inputs are set if not "palette" in kwargs.keys(): palette = "Blue-Red" else: palette = kwargs["palette"] # Find initial values from . import hclpalettes self._palettes = hclpalettes() pal = self._palettes.get_palette(palette) # Store palette name and palette type to select # the correct dropdown entries init_args["name"] = pal.name() init_args["type"] = pal.type() for key,val in pal.get_settings().items(): init_args[key] = val # Save palette settings self.settings(**pal.get_settings()) # Initialize gui self._master = self._init_master() # The different palette types self._Dropdown = self._add_paltype_dropdown() # Adding current palette has to be before the sliders # as they need the current palette canvas for the # to be able to be reactive. self._sliders = self._add_sliders() # Adding dropdown menu and select color map # Add the frame with the default palettes # on top of the GUI self._palframe = self._add_palframe(pal.type()) ## Add the horizontal color map for current colors. self._currentpalette = self._add_currentpalettecanvas() self._draw_currentpalette() self._DEMO = self._add_demo_options() self._add_return_button() # Adding control checkboxes and radio buttons self._control = self._add_control() # Initialize interface mainloop() def _add_paltype_dropdown(self): """_add_paltype_dropdown() Adds a drop down menu to the GUI which allowes to switch between the different types of the default palettes (see also :py:class:`palettes.hclpalettes`). """ opts = self.palettes().get_palette_types() paltypevar = StringVar(self.master()) paltypevar.set(opts[0]) # default value # Option menu menu = OptionMenu(self.master(), paltypevar, *opts, command = self.OnPaltypeChange) #obj.selected) menu.config(width = 40, pady = 5, padx = 5) menu.grid(column = 1, row = len(opts)) menu.place(x = 10, y = 30) return paltypevar
[docs] def OnPaltypeChange(self, *args, **kwargs): """OnPaltypeChange(*args, **kwargs) The callback function of the drop down element. Triggered every time the drop down element changes. """ # Updating the palette-frame. self._palframe = self._add_palframe(args[0]) # Take first palette p = self.palettes().get_palettes(args[0])[0] # Enable/disable/set sliders settings = p.get_settings() for elem in self.sliders(): # Setting value, ensable slider if elem.name() == "n": continue elif elem.name() in settings.keys(): elem.set(settings[elem.name()]) elem.enable() # Disable slider else: elem.disable()
def _add_control(self): """_add_control() Adds the check buttons (``Tk.Checkbutton``) and radio button (``Tk.Radiobutton``) elements. Color fixup, revert colors, and CVD options. """ control = {} frame = Frame(self.master(), height = 30, width = self.WIDTH - 20) frame.grid() frame.place(x = 10, y = self.HEIGHT - 140) col = 0; row = 0 # Fixup colors fixupvar = BooleanVar() fixupbutton = Checkbutton(frame, text="Fixup colors", variable = fixupvar, command = self.OnChange) fixupbutton.grid(column = col, row = row, sticky = "w"); row += 1 fixupbutton.select() control["fixup"] = fixupvar # Reverse colors revvar = BooleanVar() revbutton = Checkbutton(frame, text="Reverse colors", variable = revvar, command = self.OnChange) revbutton.grid(column = col, row = row, sticky = "w"); row += 1 control["reverse"] = revvar # Butons for Desaturation/CVD ypos = self.HEIGHT - 40 desatvar = BooleanVar() desatbutton = Checkbutton(frame, text="Desaturation", command = self.OnChange, variable = desatvar) desatbutton.grid(column = col, row = row, sticky = "w"); row += 1 control["desaturate"] = desatvar cvdvar = BooleanVar() cvdbutton = Checkbutton(frame, text="Color blindness", command = self.OnChange, variable = cvdvar) cvdbutton.grid(column = col, row = row, sticky = "w"); col += 1 control["cvd"] = cvdvar # Radio buttons for CVD ypos = self.HEIGHT - 20 cvdtypevar = StringVar() radio_deutan = Radiobutton(frame, text = "deutan", command = self.OnChange, variable = cvdtypevar, value = "deutan") radio_protan = Radiobutton(frame, text = "protan", command = self.OnChange, variable = cvdtypevar, value = "protan") radio_tritan = Radiobutton(frame, text = "tritan", command = self.OnChange, variable = cvdtypevar, value = "tritan") radio_deutan.grid(column = col, row = row, sticky = "w"); col += 1 radio_protan.grid(column = col, row = row, sticky = "w"); col += 1 radio_tritan.grid(column = col, row = row, sticky = "w"); col += 1 cvdtypevar.set("deutan") control["cvdtype"] = cvdtypevar return control
[docs] def control(self): """control() Returns ------- dict Returns a dictionary with the current control options (see :py:func:`_add_control`). """ if not self._control: return {"reverse" : False, "desaturate" : False, "cvd" : False, "cvdtype" : "deutan", "fixup": True} else: res = {} res["reverse"] = self._control["reverse"].get() res["desaturate"] = self._control["desaturate"].get() res["cvd"] = self._control["cvd"].get() res["cvdtype"] = self._control["cvdtype"].get() res["fixup"] = self._control["fixup"].get() return res
[docs] def settings(self, *args, **kwargs): """settings( *args, **kwargs) Used to load/store current palette settings (gui settings). Parameters ---------- args : ... strings to load one/several parameters. kwargs : ... named arguments, used to store values. Returns ------- dict Returns a dictionary with the current slider settings. """ # Return current settings if len(args) == 0 and len(kwargs) == 0: return self._settings # Return some settings elif len(args): res = {} for key in args: if not isinstance(key, str): continue # Loading setting if key in self._setting_names: res[key] = self._settings[key] if len(res) == 1: return res[list(res.keys())[0]] else: return reskk # Store values, if possible. else: for key,val in kwargs.items(): if key in self._setting_names: self._settings[key] = val
def _init_master(self): """_init_master() Initializes the ``Tk`` GUI window. """ # initialize mater TK interface master = Tk() master.wm_title("Colorspace - Choose Color Palette") master.configure() master.resizable(width=False, height=False) master.geometry("{:d}x{:d}".format(self.WIDTH, self.HEIGHT)) master.bind("<Return>", self._return_to_python) master.bind("<Escape>", self._return_to_python) return master
[docs] def master(self): """master() Returns ------- ``Tk`` Returns the ``Tk`` GUI object. """ return self._master
[docs] def palettes(self): """palettes() Returns ------- :py:class:`palettes.hclpalettes` Returns the default palettes available. """ return self._palettes
[docs] def sliders(self): """sliders() Returns ------- list List of :py:class:`Slider` objects. """ return self._sliders
[docs] def palframe(self): """palframe() Returns ------- ``Tk.Frame`` Returns the palette frame (``Tk.Frame`` object, see :py:func:`_add_palframe`). """ return self._palframe
def _add_palframe(self, type_): """_add_palframe() Adds a ``Tk.Frame`` to the ``Tk`` element (see :py:func:`_init_master`). This frame is used to take up the default palettes. """ ##scroll dev### if hasattr(self, "_palframe"): ##scroll dev### if not self._palframe is None: self._palframe.destroy() ##scroll dev### frame = Frame(self.master()) ##scroll dev### frame.place(x = 10, y = 80) ##scroll dev### # Loading palettes of currently selected palette type ##scroll dev### from numpy import min ##scroll dev### pals = self.palettes().get_palettes(type_) ###self.dd_type.get()) ##scroll dev### for child in frame.winfo_children(): child.destroy() ##scroll dev### canvas = Canvas(frame, bg = "#ffffff", ##scroll dev### scrollregion = (0,0,2000,0), ##scroll dev### height = self.FRAMEHEIGHT, width = self.FRAMEWIDTH) ##scroll dev### scroll = Scrollbar(frame, orient = HORIZONTAL) ##scroll dev### scroll.pack(side = BOTTOM,fill = X) ##scroll dev### scroll.config(command = canvas.xview) ##scroll dev### canvas.config(xscrollcommand = scroll.set) ##scroll dev### canvas.pack(fill = BOTH, expand = True) if hasattr(self, "_palframe"): if not self._palframe is None: self._palframe.destroy() frame = Frame(self.master(), bg = "#ffffff", height = self.FRAMEHEIGHT, width = self.FRAMEWIDTH) frame.place(x = 10, y = 80) # Loading palettes of currently selected palette type from numpy import min pals = self.palettes().get_palettes(type_) ###self.dd_type.get()) for child in frame.winfo_children(): child.destroy() # Adding new canvas figwidth = min([30, self.FRAMEWIDTH / len(pals)]) xpos = 0 for pal in pals: defaultpalettecanvas(frame, self.sliders(), pal, 5, xpos, figwidth, self.FRAMEHEIGHT) xpos += figwidth return frame def _add_currentpalettecanvas(self): """_add_currentpalettecanvas() Adds a ``Tk.Canvas`` object to the GUI to display the current color palette as specified by the GUI settings. Returns ------- ``Tk.Canvas`` Returns the canvas. """ canvas = currentpalettecanvas(self.master(), x = 20, y = 500, width = self.WIDTH - 40, height = 30) return canvas def _draw_currentpalette(self): """_draw_currentpalette() Shows the colors in the current palette frame. """ # Re-draw the canvas. self._currentpalette._draw_canvas(self.get_colors())
[docs] def get_colors(self): """get_colors() Returns ------- list Returns a list of hex colors and ``nan`` given the current settings on the GUI. ``numpy.nan`` will be returned if ``fixup`` is set to False but some colors lie outside the RGB color space. """ # Getting current arguments params = {} for elem in self.sliders(): if elem.is_active(): params[elem.name()] = float(elem.get()) # Manipulate params for dim in ["h", "c", "l","p"]: dim1 = "{:s}1".format(dim) dim2 = "{:s}2".format(dim) dim3 = "{:s}max".format(dim) dim = "power" if dim == "p" else dim # Boolean vector check = [dim1 in params.keys(), dim2 in params.keys(), dim3 in params.keys()] if check[0] and check[1] and check[2]: params[dim] = [params[dim1], params[dim2], params[dim3]] del params[dim1]; del params[dim2]; del params[dim3] elif check[0] and check[2]: params[dim] = [params[dim1], params[dim1], params[dim3]] del params[dim1]; del params[dim3] elif check[0] and check[1]: params[dim] = [params[dim1], params[dim2]] del params[dim1] del params[dim2] elif check[0]: params[dim] = params[dim1] del params[dim1] for elem in self.sliders(): if elem.name() == "n": n = elem.get() break # Check if we have to return the colors reversed. # and whether or not fixup is set to True/False control = self.control() if not "h" in params.keys(): sys.exit("whoops, lost h") if "n" in params: del params["n"] params["fixup"] = control["fixup"] # Craw colors from current color map from . import palettes type_ = self._Dropdown.get() colorfun = self.palettes().get_palettes(type_)[0].method() fun = getattr(palettes, colorfun) # Return colors colors = fun(**params)(n, rev = control["reverse"]) # Do we have to desaturate the colors? if control["desaturate"]: from .CVD import desaturate colors = desaturate(colors) # Do we have to apply CVD simulation? if control["cvd"]: import CVD fun = getattr(CVD, control["cvdtype"]) colors = fun(colors) return colors
[docs] def method(self): """method() Returns ------- str Returns the name of the object which has to be called to get the colors. The name of the object is defined in the palconfig config files. For "Diverging" palettes this will be :py:class:`palettes.diverging_hcl`, for "Qualitative" :py:class:`palettes.qualitative_hcl`, and for "Sequential" palettes :py:class:`palettes.sequential_hcl`. """ type_ = self._Dropdown.get() colorfun = self.palettes().get_palettes(type_)[0].method() return colorfun
def _add_sliders(self): """_add_sliders() Adds a set of sliders to the GUI. Returns ------- list a list of :py:class:`Slider` objects. """ sliders = [] # For each key in self._setting_names add a Slider object # (a Slider is a combined Tkinter.Scale, Tkinter.Entry, and # Tkinter.Label element with bindings). for idx,key in enumerate(self._setting_names): # Initialize with default 0 if nothing yet specified. s = Slider(self.master(), key, # name 10, 100 + idx * 30 + self.FRAMEHEIGHT, # x, y self.WIDTH - 20, 30, # width, height False if self.settings()[key] is None else True, # active type_ = self._slider_settings[key]["type"], from_ = self._slider_settings[key]["from"], to = self._slider_settings[key]["to"], resolution = self._slider_settings[key]["resolution"]) if not self.settings(key): s.set("0") else: s.set(str(self.settings(key))) # Append slider to list sliders.append(s) # Add the trace element to make them interactive # (an observer, call OnChange whenever the Scale changes). for x in sliders: x.trace("w", self.OnChange) return sliders # Callback when an item is getting changed
[docs] def OnChange(self, *args, **kwargs): """OnChange(*args, **kwargs) Triggered any time the slider values or control arguments change. Draws new current palette (see :py:func:`_draw_currentpalette`). """ self._draw_currentpalette() # Is the demo running? if self._demoTk: self._show_demo(True)
def _add_demo_options(self): """_add_demo_options() Adds a ``Tk.Button`` to open the demo plot window. .. todo:: Currently not enabled (have had some problems with the interaction/update). """ but = Button(self.master(), text = "Demo", command = self._show_demo, pady = 5, padx = 5) but.place(x = self.WIDTH - 70, y = self.HEIGHT - 40) # Variable to store current selection opts = ["Bar", "Heatmap", "Pie", "Spine", "Matrix", "Lines", "Spectrum"] demovar = StringVar(self.master()) demovar.set(opts[0]) # default value # Demo plot option menu. No callback menu = OptionMenu(self.master(), demovar, *opts, command = self.OnChange) menu.config(width = 10, pady = 5, padx = 5) menu.grid(column = 1, row = len(opts)) menu.place(x = 180, y = self.HEIGHT - 40) return demovar def _add_return_button(self): """_add_return_button() Adds the button to return to python, a ``Tk.Button`` element. When clicked :py:func:`_return_to_python` is triggered (callback function for this button). """ but = Button(self.master(), text = "Return to python", command = self._return_to_python, pady = 5, padx = 5) but.place(x = 10, y = self.HEIGHT - 40) def _return_to_python(self, *args): """_return_to_python() Returns to :py:func:`choose_palette`. Destroys the ``Tk`` interface but not the object such that :py:func:`choose_palette` can read the settings of the sliders and control elements. Used to create the palette which will then be returned to the console/user console. """ # Destroy the demo figure if not already closed try: import matplotlib.pyplot as plt plt.close(self._demo_Figure) except: pass # Close demo window if not already closed try: self._demoTk.destroy() except: pass self.master().destroy() self.master().quit() def _show_demo(self, update = False): """_show_demo(update = False) Show demo. .. todo:: Implement and test. .. note:: Requires matplotlib. Will show a message if matplotlib is not installed on the machine. """ try: from matplotlib.backends.backend_tkagg import FigureCanvasAgg import matplotlib.backends.tkagg as tkagg from matplotlib.figure import Figure hasmatplotlib = True except: hasmatplotlib = False # If has matplotlib: plot if hasmatplotlib: def draw_figure(canvas, figure, loc=(0, 0)): """ Draw a matplotlib figure onto a Tk canvas loc: location of top-left corner of figure on canvas in pixels. Inspired by matplotlib source: lib/matplotlib/backends/backend_tkagg.py """ figure_canvas_agg = FigureCanvasAgg(figure) figure_canvas_agg.draw() figure_x, figure_y, figure_w, figure_h = figure.bbox.bounds figure_w, figure_h = int(figure_w), int(figure_h) photo = PhotoImage(master=canvas, width=figure_w, height=figure_h) # Position: convert from top-left anchor to center anchor canvas.create_image(loc[0] + figure_w/2, loc[1] + figure_h/2, image=photo) # Unfortunately, there's no accessor for the pointer to the native renderer tkagg.blit(photo, figure_canvas_agg.get_renderer()._renderer, colormode=2) # Return a handle which contains a reference to the photo object # which must be kept live or else the picture disappears return photo import matplotlib matplotlib.use("TkAgg") from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg import matplotlib.pyplot as plt # Initialize plot if not hasattr(self, "_demo_Figure"): self._demo_Figure = plt.figure() if not update: self._demoTk = Tk() self._demoTk.protocol("WM_DELETE_WINDOW", self._close_demo) #demoTk.destroy) self._demoTk.wm_title("colorspace demoplot") self._demo_Axis = self._demo_Figure.add_subplot(211) self._demo_Canvas = FigureCanvasTkAgg(self._demo_Figure, master=self._demoTk) self._demo_Canvas._tkcanvas.pack(side=TOP, fill=BOTH, expand=1) # Getting demo function from . import demos fun = getattr(demos, self._DEMO.get()) # Update plot fun(self.get_colors(), fig = self._demo_Figure) self._demo_Canvas.draw() self._demo_Canvas.flush_events() else: info = [""] info.append("To be able to run the demo plots") info.append("the python matplotlib package has to be") info.append("installed.") info.append("") info.append("Install matplotlib and try again!") txt = Text(self._demowindow_, height=10, width=45) txt.pack() txt.insert(END, "\n".join(info)) def _close_demo(self): import matplotlib.pyplot as plt self._demoTk.destroy(); self._demoTk = None