#!/usr/bin/env python

# which2gui-0.1
# Andrei Perhinschi
# This software was built as part of Andrei Perhinschi and Jamie Wood's
# CPE 481 senior design project during the fall 2011 semester at WVU.
# Latest changes occured on: April 20 2011

# import modules
import pygtk, gtk, gtksheet, gobject
import time, random, decimal, copy
import os, threading, linecache
from subprocess import *
pygtk.require('2.0')
gtk.gdk.threads_init()

# create main class
class which2gui:

    class lisp_process(threading.Thread):
        def __init__(self, cmd):
            self.child = Popen(cmd, cwd="../")
            super(which2gui.lisp_process, self).__init__()
            
            return
            
        def run(self):
            print "Process running in thread"
            count = 0
            curses = ["|", "/", "--", "\\", "|", "/", "--", "\\", "|"]
            sent = 0
            while 1:
                if count % 10000 == 0:
                    if count % 100000 == 0:
                        buff = "working " + curses[sent]
                        if sent == 8:
                            sent = 0
                        else:
                            sent += 1
                    gtk.gdk.threads_enter()
                    try:
                        which2gui.push_more_to_status(top_loop, sheet=top_loop.data_sheet, inbuff=buff)
                        top_loop.progbar.pulse()
                    finally:
                        gtk.gdk.threads_leave()
                if self.child.poll() != None:
                    break
                count += 1
            print "Process finished in thread"
            # update results in gui
            gtk.gdk.threads_enter()
            try:
                which2gui.update_output(top_loop, top_loop.show_old)
            finally:
                gtk.gdk.threads_leave()
                
            return
    
    # callback for delete event
    def delete_event(self, widget, event, data=None):
        
        gtk.main_quit()
        
        return False
    
    def __init__(self):
        
        # these lists hold the attribute names and instance data
        # rows is a list of lists, each member list represents one
        # historical instance; attribs is a regular list
        self.attribs = []
        self.rows = []
        
        # set data precision & rounding
        self.precision = 4
        decimal.getcontext().rounding = "ROUND_HALF_DOWN"
        
        # several default values
        self.tempset = ""
        self.tempset_name = ""
        self.F_name = ""
        self.r_name = ""
        self.sent = 0
        self.datadir_nopath = "LispData/"
        self.datadir = "../" + self.datadir_nopath
        self.testfile = ""
        self.trainfile = ""
        self.testset = ""
        self.trainset = ""
        self.file_status = []
        self.temp_status = ""
        self.light_green = '#A5FF88'
        self.tt_res = []
        self.cr_res = []
        self.show_old = 1
        
        # this list contains the sane default values for which2 params
        # logflg base beam lives samples disc bins eval_score goal
        self.default_params = ('t', 10, 30, 5, 20, 1, 2, 1, "defective")
        self.default_rule_params = []
        
        # read in last available configuration, if any
        self.read_conf()
        
        # create new window
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_title("Which2gui - Which2 now in widgets!")
        #self.window.set_border_width(2)
        self.window.connect("delete_event", self.delete_event)
        
        # create boxes
        self.hbox1 = gtk.HButtonBox()
        self.hbox1.set_layout(gtk.BUTTONBOX_START)
        self.vbox2 = gtk.VBox()
        self.vbox1 = gtk.VBox()
        self.vbox1.set_homogeneous(False)
        self.hbox3 = gtk.HBox()
        self.hbox2 = gtk.HBox()
        self.hbox2.set_homogeneous(False)
        
        self.rbox = gtk.VBox()
        
        # create panes
        self.hpane1 = gtk.HPaned()
        self.vpane1 = gtk.VPaned()
        
        # create file chooser button
        self.fcbutton = gtk.FileChooserButton("Select Dataset Directory", None)
        self.fcbutton.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
        self.fcbutton.set_current_folder(self.datadir)

        # create a treestore to use as the tree model
        # each row contains dataset name, train box, test box, 
        # and whether to display train/test boxes, respectively
        self.treestore = gtk.TreeStore(str, bool, bool, bool)
        
        # create top function buttons
        self.test_button = gtk.Button("Train/Test")
        self.rule_button = gtk.Button("Custom Rule")
        #self.tenway_button = gtk.Button("Run 10 Way")
        #self.plot_button = gtk.Button("Plot")
        self.config_button = gtk.Button("Configuration")
        self.usage_button = gtk.Button("Usage")
        self.about_button = gtk.Button("About")
        self.quit_button = gtk.Button("Quit")
        
        # create bottom results manipulation buttons
        #self.clear_button = gtk.CheckButton("Train/Test")
        #self.save_button = gtk.CheckButton("Custom Rule")
        self.show_chkbutton = gtk.CheckButton("Show Older Results")
        self.show_chkbutton.set_active(True)
        
        # create notebook
        self.nb = gtk.Notebook()
        
        # create treeview scrollwindow
        self.scwin = gtk.ScrolledWindow()
        self.scwin.set_size_request(220,500)
        self.scwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        
        # create sheet scrollwindow
        self.scwin_right = gtk.ScrolledWindow()
        self.scwin_right.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        self.scwin_right.set_resize_mode(gtk.RESIZE_IMMEDIATE)
        
        # create output scrollwindows
        self.scwin_tt_out = gtk.ScrolledWindow()
        self.scwin_tt_out.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
        self.scwin_cr_out = gtk.ScrolledWindow()
        self.scwin_cr_out.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
        
        # read in results file and creat output sheets
        self.update_output()
        self.create_tt_sheet()
        self.create_cr_sheet()
        
        # create tree
        self.treecreate()
        
        # update tree on new selection
        self.fcbutton.connect("current-folder-changed", self.dirselect)
        
        # button signal connects
        self.test_button.connect("clicked", self.test)
        self.config_button.connect("clicked", self.config_dialog)
        self.rule_button.connect("clicked", self.rule_dialog)
        self.about_button.connect("clicked", self.about_dialog)
        self.usage_button.connect("clicked", self.usage_dialog)
        #self.plot_button.connect("clicked", self.plot_dialog)
        self.quit_button.connect("clicked", self.quit_dialog)
        
        # bottom button signal connects
        self.show_chkbutton.connect("toggled", self.old_res)
        
        # parse through data directories
        self.dataparse(self.F_name)
        
        # create and populate the cell sheet
        self.create_sheet()
        
        # create statusbar
        self.status = gtk.Statusbar()
        self.filecontext = self.status.get_context_id("file")
        
        # progressbar
        self.progbar = gtk.ProgressBar()
        self.progbar.set_pulse_step(0.05)
        
        # pack the horizontal button box
        self.hbox1.pack_start(self.fcbutton, True, False, 0)
        self.fcbutton.show()
        self.hbox1.pack_start(self.test_button, True, False, 0)
        self.test_button.show()
        self.hbox1.pack_start(self.rule_button, True, False, 0)
        self.rule_button.show()
        #self.hbox1.pack_start(self.tenway_button, True, False, 0)
        #self.tenway_button.show()
        #self.hbox1.pack_start(self.plot_button, True, False, 0)
        #self.plot_button.show()
        self.hbox1.pack_start(self.config_button, True, False, 0)
        self.config_button.show()
        self.hbox1.pack_start(self.usage_button, True, False, 0)
        self.usage_button.show()
        self.hbox1.pack_start(self.about_button, True, False, 0)
        self.about_button.show()
        self.hbox1.pack_start(self.quit_button, True, False, 0)
        self.quit_button.show()
        
        # pack the bottom horizontal boxes
        self.hbox3.pack_start(self.status, True, True, 0)
        self.status.show()
        #self.hbox2.pack_start(self.clear_button, True, False, 0)
        #self.clear_button.show()
        #self.hbox2.pack_start(self.save_button, True, False, 0)
        #self.save_button.show()
        self.hbox2.pack_start(self.show_chkbutton, False, False, 0)
        self.show_chkbutton.show()
        self.hbox2.pack_end(self.progbar, False, False, 0)
        self.progbar.show()
        self.hbox3.pack_end(self.hbox2, False, False, 0)
        self.hbox2.show()
        
        # pack the horizontal pane
        self.hpane1.add1(self.scwin)
        self.scwin.show()
        self.hpane1.add2(self.scwin_right)
        self.scwin_right.show()
        
        # pack the results box
        self.tt_label = gtk.Label("Train/Test Results")
        self.cr_label = gtk.Label("Custom Rule Results")
        self.nb.append_page(self.scwin_tt_out, self.tt_label)
        self.scwin_tt_out.show()
        self.nb.append_page(self.scwin_cr_out, self.cr_label)
        self.scwin_cr_out.show()
        self.vbox2.pack_start(self.nb, True, True, 0)
        self.nb.show()
        self.vbox2.pack_end(self.hbox3, False, False, 0)
        self.hbox3.show()
        
        # pack the vertical pane
        self.vpane1.add1(self.hpane1)
        self.hpane1.show()
        self.vpane1.add2(self.vbox2)
        self.vbox2.show()
        
        # pack the vertical box
        self.vbox1.pack_start(self.hbox1, False, False, 2)
        self.hbox1.show()
        self.vbox1.pack_start(self.vpane1, True, True, 0)
        self.vpane1.show()
        
        # show vertical box and add to window
        self.window.add(self.vbox1)
        self.vbox1.show()
        self.window.show()
        
        return
    
    # create and populate treeview widget
    def treecreate(self):
        
        # create a treeview widget using treestore
        self.treeview = gtk.TreeView(self.treestore)
        self.treeview.set_headers_visible(1)
        
        # populate treestore with data from directories
        self.dirselect(self.fcbutton)
        
        # create a cellrenderertext to render the data
        self.cell = gtk.CellRendererText()
        
        # create cellrenderertoggles for radiobutton columns
        self.trainrender = gtk.CellRendererToggle()
        self.trainrender.set_radio(True)
        self.trainrender.set_property('activatable', True)
        self.trainrender.connect('toggled', self.radio_button_check, (self.treestore, 1))
        
        self.testrender = gtk.CellRendererToggle()
        self.testrender.set_radio(True)
        self.testrender.set_property('activatable', True)
        self.testrender.connect('toggled', self.radio_button_check, (self.treestore, 2))
        
        # create treeviewcolumns to display the datasets
        self.treeview_column = gtk.TreeViewColumn('Datasets', self.cell, text=0)
        self.treeview_trainbox_column = gtk.TreeViewColumn('Train', self.trainrender)
        self.treeview_testbox_column = gtk.TreeViewColumn('Test', self.testrender)
        
        # assign attributes based on treestore
        self.treeview_trainbox_column.add_attribute(self.trainrender, "active", 1)
        self.treeview_testbox_column.add_attribute(self.testrender, "active", 2)
        
        # hide radio buttons on expander rows
        self.treeview_trainbox_column.add_attribute(self.trainrender, "visible", 3)
        self.treeview_testbox_column.add_attribute(self.testrender, "visible", 3)
        
        # add treeviewcolumns to treeview
        self.treeview.append_column(self.treeview_column)
        self.treeview.append_column(self.treeview_trainbox_column)
        self.treeview.append_column(self.treeview_testbox_column)
        
        # make 1st column searchable
        self.treeview.set_search_column(0)
        
        # allow sorting on 1st column
        self.treeview_column.set_sort_column_id(0)
        
        # set column resizability
        self.treeview_column.set_resizable(True)
        self.treeview_trainbox_column.set_resizable(True)
        self.treeview_testbox_column.set_resizable(True)
        
        # disable drag and drop row reordering
        self.treeview.set_reorderable(False)
        
        # grab current selection
        self.treeselect = self.treeview.get_selection()
        
        # connect redo_sheet to selection change
        self.treeselect.connect("changed", self.redo_sheet)
        
        # add and show treeview
        self.scwin.add(self.treeview)
        self.treeview.show()
        
        return
        
    # populate treestore with data from given dir
    def dirselect(self, button):
        
        # this is all magic to me now...
        if self.sent == 0:
            datadir = self.datadir
            self.sent += 1
        else:
            self.treestore.clear()
            datadir = button.get_filename()
        sets = os.listdir(datadir)
        new_sets = self.fileselect(sets, datadir)
        for dataset in new_sets:
            parent = self.treestore.append(None, (dataset[0], None, None, False))
            for child in dataset[1:]:
                if new_sets.index(dataset) == self.tempset and dataset.index(child) == self.tempset_name+1:
                    self.treestore.append(parent, (child, True, False, True))
                    self.trainfile = child
                    self.trainset = dataset[0]
                else:
                    self.treestore.append(parent, (child, False, False, True))
        # focus on default dataset
        self.treeview.expand_to_path(self.tempset)
        self.cursor_pos = (self.tempset,self.tempset_name)
        self.treeview.set_cursor(self.cursor_pos)
        
        return
        
    # scan dataset directory, store datasets and randomly pick a dataset file
    def fileselect(self, sets, datadir):
        
        new_sets = []
        for index, aset in enumerate(sets):
            # ignoring the .svn dirs
            if aset == ".svn" or aset == "prop":
                continue
            else:
                temporary = []
                temporary.append(aset)
                temporary.extend(os.listdir(datadir + "/" + sets[index]))
                for eachfile in temporary:
                    if eachfile == ".svn":
                        temporary.remove(eachfile)
                        continue
                new_sets.append(temporary)
        
        # pick a random file to display and select for training on startup
        self.tempset = random.randint(0,len(sets)-1)
        f_name = datadir + "/" + sets[self.tempset] + "/"
        tempset_deep = os.listdir(f_name)
        for aset2 in tempset_deep:
            if aset2 == ".svn":
                tempset_deep.remove(aset2)
        self.tempset_name = random.randint(0,len(tempset_deep)-1)
        self.r_name = tempset_deep[self.tempset_name]
        self.F_name = f_name + self.r_name
        
        # return list of lists containing dataset names and the associated filenames
        # new_sets = [ [dataset_name_A, fileA1, fileA2, fileA3], [dataset_name_B, fileB1, fileB2]]
        return new_sets
    
    # parse through the dataset file and grab all the data
    def dataparse(self, f_name):
        
        self.attribs = []
        self.rows = []
        infile = open(f_name, "r")
        a = 0
        for line in infile:
            curline = line.split()
            if a == 0:
                curline = curline[2:]
                
                # stripping sticky characters off our data
                for attrib in curline:
                    attrib = attrib.replace("$", "")
                    attrib = attrib.replace(")", "")
                    self.attribs.append(attrib)
                a += 1
            elif curline:
                curline = curline[1:]
                tempattr = curline.pop()
                curline.append(tempattr.replace(")", ""))
                self.rows.append(curline)
        infile.close()
        
        return
        
    # rebuild cell sheet with new data file, called 
    # immediately after selecting a new treeview row
    def redo_sheet(self, data):
        
        try:
            entry1, entry2 = data.get_selected()
            new_f_name = entry1.get_value(entry2, 0)
            new_set_name = new_f_name.split("-")[1]
            new_name = "../LispData/" + new_set_name +"/"+ new_f_name
            
            self.file_status = [new_f_name ,self.filecontext, new_name]
            self.push_to_status(self.file_status)
            
            self.dataparse(new_name)
            self.data_sheet.destroy()
            #self.nblabel.set_text(new_f_name)
            self.create_sheet()
        except TypeError:
            print "TypeError: something or other"
            
        return
        
    # create and populate cell sheet with data
    def create_sheet(self):
        
        self.data_sheet = gtksheet.Sheet(len(self.rows), len(self.attribs), "Data", True)
        self.data_sheet.show_grid(True)
        self.data_sheet.columns_set_resizable(False)
        self.data_sheet.rows_set_resizable(False)
        self.data_sheet.set_clip_text(False)
        self.data_sheet.rows_set_sensitivity(False)
        self.data_sheet.set_selection_mode(gtk.SELECTION_SINGLE)
        width = len(str(len(self.rows)))*10
        self.data_sheet.set_row_titles_width(width)
        
        # set up columns: titles, widths, etc
        dep_color = gtk.gdk.color_parse("light green")
        b = 0
        for index, column in enumerate(xrange(self.data_sheet.get_columns_count())):
            name = self.attribs[b]
            self.data_sheet.set_column_width(column, len(name)*10)
            self.data_sheet.column_button_add_label(column, name)
            # add a splash of color to class ranges
            if name[0] == '!':
                #self.data_sheet.column_set_sensitivity(column, False)
                dep_range = gtksheet.SheetRange(0, index, len(self.rows)-1, index)
                self.data_sheet.range_set_background(dep_range, dep_color)
            b += 1
        
        # set up rows: titles, widths, individual cell data, etc
        c = 0
        for row in self.rows:
            self.data_sheet.row_button_add_label(c, str(c+1))
            d = 0
            for val in row:
                val = decimal.Context.create_decimal(decimal.Context(self.precision), self.rows[c][d])
                val = str(val)
                self.data_sheet.set_cell(c, d, gtk.JUSTIFY_CENTER, val)
                d += 1
            c += 1
        
        # connect cell clicks to status bar updating function
        self.data_sheet.connect("activate", self.push_more_to_status)
        self.scwin_right.add(self.data_sheet)
        self.data_sheet.show()
        
        return
    
    # handle radio button toggling
    def radio_button_check(self, cell, path, model_data):
        
        state = cell.get_active()
        model, column = model_data
        
        # mutually exclusive by column ... magic
        rootiter = model.get_iter_root()
        citer = model.iter_children(rootiter)
        for x in range(len(model)):
            citer = model.iter_children(rootiter)
            if rootiter is not None:
                while model.iter_has_child(rootiter):
                    model.set_value(citer, column, False)
                    if model.iter_next(citer) is not None:
                        citer = model.iter_next(citer)
                    elif model.iter_next(rootiter) is None:
                        break
                    else:
                        rootiter = model.iter_next(rootiter)
                        break
        
        row = model.get_iter(path)
        
        # mutually exclusive by row
        if column == 1:
            if state == True:
                self.trainfile = ""
                self.trainset = ""
            else:
                if model.get_value(row, 2) == True:
                    self.testfile = ""
                    self.testset = ""
                model.set_value(row, column, not state)
                model.set_value(row, 2, False)
                self.trainfile = model.get_value(row, 0) 
                self.trainset = model.get_value(model.iter_parent(row), 0)
        if column == 2:
            if state == True:
                self.testfile = ""
                self.testset = ""
            else:
                if model.get_value(row, 1) == True:
                    self.trainfile = ""
                    self.trainset = ""
                model.set_value(row, column, not state)
                model.set_value(row, 1, False)
                self.testfile = model.get_value(row, 0)
                self.testset = model.get_value(model.iter_parent(row), 0)
                
        return
    
    # update the status bar with file information
    def push_to_status(self, data):
        
        buff = "file: %s" % data[0]
        numlines = sum(1 for line in open(data[2]))-3
        buff = buff + " %d" % numlines
        self.temp_status = buff
        fileinfo = self.status.push(data[1], buff)
        return
    
    # update the status bar with sheet information
    def push_more_to_status(self, sheet, row=0, col=0, inbuff="idle"):
        
        orig_status = self.temp_status.split(" ")
        numlines = orig_status.pop()
        orig_status = " ".join(orig_status)
        #print "ORIG_STATUS: ", orig_status
        buff = orig_status + " | @row/lines: " + str(row+1) + "/" + str(numlines) + " | col: " + str(col+1) + " | currently: " + inbuff
        
        #print "BUFF: ", buff
        cell = self.status.push(self.filecontext, buff)
        
        return

    # read in last used which2 configuration settings if available
    # returns list of parameter values in expected order
    def read_conf(self):
        
        # restore main.lisp from temp_main.lisp if available
        if os.path.exists("../temp_main.lisp"):
            print "temp_main.lisp exists - Restoring to main.lisp"
            if os.path.exists("../main.lisp"):
                os.remove("../main.lisp")
            os.rename("../temp_main.lisp", "../main.lisp")
        
        # load settings from last run if available
        if os.path.exists("../main.lisp") and not os.path.exists("../temp_main.lisp"):
            print "main.lisp exists - Checking integrity"
            param_list = linecache.getline("../main.lisp", 2)
            param_list = param_list.split(" ")
            if param_list[0] == "(guitest":
                print "main.lisp appears correct - Loading last used configuration"
                param_list = param_list[3:]
                last_param = param_list.pop()
                last_param = last_param[1:len(last_param)-3]
                param_list.append(last_param)
                del self.default_params
                self.default_params = copy.deepcopy(param_list)
            else:
                print "main.lisp appears corrupted or otherwise unusable - Using default configuration"
        # otherwise self.default_params stays unchanged and contains
        # default parameter values.
        else:
            print "main.lisp not found - Using default configuration"
            
        return
    
    # assemble and write main.lisp file
    def write_mainlisp(self, fun):
        
        print "Writing lisp interface file: main.lisp"
        
        if len(self.default_params) != 9:
            print "Error: Missing default parameters for which2"
        # if fun is 0 then we are doing train/test
        elif fun == 0:
            # default which2 values turned to strings
            log = str(self.default_params[0])
            base = str(self.default_params[1])
            beam = str(self.default_params[2])
            lives = str(self.default_params[3])
            samples = str(self.default_params[4])
            disc = str(self.default_params[5])
            numbins = str(self.default_params[6])
            eval_score = str(self.default_params[7])
            goal = self.default_params[8]
            
            infile1 = self.datadir_nopath + self.trainset + "/" + self.trainfile
            infile2 = self.datadir_nopath + self.testset + "/" + self.testfile
            outline1 = "(load \"boot.lisp\")\n"
            outline2 = "(guitest \"" + infile1 + "\" \"" + infile2 + "\" " + log + " " + base + " " + beam + " " + lives + " " + samples + " " + disc + " " + numbins + " " + eval_score + " \"" + goal + "\")"
            
            # open main.lisp filehandle and write new test information to it
            try:
                outfile = open("../main.lisp", "w")
                outfile.write(outline1)
                outfile.write(outline2)
                outfile.close()
            except IOError:
                print "Error: You lack permission to write files here"
        # if fun is 1 we are doing custom rule testing
        # the following block takes care of both single
        # and multiple rule cases
        elif fun == 1:
            log = str(self.default_params[0])
            base = str(self.default_params[1])
            disc = str(self.default_params[5])
            numbins = str(self.default_params[6])
            want = self.default_params[8]
            infile1 = self.datadir_nopath + self.trainset + "/" + self.trainfile
            outline1 = "(load \"boot.lisp\")\n"
            outline2 = infile1 + "\" " + disc + " " + numbins + " " + log + " " + base + " \"" + want + "\" "
            
            if len(self.default_rule_params) > 2:
                
                outline2 = "(customrule2 \"" + outline2 + "\'("
                
                for index, att_val in enumerate(self.default_rule_params):
                    if index % 2 == 0:
                        new_tail = " \"" + str(self.default_rule_params[index]).swapcase() + "\" "
                    elif index % 2 != 0:
                        new_tail = str(self.default_rule_params[index])
                    outline2 = outline2 + new_tail
                
                outline2 = outline2 + "))"
                
            else:
            
                what = "\"" + str(self.default_rule_params[0]).swapcase()
                value = str(self.default_rule_params[1])
                outline2 = "(customrule \"" + outline2 + what + "\" " + value + ")"
            
            print outline2
            
            # backup main.lisp and write new main.lisp for custom rule testing
            try:
                tempout = open("../temp_main.lisp", "w")
                mainin = open("../main.lisp", "r")
                for line in mainin:
                    tempout.write(line)
                mainin.close()
                tempout.close()
                outfile = open("../main.lisp", "w")
                outfile.write(outline1)
                outfile.write(outline2)
                outfile.close()
            except IOError:
                print "Error: You lack permission to write files here"
                
        return
    
    # update the result output sheets
    def update_output(self, w_sheet=0):
        
        self.tt_res = []
        self.cr_res = []
        try:
            guiout = open("../guiout.txt", "r")
            for line in guiout:
                if len(line.split()) != 11:
                    curline = line.split(None, 11)
                    self.tt_res.insert(0, curline)
                else:
                    curline = line.split()
                    self.cr_res.insert(0, curline)
            guiout.close()
        except IOError:
            print "Error: You lack permissions here"
        
        if w_sheet == 1:
            self.tt_sheet.destroy()
            self.create_tt_sheet()
            self.cr_sheet.destroy()
            self.create_cr_sheet()
            self.push_more_to_status(self.data_sheet, inbuff="idle")
        if w_sheet == 2:
            self.tt_sheet.destroy()
            del self.tt_res[1:]
            self.create_tt_sheet()
            self.cr_sheet.destroy()
            del self.cr_res[1:]
            self.create_cr_sheet()
            self.push_more_to_status(self.data_sheet, inbuff="idle")
            
        return
    
    # builds and populates train/test results gtksheet
    def create_tt_sheet(self):
        
        #res_num = len(self.tt_res) + len(self.cr_res)
        self.tt_sheet = gtksheet.Sheet(len(self.tt_res), 12, "Data", True)
        self.tt_sheet.show_grid(True)
        self.tt_sheet.columns_set_resizable(False)
        self.tt_sheet.rows_set_resizable(False)
        self.tt_sheet.set_clip_text(False)
        self.tt_sheet.rows_set_sensitivity(False)
        self.tt_sheet.set_selection_mode(gtk.SELECTION_SINGLE)
        width = len(str(len(self.tt_res)))*10
        self.tt_sheet.set_row_titles_width(width)
        
        f = 0
        for res_hdr in ("Train", "Test", "A", "B", "C", "D", "Pd", "Pf", "Pred", "Acc", "Bins", "Rule"):
            self.tt_sheet.set_column_width(f, len(res_hdr)*10)
            self.tt_sheet.column_button_add_label(f, res_hdr)
            self.tt_sheet.column_button_justify(f, gtk.JUSTIFY_LEFT)
            f += 1
        # train/test results
        g = 0
        for row in self.tt_res:
            self.tt_sheet.row_button_add_label(g, str(g+1))
            h = 0
            for val in row:
                self.tt_sheet.set_cell(g, h, gtk.JUSTIFY_LEFT, val)
                h += 1
            g +=1
        
        self.scwin_tt_out.add(self.tt_sheet)
        self.tt_sheet.show()
        
        return
    
    # builds and populates custom rule results gtksheet
    def create_cr_sheet(self):
        
        self.cr_sheet = gtksheet.Sheet(len(self.cr_res), 11, "Data", True)
        self.cr_sheet.show_grid(True)
        self.cr_sheet.columns_set_resizable(False)
        self.cr_sheet.rows_set_resizable(False)
        self.cr_sheet.set_clip_text(False)
        self.cr_sheet.rows_set_sensitivity(False)
        self.cr_sheet.set_selection_mode(gtk.SELECTION_SINGLE)
        width = len(str(len(self.tt_res)))*10
        self.cr_sheet.set_row_titles_width(width)
        
        f = 0
        for res_hdr in ("Set", "A", "B", "C", "D", "Pd", "Pf", "Pred", "Acc", "Bins", "Custom Rule"):
            self.cr_sheet.set_column_width(f, len(res_hdr)*10)
            self.cr_sheet.column_button_add_label(f, res_hdr)
            self.cr_sheet.column_button_justify(f, gtk.JUSTIFY_LEFT)
            f += 1
        # custom rule results
        g=0
        for row in self.cr_res:
            self.cr_sheet.row_button_add_label(g, str(g+1))
            h = 0
            for val in row:
                self.cr_sheet.set_cell(g, h, gtk.JUSTIFY_LEFT, val)
                h += 1
            g +=1
        
        self.scwin_cr_out.add(self.cr_sheet)
        self.cr_sheet.show()
        
        return
    
    #########################
    # DIALOG/BUTTON METHODS #
    #########################
    
    # most of the following two functions came from Jamie's rig.py
    def test(self, widget):
        
        def error_funct(widget, res):
            widget.destroy()
            
            return
        
        print "Performing train/test run"
        
        # create main.lisp file and write to disk
        self.write_mainlisp(0)
        
        # checking radio button consistency
        linecache.clearcache()
        main_line = linecache.getline("../main.lisp", 2)
        main_line = main_line.split(" ")
        if main_line[1] == "\"LispData//\"":
            if main_line[2] == "\"LispData//\"":
                print "Error: You must select train/test sets."
                sets_error = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Error: You must select train/test sets.")
                sets_error.connect("response", error_funct)
                sets_error.show()
            else:
                print "Error: You must select a set to train on."
                train_error = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Error: You must select a set to train on.")
                train_error.connect("response", error_funct)
                train_error.show()
        elif main_line[2] == "\"LispData//\"":
            print "Error: You must select a set to test on."
            test_error = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Error: You must select a set to test on.")
            test_error.connect("response", error_funct)
            test_error.show()
        # this shouldn't ever happen since the radiobutton sanity checks
        # should take care of this situation, this is here for extra safety
        elif main_line[1] == main_line[2]:
            print "Error: You cannot train/test on the same dataset."
            same_error = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Error: You cannot train/test on the same dataset.")
            same_error.connect("response", error_funct)
            same_error.show()
        # all is well so let's run which2
        else:
            # call lisp (writes a row to guiout.txt each call)
            self.push_more_to_status(self.data_sheet, inbuff="working")
            out = self.lisp_process(["bash", "lisp"])
            out.start()
        linecache.clearcache()
        
        return
    
    # configuration dialog
    def config_dialog(self, widget):
        
        # update parameters with configuration dialog values
        def update_params():
            
            del self.default_params
            self.default_params = []
            if log.get_active():
                self.default_params.append("t")
            else:
                self.default_params.append("nil")
            self.default_params.append(base.get_text())
            self.default_params.append(beam.get_text())
            self.default_params.append(lives.get_text())
            self.default_params.append(samplesize.get_text())
            self.default_params.append(str(1))
            self.default_params.append(bins.get_text())
            self.default_params.append(str(scmetric.get_active()+1))
            self.default_params.append(goal.get_active_text())
            
            return
        
        # takes care of the config dialog's several possible actions
        def conf_funct(widget, res):
            
            if res == gtk.RESPONSE_CANCEL:
                widget.destroy()
            elif res == gtk.RESPONSE_REJECT:
                del self.default_params
                # reset to default values
                self.default_params = ["t", 10, 30, 5, 20, 1, 2, 1, "defective"]
                # create new main.lisp file and write it to disk
                self.write_mainlisp(0)
                widget.destroy()
            elif res == gtk.RESPONSE_OK:
                # save updated parameters
                update_params()
                # create new main.lisp file and write it to disk
                self.write_mainlisp(0)
                widget.destroy()
                
            return
    
        print "Configuration Dialog"
        config = gtk.Dialog("Which2gui - Configuration", self.window, gtk.DIALOG_MODAL, (gtk.STOCK_OK, gtk.RESPONSE_OK,gtk.STOCK_REVERT_TO_SAVED, gtk.RESPONSE_REJECT, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
        cbox = config.get_content_area()
        
        log = gtk.CheckButton() 
        loglbl = gtk.Label("Logging: ")
        if self.default_params[0] == "t":
            log.set_active(1)
        elif self.default_params[0] == "nil":
            log.set_active(0)
        
        base = gtk.Entry()
        base.set_has_frame(True)
        baselbl = gtk.Label("Base: ")
        base.set_text(str(self.default_params[1]))
        
        beam = gtk.Entry()
        beam.set_has_frame(True)
        beamlbl = gtk.Label("Beam: ")
        beam.set_text(str(self.default_params[2]))
        
        lives = gtk.Entry()
        lives.set_has_frame(True)
        liveslbl = gtk.Label("Lives: ")
        lives.set_text(str(self.default_params[3]))
        
        samplesize = gtk.Entry()
        samplesize.set_has_frame(True)
        samplesizelbl = gtk.Label("Samples: ")
        samplesize.set_text(str(self.default_params[4]))
        
        dscrtizer = gtk.combo_box_new_text()
        dscrtizer.append_text("default")
        dscrtizer.set_active(0)
        dscrtizerlbl = gtk.Label("Discretization: ")
        
        bins = gtk.Entry()
        bins.set_has_frame(True)
        binslbl = gtk.Label("Bins: ")
        bins.set_text(str(self.default_params[6]))
        
        scmetric = gtk.combo_box_new_text()
        scmetric.append_text("pd")
        scmetric.append_text("(2 * pd * prec) / (pd + prec)")
        scmetric.append_text("(2 * pd * (1 - pf)) / (pd + (1 - pf))")
        scmetric.append_text("(2 * pd * support) / (pd + (1 - pf))")
        scmetric.append_text("support * acc")
        scmetric.append_text("(2 * pd * support * prec) / (pd + prec)")
        scmetric.append_text("1 - pf")
        scmetric.append_text("support * list")
        metric_index = int(self.default_params[7])-1
        scmetric.set_active(metric_index)
        scmetriclbl = gtk.Label("Scoring Metric: ")
        
        goal = gtk.combo_box_new_text()
        goal.insert_text(0,"defective")
        goal.insert_text(1,"flawless")
        goal_bool = self.default_params[8]
        if goal_bool == "defective":
            goal.set_active(0)
        elif goal_bool == "flawless":
            goal.set_active(1)
        goallbl = gtk.Label("Goal: ")
        
        cboxa = gtk.HBox()
        cboxa.pack_start(loglbl, False, False, 0)
        loglbl.show()
        cboxa.pack_end(log, False, False, 0)
        log.show()
        
        cboxb = gtk.HBox()
        cboxb.pack_start(binslbl, False, False, 0)
        binslbl.show()
        cboxb.pack_end(bins, False, False, 0)
        bins.show()
        
        cboxc = gtk.HBox()
        cboxc.pack_start(beamlbl, False, False, 0)
        beamlbl.show()
        cboxc.pack_end(beam, False, False, 0)
        beam.show()
        
        cboxd = gtk.HBox()
        cboxd.pack_start(liveslbl, False, False, 0)
        liveslbl.show()
        cboxd.pack_end(lives, False, False, 0)
        lives.show()
        
        cboxe = gtk.HBox()
        cboxe.pack_start(samplesizelbl, False, False, 0)
        samplesizelbl.show()
        cboxe.pack_end(samplesize, False, False, 0)
        samplesize.show()
        
        cboxf = gtk.HBox()
        cboxf.pack_start(dscrtizerlbl, False, False, 0)
        dscrtizerlbl.show()
        cboxf.pack_end(dscrtizer, False, False, 0)
        dscrtizer.show()
        
        cboxg = gtk.HBox()
        cboxg.pack_start(scmetriclbl, False, False, 0)
        scmetriclbl.show()
        cboxg.pack_end(scmetric, False, False, 0)
        scmetric.show()
        
        cboxh = gtk.HBox()
        cboxh.pack_start(goallbl, False, False, 0)
        goallbl.show()
        cboxh.pack_end(goal, False, False, 0)
        goal.show()
        
        cboxi = gtk.HBox()
        cboxi.pack_start(baselbl, False, False, 0)
        baselbl.show()
        cboxi.pack_end(base, False, False, 0)
        base.show()
        
        config.connect("response", conf_funct)
        cbox.pack_start(cboxg, False, False, 0)
        cbox.pack_start(cboxb, False, False, 0)
        cbox.pack_start(cboxc, False, False, 0)
        cbox.pack_start(cboxd, False, False, 0)
        cbox.pack_start(cboxe, False, False, 0)
        cbox.pack_start(cboxi, False, False, 0)
        
        cbox.pack_start(cboxh, False, False, 0)
        cbox.pack_start(cboxf, False, False, 0)
        cbox.pack_start(cboxa, False, False, 0)
        
        cboxb.show()
        cboxc.show()
        cboxd.show()
        cboxe.show()
        cboxi.show()
        cboxh.show()
        cboxf.show()
        cboxg.show()
        cboxa.show()
        config.show()
        
        return
    
    # custom rule dialog
    def rule_dialog(self, widget):
        
        def del_rule_err(datawid, res):
            datawid.destroy()
            return
        
        def rule_funct(widget, res):
            if res == gtk.RESPONSE_CANCEL:
                widget.destroy()
            elif res == gtk.RESPONSE_OK:
                print "Testing Custom Rule(s)!"
                
                self.nb.set_current_page(1)
                self.default_rule_params = []
                kids = self.rbox.get_children()
                for kid in kids[1:len(kids)-1]:
                    gkids = kid.get_children()
                    self.default_rule_params.append(gkids[0].get_active_text())
                    self.default_rule_params.append(gkids[1].get_active_text())
                
                # write new main.lisp for custom rule testing
                self.write_mainlisp(1)
                # call lisp to test on custom rule
                widget.destroy()
                self.push_more_to_status(self.data_sheet, inbuff="working")
                out = self.lisp_process(["bash", "lisp"])
                out.start()
                
            return
        
        def spawn_rule(widget):
            
            rboxa = gtk.HBox()
            
            att_cmbox = gtk.combo_box_new_text()
            for attrib in self.attribs:
                att_cmbox.append_text(attrib)
            att_cmbox.set_active(0)
            
            value = gtk.combo_box_new_text()
            for val in range(0,int(self.default_params[6])):
                value.append_text(str(val))
            value.set_active(0)
            
            button = gtk.Button(None, gtk.STOCK_ADD)
            button.connect("clicked", spawn_rule)
            blab = button.get_children()[0]
            blab = blab.get_children()[0].get_children()[1]
            blab = blab.set_label("")
            
            button2 = gtk.Button(None, gtk.STOCK_REMOVE)
            button2.connect("clicked", del_rule)
            b2lab = button2.get_children()[0]
            b2lab = b2lab.get_children()[0].get_children()[1]
            b2lab = b2lab.set_label("")
            
            rboxa.pack_start(att_cmbox, False, False, 0)
            rboxa.pack_start(value, False, False, 0)
            rboxa.pack_end(button2, False, False, 0)
            rboxa.pack_end(button, False, False, 0)
            
            att_cmbox.show()
            value.show()
            button.show()
            button2.show()
            
            self.rbox.pack_start(rboxa, False, False, 0)
            rboxa.show()
            
            return
        
        def del_rule(button):
            
            if len(self.rbox.get_children()) == 3:
                rule_error = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Error: You cannot test without at least one rule.")
                rule_error.connect("response", del_rule_err)
                rule_error.show()
            else:
                self.rbox.remove(button.get_parent())
                sz = rulein.get_size()
                rulein.resize(sz[0], sz[1]-50)
                
            return
        
        print "Custom Rule Dialog"
        rulein = gtk.Dialog("Which2gui - Custom Rule Testing", self.window, gtk.DIALOG_MODAL, ("Test Rule", gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
        rulein.connect("response", rule_funct)
        rulein.set_resize_mode(gtk.RESIZE_QUEUE)
        
        self.rbox = rulein.get_content_area()
        self.rbox.set_resize_mode(gtk.RESIZE_QUEUE)
        rbox_top = gtk.HBox()
        
        label = gtk.Label("Rule(s) (attr/val):")
        rbox_top.pack_start(label, False, False, 0)
        label.show()
        
        self.rbox.pack_start(rbox_top, False, False, 0)
        rbox_top.show()
        spawn_rule(self)
        rulein.show()
        
        return
    
    # usage dialog
    def usage_dialog(self, widget):
        
        def usage_funct(widget, res):
            if res == gtk.RESPONSE_CLOSE:
                widget.destroy()
                
            return
        
        print "Usage Dialog"
        usage = gtk.Dialog("Which2gui - Usage", self.window, 0, (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))
        ubox  = usage.get_content_area()
        utext = gtk.TextView()
        ubuff = utext.get_buffer()
        utext.set_size_request(400,300)
        infile = open("README", "r")
        for line in infile:
            ubuff.insert_at_cursor(line)
        infile.close()
        
        utext.set_editable(False)
        utext.set_cursor_visible(False)
        utext.set_wrap_mode(gtk.WRAP_WORD)
        
        usage.connect("response", usage_funct)
        ubox.pack_start(utext)
        utext.show()
        usage.show()
        
        return
    
    # show/hide old results
    def old_res(self, widget):
        
        astatus = widget.get_active()
        if astatus == False:
            self.show_old = 2
        else:
            self.show_old = 1
        self.update_output(self.show_old)
        
        return
    
    # about dialog
    def about_dialog(self, widget):

        def about_funct(widget, res):
            if res == gtk.RESPONSE_CANCEL:
                widget.destroy()
                
            return
        
        print "About Dialog"
        about = gtk.AboutDialog()
        about.set_name("Which2gui")
        about.set_version("0.1")
        about.set_comments("Which2gui is a gui for Which2!")
        about.set_authors(("Andrei Perhinschi <aperhins@mix.wvu.edu>", "Jamie Wood <jwood13@mix.wvu.edu>"))
        about.set_website("http://unbox.org/wisp/var/andrei/lisp101/ml/gui/")
        about.connect("response", about_funct)
        about.show()
        
        return
        
    # quit dialog
    def quit_dialog(self, widget):
        
        def quit_eval(widget, res):
            if res == gtk.RESPONSE_YES:
                self.delete_event(widget, self)
            else:
                widget.destroy()
                
            return
        
        print "Quit?"
        qdiag = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, "Quit?")
        qdiag.format_secondary_text("Done datamining?")
        qdiag.connect("response", quit_eval)
        qdiag.show()
        
        return
    
    # plotting dialog
    #def plot_dialog(self, widget):
        
        #def plot_funct(widget, res):
            #if res == gtk.RESPONSE_CLOSE:
                #print "Destroying plot dialog"
                #widget.destroy()
            #elif res == gtk.RESPONSE_APPLY:
                #print "Resetting plot dialog"
                #plotcurve.reset()
            #elif res == gtk.RESPONSE_YES:
                #print "Saving plot to file"
            #elif res == gtk.RESPONSE_OK:
                #print "Performing plot"
            #return
        
        #print "Plot Dialog"
        #plot = gtk.Dialog("Which2gui - Plotting", self.window, 0, ("Plot", gtk.RESPONSE_OK, gtk.STOCK_SAVE, gtk.RESPONSE_YES, "Reset", gtk.RESPONSE_APPLY, gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))
        
        #pbox = plot.get_content_area()
        
        #plotcurve = gtk.Curve()
        #plotcurve.set_size_request(200, 250)
        
        #plot.connect("response", plot_funct)
        
        #pbox.pack_start(plotcurve, True, True, 0)
        #plotcurve.show()
        #plot.show()
        
        #return
    
    ################################
    # END OF DIALOG/BUTTON METHODS #
    ################################
    
def main():
    gtk.gdk.threads_enter()
    gtk.main()
    gtk.gdk.threads_leave()
    
    return

if __name__ == "__main__":
    
    # top_loop now represents our instance of which2gui
    top_loop = which2gui()
    main()
