#!/usr/bin/python2.3
########################################
# The contents of this file are subject to the MLX PUBLIC LICENSE version
# 1.0 (the "License"); you may not use this file except in
# compliance with the License.
# 
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See
# the License for the specific language governing rights and limitations
# under the License.
# 
# The Original Source Code is "compClust", released 2003 September 03.
# 
# The Original Source Code was developed by the California Institute of
# Technology (Caltech).  Portions created by Caltech are Copyright (C)
# 2002-2003 California Institute of Technology. All Rights Reserved.
########################################

"""
Title   : CompClust Analysis microGUI
Authors : Brandon King, Diane Trout, Chris Hart
Date    : Jan 3rd, 2004
Modified: $Date: 2004/05/13 00:17:26 $
Revision: $Revision: 2.14 $
"""

#TK imports
import Tkinter
import tkFileDialog
import tkMessageBox
import Pmw

#Standard imports
import os
import sys
import string
import imp
import glob
import types
import ConfigParser
import re

#Web Server imports
import thread
import urllib
import posixpath
import BaseHTTPServer
import SimpleHTTPServer
import webbrowser

#CompClust imports
from compClust.mlx import datasets
from compClust.mlx import views
from compClust.mlx import labelings
from compClust.mlx import wrapper
from compClust.mlx.ML_Algorithm import ML_Composable_Algorithm
#from compClust.iplot import IPlotTk as IPlot
from compClust import IPlot as IPlot
from compClust.util import DistanceMetrics
from compClust.util import WrapperPlugins
from compClust.util import WrapperParameters
from compClust.util.WrapperBuilder  import WrapperBuilder

#CompClust Clustering Wrappers
#from compClust.mlx.wrapper import KMeans#Windows specific setup

def fixWin32FilePath(path):
  # FIXME: this is almost right  
  dirFragments = string.split(path, os.sep)
  currentLongName = []
  currentShortName = [dirFragments[0]]
  dirMap = {}
  
  for i in range(len(dirFragments)-1):
    # make dirMap  
    currentLongName.append(dirFragments[i])
    cmd = 'dir /x "%s"'% (string.join(currentLongName, os.sep)+os.sep)
    dirContents = os.popen(cmd).readlines()
    dirMap = dict(map(lambda x: (string.strip(x[52:-1]).lower(), string.strip(x[39:47])), dirContents[8:-3]))
    # try to lookup key
    if dirMap.has_key(dirFragments[i+1].lower()):
      shortName = dirMap[dirFragments[i+1].lower()]
      if len(shortName) > 0:
        currentShortName.append(shortName)
      else:
        currentShortName.append(dirFragments[i+1])
    else:
      currentShortName.append(dirFragments[i+1])
  return string.join(currentShortName, os.sep)

#Help files default
HELP_PATH = None

#Icon Path
ICON_PATH = None

if sys.platform == 'linux2':
  if os.path.exists(os.path.abspath('./Docs/compclusttk_tutorial/')):
    HELP_PATH = os.path.abspath('./Docs/compclusttk_tutorial/index.html')
  elif os.path.exists('/usr/share/compclust/compclusttk_tutorial/'):
    HELP_PATH = '/usr/share/compclust/compclusttk_tutorial/index.html'

#True off by default
PY2EXE_MODE = False
if sys.platform == 'win32':
  #Find CompClustTk installation directory
  #compClustDir, notNeeded = os.path.split(os.path.split(string.__file__)[0])
  compClustDir = r'C:\Program Files\CompClustTk'

  iniPath = os.path.join(compClustDir, 'compClust.ini')

  parser = ConfigParser.ConfigParser()
  parser.read(iniPath)

  section = 'Clustering Algorithms'

  for opt in parser.options(section):
    print 'Setting Environment: %s=%s' % (opt.upper(), fixWin32FilePath(parser.get(section, opt)))
    os.environ[opt.upper()] = fixWin32FilePath(parser.get(section, opt))

  #Setup win32 help path
  section = 'Help'
  HELP_PATH = parser.get(section, 'Tutorial')

  #Setup icon path
  section = 'Icons'
  ICON_PATH = parser.get(section, 'MainIcon')
      
  #Win32 Hack for IPython to work properly when using py2exe
  sys.path.append(compClustDir)

  #HACK for allowing py2exe to work properly
  PY2EXE_MODE = True

#CompClust Interactive Analysis Shell (ipython)  
try:
  from IPython.Shell import IPythonShellEmbed
  #from IPython import hooks is used to get py2exe to actually
  #include the hooks module when making CompClustTk.exe. Without
  #it, the program crashes. Only remove if py2exe no longer has
  #this problem.
  from IPython import hooks
  IPYTHON_ENABLED = True
except:
  print 'NOTE: ipython not found.'
  IPYTHON_ENABLED = False

#GLOBALS
## Old CVS way of getting version number
#rev = '$Revision: 2.14 $'
#rev = rev.replace('$Revision: ', '')
#rev = rev.replace(' $', '')
#VERSION = '0.%s' % (rev)

## New SVN way... manually!! =o)
VERSION = '1.0.0'

_APPEND_HISTORY = 0

CONFIG_FILE_PATH_LINUX = os.path.expanduser('~/.compClustGui')

#WEB SERVER GLOBALS
HELP_LOCAL_PATH = '/home/king/public_html/compClust/'
DEFAULT_PORT = 8008



##############################
# Functions


# def _getConfDict(path):
#   f = open(path, 'r')
#   data = f.read()
#   f.close()
# 
#   data = string.split(data, '\n')
# 
#   confDict = {}
#   newData = []
# 
#   for line in data:
#     newLine = string.split(line.strip(), '=')
#     if len(newLine) == 2:
#       confDict[newLine[0]] = newLine[1]
#       
#   return confDict
#   
#
# def _setupEnvPaths(supportedKeys, confDict):
# 
#   for key in supportedKeys:
#     newKey = key.upper() + '_COMMAND'
# 
#     if not confDict.has_key(newKey):
#       try:
#         confDict[newKey] = os.environ[newKey]
#         continue
#       except KeyError, msg:
#         print 'Environment Variable %s not set.' % (msg)
# 
#       print 'Searching for %s command now.' % (key.lower())
# 
#       p = os.popen('which %s' % (key.lower()))
#       line = p.read()
#       p.close()
# 
#       if len(line) == 0:
#         print 'Could not find command %s. Please set environment variable %s.' % (key.lower(), newKey)
#         continue
# 
#       command = line.strip()
# 
#       print 'Found command %s at %s.' % (key.lower(), command)
# 
#       confDict[newKey] = command
# 
#   for key in confDict.keys():
#     print 'SETTING %s TO %s' % (key, confDict[key])
#     os.environ[key] = confDict[key]
# 
#   return confDict
#         
# 
# def _saveConfDict(path, confDict):
#   f = open(path, 'w')
# 
#   for key in confDict.keys():
#     f.write('%s=%s\n' % (key, confDict[key]))
# 
#   f.close()

def _labelingFilter(item):

  #Filter out any private labelings
  s1 = re.compile('__.*__')
  if len(s1.findall(item)) > 0:
    return False

  #Filter out ConfusionMatrix generated labelings
  #s2 = re.compile('ConfusionMatrix[\W]*\([\S\s]*\)')
  s2 = re.compile('ConfMat[\W]*\([\S\s]*\)')
  if len(s2.findall(item)) > 0:
    return False

  return True

  

  
# def _setupEnvironmentVars():
#   wrapperPlugins = WrapperPlugins.WrapperPlugins()
#   
#   #algoDict, algoClassDict = _getAlgorithmDictionaries()
#   #Proccess Linux
#   if sys.platform == 'linux2':
#     if os.path.isfile(CONFIG_FILE_PATH_LINUX):
#       confDict = _getConfDict(CONFIG_FILE_PATH_LINUX)
#       confDict = _setupEnvPaths(wrapperPlugins.keys(), confDict)
#       _saveConfDict(CONFIG_FILE_PATH_LINUX, confDict)
#     else:
#       confDict = _setupEnvPaths(wrapperPlugins.keys(), {})
#       _saveConfDict(CONFIG_FILE_PATH_LINUX, confDict)
#   elif sys.platform == 'win32':
#     #Already handled by CompClustTk (reads ini file created by installer)
#     pass
#   else:
#    print 'NOTE: %s platform config file not supported yet.' % (sys.platform)


