commit 9a4d83982992ee7a284d9603303041ed4d8ea7ea
Author: Yuyang Guo <yuyang@yuyang-VirtualBox.(none)>
Date:   Wed Feb 13 00:22:59 2013 -0500

    New GUI should be working now! (run, pause resume should all work
    pretty robustly). Teleop is not working yet...

diff --git a/scout/scoutsim/GUI.py b/scout/scoutsim/GUI.py
index 8caeb9d..c761e51 100755
--- a/scout/scoutsim/GUI.py
+++ b/scout/scoutsim/GUI.py
@@ -25,13 +25,38 @@ from scoutsim.srv import *
 
 from collections import defaultdict
 
+
+class Behaviors(object):
+    def __init__(self):
+        print "Warning: You should not need to create an instance of Behaviors"
+
+    # this takes advantage of the fact that our behavior list start at 1    
+    behaviors = ["Pause", "CW Circle", "CCW Circle", "Odometry",
+                          "Navigation Map", "Scheduler", "Warehouse",
+                          "Line Follow", "WL Test"]
+    @classmethod
+    def getNumber(self, behaviorName):
+        if (behaviorName in Behaviors.behaviors):
+            return Behaviors.behaviors.index(behaviorName)
+    
+    @classmethod
+    def getName(self, index):
+        if (0 <= index < len(self.Behavior)):
+            return Behaviors.behaviors[index]
+        else:
+            return -1
+    
+    @classmethod
+    def getBehaviors(self):
+        # "Pause" is not considered a behavior in GUI
+        return Behaviors.behaviors[1:]
+
 # each scout is represented by this class
 class Scout(object):
     numOfScouts = 0 # class variable keeping track of scouts
     
     def __init__(self, x=0, y=0, theta=0, name=u"",
                        Scouts=None, oninit = False):
-        print (len(name))
         Scout.numOfScouts += 1 # reverted if not successful
         if len(name) == 0:
             # automatically give name according to numOfScouts
@@ -41,6 +66,7 @@ class Scout(object):
         self.behavior = "chilling"
         self.process =  None# used to keep track of all processes
                         # related to this scout  
+        self.paused = False
         self.valid = False
         # these vars are right now just the original position
         # but when ODOMETRY is done, we could actually use them
@@ -68,7 +94,6 @@ class Scout(object):
         
 
     def SpawnInSimulator(self):
-        print "trying to spawn scout!!!"
         try:
             # ros spawn routine
             rospy.wait_for_service('/spawn');
@@ -76,32 +101,41 @@ class Scout(object):
             response = service(self.x, self.y, self.theta, self.name)
             return True
         except rospy.ServiceException as Error:
-            print "things are bad!!!"
-            print Error
             return False
-    
-    def changeBehavior(self, behavior):
-        self.behavior = behavior
+   
+    def terminateOldBehavior(self):
+        if self.process != None:
+            # now terminate the process
+            self.process.terminate() #TODO: this kills the process
+                                     #      but maybe not the rosnode
+                                     #      change to "rosnode kill" instead
+            # make sure old behavior is terminated before we run the new one
+            self.process.wait()
 
-        # WARNING: Check this but ROS kills the old behavior for us
-            # kill the old behavior
-#        if self.process != None:
-#            self.process.terminate() #TODO: this kills the process
-#                                     #      but maybe not the rosnode
-#                                     #      change to "rosnode kill" instead
-#        # make sure the old behavior is terminated before we run the new one
-#        self.process.wait()
+            # the following lines causes lag and does not solve real issues
+            # first unregister the node
+            #cmd = "rosnode kill /%s_behavior"%self.name
+            #temp = subprocess.Popen(cmd, shell=True)
+            #temp.wait() 
+            # this node loses contact but still is not cleaned up in rosnode
+            # however, this should not affect usage at all.
         
+    def changeBehavior(self, behavior):
+        self.behavior = behavior
+        if (behavior == "pause"):
+            self.paused = True
+        else:
+            self.paused = False
+        self.terminateOldBehavior()
         # do rosprocess calls for new behavior
         roscommand = shlex.split("rosrun libscout libscout %s %s"%
-                                            (self.name, self.behavior))
-        subprocess.Popen(roscommand, shell=False)
+                            (self.name, Behaviors.getNumber(self.behavior)))
+        self.process = subprocess.Popen(roscommand, shell=False)
 
     def killSelf(self):
         # terminate its current behavior
-        if self.process != None:
-            self.process.terminate() # TODO: same here, try "rosnode kill"
-            self.process.wait()
+        self.terminateOldBehavior()
+        
         # ros call to kill scout in simulator
         try:
             rospy.wait_for_service("/kill")
@@ -161,9 +195,9 @@ class GUI(wx.Frame):
 
     def initData(self):
         self.scouts = {}
+        #FIXME: these arguments are not right...fix them!!!
         self.scouts["scout1"] = Scout(x=0, y=0, theta=0, name="scout1",
                                       Scouts = self.scouts, oninit=True)
