# Python interface to some of the commands of the 2.1 version of the
# BLT extension to tcl.

import types
import Tkinter

# Supported commands:
if Tkinter.TkVersion >= 8.0:
    _busyCommand = 'blt::busy'
    _vectorCommand = 'blt::vector'
    _graphCommand = 'blt::graph'
else:
    _busyCommand = 'busy'
    _vectorCommand = 'vector'
    _graphCommand = 'graph'

# The graph and barchart widgets are essentially the same, so only
# graph has been ported.  If you want bars, use the element_bar()
# method of Graph.

_haveBlt = None

def _checkForBlt(window):
    global _haveBlt

    # Blt may be a package which has not yet been loaded.
    try:
	window.tk.call('package', 'require', 'BLT')
    except Tkinter.TclError:
	# Another way to try to dynamically load blt:
	try:
	    window.tk.call('load', '', 'Blt')
	except Tkinter.TclError:
	    pass

    try:
	window.tk.call(_busyCommand, 'windows')
	_haveBlt = 1
    except Tkinter.TclError:
	_haveBlt = 0

def haveblt(window):
    if _haveBlt is None:
	_checkForBlt(window)
    return _haveBlt

def busy_hold(window):
    window.tk.call(_busyCommand, 'hold', window._w)

def busy_release(window):
    window.tk.call(_busyCommand, 'release', window._w)

#=============================================================================
# Interface to the blt vector command
# The dup, offset and populate methods and the +, -, * and / operations
# are not supported.

class Vector:
    _varnum = 0
    def __init__(self, size=None, master=None):
	if master:
	    self._master = master
	else:
	    self._master = Tkinter._default_root
	self._tk = self._master.tk
	self._name = 'PY_VEC' + str(Vector._varnum)
	Vector._varnum = Vector._varnum + 1
	if size is None:
	    self._tk.call(_vectorCommand, self._name)
	else:
	  self._tk.call(_vectorCommand, self._name + '(' + str(size) + ')')
    def __del__(self):
	self._tk.globalunsetvar(self._name)
    def __str__(self):
	return self._name

    def __repr__(self):
	rtnstr = '['
	butFirst = 0
	for value in self:
	    if butFirst:
		rtnstr = rtnstr + ', '
	    else:
		butFirst = 1
	    rtnstr = rtnstr + str(value)
	rtnstr = rtnstr + ']'
	return rtnstr
    def __cmp__(self, list):
	return cmp(self[:], list)

    def __len__(self): 
	return self._tk.getint(self._tk.call(self._name, 'length'))
    def __getitem__(self, key): 
	if key < 0:
	    key = key + len(self)
	try:
	    return self._tk.getdouble(self._tk.globalgetvar(self._name, str(key)))
        except Tkinter.TclError:
	    raise IndexError
    def __setitem__(self, key, value): 
	if key < 0:
	    key = key + len(self)
	return self._tk.globalsetvar(self._name, str(key), float(value))

    def __delitem__(self, key):
	if key < 0:
	    key = key + len(self)
	return self._tk.globalunsetvar(self._name, str(key))

    def __getslice__(self, start, end):
	string = self._tk.globalgetvar(self._name, str(start) + ':' + str(end))
	return map(self._tk.getdouble, self._tk.splitlist(string))
    def __setslice__(self, start, end, list):
	if end - start == len(list):
	    for count in range(end - start):
	        self[start + count] = list[count]
	else:
	    self.set(self[:start] + list + self[end:])
    def __delslice__(self, start, end):
	self.unset(start, end - 1)

    def set(self, list):
	if type(list) != types.TupleType:
	    list = tuple(list)
	self._tk.call(self._name, 'set', list)

    def get(self):
	# This should be "return self[:]", but there is a bug in blt vector.
	string = self._tk.call(self._name, 'range', 0, 'end')
	return map(self._tk.getdouble, self._tk.splitlist(string))

    def append(self, value):
	# This should be as follows, but there is a bug in blt vector unset.
	# self._tk.call(self._name, 'append', args)   (given *args as second param)
	return self._tk.globalsetvar(self._name, '++end', float(value))
    def count(self, start):
	return len(self._tk.splitlist(self._tk.call(self._name, 'search', start)))
    def search(self, start, end=None):
	return self._master._getints(self._tk.call(
		self._name, 'search', start, end))

    def index(self, value):
	strings = self._tk.splitlist(self._tk.call(self._name, 'search', value))
	if len(strings) == 0:
	    raise ValueError, str(value) + ' not in list'
	return self._tk.getint(strings[0])

    def insert(self, index, value):
	self[index:index] = [value]
    def remove(self, value):
	del self[self.index(value)]
    def reverse(self):
	s = self[:]
	s.reverse()
	self[:] = s
    def sort(self, *args):
	apply(self._tk.call, (self._name, 'sort') + args)
    def sort_reverse(self, *args):
	apply(self._tk.call, (self._name, 'sort', '-reverse') + args)

    def min(self):
	return self._tk.getdouble(self._tk.globalgetvar(self._name, 'min'))
    def max(self):
	return self._tk.getdouble(self._tk.globalgetvar(self._name, 'max'))

    def clear(self):
	self._tk.call(self._name, 'clear')
    def delete(self, *args):
	apply(self._tk.call, (self._name, 'delete') + args)
    def length(self, newSize=None): 
	return self._tk.getint(self._tk.call(self._name, 'length', newSize))
    def range(self, first, last=None):
	string = self._tk.call(self._name, 'range', first, last)
	return map(self._tk.getdouble, self._tk.splitlist(string))