##############################
# Classes
class SimplePathHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
  """
  Used to override the default server path... was os.getcwd()
  """

  def translate_path(self, path):
    """Translate a /-separated PATH to the local filename syntax.
    
    Components that mean special things to the local file system
    (e.g. drive or directory names) are ignored.  (XXX They should
    probably be diagnosed.)
    
    """
    path = posixpath.normpath(urllib.unquote(path))
    words = path.split('/')
    words = filter(None, words)
    path = HELP_LOCAL_PATH
    for word in words:
      drive, word = os.path.splitdrive(word)
      head, word = os.path.split(word)
      if word in (os.curdir, os.pardir): continue
      path = os.path.join(path, word)
    return path


def runWebServer(server_class=BaseHTTPServer.HTTPServer,
                 handler_class=SimplePathHTTPRequestHandler,
                 port=DEFAULT_PORT):
  server_address = ('', port)
  httpd = server_class(server_address, handler_class)
  httpd.serve_forever()


class ClusteringDialog:
  """Create the dialog box that prompts the user for the clustering algorithm parameters
  
  Returns labeling name, True, and wrapper builder object
  """
  def __init__(self, parent, dataset, algorithm, wrapperPlugins):
    #Tkinter parent
    self.parent = parent

    self.wrapper_plugins = wrapperPlugins
    self.wrapper_algorithm_names = self.wrapper_plugins.keys()
    self.wrapper_algorithm_names.sort()

    #Class vars
    self.builder = WrapperBuilder(dataset)

    self.AUTO_UPDATE = True

    #Pop up Dialog
    self.dialog = Pmw.Dialog(self.parent,
                             buttons=('Cluster', 'Cancel'),
                             defaultbutton='Cancel',
                             title='%s Clustering Algorithm' % (algorithm.name),
                             command=self._processButtonBox)

    #Disable Load Button Until it can be used
    bbox = self.dialog.component('buttonbox')
    self._clusterButton = bbox._buttonList[bbox.index('Cluster')][1]
    self._clusterButton.configure(state=Tkinter.DISABLED)
        
    self.dialog.withdraw()

    #Grab interior of dialog box
    self.interior = self.dialog.interior()
    
    # Add a labeling name to top of clustering dialog box
    self.labelingFrame = Tkinter.Frame(self.interior)
    self.labelingName = Pmw.EntryField(self.labelingFrame,
                                       validate={'min': '1', 'minstrict': False},
                                       labelpos = 'w',
                                       label_text = 'Labeling Name:',)
                                       #modifiedcommand=self.valid)
    self.labelingName.grid(row=0, column=0, sticky='e')

    self.labelingNameAutoUpdate = Pmw.RadioSelect(self.labelingFrame,
                                                  buttontype = 'checkbutton',
                                                  orient = 'horizontal',
                                                  labelpos = 'w',
                                                  command = self.__autoUpdateCallBack,
                                                  )
    self.labelingNameAutoUpdate.grid(row=0, column=1, sticky='w')
    self.labelingNameAutoUpdate.add('Automatic')
    self.labelingNameAutoUpdate.invoke('Automatic')
    self.labelingFrame.pack(fill='both', expand=0, padx=2, pady=2)
    
    # add in the frame to hold the parameters 
    # use a tabbed notebook if its a composable algorithm, otherwise
    # use a simple frame.
    if issubclass(algorithm, ML_Composable_Algorithm):
      tabpos = "n"
    else:
      tabpos = None
    self.notebookFrame = Pmw.NoteBook(self.interior, tabpos=tabpos)
    self.clusteringTabs = []
    self.addClusteringTab(algorithm)
    self.notebookFrame.setnaturalsize(pageNames=None)
    
    # add control buttons

  def addClusteringTab(self, algorithm_class):
    """Add a ClusteringTab to our dialog box so users can set the parameters for sub algorithms.
    """
    # come up with tab names of the form Name, Name <1>, Name <2>, 
    # when there are duplicate "Name"s
    self.builder.append(algorithm_class)
    algorithm = self.builder[-1]
    alg_name_count = 0
    for alg_name in [ x.name for x in self.builder[:-1] ]:
      if alg_name == algorithm.name:
        alg_name_count += 1
    if alg_name_count == 0:
      page_name = algorithm.name
    else:
      page_name = "%s <%d>" % (algorithm.name, alg_name_count)
      
    nextTab = ClusteringTab(self, self.notebookFrame, page_name, self.builder[-1])
    self.clusteringTabs.append(nextTab)
    self.notebookFrame.selectpage(self.notebookFrame.index(Pmw.END))
    self.notebookFrame.pack(fill='both', expand=1)

  def __autoUpdateCallBack(self, tag, state):
    # This is called whenever the user clicks on a button
    # in the checkbutton RadioSelect widget.
    if state:
      self.AUTO_UPDATE = True
    else:
      self.AUTO_UPDATE = False
   
  def _updateLabelingName(self):
    if not self.AUTO_UPDATE:
      return
    
    labelingNameText = '%s' % (self.builder[0].name)
    for name, prop in self.builder[0].parameters.getProperties(WrapperParameters.Priority.REQUIRED).items():
      if prop.priority & WrapperParameters.Priority.REQUIRED:
        labelingNameText += ' %s=%s' % (name[:3], self.builder[0].parameters[name])

    labelingNameText = string.replace(labelingNameText, '_', '-')
    self.labelingName.setvalue(labelingNameText)
   

  def show(self):
    return self.dialog.activate()
    

  def _processButtonBox(self, value):
    if value == 'Cluster':
      self.dialog.deactivate(self._choices())
    elif value == 'Cancel':
      self.dialog.deactivate((None, False, None))

  def _choices(self):
#     for tab in self.clusteringTabs:
#       wp = tab.algorithm.parameters
#       for key in self.inputFields.keys():
#         prop = wp[key]
#         if isinstance(prop, WrapperParameters.IntProperty):
#           value = int(self.inputFields[key].getvalue())
#         elif isinstance(prop, WrapperParameters.FloatProperty):
#           value = float(self.inputFields[key].getvalue())
#         else:
#           value = self.inputFields[key].getvalue()
#         wp[key] = value
#        print value
    return self.labelingName.getvalue(), True, self.builder