-        #TODO: these arguments are not right...fix them!!!
 
     # addButton callback
     def addScout(self, x_wx, y_wx, theta_wx, name_wx):
@@ -172,7 +206,6 @@ class GUI(wx.Frame):
         y = y_wx.GetValue()
         theta = theta_wx.GetValue()
         name = name_wx.GetValue()
-        print "in GUI trying to add scout!"
         newSc = Scout(x, y, theta, name, self.scouts)
         if (newSc.valid):
             # successful
@@ -190,14 +223,13 @@ class GUI(wx.Frame):
 
     def removeScout(self, name):
         sct = self.scouts[name]
-        print "removing scout %s"%name
         sct.killSelf()
         if (sct.valid == False):
             # successful, remove from the dictionary
             del self.scouts[name]
-            # refresh the display
-            print self.mainArea.Hide(self.sizer[name])
-            print self.mainArea.Remove(self.sizer[name])
+            # delete the refresh the display
+            self.mainArea.Hide(self.sizer[name]) #required by wx before remove
+            self.mainArea.Remove(self.sizer[name])
             del self.sizer[name]
             self.window.Layout()
             self.window.Refresh()
@@ -205,6 +237,52 @@ class GUI(wx.Frame):
         else:
             raise Exception
 
+    # change UI display and invoke change Behavior in scout
+    def changeBehavior(self, name, currBehaviorLabel, 
+                        pauseButton, newBehavior):
+        
+        # to handle user pressing "Run" during pause
+        if (newBehavior != "Pause" and self.scouts[name].paused == True):
+            print "trying to do stuff!!!"
+            self.correctPauseButton(name, 
+                                        pauseButton, pauseToResume=True)
+        
+        currBehaviorLabel.SetLabel(" | Current Behavior: %s"%newBehavior)
+        scout = self.scouts[name]
+        scout.changeBehavior(newBehavior)
+
+
+    def correctPauseButton(self, name, pauseButton, pauseToResume):
+        if (pauseToResume):
+            print "correcting button!"
+            self.scouts[name].paused = False
+            pauseButton.SetValue(False) # unpress it
+            pauseButton.SetLabel("Pause")
+        else:
+            self.scouts[name].paused = True
+            pauseButton.SetValue(True) # press it
+            pauseButton.SetLabel("Resume")
+
+    def buttonDown(button):
+        return button.GetValue()
+
+    def pauseResumeScout(self, name, pauseButton, currBehaviorLabel, dropMenu):
+        if (pauseButton.GetValue() == True): # 
+            # change behavior to Pause
+            self.changeBehavior(name, currBehaviorLabel, pauseButton, "Pause")    
+            # change button to "Resume" & label to "Pause"
+            currBehaviorLabel.SetLabel("Pause")
+            self.correctPauseButton(name, 
+                                        pauseButton, pauseToResume=False)
+        else:
+            # resume
+            # change behavior to whatever is in the drop down menu
+            self.changeBehavior(name, currBehaviorLabel, pauseButton, 
+                        dropMenu.GetStringSelection())
+            self.correctPauseButton(name, 
+                                        pauseButton, pauseToResume=True)
+            
+
     ############################ UI stuff here ###########################
     
     def initUI(self):
@@ -268,14 +346,15 @@ class GUI(wx.Frame):
     def addScoutBox(self, name):
         self.sizer[name] = wx.FlexGridSizer(rows=2, cols=5, hgap=5, vgap=5)
         # Labels
-        scoutName = wx.StaticText(self.window, label="Scout: " + name)
+        scoutName = wx.StaticText(self.window, label="Scout: %s"%name)
         behaviorLabel = wx.StaticText(self.window, label="Behavior: ")
         currBehaviorLabel = wx.StaticText(self.window,
             label="  |  Current Behavior: Slacking off")
 
         # Inputs
         # drop down menue
-        scoutChoices = wx.Choice(self.window) #choices=GetBehaviors())
+        scoutChoices = wx.Choice(self.window, 
+                                    choices=Behaviors.getBehaviors())
         #   buttons
         pauseButton = wx.ToggleButton(self.window, label="Pause")
         runButton = wx.Button(self.window, label="Run")
@@ -297,6 +376,13 @@ class GUI(wx.Frame):
         self.sizer[name].Add(teleopButton) 
         # Events
         killButton.Bind(wx.EVT_BUTTON, lambda event: self.removeScout(name))
+        runButton.Bind(wx.EVT_BUTTON,
+                lambda event: self.changeBehavior(name, currBehaviorLabel,
+                    pauseButton, # needed to handle press "run" during pause
+                    scoutChoices.GetStringSelection()))
+        pauseButton.Bind(wx.EVT_TOGGLEBUTTON, 
+            lambda event: self.pauseResumeScout(name, pauseButton,
+                                        currBehaviorLabel, scoutChoices))
 
         self.mainArea.Add(self.sizer[name], proportion=1,
             flag=wx.ALL | wx.EXPAND, border=10)