#=============================================================================
class Graph(Tkinter.Widget):
    # Wrapper for the blt graph widget, version 2.1.

    def __init__(self, master=None, cnf={}, **kw):
	Tkinter.Widget.__init__(self, master, _graphCommand, cnf, kw)

    def _configure(self, subcommand, option, kw):
	# Handle configuration of widgets, canvas items, text items,
	# images, etc.  Supports the forms configure() and
	# configure('font') for querying and configure(font = 'fixed',
	# text = 'hello') for setting.

	if not option and not kw:
	    # Return a description of all options.
	    ret = {}
	    options = self.tk.splitlist(apply(self.tk.call, subcommand))
	    for optionString in options:
		optionInfo = self.tk.splitlist(optionString)
		option = optionInfo[0][1:]
		ret[option] = (option,) + optionInfo[1:]
	    return ret

	if option:
	    # Return a description of the option given by <option>.
	    if kw:
		# Having keywords implies setting configuration options.
		# Can't set and get in one command!
		raise ValueError, 'cannot have option argument with keywords'
	    option = '-' + option
	    optionInfo = self.tk.splitlist(
		    apply(self.tk.call, subcommand + (option,)))
	    return (optionInfo[0][1:],) + optionInfo[1:]

	# Otherwise, set the given configuration options.
	apply(self.tk.call, subcommand + self._options(kw))

    def extents(self, item):
	return self.tk.getint(self.tk.call(self._w, 'extents', item))

    def invtransform(self, winX, winY):
	return self._getdoubles(
		self.tk.call(self._w, 'invtransform', winX, winY))
    def transform(self, x, y):
	return self._getdoubles(self.tk.call(self._w, 'transform', x, y))

    # The axis commands may be called in one of two ways. For example:
    #   axis_cget('x', 'justify')
    # or
    #   xaxis_cget('justify')
    def axis_cget(self, axis, key):
	return self.tk.call(self._w, axis + 'axis', 'cget', '-' + key)
    def axis_configure(self, axis, option=None, **kw):
	subcommand = (self._w, axis + 'axis', 'configure')
	return self._configure(subcommand, option, kw)
    def axis_invtransform(self, axis, value):
	return self.tk.getdouble(self.tk.call(
		self._w, axis + 'axis', 'invtransform', value))
    def axis_limits(self, axis):
	return self._getdoubles(self.tk.call(
		self._w, axis + 'axis', 'limits'))
    def axis_transform(self, axis, value):
	return self.tk.getint(self.tk.call(
		self._w, axis + 'axis', 'transform', value))

    def xaxis_cget(self, key):
	return self.axis_cget('x', key)
    def xaxis_configure(self, option=None, **kw):
	return apply(self.axis_configure, ('x', option), kw)
    def xaxis_invtransform(self, value):
	return self.axis_invtransform('x', value)
    def xaxis_limits(self):
	return self.axis_limits('x')
    def xaxis_transform(self, value):
	return self.axis_transform('x', value)

    def x2axis_cget(self, key):
	return self.axis_cget('x2', key)
    def x2axis_configure(self, option=None, **kw):
	return apply(self.axis_configure, ('x2', option), kw)
    def x2axis_invtransform(self, value):
	return self.axis_invtransform('x2', value)
    def x2axis_limits(self):
	return self.axis_limits('x2')
    def x2axis_transform(self, value):
	return self.axis_transform('x2', value)

    def yaxis_cget(self, key):
	return self.axis_cget('y', key)
    def yaxis_configure(self, option=None, **kw):
	return apply(self.axis_configure, ('y', option), kw)
    def yaxis_invtransform(self, value):
	return self.axis_invtransform('y', value)
    def yaxis_limits(self):
	return self.axis_limits('y')
    def yaxis_transform(self, value):
	return self.axis_transform('y', value)

    def y2axis_cget(self, key):
	return self.axis_cget('y2', key)
    def y2axis_configure(self, option=None, **kw):
	return apply(self.axis_configure, ('y2', option), kw)
    def y2axis_invtransform(self, value):
	return self.axis_invtransform('y2', value)
    def y2axis_limits(self):
	return self.axis_limits('y2')
    def y2axis_transform(self, value):
	return self.axis_transform('y2', value)

    def crosshairs_cget(self, key):
	return self.tk.call(self._w, 'crosshairs', 'cget', '-' + key)
    def crosshairs_configure(self, option=None, **kw):
	subcommand = (self._w, 'crosshairs', 'configure')
	return self._configure(subcommand, option, kw)
    def crosshairs_off(self):
	self.tk.call(self._w, 'crosshairs', 'off')
    def crosshairs_on(self):
	self.tk.call(self._w, 'crosshairs', 'on')
    def crosshairs_toggle(self):
	self.tk.call(self._w, 'crosshairs', 'toggle')

    def element_activate(self, name, *args):
	apply(self.tk.call, (self._w, 'element', 'activate', name) + args)
    def element_bar(self, name, **kw):
	apply(self.tk.call,
	    (self._w, 'element', 'bar', name) + self._options(kw))
    def element_cget(self, name, key):
	return self.tk.call(self._w, 'element', 'cget', name, '-' + key)

    def element_closest(self, x, y, *args, **kw):
	success = self.tk.getint(apply(self.tk.call,
	    (self._w, 'element', 'closest', x, y, 'python_private_1') + 
		    args + self._options(kw)))
	if success:
	    rtn = {}
	    rtn['dist'] = self.tk.getdouble(self.tk.globalgetvar('python_private_1', 'dist'))
	    rtn['x'] = self.tk.getdouble(self.tk.globalgetvar('python_private_1', 'x'))
	    rtn['y'] = self.tk.getdouble(self.tk.globalgetvar('python_private_1', 'y'))
	    rtn['index'] = self.tk.getint(self.tk.globalgetvar('python_private_1', 'index'))
	    rtn['name'] = self.tk.globalgetvar('python_private_1', 'name')
	    return rtn
	else:
	    return None

    def element_configure(self, name, option=None, **kw):
	subcommand = (self._w, 'element', 'configure', name)
	return self._configure(subcommand, option, kw)
    def element_create(self, name, **kw):
	apply(self.tk.call,
	    (self._w, 'element', 'create', name) + self._options(kw))

    def element_deactivate(self, *args):
	apply(self.tk.call, (self._w, 'element', 'deactivate') + args)

    def element_delete(self, *args):
	apply(self.tk.call, (self._w, 'element', 'delete') + args)
    def element_exists(self, name):
	return self.tk.getboolean(
		self.tk.call(self._w, 'element', 'exists', name))

    def element_line(self, name, **kw):
	apply(self.tk.call,
	    (self._w, 'element', 'line', name) + self._options(kw))
    def element_names(self):
	return self.tk.splitlist(self.tk.call(self._w, 'element', 'names'))
    def element_show(self, nameList=None):
	if nameList is not None:
	    nameList = tuple(nameList)
	return self.tk.splitlist(
		self.tk.call(self._w, 'element', 'show', nameList))
    def element_type(self, name):
	return self.tk.call(self._w, 'element', 'type', name)

    def grid_cget(self, key):
	return self.tk.call(self._w, 'grid', 'cget', '-' + key)
    def grid_configure(self, option=None, **kw):
	subcommand = (self._w, 'grid', 'configure')
	return self._configure(subcommand, option, kw)

    def grid_off(self):
	self.tk.call(self._w, 'grid', 'off')
    def grid_on(self):
	self.tk.call(self._w, 'grid', 'on')
    def grid_toggle(self):
	self.tk.call(self._w, 'grid', 'toggle')

    def legend_activate(self, *args):
	apply(self.tk.call, (self._w, 'legend', 'activate') + args)
    def legend_cget(self, key):
	return self.tk.call(self._w, 'legend', 'cget', '-' + key)
    def legend_configure(self, option=None, **kw):
	subcommand = (self._w, 'legend', 'configure')
	return self._configure(subcommand, option, kw)
    def legend_deactivate(self, *args):
	apply(self.tk.call, (self._w, 'legend', 'deactivate') + args)
    def legend_get(self, pos):
	return self.tk.call(self._w, 'legend', 'get', pos)

    def postscript_cget(self, key):
	return self.tk.call(self._w, 'postscript', 'cget', '-' + key)
    def postscript_configure(self, option=None, **kw):
	subcommand = (self._w, 'postscript', 'configure')
	return self._configure(subcommand, option, kw)
    def postscript_output(self, fileName=None, **kw):
	prefix = (self._w, 'postscript', 'output')
	if fileName is None:
	    return apply(self.tk.call, prefix + self._options(kw))
	else:
	    apply(self.tk.call, prefix + (fileName,) + self._options(kw))

    def marker_after(self, first, second=None):
	self.tk.call(self._w, 'marker', 'after', first, second)
    def marker_before(self, first, second=None):
	self.tk.call(self._w, 'marker', 'before', first, second)
    def marker_cget(self, name, key):
	return self.tk.call(self._w, 'marker', 'cget', name, '-' + key)
    def marker_configure(self, name, option=None, **kw):
	subcommand = (self._w, 'marker', 'configure', name)
	return self._configure(subcommand, option, kw)
    def marker_create(self, type, **kw):
	return apply(self.tk.call,
	    (self._w, 'marker', 'create', type) + self._options(kw))

    def marker_delete(self, *args):
	apply(self.tk.call, (self._w, 'marker', 'delete') + args)
    def marker_exists(self, name):
	return self.tk.getboolean(
		self.tk.call(self._w, 'marker', 'exists', name))
    def marker_names(self, pattern=None):
	return self.tk.splitlist(
		self.tk.call(self._w, 'marker', 'names', pattern))
    def marker_type(self, name):
	type = self.tk.call(self._w, 'marker', 'type', name)
	if type == '':
	    type = None
	return type