class ClusteringTab:
  """This class handles rendering the notetab portion of the ClusteringDialog box
  """
  def __init__(self, dialog, notebookFrame, page_name, algorithm):
    self.clustering_dialog = dialog
    self.algorithm = algorithm
    
    self.inputFields = {}
    
    self.page_name = page_name
    self.notebookTabFrame = notebookFrame.add(self.page_name)
    self.notebookScrolledFrame = Pmw.ScrolledFrame(self.notebookTabFrame, horizflex= 'expand', vertflex = 'expand')
    self.notebookTab = self.notebookScrolledFrame.interior()
    #Add Required Group
    self.requiredGroup = Pmw.Group(self.notebookTab,
                                   tag_text='Required Parameters')
    self.requiredGroup.pack(fill='both', expand=1, padx=6, pady=6)
    
    #Add Optional Group
    self.optionalGroup = Pmw.Group(self.notebookTab,
                                   tag_text='Optional Parameters')
    self.optionalGroup.pack(fill='both', expand=1, padx=6, pady=6)
    
    self.reqCount = 0
    self.optCount = 0
    
    for name, prop in algorithm.parameters.getProperties().items():

      #Create validated integer entry fields
      #if prop.dataType == types.IntType:
      #Int Type
      if isinstance(prop, WrapperParameters.IntProperty):
        self.__format_int(prop, name)
      #Float Type
      elif isinstance(prop, WrapperParameters.FloatProperty):
        self.__format_float(prop, name)
      #String Type
      elif isinstance(prop, WrapperParameters.StrProperty):
        self.__format_string(prop, name)
      #Create option menus
      elif isinstance(prop, WrapperParameters.ComboProperty):
        self.__format_combo(prop, name)
      elif isinstance(prop, WrapperParameters.ListProperty):
        self.__format_list(prop, name)
      else:
        msg = 'Parameter %s datatype of %s is unknown.' % (name, type(prop))
        raise ValueError, msg
    
    # if the algorithm is composable add a combo box at the end to add more 
    if isinstance(self.algorithm, ML_Composable_Algorithm):
      subAlgorithmFrame = Tkinter.Frame(self.notebookTab)
      self.__format_subalgorithms(subAlgorithmFrame)
      subAlgorithmFrame.pack()
      
    self.notebookTab.pack(fill='both', expand=1)
    self.notebookScrolledFrame.pack(fill='both', expand=1)
    #Update menu items
    self.clustering_dialog._updateLabelingName()
        
  def __subalgorithm_choice(self, combo):
    """handle what happens when the user selects a clustering algorithm
    """
    choice = combo.getvalue()
    print "subalgorithm_choice", choice
    if len(choice) == 1:
      choice = choice[0]
    else:
      raise RuntimeError("subalgorith_choice.combo.getvalue was supposed to return only one value")
    algorithm = self.clustering_dialog.wrapper_plugins[choice]
    self.clustering_dialog.addClusteringTab(algorithm)
    
  def __format_subalgorithms(self, parent):
    """Format a combo box allowing the user to choose sub algorithms
    """
    algorithms = self.clustering_dialog.wrapper_algorithm_names
    
    nextAlgorithmFrame = Tkinter.Frame(parent)
    combo = Pmw.ComboBox(nextAlgorithmFrame, 
                         labelpos='w', 
                         label_text="Next Algorithm", 
                         scrolledlist_items = algorithms, 
                         dropdown=1)                         
    # set diagem to be the default
    combo.selectitem(algorithms.index('DiagEM'),setentry=True)                        
    combo.grid(row=0, column=0)
    nextButton = Tkinter.Button(nextAlgorithmFrame, text="Add algorithm", 
                                command=lambda combo=combo: self.__subalgorithm_choice(combo=combo))
    nextButton.grid(row=0, column=1)
    nextAlgorithmFrame.pack(fill='both')
    return nextAlgorithmFrame
                 
  def __format_documentation(self, param, name, group, curCount):
    """Add documentation to a field
    """
    if param.doc is not None:
      b = Tkinter.Button(group,
                         bitmap='question',
                         command=lambda s=self, h=param.doc: s.help(h))
      b.grid(row=curCount, column=1, sticky='w')

          
  def __format_float(self, param, name):
    validateDict = {'validator': 'real'}
    if param.min is not None:
      validateDict['min'] = param.min
      validateDict['minstrict'] = False
    if param.max is not None:
      validateDict['max'] = param.max
      validateDict['maxstrict'] = False
        
    if param.priority & WrapperParameters.Priority.REQUIRED:
      group = self.requiredGroup.interior()
      curCount = self.reqCount
      self.reqCount += 1
    elif param.priority & WrapperParameters.Priority.OPTIONAL:
      group= self.optionalGroup.interior()
      curCount = self.optCount
      self.optCount += 1
    else:
      return
        
    self.inputFields[name] = Pmw.EntryField(group,
                                           validate=validateDict,
                                           labelpos = 'w',
                                           label_text = '%s:' % (name),
                                           modifiedcommand=lambda name=name: self.update_parameter(name))
    self.inputFields[name].grid(row=curCount, column=0, sticky='e')

    self.__format_documentation(param, name, group, curCount)

    if param.default is not None:
      self.inputFields[name].setvalue(param.default)

          
  def __format_int(self, param, name):
    validateDict = {'validator': 'integer'}
    if param.min is not None:
      validateDict['min'] = param.min
      validateDict['minstrict'] = False
    if param.max is not None:
      validateDict['max'] = param.max
      validateDict['maxstrict'] = False

    if param.priority & WrapperParameters.Priority.REQUIRED:
      group = self.requiredGroup.interior()
      curCount = self.reqCount
      self.reqCount += 1
    elif param.priority & WrapperParameters.Priority.OPTIONAL:
      group= self.optionalGroup.interior()
      curCount = self.optCount
      self.optCount += 1
    else:
      return
      
    self.inputFields[name] = Pmw.EntryField(group,
                                           validate=validateDict,
                                           labelpos = 'w',
                                           label_text = '%s:' % (name),
                                           modifiedcommand=lambda name=name: self.update_parameter(name))
    self.inputFields[name].grid(row=curCount, column=0, sticky='e')
      
    self.__format_documentation(param, name, group, curCount)

    if param.default is not None:
      self.inputFields[name].setvalue(param.default)


  def __format_combo(self, param, name):
    if param.priority & WrapperParameters.Priority.REQUIRED:
      group = self.requiredGroup.interior()
      curCount = self.reqCount
      self.reqCount += 1
    elif param.priority & WrapperParameters.Priority.OPTIONAL:
      group= self.optionalGroup.interior()
      curCount = self.optCount
      self.optCount += 1
    else:
      return
    
    # we have a list of parameters provided
    self.inputFields[name] = Pmw.OptionMenu(group, labelpos='w', label_text='%s:' % (name),
                                            command=lambda x, name=name: self.update_combo(name, x))
    self.inputFields[name].setitems(param.choices)
      
    self.inputFields[name].grid(row=curCount, column=0, sticky='e')

    self.__format_documentation(param, name, group, curCount)
    
    if param.default is not None:
      self.inputFields[name].setvalue(param.default)
        
  def __format_string(self, param, name):

    if param.priority & WrapperParameters.Priority.REQUIRED:
      group = self.requiredGroup.interior()
      curCount = self.reqCount
      self.reqCount += 1
    elif param.priority & WrapperParameters.Priority.OPTIONAL:
      group= self.optionalGroup.interior()
      curCount = self.optCount
      self.optCount += 1
    else:
      return
    
    validateDict = {'validator': 'alphanumeric', 'min': 1}
    self.inputFields[name] = Pmw.EntryField(group,
                                           validate=validateDict,
                                           labelpos = 'w',
                                           label_text = '%s:' % (name),
                                           modifiedcommand=lambda name=name: self.update_parameter(name))
    self.inputFields[name].grid(row=curCount, column=0, sticky='e')

    self.__format_documentation(param, name, group, curCount)
    
    if param.default is not None:
      self.inputFields[name].setvalue(param.default)
        
  def __format_list(self, param, name):

    if param.priority & WrapperParameters.Priority.REQUIRED:
      group = self.requiredGroup.interior()
      curCount = self.reqCount
      self.reqCount += 1
    elif param.priority & WrapperParameters.Priority.OPTIONAL:
      group= self.optionalGroup.interior()
      curCount = self.optCount
      self.optCount += 1
    else:
      return
    
    #validateDict = {'validator': 'alphanumeric', 'min': 1}
#     self.inputFields[name] = Pmw.ScrolledText(group,
#                                               labelpos = 'w',
#                                               label_text = '%s:' % (name))
#     self.inputFields[name].bind("<FocusOut>", lambda e, name=name: self.update_scrolledtext(name, e))
    self.inputFields[name] = Pmw.EntryField(group, labelpos='w', label_text='%s: ' % (name),
                                            modifiedcommand=lambda name=name: self.update_parameter(name))
    self.inputFields[name].grid(row=curCount, column=0, sticky='e')

    self.__format_documentation(param, name, group, curCount)
    
    if param.default is not None:
      self.inputFields[name].setvalue(param.default)
        
  def help(self, text):
    """Popup dialog box showing the docstring of the selected parameter
    """
    tkMessageBox.showinfo("%s Clustering Algorithm" % (self.algorithm.name), text)

          
  def update_combo(self, parameter_name, text):
    """Update parameter_name control with text
    """
    print "Updating_combo", parameter_name, text
    self.algorithm.parameters[parameter_name] = text
    self.update_clustering_button(True)
  
  def update_scrolledtext(self, parameter_name, event):
    """Update a scrolled text field
    
    Unfortunately since there was no modified callback I had to bind to FocusOut 
    as an event handler.
    """
    return self.update_parameter(parameter_name)
  
  def update_parameter(self, parameter_name):
    """Update and validate user entry in the dialog box
    """
    ACTIVE=True
    prop = self.algorithm.parameters.getProperty(parameter_name)
    value = self.inputFields[parameter_name].getvalue()
    print "update_param", parameter_name, "to", value
    try:
      if isinstance(prop, WrapperParameters.ListProperty):
        value = string.split(value)
        print "corrected list to", value
      self.algorithm.parameters[parameter_name] = value
    except ValueError, e:
      print "update failed", e
      ACTIVE = False
    self.update_clustering_button(ACTIVE)
    
  def update_clustering_button(self, ACTIVE):
    if not self.clustering_dialog.labelingName.valid():
      ACTIVE = False
    
    if ACTIVE:
      self.clustering_dialog._clusterButton.configure(state=Tkinter.ACTIVE)
    else:
      self.clustering_dialog._clusterButton.configure(state=Tkinter.DISABLED)

    self.clustering_dialog._updateLabelingName()

