Source code for tools.toggleablebutton

######################################
#    toggleablebutton.py
#    @author: Grant Mercer
#    6/24/2015
######################################
from Tkinter import Button
import ttk
from sys import platform as _platform
from constants import EFFECT_ON, EFFECT_OFF

# global button container for managing state
toggleContainer = []


[docs]class ToggleableButton(Button): """ Button wrapper which simulates the toggled button as you see in the draw, magnify, etc. buttons. Interally keeps a bind map which on toggle binds the keys in the map, and unbinds them on untoggle or forced untoggle. :param root: Root of the program, which handles the cursor :param canvas: The matplotlib Tkinter canvas to connect binds to :param master: The location to draw the button to :param cnf: Button forwarded args :param \*\*kw: Button forwarded args """ # noinspection PyDefaultArgument def __init__(self, root, master, cnf={}, **kw): self.__bindMap = [] # bind map to be bound once toggled self.isToggled = False # internal var to keep track of toggling self.__root = root # root variable for setting the cursor self.__cursor = '' # cursor private var self.__destructor = None # destructor var called when untoggled self.__cid_stack = [] self.__master = master Button.__init__(self, master, cnf, **kw) # call button constructor self.configure(command=self.toggle) # button command is always toggle toggleContainer.append(self) # push button to static container
[docs] def latch(self, target=None, key='', command=None, cursor='', destructor=None): """ Allows the binding of keys to certain functions. These bindings will become active once the button is in a toggled state. latch can be called **multiple** times and keeps an internal bindmap. :param str key: A valid Tkinter key string (matplotlib key event string) :param command: Function to be bound to key :param str cursor: A valid Tkinter cursor string :param destructor: A function called when untoggled """ # only set these variables if the user entered one if cursor != '': self.__cursor = cursor if key != '' and command is not None and target is not None: self.__bindMap.append((target, key, command)) if destructor is not None: self.__destructor = destructor
[docs] def untoggle(self): """ Forcefully untoggles the button. Used when ensuring only one button in the global container is active at any time """ self.isToggled = False self.config(**EFFECT_OFF) self.__root.config(cursor='') for pair in self.__bindMap: if self.__cid_stack: pair[0].mpl_disconnect(self.__cid_stack.pop()) if self.__destructor: self.__destructor()
[docs] def toggle(self): """ The method bound to the button, *Toggle* will internally bind the inputed keys when toggled, and unbind them accordingly. Also keeps track of all toggled button via a static container and ensures only one button can be toggled at any time """ # first flip the toggle switch self.isToggled = not self.isToggled # if any buttons are currently active, untoggle them for s in [x for x in toggleContainer if x.isToggled is True and x is not self]: s.untoggle() # else if next state it false if self.isToggled is False: self.__root.config(cursor='') self.config(**EFFECT_OFF) # raise the button, e.g. deactivated for pair in self.__bindMap: # unbind using the bindmap if self.__cid_stack: pair[0].mpl_disconnect(self.__cid_stack.pop()) if self.__destructor: self.__destructor() # call the pseudo 'destructor' # else if next state is true else: self.__root.config(cursor=self.__cursor) self.config(**EFFECT_ON) # sink the button, e.g. activate for pair in self.__bindMap: # bind using the bindmap self.__cid_stack.append(pair[0].mpl_connect(pair[1], pair[2]))
[docs]class ToolbarToggleableButton(Button): """ GUI button used to implement the backend matplotlib plot functions. Instead of placing more overhead in the ToggleableButton another class is created since the number of matplotlib functions will remain constant, while we may continue creating new tools that use ToggleableButton :param root: Root of the program, or the location of the cursor to be changed :param master: Location of the button to be drawn to :param func: Function to be called each time the button is 'toggled' :param cnf: Button forwarded args :param \*\*kw: Button forwarded args """ # noinspection PyDefaultArgument def __init__(self, root, master=None, func=None, cnf={}, **kw): if not cnf: cnf = {} self.isToggled = False # internal var to keep track of toggling self.__root = root # root variable for setting the cursor self.__cursor = '' # cursor private var self.__master = master self.__func = func Button.__init__(self, master, cnf, **kw) # call button constructor self.configure(command=self.toggle) # button command is always toggle toggleContainer.append(self) # push button to static container
[docs] def latch(self, cursor=''): """ Set the internal cursor variable to the cursor to be used when the button is in a toggled state :param str cursor: A valid Tkinter cursor string """ # only set these variables if the user entered one if cursor != '': self.__cursor = cursor
[docs] def untoggle(self): """ Forcefully untoggles the button and invokes ``func``. Used when ensuring only one button in the global container is active at any time """ self.isToggled = False self.config(**EFFECT_OFF) if self.__func: self.__func()
# Call the super classes Toggle, and execute our function as well
[docs] def toggle(self): """ Calls the passed function ``func`` and manages a toggle state below. Ensures only one toggled button is active at any time and the button is correctly raised/sunk """ self.isToggled = not self.isToggled # if any buttons are currently active, untoggle them for s in [x for x in toggleContainer if x.isToggled is True and x is not self]: s.untoggle() # first flip the toggle switch if self.__func: self.__func() # else if next state it false if self.isToggled is False: self.config(**EFFECT_OFF) # raise the button, e.g. deactivated # else if next state is true else: self.__root.config(cursor=self.__cursor) self.config(**EFFECT_ON) # sink the button, e.g. activate