#   def valid(self):
#     """Validate user input and update the labeling name and clustering buttons
#     """
#     ACTIVE = True
# 
#     #print '-----------------------------------------------'
#     #print self.inputFields
#     #print '-----------------------------------------------'
# 
#     parameters = self.algorithm.parameters.getProperties(WrapperParameters.Priority.REQUIRED)
#     for name, prop in parameters.items():
#       print "validating", name
#       if not self.inputFields.has_key(name):
#         ACTIVE = False
#         continue
#       if hasattr(self.inputFields[name],'valid') and not self.inputFields[name].valid():
#         ACTIVE = False
#       else:
#         value = self.inputFields[name].getvalue()
#         if isinstance(prop, WrapperParameters.ListProperty):
#           value = string.split(value)
#         try:
#           print "setting", name, "to", value
#           self.algorithm.parameters[name] = value
#         except ValueError, e:
#           print e
#           ACTIVE = False
# 
#     if not self.clustering_dialog.labelingName.valid():
#       ACTIVE = False
#     
#     if ACTIVE:
#       self.clustering_dialog._clusterButton.configure(state=Tkinter.ACTIVE)
#     else:
#       self.clustering_dialog._clusterButton.configure(state=Tkinter.DISABLED)
# 
#     self.clustering_dialog._updateLabelingName()
    

class ConfusionMatrixSummaryDialog:

  def __init__(self, parent, data=None):
    # Parent class
    self.parent = parent

    # Data
    if data is None:
      self.data = {}
      self.data['myDataSet'] = None
      self.data['myRowLabelings'] = []
      self.data['myColLabelings'] = []
    else:
      self.data = data

    #Labeling Type (Row or Column)
    #self.labelType = None

    #Pop up Dialog
    self.dialog = Pmw.Dialog(self.parent,
                             buttons=('Plot', 'Cancel'),
                             defaultbutton='Cancel',
                             title='Confusion Matrix Summary Plot',
                             command=self._processButtonBox)

    #Disable Load Button Until it can be used
    #bbox = self.dialog.component('buttonbox')
    #self._plotButton = bbox._buttonList[bbox.index('Plot')][1]
    #self._plotButton.configure(state=Tkinter.DISABLED)
    
    
    self.dialog.withdraw()

    #Grab interior of dialog box
    self.interior = self.dialog.interior()


    #Add option menus to dialog box
    self._cluster1LabelOption = Pmw.OptionMenu(self.interior, labelpos='w', label_text='1st Cluster Labeling:')
    self._cluster1LabelOption.grid(row=0, column=0, sticky='e')

    self._cluster2LabelOption = Pmw.OptionMenu(self.interior, labelpos='w', label_text='2nd Cluster Labeling:')
    self._cluster2LabelOption.grid(row=1, column=0, sticky='e')


    #Update menu items
    self._updateOptionMenus()

  def _updateOptionMenus(self):
    labelingList = self.data['myDataSet'].getLabelings()
    labelNamesList = map(lambda x: x.getName(), labelingList)
    labelNamesList = filter(_labelingFilter, labelNamesList)
    labelNamesList.sort()
    #labelNamesList = ['None'] + labelNamesList

    self._cluster1LabelOption.setitems(labelNamesList)
    self._cluster2LabelOption.setitems(labelNamesList)

  def show(self):
    self._updateOptionMenus()
    return self.dialog.activate()
    

  def _processButtonBox(self, value):
    if value == 'Plot':
      self.dialog.deactivate(self._choices())
    elif value == 'Cancel':
      self.dialog.deactivate(None)

  def _choices(self):
    results = {}
    results['cluster1'] = self._cluster1LabelOption.getcurselection()
    results['cluster2'] = self._cluster2LabelOption.getcurselection()
    return results



class TrajectorySummaryDialog:

  def __init__(self, parent, data=None):
    # Parent class
    self.parent = parent

    # Data
    if data is None:
      self.data = {}
      self.data['myDataSet'] = None
      self.data['myRowLabelings'] = []
      self.data['myColLabelings'] = []
    else:
      self.data = data

    #Labeling Type (Row or Column)
    #self.labelType = None

    #Pop up Dialog
    self.dialog = Pmw.Dialog(self.parent,
                             buttons=('Plot', 'Cancel'),
                             defaultbutton='Cancel',
                             title='Trajectory Summary Plot',
                             command=self._processButtonBox)

    #Disable Load Button Until it can be used
    #bbox = self.dialog.component('buttonbox')
    #self._loadButton = bbox._buttonList[bbox.index('Plot')][1]
    #self._loadButton.configure(state=Tkinter.DISABLED)
    
    
    self.dialog.withdraw()

    #Grab interior of dialog box
    self.interior = self.dialog.interior()


    #Add option menus to dialog box
    self._clusterLabelOption = Pmw.OptionMenu(self.interior, labelpos='w', label_text='Cluster Labeling:')
    self._clusterLabelOption.grid(row=0, column=0, sticky='e')

    self._primaryLabelOption = Pmw.OptionMenu(self.interior, labelpos='w', label_text='Primary Labeling:')
    self._primaryLabelOption.grid(row=1, column=0, sticky='e')

    self._secondaryLabelOption = Pmw.OptionMenu(self.interior, labelpos='w', label_text='Secondary Labeling:')
    self._secondaryLabelOption.grid(row=2, column=0, sticky='e')

    #Update menu items
    self._updateOptionMenus()

  def _updateOptionMenus(self):
    labelingList = self.data['myDataSet'].getLabelings()
    labelNamesList = map(lambda x: x.getName(), labelingList)
    labelNamesList = filter(_labelingFilter, labelNamesList)
    labelNamesList.sort()
    #labelNamesList = ['None'] + labelNamesList

    self._clusterLabelOption.setitems(labelNamesList)
    self._primaryLabelOption.setitems(['None'] + labelNamesList)
    self._secondaryLabelOption.setitems(['None'] + labelNamesList)

  def show(self):
    self._updateOptionMenus()
    return self.dialog.activate()
    

  def _processButtonBox(self, value):
    if value == 'Plot':
      self.dialog.deactivate(self._choices())
    elif value == 'Cancel':
      self.dialog.deactivate(None)

  def _choices(self):
    results = {}
    results['clusterLabeling'] = self._clusterLabelOption.getcurselection()
    
    primaryName = self._primaryLabelOption.getcurselection()
    if primaryName != 'None':
      results['primaryLabeling'] = primaryName
    else:
      results['primaryLabeling'] = None

    secondName = self._secondaryLabelOption.getcurselection()
    if secondName != 'None':
      results['secondaryLabeling'] = secondName
    else:
      results['secondaryLabeling'] = None
  
    return results



class RocPlotDialog:

  def __init__(self, parent, data=None):
    # Parent class
    self.parent = parent

    # Data
    if data is None:
      self.data = {}
      self.data['myDataSet'] = None
      self.data['myRowLabelings'] = []
      self.data['myColLabelings'] = []
    else:
      self.data = data

    #Labeling Type (Row or Column)
    #self.labelType = None

    #Pop up Dialog
    self.dialog = Pmw.Dialog(self.parent,
                             buttons=('Plot', 'Cancel'),
                             defaultbutton='Cancel',
                             title='ROC Plot',
                             command=self._processButtonBox)

    #Disable Load Button Until it can be used
    #bbox = self.dialog.component('buttonbox')
    #self._loadButton = bbox._buttonList[bbox.index('Plot')][1]
    #self._loadButton.configure(state=Tkinter.DISABLED)
    
    
    self.dialog.withdraw()

    #Grab interior of dialog box
    self.interior = self.dialog.interior()


    #Add option menus to dialog box
    self._clusterLabelOption = Pmw.OptionMenu(self.interior,
                                              command=self._updateLabelOption,
                                              labelpos='w',
                                              label_text='Clustering Labeling:')
    self._clusterLabelOption.grid(row=0, column=0, sticky='e')

    self._labelOption = Pmw.OptionMenu(self.interior, labelpos='w', label_text='Cluster label:')
    self._labelOption.grid(row=1, column=0, sticky='e')

    self._distanceMetricOption = Pmw.OptionMenu(self.interior, labelpos='w', label_text='Distance Metric:')
    self._distanceMetricOption.setitems(['Euclidean', 'Correlation'])
    self._distanceMetricOption.grid(row=2, column=0, sticky='e')

    #Distance Metric Dictionary
    self._distanceMetricDict = {}
    self._distanceMetricDict['Euclidean'] = DistanceMetrics.EuclideanDistance
    self._distanceMetricDict['Correlation'] = DistanceMetrics.InverseCorrelationDistance

    #Update menu items
    self._updateOptionMenus()

  def _updateOptionMenus(self):
    labelingList = self.data['myDataSet'].getLabelings()
    labelNamesList = map(lambda x: x.getName(), labelingList)
    labelNamesList = filter(_labelingFilter, labelNamesList)
    labelNamesList.sort()
    #labelNamesList = ['None'] + labelNamesList

    self._clusterLabelOption.setitems(labelNamesList)
    self._updateLabelOption(self._clusterLabelOption.getcurselection())

  def _updateLabelOption(self, choice):
    labeling = self.data['myDataSet'].getLabeling(choice)
    labels = labeling.getLabels()
    labels.sort()
    self._labelOption.setitems(labels)

  def show(self):
    self._updateOptionMenus()
    return self.dialog.activate()
    

  def _processButtonBox(self, value):
    if value == 'Plot':
      self.dialog.deactivate(self._choices())
    elif value == 'Cancel':
      self.dialog.deactivate(None)

  def _choices(self):
    results = {}
    results['clusterLabeling'] = self._clusterLabelOption.getcurselection()
    
    label = self._labelOption.getcurselection()
    if label != 'None':
      results['label'] = label
    else:
      results['label'] = None

    dm = self._distanceMetricOption.getcurselection()
    if dm != 'None':
      results['distanceMetric'] = self._distanceMetricDict[dm]
    else:
      results['distanceMetric'] = None
  
    return results




class PcaExplorerDialog:

  def __init__(self, parent, data=None):
    # Parent class
    self.parent = parent

    # Data
    if data is None:
      self.data = {}
      self.data['myDataSet'] = None
      self.data['myRowLabelings'] = []
      self.data['myColLabelings'] = []
    else:
      self.data = data

    #Labeling Type (Row or Column)
    #self.labelType = None

    #Pop up Dialog
    self.dialog = Pmw.Dialog(self.parent,
                             buttons=('Plot', 'Cancel'),
                             defaultbutton='Cancel',
                             title='PCA Explorer',
                             command=self._processButtonBox)

    #Disable Load Button Until it can be used
    #bbox = self.dialog.component('buttonbox')
    #self._loadButton = bbox._buttonList[bbox.index('Plot')][1]
    #self._loadButton.configure(state=Tkinter.DISABLED)
    
    
    self.dialog.withdraw()

    #Grab interior of dialog box
    self.interior = self.dialog.interior()


    #Add option menus to dialog box
    self._primaryLabelOption = Pmw.OptionMenu(self.interior, labelpos='w', label_text='Primary Labeling:')
    self._primaryLabelOption.grid(row=0, column=0, sticky='e')

    self._secondaryLabelOption = Pmw.OptionMenu(self.interior, labelpos='w', label_text='Secondary Labeling:')
    self._secondaryLabelOption.grid(row=1, column=0, sticky='e')

    #Update menu items
    self._updateOptionMenus()

  def _updateOptionMenus(self):
    labelingList = self.data['myDataSet'].getLabelings()
    labelNamesList = map(lambda x: x.getName(), labelingList)
    labelNamesList = filter(_labelingFilter, labelNamesList)
    labelNamesList.sort()
    #labelNamesList = ['None'] + labelNamesList

    self._primaryLabelOption.setitems(['None'] + labelNamesList)
    self._secondaryLabelOption.setitems(['None'] + labelNamesList)

  def show(self):
    self._updateOptionMenus()
    return self.dialog.activate()
    

  def _processButtonBox(self, value):
    if value == 'Plot':
      self.dialog.deactivate(self._choices())
    elif value == 'Cancel':
      self.dialog.deactivate(None)

  def _choices(self):
    results = {}
    
    primaryName = self._primaryLabelOption.getcurselection()
    if primaryName != 'None':
      results['primaryLabeling'] = primaryName
    else:
      results['primaryLabeling'] = None

    secondName = self._secondaryLabelOption.getcurselection()
    if secondName != 'None':
      results['secondaryLabeling'] = secondName
    else:
      results['secondaryLabeling'] = None
  
    return results



class LoadLabelingDialog:

  def __init__(self, parent, data=None):
    # Parent class
    self.parent = parent

    # Data
    if data is None:
      self.data = {}
      self.data['myDataSet'] = None
      self.data['myRowLabelings'] = []
      self.data['myColLabelings'] = []
    else:
      self.data = data

    #Labeling Type (Row or Column)
    self.labelType = None

    #Pop up Dialog
    self.dialog = Pmw.Dialog(self.parent,
                             buttons=('Load', 'Cancel'),
                             defaultbutton='Cancel',
                             title='Load Labeling',
                             command=self._processButtonBox)

    #Disable Load Button Until it can be used
    bbox = self.dialog.component('buttonbox')
    self._loadButton = bbox._buttonList[bbox.index('Load')][1]
    self._loadButton.configure(state=Tkinter.DISABLED)
    
    
    self.dialog.withdraw()

    #Grab interior of dialog box
    self.interior = self.dialog.interior()

    #Add buttons to dialog
    self._labelNameEntry = Pmw.EntryField(self.interior,
                                          labelpos = 'w',
                                          label_text = 'Label Name:',
                                          validate = {'min' : 1, 'minstrict' : 0},
                                          modifiedcommand=self.valid)
    self._labelNameEntry.grid(row=0, column=0, sticky='e')

    
    self._filePathEntry = Pmw.EntryField(self.interior,
                                         labelpos = 'w',
                                         label_text = 'File Path:',
                                         validate = {'min' : 1, 'minstrict' : 0},
                                         modifiedcommand=self.valid)
    self._filePathEntry.grid(row=1, column=0, sticky='e')

    self._browseButton = Tkinter.Button(self.interior, text="Browse", command=self._askopenfilename)
    self._browseButton.grid(row=1, column=1, sticky='w')

    self._rowOrColLabelingRadio = Pmw.RadioSelect(self.interior,
                                                  buttontype='radiobutton',
                                                  command=self._setLabelType,
                                                  labelpos='w',
                                                  orient='horizontal',
                                                  label_text='Type:')
    self._rowOrColLabelingRadio.grid(row=2, column=0, sticky='e')
    #self._rowOrColLabelingRadio.component('label_text').configure(text='Labeling Type:')
    self._rowOrColLabelingRadio.add('Row')
    self._rowOrColLabelingRadio.add('Column')
    
    

  def valid(self):
    if self._labelNameEntry.valid() and self._filePathEntry.valid() and self.labelType is not None:
      self._loadButton.configure(state=Tkinter.ACTIVE)
    else:
      self._loadButton.configure(state=Tkinter.DISABLED)


  def _processButtonBox(self, value):
    if value == 'Load':
      self._attachLabeling()
    elif value == 'Cancel':
      self.dialog.deactivate(None)


  def show(self):
    result = self.dialog.activate()
    #result = self.dialog.activate(geometry = 'centerscreenalways')
    #print result
    
    
  def _askopenfilename(self):
    filename = tkFileDialog.askopenfilename(title='Load Labeling',
                                            filetypes=[('Tab Delimited Text', '*.txt *.rlab *.clab'), ('All', '*')])
    if filename is not None:
      self._filePathEntry.setvalue(filename)

  def _setLabelType(self, labelType):
    self.labelType = labelType
    self.valid()

  def _attachLabeling(self):

    myLabeling = labelings.GlobalLabeling(self.data['myDataSet'], self._labelNameEntry.getvalue())
    labelType = self._rowOrColLabelingRadio.getvalue()

    if labelType == 'Column':
      myLabeling.labelCols(self.data['myDataSet'], self._filePathEntry.getvalue())
    elif labelType == 'Row':
      myLabeling.labelRows(self.data['myDataSet'], self._filePathEntry.getvalue())
    else:
      raise ValueError, 'Labeling of type %s is invalid.' % (labelType)

    #Add history
    hist = []
    hist.append('')
    hist.append('# Attach Labeling')
    hist.append('labelPath = \'%s\'' % (self._filePathEntry.getvalue()))
    hist.append('label = labelings.GlobalLabeling(dataSet, \'%s\')' % (self._labelNameEntry.getvalue()))
    if labelType == 'Column':
      hist.append('label.labelCols(dataSet, labelPath)')
    elif labelType == 'Row':
      hist.append('label.labelRows(dataSet, labelPath)')
    hist.append('')
    
    if _APPEND_HISTORY == 1:
      self.parentGui.history.appendtext(string.join(hist, '\n'))
    elif _APPEND_HISTORY == 0:
      self.parentGui._historyCache += string.join(hist, '\n')
    else:
      raise ValueError, '_APPEND_HISTORY=%s' % (_APPEND_HISTORY)


    self.dialog.deactivate()
    tkMessageBox.showinfo("Labeling Loader",
                          "Loading of %s labeling complete." % (labelType))
    

class CompClustUI:

  def __init__(self, parent):
    #############################
    # Class Varibables
    
    # ipython global variables dictionary
    self.data = {}
    self.data['myDataSet'] = None
    self.data['myRowLabelings'] = []
    self.data['myColLabelings'] = []

    # Class vars    
    self.toggleHistoryVar = Tkinter.IntVar()
    self._historyCache = ''
    _APPEND_HISTORY = 0 #0 if append to self._historyCache, 1 if append to self.history
    self._WEB_SERVER_STARTED = False

    #algorithmParamDict, algorithmClassDict = _getAlgorithmDictionaries()
    self._wrapperPlugins = WrapperPlugins.WrapperPlugins()

    # If CompClustTk is being used with py2exe (a.k.a Windows Installer Version),
    # then the WrapperPlugins dynamic loading of compatible algorithm wrappers
    # won't work properly. To fix this, we need to define self._wrapperPlugins.algorithms
    # dictionary manually.
    if PY2EXE_MODE:
      from compClust.mlx.wrapper.DiagEM import DiagEM
      #from compClust.mlx.wrapper.DiagEM import getDefaultParameterDescriptions as DiagEMDefaults
      from compClust.mlx.wrapper.KMeans import KMeans
      #from compClust.mlx.wrapper.KMeans import getDefaultParameterDescriptions as KMeansDefaults
      
      self._wrapperPlugins.algorithms['DiagEM'] = DiagEM
      self._wrapperPlugins.algorithms['KMeans'] = KMeans
    
    # Parent class
    self.parent = parent

    self.panedWidget = Pmw.PanedWidget(self.parent)

    self.corePane = self.panedWidget.add('corePane', size=500)
    #self.historyPane = self.panedWidget.add('historyPane', size=100)

    #############################
    # DIALOG BOXES

    # Load Data Set Dialog
    self._loadLabelingDialog = LoadLabelingDialog(self.parent, self.data)
    #self._loadLabelingDialog.data = self.data


    #############################
    # MENU

    # Create the Balloon.
    self.balloon = Pmw.Balloon(parent)

    # Create and pack the MenuBar.
    self.menuBar = Pmw.MenuBar(parent, hull_relief = 'raised',
                               hull_borderwidth = 1, balloon=self.balloon)

    # File menu
    self.menuBar.addmenu('File', 'Load Data Sets and Labels')
    self.menuBar.addmenuitem('File', 'command', 'Load Data from Tab Delimited Text File',
                             command=self.loadDataSet, label='Load Data Set...')
    self.menuBar.addmenuitem('File', 'command', 'Load Labeling',
                             command=self.dialogLoadLabeling, label='Load Labeling...')
    self.menuBar.addmenuitem('File', 'separator')
    self.menuBar.addmenuitem('File', 'command', 'Save History As',
                             command=self.saveHistory, label='Save History As...')
    self.menuBar.addmenuitem('File', 'separator')
    self.menuBar.addmenuitem('File', 'command', 'Quit',
                             command=self.parent.destroy, label='Quit')

    # Tabs Menu
    self.menuBar.addmenu('Tabs', 'Used to close an individual or all tabs.')
    self.menuBar.addmenuitem('Tabs', 'command', 'Close Current Tab',
                             command=self.closeCurrentTab, label='Close Current Tab')
    
    self.menuBar.pack(fill = 'x')

    # Clustering Menu
    self.menuBar.addmenu('Clustering', 'Clustering Algorithms')
    for name in self._wrapperPlugins.keys():
      # FIXME: since mccv and mutlirun aren't implemented in Tk yet
      # FIXME: don't let users touch broken code
      self.menuBar.addmenuitem('Clustering', 'command', name,
                               command=lambda s=self, n=name: s._launchClusteringDialog(n), label=name)

    # Analysis Menu
    self.menuBar.addmenu('Analysis', 'Analysis of Data Sets using Confusion Matrices, Trajectory Summary, and Cluster ROC Analysis')
    self.menuBar.addmenuitem('Analysis', 'command', 'Build Confusion Matrix',
                             command=self.launchConfusionMatrixSummary, label='Build Confusion Matrix')
    self.menuBar.addmenuitem('Analysis', 'command', 'Build Trajectory Summary',
                             command=self.launchTrajectorySummary, label='Build Trajectory Summary')
    self.menuBar.addmenuitem('Analysis', 'command', 'Cluster ROC Analysis',
                             command=self.launchRocPlot, label='Cluster ROC Analysis')
    self.menuBar.addmenuitem('Analysis', 'command', 'PCA Explorer',
                             command=self.launchPcaExplorer, label='PCA Explorer')

    # View Menu
    self.menuBar.addmenu('View', 'View Modes')
    self.menuBar.addmenuitem('View', 'checkbutton', 'Toggle Analysis History',
                             label = 'Toggle Analysis History',
                             command = self._toggleHistory,
                             variable = self.toggleHistoryVar)

    # Help Menu
    self.menuBar.addmenu('Help', 'Help Menu')
    self.menuBar.addmenuitem('Help', 'command', 'Help',
                             command=self._launchWebBrowser, label='Help')
    self.menuBar.addmenuitem('Help', 'separator')
    self.menuBar.addmenuitem('Help', 'command', 'About',
                             command=self.launchAboutBox, label='About')

    if IPYTHON_ENABLED:
      #Add ipython shell to menu
      self.menuBar.addmenuitem('Analysis', 'separator')
      self.menuBar.addmenuitem('Analysis', 'command', 'CompClust Interactive Analysis Shell',
                               command=self.ipythonShell, label='Analysis Shell')


    #############################
    # Notebook Setup
    self.notebook = Pmw.NoteBook(self.corePane,
                                 hull_width=self.parent.winfo_width(),
                                 hull_height=self.parent.winfo_height() - self.menuBar.winfo_height())

    #############################
    # History Setup
    #self.history = Pmw.ScrolledText(self.historyPane,
    #                                hscrollmode='dynamic',
    #                                labelpos='n',
    #                                label_text='Analysis History - Python Code')
    
    #self.saveHistoryButton = Tkinter.Button(self.historyPane, text="Save As", command=self.notImplemented)

    
    self.toggleHistoryVar.set(1)
    self._toggleHistory()
                                    
    #FIXME: The following line is a little hackish and should be passed in via a function call
    self._loadLabelingDialog.parentGui = self

    self.toggleHistoryVar.set(0)
    self._toggleHistory()
        
    self.panedWidget.pack(fill='both', expand=1)
    self.notebook.pack(fill='both', expand=1)
    #self.history.pack(fill='both', expand=1)
    #self.saveHistoryButton.pack()

    # FIXME: do we still need this?
    self.statusBar = Pmw.MessageBar(self.parent, entry_relief='groove')
    self.statusBar.pack(fill='x')


  def _launchWebBrowser(self):
    #Only need to start the web server once.
    #if not self._WEB_SERVER_STARTED:
    #  thread.start_new_thread(runWebServer, ())
    #  self._WEB_SERVER_STARTED = True
    #Connect web browser to local web server.
    #webbrowser.open("http://localhost:%s/" % (DEFAULT_PORT))
    if HELP_PATH is not None:
      url = r'file:///' + HELP_PATH.replace('\\', '/')
      webbrowser.open(url)
    else:
      tkMessageBox.showwarning("Help", "Sorry, I could not find the help files.")


  def _launchClusteringDialog(self, name):
    if self.data['myDataSet'] is None:
      tkMessageBox.showinfo("%s Clustering Algorithm" % (name), "You need to load a dataset before clustering. " \
                            "I know it's tempting, but it just won't work. =o)")
      return
    
    print 'Launching: %s' % (name)
    print 'SETTING UP: %s' % (name)
    dataset = self.data['myDataSet']
    algorithm = self._wrapperPlugins.getClass(name)
    dlg = ClusteringDialog(self.parent, dataset, algorithm, self._wrapperPlugins)
    labelingName, readyToRun, builder = dlg.show()

    if not readyToRun:
      return

    print 'VALIDATING: %s' % (name)
    if not builder.validate():
      tkMessageBox.showinfo("%s Clustering Algorithm" % (name), "Parameters not valid, check console for info.")
      return
    print 'RUNNING: %s' % (name)
    builder.run()
    print 'DONE'
    labeling = builder.getLabeling()
    labeling.setName(labelingName)
    labelings.castToGlobalLabeling(labeling)
    tkMessageBox.showinfo("%s Clustering Algorithm" % (name), "Clustering completed successfully.")
    
  def _toggleHistory(self):
    #print self.toggleHistoryVar.get()

    global _APPEND_HISTORY

    if self.toggleHistoryVar.get() == 0:
      try:
        self._historyCache = self.history.getvalue()
        self._historyCache = self._historyCache.strip() + '\n'
        self.panedWidget.delete('historyPane')
        self.panedWidget.updatelayout()
        #Set flag to tell the app to append history to self._historyCache
        _APPEND_HISTORY = 0
      except:
        pass
    elif self.toggleHistoryVar.get() == 1:
      self.historyPane = self.panedWidget.add('historyPane', min=0.25, size=0.25)
      self.history = Pmw.ScrolledText(self.historyPane,
                                      hscrollmode='dynamic',
                                      labelpos='n',
                                      label_text='Analysis History - Python Code')
      self.history.pack(fill='both', expand=1)
      self.history.setvalue(self._historyCache)
      self.panedWidget.updatelayout()
      #Set flag to tell the app to append history to self.history
      _APPEND_HISTORY = 1
    

  def saveHistory(self):
    f = tkFileDialog.asksaveasfile(defaultextension='*.txt',
                                   filetypes=[('Text File', '*.txt'), ('All', '*')],
                                   title='Save History As',
                                   parent=self.parent)
    if _APPEND_HISTORY == 1:
      f.write(self.history.getvalue())
    elif _APPEND_HISTORY == 0:
      f.write(self._historyCache)
    else:
      raise ValueError, '_APPEND_HISTORY=%s' % (_APPEND_HISTORY)
    
    tkMessageBox.showinfo("Save History As", "History saved.")


  def closeCurrentTab(self):
    if len(self.notebook.pagenames()) < 1:
      return
    self.notebook.delete(self.notebook.getcurselection())


  def notImplemented(self):
    # Not implemented Dialog box
    dialog = Pmw.MessageDialog(title='Not Implemented', message_text = 'Sorry, this feature is not implemented yet.')
    #self.dialog.withdraw()
    result = dialog.activate()


  def ipythonShell(self):
    msg = []
    msg.append('')
    msg.append('Welcome to the CompClust Interactive Analysis Shell')
    msg.append('')
    msg.append('USEFUL VARIABLES:')
    msg.append('     Datasets module: datasets')
    msg.append('        Views module: views')
    msg.append('    Labelings module: labelings')
    msg.append('   Clustering module: wrapper')
    msg.append('')
    msg.append('Data Set used by GUI: gui.data[\'myDataSet\']')
    msg.append('      Root Tk Window: gui.parent')
    msg.append('')
    msg.append('EXAMPLE USE: Log2 transform of a data set')
    msg.append('')
    msg.append('  import math                                #Import math module')
    msg.append('  def log2(n):                               #Define log2(n) function')
    msg.append('     return math.log(n,2)')
    msg.append('')
    msg.append('  dataSet = gui.data[\'myDataSet\']          #Grab data set')
    msg.append('  gui.data[\'originalDataSet\'] = dataSet    #Backup original data set')
    msg.append('  log2ds = views.FunctionView(dataSet, log2) #Apply log2 function to each ')
    msg.append('                                             # element in the data set')
    msg.append('  gui.data[\'myDataSet\'] = log2ds           #Tell the gui to use the log2')
    msg.append('                                             # transformed view of the dataset')
    msg.append('')
    msg.append('HELP:')
    msg.append('     ? - Putting a \'?\' after a variable/object/function will provide more')
    msg.append('         information on that item (i.e. views.FunctionView?)')
    msg.append('  help - Typing help will provide you with more information as well') 
    msg.append('')
    msg.append('QUIT: Ctrl+D on Linux & Windows')
    msg.append('')
               

    msg = string.join(msg, os.linesep)

    ipshell = IPythonShellEmbed(banner=msg)
    #ipshell(local_ns=self.data, global_ns=vars())
    ipshell()

  def dialogLoadLabeling(self):
    if self.data['myDataSet'] is None:
      tkMessageBox.showwarning('Labeling Loader','You must load a data set before loading labelings.')
    else:
      self._loadLabelingDialog.show()

  def loadDataSet(self):
    filePath = tkFileDialog.askopenfilename(title='Load Data Set',
                                          filetypes=[('Tab Delimited Text', '*.txt *.dat *.tab'), ('All', '*')])
    if filePath is None or len(filePath) == 0:
      return

    if self.data['myDataSet'] is not None:
      answer = tkMessageBox.askokcancel('Replace existing dataset?',
                 'Are you sure you want to replace the existing %sx%s dataset?' % (self.data['myDataSet'].getNumRows(),
                                                                                   self.data['myDataSet'].getNumCols()))
      #print 'Answer:', answer, 'Type:', type(answer)
      if answer == False:
        return

    #Load data set
    ds = datasets.Dataset(filePath)

    #Validation code
    if ds.getNumRows() == 0 or ds.getNumCols() == 0:
      tkMessageBox.showinfo("Data Set Loader",
                             "%s is not a valid dataset." % (filePath))
      return
    
    #Pass data set to global variable for ipython use
    self.data['myDataSet'] = ds

    
    #Add history
    hist = []
    hist.append('# Load Data Set')
    hist.append('filePath = \'%s\'' % (filePath))
    hist.append('dataSet = datasets.Dataset(filePath)')
    hist.append('')

    if _APPEND_HISTORY == 1:
      self.history.appendtext(string.join(hist, '\n'))
    elif _APPEND_HISTORY == 0:
      self._historyCache += string.join(hist, '\n')
    else:
      raise ValueError, '_APPEND_HISTORY=%s' % (_APPEND_HISTORY)
    
    tkMessageBox.showinfo("Data Set Loader",
                          "Loaded %sx%s dataset." % (ds.getNumRows(), ds.getNumCols()))


  def launchTrajectorySummary(self):
    # Make sure data set is loaded first
    if self.data['myDataSet'] is None:
      tkMessageBox.showinfo("Info", "You need to load a data set first.")
      return
    
    # Create dialog
    tsDialog = TrajectorySummaryDialog(self.parent, self.data)
    choices = tsDialog.show()

    # return if cancel button was choosen
    if choices is None:
      return

    # Create page title (tab name)
    pageTitle = choices['clusterLabeling']
    if choices['primaryLabeling'] is not None:
      pageTitle += ' | ' + choices['primaryLabeling']

    if choices['secondaryLabeling'] is not None:
      pageTitle += ' | ' + choices['secondaryLabeling']

    # Create page
    page = self.notebook.add('Trajectory Summary - %s' % (pageTitle))
    self.notebook.selectpage('Trajectory Summary - %s' % (pageTitle))
    IPlot.TrajectorySummary(self.data['myDataSet'],
                            clusterLabeling=self.data['myDataSet'].getLabeling(choices['clusterLabeling']),
                            primaryLabeling=self.data['myDataSet'].getLabeling(choices['primaryLabeling']),
                            secondaryLabeling=self.data['myDataSet'].getLabeling(choices['secondaryLabeling']),
                            parent=page)

    #Add history
    hist = []
    hist.append('')
    hist.append('# Trajectory Summary Plot')
    hist.append('IPlot.TrajectorySummary(dataSet,')
    hist.append('      clusterLabeling=dataSet.getLabeling(\'%s\'),' % (choices['clusterLabeling']))
    if choices['primaryLabeling'] is not None:
      hist.append('      primaryLabeling=dataSet.getLabeling(\'%s\'),' % (choices['primaryLabeling']))
    if choices['secondaryLabeling'] is not None:
      hist.append('      secondaryLabeling=dataSet.getLabeling(\'%s\')' % (choices['secondaryLabeling']))
    hist.append(')')
    hist.append('')
    
    if _APPEND_HISTORY == 1:
      self.history.appendtext(string.join(hist, '\n'))
    elif _APPEND_HISTORY == 0:
      self._historyCache += string.join(hist, '\n')
    else:
      raise ValueError, '_APPEND_HISTORY=%s' % (_APPEND_HISTORY)


  def launchConfusionMatrixSummary(self):
    # Make sure data set is loaded first
    if self.data['myDataSet'] is None:
      tkMessageBox.showinfo("Info", "You need to load a data set first.")
      return
    
    cmDialog = ConfusionMatrixSummaryDialog(self.parent, self.data)
    choices = cmDialog.show()

    if choices is None or choices == '':
      return

    pageTitle = '%s vs %s' % (choices['cluster1'], choices['cluster2'])

    # Create page
    page = self.notebook.add('Confusion Matrix Summary - %s' % (pageTitle))
    self.notebook.selectpage('Confusion Matrix Summary - %s' % (pageTitle))
    IPlot.ConfusionMatrixSummary(self.data['myDataSet'],
                                 self.data['myDataSet'].getLabeling(choices['cluster1']),
                                 self.data['myDataSet'].getLabeling(choices['cluster2']),
                                 parent=page)

    #Add history
    hist = []
    hist.append('')
    hist.append('# Confusion Matrix Summary Plot')
    hist.append('IPlot.ConfusionMatrixSummary(dataSet,')
    hist.append('      dataSet.getLabeling(\'%s\'),' % (choices['cluster1']))
    hist.append('      dataSet.getLabeling(\'%s\'))' % (choices['cluster2']))
    hist.append('')
    
    if _APPEND_HISTORY == 1:
      self.history.appendtext(string.join(hist, '\n'))
    elif _APPEND_HISTORY == 0:
      self._historyCache += string.join(hist, '\n')
    else:
      raise ValueError, '_APPEND_HISTORY=%s' % (_APPEND_HISTORY)



  def launchRocPlot(self):
    # Make sure data set is loaded first
    if self.data['myDataSet'] is None:
      tkMessageBox.showinfo("Info", "You need to load a data set first.")
      return
    
    rpDialog = RocPlotDialog(self.parent, self.data)
    choices = rpDialog.show()

    if choices is None or choices == '':
      return

    pageTitle = 'ROC Plot - Label(%s) Cluster(%s)' % (choices['clusterLabeling'], choices['label'])
    page = self.notebook.add(pageTitle)
    self.notebook.selectpage(pageTitle)
    IPlot.ROCPlot(self.data['myDataSet'],
                  self.data['myDataSet'].getLabeling(choices['clusterLabeling']),
                  choices['label'],
                  choices['distanceMetric'],
                  parent=page)

    #Add history
    hist = []
    hist.append('')
    hist.append('# Roc Plot')
    hist.append('IPlot.rocPlot(dataSet,')
    hist.append('      dataSet.getLabeling(\'%s\'),' % (choices['clusterLabeling']))
    hist.append('      \'%s\',' % (choices['label']))
    if choices['distanceMetric'] == DistanceMetrics.InverseCorrelationDistance:
      hist.append('      DistanceMetrics.InverseCorrelationDistance')
    elif choices['distanceMetric'] == DistanceMetrics.Euclidean:
      hist.append('      DistanceMetrics.Euclidean)')
    hist.append('')

    if _APPEND_HISTORY == 1:
      self.history.appendtext(string.join(hist, '\n'))
    elif _APPEND_HISTORY == 0:
      self._historyCache += string.join(hist, '\n')
    else:
      raise ValueError, '_APPEND_HISTORY=%s' % (_APPEND_HISTORY)
    

  def launchPcaExplorer(self):
    # Make sure data set is loaded first
    if self.data['myDataSet'] is None:
      tkMessageBox.showinfo("Info", "You need to load a data set first.")
      return
    
    peDialog = PcaExplorerDialog(self.parent, self.data)
    choices = peDialog.show()
    if choices is None or choices == '':
      return

    pageTitle = ''

    if choices['primaryLabeling'] is not None:
      pageTitle += ' | %s' % (choices['primaryLabeling'])

    if choices['primaryLabeling'] is not None:
      pageTitle += ' | %s' % (choices['secondaryLabeling'])
    
    page = self.notebook.add('PCA Explorer%s' % (pageTitle))
    self.notebook.selectpage('PCA Explorer%s' % (pageTitle))
    dataset = self.data['myDataSet']
    primary = self.data['myDataSet'].getLabeling(choices['primaryLabeling'])
    secondary =self.data['myDataSet'].getLabeling(choices['secondaryLabeling'])
    # FIXME: HACK HACK HACK
    # FIXME: since the matplotlib version of IPlot doesn't have
    # FIXME: the pcaExplorer import the old BLT version for just this plot
    # FIXME: eeww.. icky isn't it.
    if not hasattr(IPlot, "PCAExplorer"):
      import compClust.IPlot as IPlotBLT
      IPlotBLT._ROOT = self.parent
      iplot= IPlotBLT
    else:
      iplot = IPlot
    iplot.PCAExplorer(dataset, primary, secondary, parent=page)

    #Add history
    hist = []
    hist.append('')
    hist.append('# PCA Explore')
    hist.append('IPlot.pcaExplore(dataSet,')
    if choices['primaryLabeling'] is not None:
      hist.append('      primaryLabeling=dataSet.getLabeling(\'%s\'),' % (choices['primaryLabeling']))
    if choices['secondaryLabeling'] is not None:
      hist.append('      secondaryLabeling=dataSet.getLabeling(\'%s\')' % (choices['secondaryLabeling']))
    hist.append(')')
    hist.append('')
    
    if _APPEND_HISTORY == 1:
      self.history.appendtext(string.join(hist, '\n'))
    elif _APPEND_HISTORY == 0:
      self._historyCache += string.join(hist, '\n')
    else:
      raise ValueError, '_APPEND_HISTORY=%s' % (_APPEND_HISTORY)
    
    
  def launchAboutBox(self):
    Pmw.aboutversion(VERSION)
    Pmw.aboutcopyright('Copyright (c) California Institute of Technology\nAll rights reserved')
    Pmw.aboutcontact('http://woldlab.caltech.edu/compClust/')
    #                 'CompClustTk: Brandon King\n'\
    #                 'IPlot: Chris Hart\n'\
    #                 'pyMLX: Lucas Scharenbroic, Diane Trout, Chris Hart')

    self.about = Pmw.AboutDialog(self.parent, applicationname='CompClustTk')
    self.about.show()
    

if __name__ == '__main__':  
  #_setupEnvironmentVars()
  root = Pmw.initialise()
  root.title('CompClust Analysis microGUI v%s' % (VERSION))

  #Use icon if we know where it is.
  if ICON_PATH is not None and os.path.exists(ICON_PATH):
    root.iconbitmap(ICON_PATH)
    
  gui = CompClustUI(root)
  root.mainloop()
