def generate_q_double_spin_box(default_val, range_min, range_max, decimals,
                               single_step):
    spin_box = QDoubleSpinBox()
    spin_box.setValue(default_val)
    spin_box.setRange(range_min, range_max)
    spin_box.setDecimals(decimals)
    spin_box.setSingleStep(single_step)
    #spin_box.valueChanged[unicode].connect(self.callback_spin_box)
    return spin_box
def generate_q_double_spin_box(default_val, range_min, range_max, decimals, single_step):
    spin_box = QDoubleSpinBox()
    spin_box.setValue(default_val)
    spin_box.setRange(range_min, range_max)
    spin_box.setDecimals(decimals)
    spin_box.setSingleStep(single_step)
    #spin_box.valueChanged[unicode].connect(self.callback_spin_box)
    return spin_box
    def addOnceOrRepeat_And_VoiceRadioButtons(self, layout):
        '''
        Creates radio buttons for selecting whether a
        sound is to play once, or repeatedly until stopped.
        Also adds radio buttons for selecting voices.
        Places all in a horizontal box layout. Adds 
        that hbox layout to the passed-in layout.
        
        Sets instance variables:
            1. C{self.onceOrRepeatDict}
            2. C{self.voicesRadioButtonsDict}
         
        @param layout: Layout object to which the label/txt-field C{hbox} is to be added.
        @type  layout: QLayout
         '''
        
        hbox = QHBoxLayout();

        (self.onceOrRepeatGroup, onceOrRepeatButtonLayout, self.onceOrRepeatDict) =\
            self.buildRadioButtons([SpeakEasyGUI.interactionWidgets['PLAY_ONCE'], 
                                    SpeakEasyGUI.interactionWidgets['PLAY_REPEATEDLY']
                                    ],
                                   Orientation.HORIZONTAL,
                                   Alignment.LEFT,
                                   activeButtons=[SpeakEasyGUI.interactionWidgets['PLAY_ONCE']],
                                   behavior=CheckboxGroupBehavior.RADIO_BUTTONS);

        self.replayPeriodSpinBox = QDoubleSpinBox(self);
        self.replayPeriodSpinBox.setRange(0.0, 99.9); # seconds
        self.replayPeriodSpinBox.setSingleStep(0.5);
        self.replayPeriodSpinBox.setDecimals(1);
        onceOrRepeatButtonLayout.addWidget(self.replayPeriodSpinBox);
        secondsLabel = QLabel("secs delay");
        onceOrRepeatButtonLayout.addWidget(secondsLabel);
        
        # Create an array of voice radio button labels:
        voiceRadioButtonLabels = [];
        for voiceKey in SpeakEasyGUI.voices.keys():
            voiceRadioButtonLabels.append(SpeakEasyGUI.interactionWidgets[voiceKey]);
            
        (self.voicesGroup, voicesButtonLayout, self.voicesRadioButtonsDict) =\
            self.buildRadioButtons(voiceRadioButtonLabels,
                                   Orientation.HORIZONTAL,
                                   Alignment.RIGHT,
                                   activeButtons=[SpeakEasyGUI.interactionWidgets['VOICE_1']],
                                   behavior=CheckboxGroupBehavior.RADIO_BUTTONS);
                                   
        # Style all the radio buttons:
        for playFreqButton in self.onceOrRepeatDict.values():
            playFreqButton.setStyleSheet(SpeakEasyGUI.playOnceRepeatButtonStylesheet); 
        for playFreqButton in self.voicesRadioButtonsDict.values():
            playFreqButton.setStyleSheet(SpeakEasyGUI.voiceButtonStylesheet);
        #...and the replay delay spinbox:
        self.replayPeriodSpinBox.setStyleSheet(SpeakEasyGUI.playRepeatSpinboxStylesheet);
        #****** replayPeriodSpinBox styling 
                                   
        hbox.addLayout(onceOrRepeatButtonLayout);
        hbox.addStretch(1);
        hbox.addLayout(voicesButtonLayout);
        layout.addLayout(hbox);
    def __init__(self, name, sensor_bias, control_bias, gearing_bias, controller):
        self.name             = name
        self.sensor_bias      = sensor_bias
        self.control_bias     = control_bias
        self.controller       = controller
        self.gearing_bias     = gearing_bias

        print "Initial data for joint ",self.name, " biases=(",self.sensor_bias,", ",self.control_bias,")"
        self.max_sensor_bias  =  3.5
        self.min_sensor_bias  = -3.5
        self.range_sensor_bias = self.max_sensor_bias - self.min_sensor_bias
        self.max_control_bias =  0.3
        self.min_control_bias = -0.3
        self.max_gearing_bias =  1.3
        self.min_gearing_bias =  0.7
        self.range_gearing_bias = self.max_gearing_bias - self.min_gearing_bias

        self.range_control_bias = self.max_control_bias - self.min_control_bias
        self.sensor_bias_spinbox = QDoubleSpinBox()
        self.sensor_bias_spinbox.setDecimals(4);
        self.sensor_bias_spinbox.setRange(self.min_sensor_bias, self.max_sensor_bias)
        self.sensor_bias_spinbox.setValue(self.sensor_bias)
        self.sensor_bias_spinbox.setSingleStep(self.range_sensor_bias/500)
        self.sensor_bias_spinbox.valueChanged.connect(self.on_biasChanged)

        self.control_bias_spinbox = QDoubleSpinBox()
        self.control_bias_spinbox.setDecimals(4);
        self.control_bias_spinbox.setRange(self.min_control_bias, self.max_control_bias)
        self.control_bias_spinbox.setSingleStep(self.range_control_bias/50)
        self.control_bias_spinbox.setValue(self.control_bias)
        self.control_bias_spinbox.valueChanged.connect(self.on_biasChanged)

        self.gearing_bias_spinbox = QDoubleSpinBox()
        self.gearing_bias_spinbox.setDecimals(4);
        self.gearing_bias_spinbox.setRange(self.min_gearing_bias, self.max_gearing_bias)
        self.gearing_bias_spinbox.setSingleStep(self.range_gearing_bias/50)
        self.gearing_bias_spinbox.setValue(self.gearing_bias)
        self.gearing_bias_spinbox.valueChanged.connect(self.on_biasChanged)
 def createEditor(self, parent, option, index):
     '''
     Creates a editor in the TreeView depending on type of the settings data.
     '''
     item = self._itemFromIndex(index)
     if item.edit_type() == SettingsValueItem.EDIT_TYPE_AUTODETECT:
         if isinstance(item.value(), bool):
             box = QCheckBox(parent)
             box.setFocusPolicy(Qt.StrongFocus)
             box.setAutoFillBackground(True)
             box.stateChanged.connect(self.edit_finished)
             return box
         elif isinstance(item.value(), int):
             box = QSpinBox(parent)
             box.setValue(item.value())
             if not item.value_min() is None:
                 box.setMinimum(item.value_min())
             if not item.value_max() is None:
                 box.setMaximum(item.value_max())
             if not item.value_step() is None:
                 box.setSingleStep(item.value_step())
             return box
         elif isinstance(item.value(), float):
             box = QDoubleSpinBox(parent)
             box.setValue(item.value())
             if not item.value_min() is None:
                 box.setMinimum(item.value_min())
             if not item.value_max() is None:
                 box.setMaximum(item.value_max())
             if not item.value_step() is None:
                 box.setSingleStep(item.value_step())
             box.setDecimals(3)
             return box
     elif item.edit_type() == SettingsValueItem.EDIT_TYPE_FOLDER:
         editor = PathEditor(item.value(), parent)
         editor.editing_finished_signal.connect(self.edit_finished)
         return editor
     elif item.edit_type() == SettingsValueItem.EDIT_TYPE_LIST:
         box = QComboBox(parent)
         box.addItems(item.value_list())
         index = box.findText(item.value())
         if index >= 0:
             box.setCurrentIndex(index)
         box.setEditable(False)
         return box
     return QStyledItemDelegate.createEditor(self, parent, option, index)
class SensorParamControlDialog(Plugin):

    def __init__(self, context):
        super(SensorParamControlDialog, self).__init__(context)
        self.setObjectName('SensorParamControlDialog')
        
        self._widget = QWidget()
        vbox = QVBoxLayout()


        ### Multisense ###

        ms_groupbox = QGroupBox( "Multisense" )
        
        ms_vbox = QVBoxLayout()
        
        ms_gain_hbox = QHBoxLayout()
       
        self.ms_gain_label = QLabel("Image Gain [1, 1, 8]")
        ms_gain_hbox.addWidget( self.ms_gain_label )
        
        self.ms_gain = QDoubleSpinBox()
        self.ms_gain.setSingleStep(.01)
        self.ms_gain.setRange(1,8)

        ms_gain_hbox.addWidget( self.ms_gain ) 
    
        ms_vbox.addLayout( ms_gain_hbox )       

        
        ms_exp_hbox = QHBoxLayout()
        
        self.ms_exp_auto = QCheckBox("Image Exposure [.03, 0.5]")
        ms_exp_hbox.addWidget( self.ms_exp_auto )
        
        self.ms_exp = QDoubleSpinBox()
        self.ms_exp.setSingleStep( .001 )
        self.ms_exp.setRange( .025,.5 ) 

        ms_exp_hbox.addWidget( self.ms_exp ) 
    
        ms_vbox.addLayout( ms_exp_hbox )       
        
        ms_spindle_hbox = QHBoxLayout()
        
        ms_spindle_label = QLabel("Spindle Speed [0, 5.2]")        
        
        ms_spindle_hbox.addWidget( ms_spindle_label )

        self.ms_spindle = QDoubleSpinBox()
        self.ms_spindle.setSingleStep(.01)
        self.ms_spindle.setRange( 0,15.2 )

        ms_spindle_hbox.addWidget( self.ms_spindle ) 
    
        ms_vbox.addLayout( ms_spindle_hbox )       

        ms_light_hbox = QHBoxLayout()
        
        ms_light_label = QLabel("Light Brightness")
        ms_light_hbox.addWidget(ms_light_label)

        self.ms_light = QSlider(Qt.Horizontal)
        self.ms_light.setRange(0,100)
    
        ms_light_hbox.addWidget( self.ms_light )

        ms_vbox.addLayout( ms_light_hbox )

        ms_button_hbox = QHBoxLayout()

        ms_button_hbox.addStretch(1)        

        ms_button_get = QPushButton("Get Settings")
        ms_button_get.pressed.connect(self.ms_get_callback)
        #ms_button_hbox.addWidget( ms_button_get )
        
        ms_button_set = QPushButton("Set Settings")
        ms_button_set.pressed.connect(self.ms_set_callback)
        ms_button_hbox.addWidget( ms_button_set )

        ms_vbox.addLayout( ms_button_hbox ) 

        ms_groupbox.setLayout( ms_vbox )

        vbox.addWidget( ms_groupbox )


        ### Left SA ###

        sa_left_groupbox = QGroupBox( "Left SA Camera" )
        
        sa_left_vbox = QVBoxLayout()
        
        sa_left_gain_hbox = QHBoxLayout()
        
        sa_left_gain_label = QLabel("Image Gain [0, 0, 25]")        
        
        sa_left_gain_hbox.addWidget( sa_left_gain_label )

        self.sa_left_gain = QDoubleSpinBox()
        self.sa_left_gain.setSingleStep(.01)
        self.sa_left_gain.setRange( 0, 25 )

        sa_left_gain_hbox.addWidget( self.sa_left_gain ) 
    
        sa_left_vbox.addLayout( sa_left_gain_hbox )       

        
        sa_left_exp_hbox = QHBoxLayout()
        
        sa_left_exp_label = QLabel("Image Shutter [0, 5.5, 25]")        
        
        sa_left_exp_hbox.addWidget( sa_left_exp_label )

        self.sa_left_exp = QDoubleSpinBox()
        self.sa_left_exp.setSingleStep(.01)
        self.sa_left_exp.setRange( 0, 25 )

        sa_left_exp_hbox.addWidget( self.sa_left_exp ) 
    
        sa_left_vbox.addLayout( sa_left_exp_hbox )       
        
        sa_left_button_hbox = QHBoxLayout()

        sa_left_button_hbox.addStretch(1)        

        sa_left_button_get = QPushButton("Get Settings")
        sa_left_button_get.pressed.connect(self.sa_left_get_callback)
        #sa_left_button_hbox.addWidget( sa_left_button_get )
        
        sa_left_button_set = QPushButton("Set Settings")
        sa_left_button_set.pressed.connect(self.sa_left_set_callback)
        sa_left_button_hbox.addWidget( sa_left_button_set )

        sa_left_vbox.addLayout( sa_left_button_hbox ) 

        sa_left_groupbox.setLayout( sa_left_vbox )

        vbox.addWidget(sa_left_groupbox)
        
        ### Right SA ###

        sa_right_groupbox = QGroupBox( "Right SA Camera" )
        
        sa_right_vbox = QVBoxLayout()
        
        sa_right_gain_hbox = QHBoxLayout()
        
        sa_right_gain_label = QLabel("Image Gain [0, 0, 25]")        
        
        sa_right_gain_hbox.addWidget( sa_right_gain_label )

        self.sa_right_gain = QDoubleSpinBox()
        self.sa_right_gain.setSingleStep(.01)
        self.sa_right_gain.setRange( 0, 25 )

        sa_right_gain_hbox.addWidget( self.sa_right_gain ) 
    
        sa_right_vbox.addLayout( sa_right_gain_hbox )       

        
        sa_right_exp_hbox = QHBoxLayout()
        
        sa_right_exp_label = QLabel("Image Shutter [0, 5.5, 25]")        
        
        sa_right_exp_hbox.addWidget( sa_right_exp_label )

        self.sa_right_exp = QDoubleSpinBox()
        self.sa_right_exp.setSingleStep(.01)
        self.sa_right_exp.setRange( 0, 25 )    
    
        sa_right_exp_hbox.addWidget( self.sa_right_exp ) 
    
        sa_right_vbox.addLayout( sa_right_exp_hbox )       
        
        sa_right_button_hbox = QHBoxLayout()

        sa_right_button_hbox.addStretch(1)        

        sa_right_button_get = QPushButton("Get Settings")
        sa_right_button_get.pressed.connect(self.sa_right_get_callback)
        #sa_right_button_hbox.addWidget( sa_right_button_get )
        
        sa_right_button_set = QPushButton("Set Settings")
        sa_right_button_set.pressed.connect(self.sa_right_set_callback)
        sa_right_button_hbox.addWidget( sa_right_button_set )

        sa_right_vbox.addLayout( sa_right_button_hbox ) 

        sa_right_groupbox.setLayout( sa_right_vbox )

        vbox.addWidget(sa_right_groupbox)

        vbox.addStretch(1)

        self._widget.setLayout(vbox)

        context.add_widget(self._widget)

        # publishers and subscribers

        self.ms_cam_pub = rospy.Publisher( '/multisense_sl/set_cam_parameters', Float64MultiArray, queue_size=10)
        self.ms_light_pub = rospy.Publisher( '/multisense_sl/set_light_brightness', Float64, queue_size=10)
        self.ms_spindle_pub = rospy.Publisher( '/multisense_sl/set_spindle_speed', Float64, queue_size=10)

        self.sa_left_cam_pub = rospy.Publisher( '/sa/left/set_cam_parameters', Float64MultiArray, queue_size=10)
        self.sa_right_cam_pub = rospy.Publisher( '/sa/right/set_cam_parameters', Float64MultiArray, queue_size=10)


 #       self.param_sub = self.stateSubscriber   = rospy.Subscriber('/ros_footstep_planner/params', FootstepPlannerParams, self.getParamCallbackFcn)


    def shutdown_plugin(self):
        print "Shutdown ..." 
#        self.param_pub.unregister()
        print "Done!"

    def ms_get_callback(self):
        print "fill values"

    def ms_set_callback(self):
        ms_cam = Float64MultiArray()
        
        ms_cam_dim_h = MultiArrayDimension()
        ms_cam_dim_h.label = "height"
        ms_cam_dim_h.size = 1
        ms_cam_dim_h.stride  = 2        

        ms_cam.layout.dim.append( ms_cam_dim_h )
        
        ms_cam_dim_w = MultiArrayDimension()
        ms_cam_dim_w.label = "width"
        ms_cam_dim_w.size = 2
        ms_cam_dim_w.stride  = 2        
        
        ms_cam.layout.dim.append(ms_cam_dim_w)

        ms_cam.data.append( self.ms_gain.value() )

        if self.ms_exp_auto.isChecked() :
            ms_cam.data.append( 1 )   
        else:
            ms_cam.data.append( self.ms_exp.value() )
        
        self.ms_cam_pub.publish( ms_cam )
    

        light  = Float64()
        light.data = self.ms_light.value()/100.0

        self.ms_light_pub.publish( light )

        spindle_speed = Float64()
        spindle_speed.data = self.ms_spindle.value()

        self.ms_spindle_pub.publish( spindle_speed )
        
    def sa_left_get_callback(self):
        print "fill values"

    def sa_left_set_callback(self):
        sa_left_cam = Float64MultiArray()
        
        sa_left_cam_dim_h = MultiArrayDimension()
        sa_left_cam_dim_h.label = "height"
        sa_left_cam_dim_h.size = 1
        sa_left_cam_dim_h.stride  = 2        

        sa_left_cam.layout.dim.append( sa_left_cam_dim_h )
        
        sa_left_cam_dim_w = MultiArrayDimension()
        sa_left_cam_dim_w.label = "width"
        sa_left_cam_dim_w.size = 2
        sa_left_cam_dim_w.stride  = 2        
        
        sa_left_cam.layout.dim.append(sa_left_cam_dim_w)

        sa_left_cam.data.append( self.sa_left_gain.value() )
        sa_left_cam.data.append( self.sa_left_exp.value() )
        
        self.sa_left_cam_pub.publish( sa_left_cam )
    
    def sa_right_get_callback(self):
        print "fill values"

    def sa_right_set_callback(self):
        sa_right_cam = Float64MultiArray()
        
        sa_right_cam_dim_h = MultiArrayDimension()
        sa_right_cam_dim_h.label = "height"
        sa_right_cam_dim_h.size = 1
        sa_right_cam_dim_h.stride  = 2        

        sa_right_cam.layout.dim.append( sa_right_cam_dim_h )
        
        sa_right_cam_dim_w = MultiArrayDimension()
        sa_right_cam_dim_w.label = "width"
        sa_right_cam_dim_w.size = 2
        sa_right_cam_dim_w.stride  = 2        
        
        sa_right_cam.layout.dim.append(sa_right_cam_dim_w)

        sa_right_cam.data.append( self.sa_right_gain.value() )
        sa_right_cam.data.append( self.sa_right_exp.value() )
        
        self.sa_right_cam_pub.publish( sa_right_cam )
    def __init__(self, context):
        #super(FootstepParamControlDialog, self).__init__(context)
        #self.setObjectName('FootstepParamControlDialog')
        super(FootstepParamControlWidget, self).__init__()
        self.name = 'FootstepParamControlWidget'
        
        #self._widget = QWidget()
        self._widget = context
        vbox = QVBoxLayout()

        ### STEP_COST_ESTIMATOR ########
        self.step_cost_vbox = QVBoxLayout()
   
        self.step_cost_groupbox = QGroupBox( "Step Cost Estimator" )
        self.step_cost_groupbox.setCheckable( True )
        self.step_cost_groupbox.setChecked(False)

        self.step_cost_combobox = QComboBox()

        self.step_cost_combobox.addItem( "Euclidean" )
        self.step_cost_combobox.addItem( "GPR" )       
        self.step_cost_combobox.addItem( "Map" ) 
        self.step_cost_combobox.addItem( "Boundary" )
        self.step_cost_combobox.addItem( "Dynamics" )
        
        self.step_cost_vbox.addWidget( self.step_cost_combobox )
       
        self.step_cost_groupbox.setLayout( self.step_cost_vbox )
 
        vbox.addWidget( self.step_cost_groupbox )

# #HARD       ### FOOTSTEP_SET ########
#        # parameters for discret footstep planning mode
#        vigir_atlas_control_msgs/VigirBehaviorStepData[] footstep_set            # set of footsteps (displacement vectors (in meter / rad))
#        float32[] footstep_cost                                         # cost for each footstep given in footstep set
#
# #HARD       ### LOAD_GPR_STEP_COST ########
#        # map step cost file
#        std_msgs/String map_step_cost_file
#
# #HARD       ### LOAD_MAP_STEP_COST ########
#        # destination of gpr file
#        std_msgs/String gpr_step_cost_file

        ### COLLISION_CHECK_TYPE ########
        self.collision_check_groupbox = QGroupBox( "Collision Check Type" )
        self.collision_check_groupbox.setCheckable( True )
        self.collision_check_groupbox.setChecked( False)

        self.collision_check_vbox = QVBoxLayout()
   
        self.collision_check_feet_checkbox = QCheckBox( "Feet" )      
        self.collision_check_vbox.addWidget( self.collision_check_feet_checkbox  )
        
        self.collision_check_ub_checkbox = QCheckBox( "Upper Body" )
        self.collision_check_vbox.addWidget( self.collision_check_ub_checkbox  )
        
        self.collision_check_fc_checkbox = QCheckBox( "Foot Contact Support" ) 
        self.collision_check_vbox.addWidget( self.collision_check_fc_checkbox  )

        self.collision_check_groupbox.setLayout( self.collision_check_vbox )
        
        vbox.addWidget( self.collision_check_groupbox )

        ### FOOT_SIZE ########
        self.foot_size_vbox = QVBoxLayout()
        
        self.foot_size_groupbox = QGroupBox( "Foot Size" )
        self.foot_size_groupbox.setCheckable( True )
        self.foot_size_groupbox.setChecked( False )

        self.foot_size_hbox = QHBoxLayout()

        self.foot_size_label = QLabel("Foot Size")
        self.foot_size_hbox.addWidget( self.foot_size_label )

        self.foot_size_x = QDoubleSpinBox()
        self.foot_size_x.setSingleStep(.01)
        self.foot_size_hbox.addWidget(self.foot_size_x)

        self.foot_size_y = QDoubleSpinBox()
        self.foot_size_y.setSingleStep(.01)
        self.foot_size_hbox.addWidget(self.foot_size_y)

        self.foot_size_z = QDoubleSpinBox()
        self.foot_size_z.setSingleStep(.01)
        self.foot_size_hbox.addWidget(self.foot_size_z)

        self.foot_size_vbox.addLayout( self.foot_size_hbox )
        
        self.foot_shift_hbox = QHBoxLayout()

        self.foot_shift_label = QLabel("Foot Origin Shift")
        self.foot_shift_hbox.addWidget( self.foot_shift_label )

        self.foot_shift_x = QDoubleSpinBox()
        self.foot_shift_x.setRange(-1.0, 1.0)
        self.foot_shift_x.setSingleStep(.01)
        self.foot_shift_hbox.addWidget(self.foot_shift_x)

        self.foot_shift_y = QDoubleSpinBox()
        self.foot_shift_y.setRange(-1.0, 1.0)
        self.foot_shift_y.setSingleStep(.01)
        self.foot_shift_hbox.addWidget(self.foot_shift_y)

        self.foot_shift_z = QDoubleSpinBox()
        self.foot_shift_z.setRange(-1.0, 1.0)
        self.foot_shift_z.setSingleStep(.01)
        self.foot_shift_hbox.addWidget(self.foot_shift_z)

        self.foot_size_vbox.addLayout( self.foot_shift_hbox )
    
        self.foot_size_groupbox.setLayout( self.foot_size_vbox )
        vbox.addWidget( self.foot_size_groupbox )

        ### UPPER_BODY_SIZE ########
            
        self.upper_vbox = QVBoxLayout()
   
        self.upper_groupbox = QGroupBox( "Upper Body Size" )
        self.upper_groupbox.setCheckable( True )
        self.upper_groupbox.setChecked( False )
        
        self.upper_size_hbox = QHBoxLayout()

        self.upper_size_label = QLabel("Upper Body Size")
        self.upper_size_hbox.addWidget( self.upper_size_label )

        self.upper_size_x = QDoubleSpinBox()
        self.upper_size_x.setSingleStep(.01)
        self.upper_size_hbox.addWidget(self.upper_size_x)

        self.upper_size_y = QDoubleSpinBox()
        self.upper_size_y.setSingleStep(.01)
        self.upper_size_hbox.addWidget(self.upper_size_y)

        self.upper_size_z = QDoubleSpinBox()
        self.upper_size_z.setSingleStep(.01)
        self.upper_size_hbox.addWidget(self.upper_size_z)

        self.upper_vbox.addLayout( self.upper_size_hbox )
        
        self.upper_origin_hbox = QHBoxLayout()

        self.upper_origin_label = QLabel("Upper Body Origin")
        self.upper_origin_hbox.addWidget( self.upper_origin_label )

        self.upper_origin_x = QDoubleSpinBox()
        self.upper_origin_x.setRange(-1.0, 1.0)
        self.upper_origin_x.setSingleStep(.01)
        self.upper_origin_hbox.addWidget(self.upper_origin_x)

        self.upper_origin_y = QDoubleSpinBox()
        self.upper_origin_y.setRange(-1.0, 1.0)
        self.upper_origin_y.setSingleStep(.01)
        self.upper_origin_hbox.addWidget(self.upper_origin_y)

        self.upper_origin_z = QDoubleSpinBox()
        self.upper_origin_z.setRange(-1.0, 1.0)
        self.upper_origin_z.setSingleStep(.01)
        self.upper_origin_hbox.addWidget(self.upper_origin_z)

        self.upper_vbox.addLayout( self.upper_origin_hbox )
       
        self.upper_groupbox.setLayout( self.upper_vbox ) 
        vbox.addWidget( self.upper_groupbox )
    
        ### TERRAIN_MODEL ########
        self.terrain_model_groupbox = QGroupBox( "Terrain Model" )
        self.terrain_model_groupbox.setCheckable( True )
        self.terrain_model_groupbox.setChecked( False )

        self.terrain_model_vbox = QVBoxLayout()
        self.terrain_model_checkbox = QCheckBox( "Use Terrain Model" )      
        self.terrain_model_vbox.addWidget( self.terrain_model_checkbox )
        
        self.terrain_min_ssx_hbox = QHBoxLayout()
        
        self.terrain_min_ssx_label = QLabel("Min Sampling Steps x")
        self.terrain_min_ssx_hbox.addWidget( self.terrain_min_ssx_label )

        self.terrain_min_ssx_val = QDoubleSpinBox()
        self.terrain_min_ssx_val.setSingleStep(1)
        self.terrain_min_ssx_val.setRange(0,100)
        self.terrain_min_ssx_hbox.addWidget( self.terrain_min_ssx_val )

        self.terrain_model_vbox.addLayout( self.terrain_min_ssx_hbox )
    
        self.terrain_min_ssy_hbox = QHBoxLayout()
        
        self.terrain_min_ssy_label = QLabel("Min Sampling Steps y")
        self.terrain_min_ssy_hbox.addWidget( self.terrain_min_ssy_label )

        self.terrain_min_ssy_val = QDoubleSpinBox()
        self.terrain_min_ssy_val.setSingleStep(1)
        self.terrain_min_ssy_val.setRange(0,100)
        self.terrain_min_ssy_hbox.addWidget( self.terrain_min_ssy_val )

        self.terrain_model_vbox.addLayout( self.terrain_min_ssy_hbox )
        
        self.terrain_max_ssx_hbox = QHBoxLayout()
        
        self.terrain_max_ssx_label = QLabel("Max Sampling Steps x")
        self.terrain_max_ssx_hbox.addWidget( self.terrain_max_ssx_label )

        self.terrain_max_ssx_val = QDoubleSpinBox()
        self.terrain_max_ssx_val.setSingleStep(1)
        self.terrain_max_ssx_val.setRange(0,100)
        self.terrain_max_ssx_hbox.addWidget( self.terrain_max_ssx_val )

        self.terrain_model_vbox.addLayout( self.terrain_max_ssx_hbox )
    
        self.terrain_max_ssy_hbox = QHBoxLayout()
        
        self.terrain_max_ssy_label = QLabel("Max Sampling Steps y")
        self.terrain_max_ssy_hbox.addWidget( self.terrain_max_ssy_label )

        self.terrain_max_ssy_val = QDoubleSpinBox()
        self.terrain_max_ssy_val.setSingleStep(1)
        self.terrain_max_ssy_val.setRange(0,100)
        self.terrain_max_ssy_hbox.addWidget( self.terrain_max_ssy_val )

        self.terrain_model_vbox.addLayout( self.terrain_max_ssy_hbox )

        self.terrain_max_iz_hbox = QHBoxLayout()
        
        self.terrain_max_iz_label = QLabel("Max Intrusion z")
        self.terrain_max_iz_hbox.addWidget( self.terrain_max_iz_label )

        self.terrain_max_iz_val = QDoubleSpinBox()
        self.terrain_max_iz_val.setDecimals(4)
        self.terrain_max_iz_val.setSingleStep(.0001)
        self.terrain_max_iz_val.setRange(0,.25)
        self.terrain_max_iz_hbox.addWidget( self.terrain_max_iz_val )

        self.terrain_model_vbox.addLayout( self.terrain_max_iz_hbox )

        self.terrain_max_gc_hbox = QHBoxLayout()
        
        self.terrain_max_gc_label = QLabel("Max Ground Clearance")
        self.terrain_max_gc_hbox.addWidget( self.terrain_max_gc_label )

        self.terrain_max_gc_val = QDoubleSpinBox()
        self.terrain_max_gc_val.setDecimals(4)
        self.terrain_max_gc_val.setSingleStep(.0001)
        self.terrain_max_gc_val.setRange(0,.25)
        self.terrain_max_gc_hbox.addWidget( self.terrain_max_gc_val )

        self.terrain_model_vbox.addLayout( self.terrain_max_gc_hbox )

        self.terrain_ms_hbox = QHBoxLayout()
        
        self.terrain_ms_label = QLabel("Minimal Support")
        self.terrain_ms_hbox.addWidget( self.terrain_ms_label )

        self.terrain_ms_val = QDoubleSpinBox()
        self.terrain_ms_val.setDecimals(2)
        self.terrain_ms_val.setSingleStep(.01)
        self.terrain_ms_val.setRange(0,1)
        self.terrain_ms_hbox.addWidget( self.terrain_ms_val )

        self.terrain_model_vbox.addLayout( self.terrain_ms_hbox )

        self.terrain_model_groupbox.setLayout( self.terrain_model_vbox )
        
        vbox.addWidget( self.terrain_model_groupbox ) 

        ### STANDARD_STEP_PARAMS ########

        self.std_step_vbox = QVBoxLayout()

        self.std_step_groupbox = QGroupBox( "Standard Step Parameters" )
        self.std_step_groupbox.setCheckable( True )
        self.std_step_groupbox.setChecked( False )

        self.foot_sep_hbox = QHBoxLayout()
        
        self.foot_sep_label = QLabel("Foot Separation")
        self.foot_sep_hbox.addWidget( self.foot_sep_label )

        self.foot_sep_val = QDoubleSpinBox()
        self.foot_sep_val.setSingleStep(.01)
        self.foot_sep_hbox.addWidget(self.foot_sep_val)

        self.std_step_vbox.addLayout( self.foot_sep_hbox )

        self.std_step_step_duration_hbox = QHBoxLayout()
        
        self.std_step_step_duration_label = QLabel("Step Duration")
        self.std_step_step_duration_hbox.addWidget( self.std_step_step_duration_label )

        self.std_step_step_duration_val = QDoubleSpinBox()
        self.std_step_step_duration_val.setSingleStep(.01)
        self.std_step_step_duration_hbox.addWidget( self.std_step_step_duration_val )

        self.std_step_vbox.addLayout( self.std_step_step_duration_hbox )

        self.std_step_sway_duration_hbox = QHBoxLayout()
        
        self.std_step_sway_duration_label = QLabel("Sway Duration")
        self.std_step_sway_duration_hbox.addWidget( self.std_step_sway_duration_label )

        self.std_step_sway_duration_val = QDoubleSpinBox()
        self.std_step_sway_duration_val.setSingleStep(.01)
        self.std_step_sway_duration_hbox.addWidget( self.std_step_sway_duration_val )

        self.std_step_vbox.addLayout( self.std_step_sway_duration_hbox )

        self.std_step_swing_height_hbox = QHBoxLayout()
        
        self.std_step_swing_height_label = QLabel("Swing Height")
        self.std_step_swing_height_hbox.addWidget( self.std_step_swing_height_label )

        self.std_step_swing_height_val = QDoubleSpinBox()
        self.std_step_swing_height_val.setSingleStep(.01)
        self.std_step_swing_height_hbox.addWidget( self.std_step_swing_height_val )

        self.std_step_vbox.addLayout( self.std_step_swing_height_hbox )

        self.std_step_lift_height_hbox = QHBoxLayout()
        
        self.std_step_lift_height_label = QLabel("Lift Height")
        self.std_step_lift_height_hbox.addWidget( self.std_step_lift_height_label )

        self.std_step_lift_height_val = QDoubleSpinBox()
        self.std_step_lift_height_val.setSingleStep(.01)
        self.std_step_lift_height_hbox.addWidget( self.std_step_lift_height_val )

        self.std_step_vbox.addLayout( self.std_step_lift_height_hbox )
       
        self.std_step_groupbox.setLayout( self.std_step_vbox ) 
        vbox.addWidget( self.std_step_groupbox )

        button_hbox = QHBoxLayout()
        
        button_get = QPushButton("Get Current Values")
        button_hbox.addWidget( button_get )

        button_submit = QPushButton("Send Values")
        button_hbox.addWidget( button_submit)

        vbox.addLayout( button_hbox )

        vbox.addStretch(1)

        self._widget.setLayout(vbox)

        #context.add_widget(self._widget)

        # publishers and subscribers

        self.param_pub = rospy.Publisher('/flor/footstep_planner/set_params', FootstepPlannerParams, queue_size=10)
        button_submit.pressed.connect(self.sendParams)

        self.param_sub = self.stateSubscriber   = rospy.Subscriber('/ros_footstep_planner/params', FootstepPlannerParams, self.getParamCallbackFcn)
        button_get.pressed.connect(self.getParams)
Beispiel #8
0
    def __init__(self, context):
        super(HeadControlDialog, self).__init__(context)
        self.setObjectName('HeadControlDialog')

        self.head_max_pitch_deg =  65.0
        self.head_min_pitch_deg = -60.0
        self.head_pitch_range_deg = self.head_max_pitch_deg - self.head_min_pitch_deg
        self.scan_period_secs   = 20.0
        self.head_pitch_cmd_deg =  0.0
        self.scan_offset        = rospy.get_rostime()
        self.last_publish_time  = self.scan_offset
        self._timer = None

        self._widget = QWidget()
        vbox = QVBoxLayout()

        spindle_speed_hbox = QHBoxLayout()

        #Add input for setting the spindle speed
        spindle_label = QLabel("Spindle Speed [deg/s]")
        spindle_speed_hbox.addWidget(spindle_label)

        #Add a spinbox for setting the spindle speed
        spindle_speed_spinbox = QDoubleSpinBox()
        spindle_speed_spinbox.setRange(-360 * 4, 360 * 4)
        spindle_speed_spinbox.valueChanged.connect(self.handle_spindle_speed)
        self.spindle_speed_pub = rospy.Publisher('/multisense/set_spindle_speed', Float64, queue_size=10)
        spindle_speed_hbox.addWidget(spindle_speed_spinbox)

        vbox.addLayout(spindle_speed_hbox)

        #Add input for directly setting the head pitch
        head_pitch_hbox = QHBoxLayout()

        head_pitch_label = QLabel("Head Pitch [deg]")
        head_pitch_hbox.addWidget(head_pitch_label)

        #Add a spinbox for setting the head pitch directly
        self.head_pitch_spinbox = QDoubleSpinBox()
        self.head_pitch_spinbox.setRange(self.head_min_pitch_deg, self.head_max_pitch_deg)
        self.head_pitch_spinbox.valueChanged.connect(self.handle_head_pitch)
        self.head_pitch_pub = rospy.Publisher('/atlas/pos_cmd/neck_ry', Float64, queue_size=10)
        head_pitch_hbox.addWidget(self.head_pitch_spinbox)

        vbox.addLayout(head_pitch_hbox)

        #Publisher for head trajectory
        self.head_trajectory_pub = rospy.Publisher('/trajectory_controllers/neck_traj_controller/command', JointTrajectory, queue_size=10)

        #Add checkbox for invoking scanning behavior
        self.head_scan_chkbox = QCheckBox("Scanning behavior")
        self.head_scan_chkbox.stateChanged.connect(self.handle_scan_chg)
        vbox.addWidget(self.head_scan_chkbox)

        #Add input for setting the minimum head pitch
        head_min_pitch_hbox = QHBoxLayout()
        head_min_pitch_label = QLabel("Min Head Pitch [deg] (raise head)")
        head_min_pitch_hbox.addWidget(head_min_pitch_label)
        head_min_pitch_spinbox = QDoubleSpinBox()
        head_min_pitch_spinbox.setRange(self.head_min_pitch_deg, self.head_max_pitch_deg)
        head_min_pitch_spinbox.setValue(self.head_min_pitch_deg)
        head_min_pitch_spinbox.valueChanged.connect(self.handle_head_min_pitch)
        head_min_pitch_hbox.addWidget(head_min_pitch_spinbox)
        vbox.addLayout(head_min_pitch_hbox)

        #Add input for setting the maximum head pitch
        head_max_pitch_hbox = QHBoxLayout()
        head_max_pitch_label = QLabel("Max Head Pitch [deg] (lower head)")
        head_max_pitch_hbox.addWidget(head_max_pitch_label)
        head_max_pitch_spinbox = QDoubleSpinBox()
        head_max_pitch_spinbox.setRange(self.head_min_pitch_deg, self.head_max_pitch_deg)
        head_max_pitch_spinbox.setValue(self.head_max_pitch_deg)
        head_max_pitch_spinbox.valueChanged.connect(self.handle_head_max_pitch)
        head_max_pitch_hbox.addWidget(head_max_pitch_spinbox)
        vbox.addLayout(head_max_pitch_hbox)

        #Add input for setting the scan period
        head_period_hbox = QHBoxLayout()
        head_period_label = QLabel("Scanning Period [secs]")
        head_period_hbox.addWidget(head_period_label)
        head_period_spinbox = QDoubleSpinBox()
        head_period_spinbox.setRange(0.01, 60.0)
        head_period_spinbox.setValue(self.scan_period_secs)
        head_period_spinbox.valueChanged.connect(self.handle_head_period)
        head_period_hbox.addWidget(head_period_spinbox)
        vbox.addLayout(head_period_hbox)

        #add stretch at end so all GUI elements are at top of dialog
        vbox.addStretch(1)

        self._widget.setLayout(vbox)

        context.add_widget(self._widget)
class Joint(object):
    def __init__(self, name, sensor_bias, control_bias, gearing_bias, controller):
        self.name             = name
        self.sensor_bias      = sensor_bias
        self.control_bias     = control_bias
        self.controller       = controller
        self.gearing_bias     = gearing_bias

        print "Initial data for joint ",self.name, " biases=(",self.sensor_bias,", ",self.control_bias,")"
        self.max_sensor_bias  =  3.5
        self.min_sensor_bias  = -3.5
        self.range_sensor_bias = self.max_sensor_bias - self.min_sensor_bias
        self.max_control_bias =  0.3
        self.min_control_bias = -0.3
        self.max_gearing_bias =  1.3
        self.min_gearing_bias =  0.7
        self.range_gearing_bias = self.max_gearing_bias - self.min_gearing_bias

        self.range_control_bias = self.max_control_bias - self.min_control_bias
        self.sensor_bias_spinbox = QDoubleSpinBox()
        self.sensor_bias_spinbox.setDecimals(4);
        self.sensor_bias_spinbox.setRange(self.min_sensor_bias, self.max_sensor_bias)
        self.sensor_bias_spinbox.setValue(self.sensor_bias)
        self.sensor_bias_spinbox.setSingleStep(self.range_sensor_bias/500)
        self.sensor_bias_spinbox.valueChanged.connect(self.on_biasChanged)

        self.control_bias_spinbox = QDoubleSpinBox()
        self.control_bias_spinbox.setDecimals(4);
        self.control_bias_spinbox.setRange(self.min_control_bias, self.max_control_bias)
        self.control_bias_spinbox.setSingleStep(self.range_control_bias/50)
        self.control_bias_spinbox.setValue(self.control_bias)
        self.control_bias_spinbox.valueChanged.connect(self.on_biasChanged)

        self.gearing_bias_spinbox = QDoubleSpinBox()
        self.gearing_bias_spinbox.setDecimals(4);
        self.gearing_bias_spinbox.setRange(self.min_gearing_bias, self.max_gearing_bias)
        self.gearing_bias_spinbox.setSingleStep(self.range_gearing_bias/50)
        self.gearing_bias_spinbox.setValue(self.gearing_bias)
        self.gearing_bias_spinbox.valueChanged.connect(self.on_biasChanged)

    def on_biasChanged(self, value):

        # grab both if either change
        self.sensor_bias  = self.sensor_bias_spinbox.value()
        self.control_bias = self.control_bias_spinbox.value()
        self.gearing_bias = self.gearing_bias_spinbox.value()

        joint_state = JointState()
        joint_state.name = [self.name];
        joint_state.position = [self.sensor_bias];
        joint_state.effort   = [self.control_bias];
        joint_state.velocity   = [self.gearing_bias];

        print "Set biases:\n ",joint_state
        self.controller.parent.parent.bias_pub.publish(joint_state)

    def saveData(self, saveFilePtr):
        saveFilePtr.write(","+self.name+"/"+str(self.sensor_bias)+"/"+str(self.control_bias)+"/"+str(self.gearing_bias))
    def __init__(self, context):
        super(FootstepTerrainControlDialog, self).__init__(context)
        self.setObjectName('FootstepTerrainControlDialog')
       
        self.request_seq = 0 
        self._widget = QWidget()
        vbox = QVBoxLayout()
        
        self.cube_vbox = QVBoxLayout()
        
        self.cube_groupbox = QGroupBox( "Terrain Box" )

        self.cube_min_hbox = QHBoxLayout()

        self.cube_min_label = QLabel("Bottom Left")
        self.cube_min_hbox.addWidget( self.cube_min_label )

        self.cube_min_x = QDoubleSpinBox()
        self.cube_min_x.setSingleStep(.01)
        self.cube_min_x.setRange(-10.0, 10.0)
        self.cube_min_x.setValue(-3.0)
        self.cube_min_hbox.addWidget(self.cube_min_x)

        self.cube_min_y = QDoubleSpinBox()
        self.cube_min_y.setSingleStep(.01)
        self.cube_min_y.setRange(-10.0, 10.0)
        self.cube_min_y.setValue(-3.0)
        self.cube_min_hbox.addWidget(self.cube_min_y)

        self.cube_min_z = QDoubleSpinBox()
        self.cube_min_z.setSingleStep(.01)
        self.cube_min_z.setRange(-10.0, 10.0)
        self.cube_min_z.setValue(-1.0)
        self.cube_min_hbox.addWidget(self.cube_min_z)

        self.cube_vbox.addLayout( self.cube_min_hbox )
        
        self.cube_max_hbox = QHBoxLayout()

        self.cube_max_label = QLabel("Top Right")
        self.cube_max_hbox.addWidget( self.cube_max_label )

        self.cube_max_x = QDoubleSpinBox()
        self.cube_max_x.setSingleStep(.01)
        self.cube_max_x.setRange(-10.0, 10.0)
        self.cube_max_x.setValue(3.0)
        self.cube_max_hbox.addWidget(self.cube_max_x)

        self.cube_max_y = QDoubleSpinBox()
        self.cube_max_y.setSingleStep(.01)
        self.cube_max_y.setRange(-10.0, 10.0)
        self.cube_max_y.setValue(3.0)
        self.cube_max_hbox.addWidget(self.cube_max_y)

        self.cube_max_z = QDoubleSpinBox()
        self.cube_max_z.setSingleStep(.01)
        self.cube_max_z.setRange(-10.0, 10.0)
        self.cube_max_z.setValue(1.0)
        self.cube_max_hbox.addWidget(self.cube_max_z)

        self.cube_vbox.addLayout( self.cube_max_hbox )
    
        self.cube_groupbox.setLayout( self.cube_vbox )
        vbox.addWidget( self.cube_groupbox )

        self.type_hbox = QHBoxLayout()
  
#        self.type_label = QLabel( "Type" )
#        self.type_hbox.addWidget( self.type_label )
#
#        self.type_combobox = QComboBox()
#
#        self.type_combobox.addItem( "Type 1" )
#        self.type_combobox.addItem( "Type 2" )       
#        self.type_combobox.addItem( "Type 3" ) 
#        
#        self.type_hbox.addWidget( self.type_combobox )
#       
#        vbox.addLayout( self.type_hbox )

        self.scan_number_hbox = QHBoxLayout()
        
        self.scan_number_label = QLabel("Number of Scans Used")
        self.scan_number_hbox.addWidget( self.scan_number_label )

        self.scan_number_val = QDoubleSpinBox()
        self.scan_number_val.setDecimals(0)
        self.scan_number_val.setRange(0,10000)
        self.scan_number_val.setValue(2000)
        self.scan_number_hbox.addWidget(self.scan_number_val)

        vbox.addLayout( self.scan_number_hbox )

#       self.use_terrain_checkbox = QCheckBox( "Use Terrain" )
#       vbox.addWidget( self.use_terrain_checkbox )


        button_hbox = QHBoxLayout()
        
#        button_get = QPushButton("Get Current Values")
#        button_hbox.addWidget( button_get )

        button_submit = QPushButton("Send Values")
        button_hbox.addWidget( button_submit)

        vbox.addLayout( button_hbox )

        vbox.addStretch(1)

        self._widget.setLayout(vbox)

        context.add_widget(self._widget)

        # publishers and subscribers
        self.terrain_pub = rospy.Publisher("/flor/terrain_classifier/generate_terrain_model", TerrainModelRequest, queue_size=10 )
        button_submit.pressed.connect(self.sendParams)
Beispiel #11
0
class HeadControlDialog(Plugin):

    def __init__(self, context):
        super(HeadControlDialog, self).__init__(context)
        self.setObjectName('HeadControlDialog')

        self.head_max_pitch_deg =  65.0
        self.head_min_pitch_deg = -60.0
        self.head_pitch_range_deg = self.head_max_pitch_deg - self.head_min_pitch_deg
        self.scan_period_secs   = 20.0
        self.head_pitch_cmd_deg =  0.0
        self.scan_offset        = rospy.get_rostime()
        self.last_publish_time  = self.scan_offset
        self._timer = None

        self._widget = QWidget()
        vbox = QVBoxLayout()

        spindle_speed_hbox = QHBoxLayout()

        #Add input for setting the spindle speed
        spindle_label = QLabel("Spindle Speed [deg/s]")
        spindle_speed_hbox.addWidget(spindle_label)

        #Add a spinbox for setting the spindle speed
        spindle_speed_spinbox = QDoubleSpinBox()
        spindle_speed_spinbox.setRange(-360 * 4, 360 * 4)
        spindle_speed_spinbox.valueChanged.connect(self.handle_spindle_speed)
        self.spindle_speed_pub = rospy.Publisher('/multisense/set_spindle_speed', Float64, queue_size=10)
        spindle_speed_hbox.addWidget(spindle_speed_spinbox)

        vbox.addLayout(spindle_speed_hbox)

        #Add input for directly setting the head pitch
        head_pitch_hbox = QHBoxLayout()

        head_pitch_label = QLabel("Head Pitch [deg]")
        head_pitch_hbox.addWidget(head_pitch_label)

        #Add a spinbox for setting the head pitch directly
        self.head_pitch_spinbox = QDoubleSpinBox()
        self.head_pitch_spinbox.setRange(self.head_min_pitch_deg, self.head_max_pitch_deg)
        self.head_pitch_spinbox.valueChanged.connect(self.handle_head_pitch)
        self.head_pitch_pub = rospy.Publisher('/atlas/pos_cmd/neck_ry', Float64, queue_size=10)
        head_pitch_hbox.addWidget(self.head_pitch_spinbox)

        vbox.addLayout(head_pitch_hbox)

        #Publisher for head trajectory
        self.head_trajectory_pub = rospy.Publisher('/trajectory_controllers/neck_traj_controller/command', JointTrajectory, queue_size=10)

        #Add checkbox for invoking scanning behavior
        self.head_scan_chkbox = QCheckBox("Scanning behavior")
        self.head_scan_chkbox.stateChanged.connect(self.handle_scan_chg)
        vbox.addWidget(self.head_scan_chkbox)

        #Add input for setting the minimum head pitch
        head_min_pitch_hbox = QHBoxLayout()
        head_min_pitch_label = QLabel("Min Head Pitch [deg] (raise head)")
        head_min_pitch_hbox.addWidget(head_min_pitch_label)
        head_min_pitch_spinbox = QDoubleSpinBox()
        head_min_pitch_spinbox.setRange(self.head_min_pitch_deg, self.head_max_pitch_deg)
        head_min_pitch_spinbox.setValue(self.head_min_pitch_deg)
        head_min_pitch_spinbox.valueChanged.connect(self.handle_head_min_pitch)
        head_min_pitch_hbox.addWidget(head_min_pitch_spinbox)
        vbox.addLayout(head_min_pitch_hbox)

        #Add input for setting the maximum head pitch
        head_max_pitch_hbox = QHBoxLayout()
        head_max_pitch_label = QLabel("Max Head Pitch [deg] (lower head)")
        head_max_pitch_hbox.addWidget(head_max_pitch_label)
        head_max_pitch_spinbox = QDoubleSpinBox()
        head_max_pitch_spinbox.setRange(self.head_min_pitch_deg, self.head_max_pitch_deg)
        head_max_pitch_spinbox.setValue(self.head_max_pitch_deg)
        head_max_pitch_spinbox.valueChanged.connect(self.handle_head_max_pitch)
        head_max_pitch_hbox.addWidget(head_max_pitch_spinbox)
        vbox.addLayout(head_max_pitch_hbox)

        #Add input for setting the scan period
        head_period_hbox = QHBoxLayout()
        head_period_label = QLabel("Scanning Period [secs]")
        head_period_hbox.addWidget(head_period_label)
        head_period_spinbox = QDoubleSpinBox()
        head_period_spinbox.setRange(0.01, 60.0)
        head_period_spinbox.setValue(self.scan_period_secs)
        head_period_spinbox.valueChanged.connect(self.handle_head_period)
        head_period_hbox.addWidget(head_period_spinbox)
        vbox.addLayout(head_period_hbox)

        #add stretch at end so all GUI elements are at top of dialog
        vbox.addStretch(1)

        self._widget.setLayout(vbox)

        context.add_widget(self._widget)

    def shutdown_plugin(self):
        print "Shutdown ..."
        if self._timer is not None:
            self._timer.shutdown()
            rospy.sleep(0.1)
        self.spindle_speed_pub.unregister()
        self.head_pitch_pub.unregister()
        self.head_trajectory_pub.unregister()
        print "Done!"

    #Slot for setting the spindle speed
    def handle_spindle_speed(self, degree_per_sec):
        self.spindle_speed_pub.publish(data=math.radians(degree_per_sec))

    #Slot for setting the head pitch state
    def handle_head_pitch(self, pitch_degree):
        self.head_pitch_cmd_deg = pitch_degree
        self.head_pitch_pub.publish(data=math.radians(pitch_degree))
        #Publish neck trajectory
        trajectory = JointTrajectory()
        trajectory.header.stamp = rospy.Time.now()
        trajectory.joint_names = ['neck_ry']
        trajectory.points = [JointTrajectoryPoint()]
        trajectory.points[0].positions = [math.radians(pitch_degree)]
        trajectory.points[0].velocities = [0.0]
        trajectory.points[0].time_from_start = rospy.Duration(0.75)
        self.head_trajectory_pub.publish(trajectory)

    #Slot for setting the head max pitch state
    def handle_head_max_pitch(self, value):
        self.head_max_pitch_deg = value
        self.head_pitch_range_deg = self.head_max_pitch_deg - self.head_min_pitch_deg
        if (self.head_pitch_range_deg < 0.0001):
            print 'invalid pitch limits - fix range!'
            self.head_pitch_range_deg = 0.01

    #Slot for setting the head min pitch state
    def handle_head_min_pitch(self, value):
        self.head_min_pitch_deg = value
        self.head_pitch_range_deg = self.head_max_pitch_deg - self.head_min_pitch_deg
        if (self.head_pitch_range_deg < 0.0001):
            print 'invalid pitch limits - fix range!'
            self.head_pitch_range_deg = 0.01

    def handle_head_period(self, value):
        self.scan_period_secs = value

    @Slot(bool)
    def handle_scan_chg(self, checked):
        ros_time = rospy.get_rostime()
        if checked:
            print 'Start scanning ...'
            if ((self.head_pitch_cmd_deg >= self.head_min_pitch_deg) and (self.head_pitch_cmd_deg <= self.head_max_pitch_deg)):
                # adjust offset so that scanning command starts from current angle
                # to provide bumpless transfer

                # cos value corresponding to the current head pitch command
                tmp = ((self.head_pitch_cmd_deg - self.head_min_pitch_deg) / self.head_pitch_range_deg - 0.5) * 2.0

                # angle to give the same cosine value as current command
                tmp_rad = math.acos(tmp)
                tmp_time = tmp_rad * self.scan_period_secs / (math.pi * 2.0)

                #Use integer math to avoid issues with UTC time having large numbers
                self.scan_offset.secs = int(math.floor(tmp_time))
                self.scan_offset.nsecs = int(math.floor((tmp_time - self.scan_offset.secs) * 1000000000))
                self.scan_offset.secs -= ros_time.secs
                self.scan_offset.nsecs -= ros_time.nsecs
                if (self.scan_offset.nsecs < 0):
                    self.scan_offset.secs -= 1
                    self.scan_offset.nsecs += 1000000000

            else:
                print 'outside of range - ignore offset '
                self.scan_offset = ros_time

            self._timer = rospy.Timer(rospy.Duration(0.01), self.time_callback)

        else:
            print 'Stop scanning!'
            if self._timer is not None:
                self._timer.shutdown()

    #Update the time
    def time_callback(self, timer_event):
        ros_time = rospy.get_rostime()
        # Test and only publish at 100Hz so often to avoid spamming at 1khz
        delta_time = ros_time - self.last_publish_time
        if (delta_time.secs > 0) or (delta_time.nsecs > 10000000):
            self.last_publish_time = ros_time
            float_time = float(ros_time.secs + self.scan_offset.secs) + float(ros_time.nsecs+ self.scan_offset.nsecs) * 1e-9
            rad = (float_time) * (math.pi * 2.0 / self.scan_period_secs)
            cos_rad = math.cos(rad)
            frac_range = 0.5 * cos_rad + 0.5

            tmp_deg = frac_range * self.head_pitch_range_deg + self.head_min_pitch_deg

            # Set value and publish inside spinbox callback
            self.head_pitch_spinbox.setValue(tmp_deg)
Beispiel #12
0
 def createEditor(self, parent, option, index):
     editor = QDoubleSpinBox(parent)
     editor.setDecimals(self._decimals)
     editor.setMaximum(self._min)
     editor.setMaximum(self._max)
     return editor
class TrapezoidalTrajectoryDialog(Plugin):
    updateStateSignal = Signal(object)

    def __init__(self, context):
        super(TrapezoidalTrajectoryDialog, self).__init__(context)
        self.setObjectName("TrapezoidalTrajectoryDialog")
        # self.updateStateSignal = Signal(object)
        self.updateStateSignal.connect(self.on_updateState)

        self.robot = URDF.from_parameter_server()
        self.joint_list = {}

        for ndx, jnt in enumerate(self.robot.joints):
            self.joint_list[jnt.name] = ndx

        self.chain = []
        self.chain_file = rospy.get_param("~chain_file")
        self.chain_name = rospy.get_param("~chain_name")

        print
        print "Define order of splines used to approximate trapezoid"
        self.spline_order = rospy.get_param("~spline_order", 5)  # 1 # 3 # 5 # linear, cubic, quintic
        if (self.spline_order == 1) or (self.spline_order == 3) or (self.spline_order == 5):
            print "Spline order=", self.spline_order
        else:
            print "Invalid spline order!  Must be 1, 3, or 5"
            print "Spline order=", self.spline_order
            sys.exit(-1)

        yaml_file = self.chain_file + self.chain_name + "_chain.yaml"
        print "Chain definition file:"
        print yaml_file
        print

        # define structures
        self.robot_state = JointState()
        self.robot_command = JointTrajectory()

        stream = open(yaml_file, "r")
        jointChain = yaml.load_all(stream)

        for ndx, data in enumerate(jointChain):
            print ndx, " : ", data
            self.delay_time = data["delay_time"]
            self.ramp_up_time = data["ramp_up_time"]
            self.dwell_time = data["dwell_time"]
            self.ramp_down_time = data["ramp_down_time"]
            self.hold_time = data["hold_time"]
            self.ramp_start_fraction = data["ramp_start_fraction"]
            self.ramp_end_fraction = data["ramp_end_fraction"]

            self.joint_state_topic = data["joint_state_topic"]
            self.trajectory_topic = data["trajectory_topic"]

            if rospy.search_param(data["chain_param_name"]):
                print "Found ", data["chain_param_name"]
            else:
                print "Failed to find the ", data["chain_param_name"], " in the parameter server!"
                sys.exit(-1)

            joint_names = rospy.get_param(data["chain_param_name"])
            for joint in joint_names:
                print joint
                self.robot_state.name.append(joint)
                self.chain.append(JointData(self, joint))

        self.robot_command.joint_names = self.robot_state.name

        stream.close()

        self.robot_state.position = [0.0] * len(self.robot_state.name)
        self.robot_state.velocity = [0.0] * len(self.robot_state.name)
        self.robot_state.effort = [0.0] * len(self.robot_state.name)
        self.robot_joint_state = JointState()

        print "delay_time    =", self.delay_time
        print "ramp_up_time  =", self.ramp_up_time
        print "dwell_time    =", self.dwell_time
        print "ramp_down_time=", self.ramp_down_time
        print "hold_time     =", self.hold_time
        print "spline order  =", self.spline_order

        if (
            (self.ramp_start_fraction < 0.001)
            or (self.ramp_end_fraction > 0.999)
            or (self.ramp_start_fraction >= self.ramp_end_fraction)
        ):
            print "Invalid ramp fractions - abort!"
            print "0.0 < ", self.ramp_start_fraction, " < ", self.ramp_end_fraction, " < 1.0"
            return

        print "Robot State Structure", self.robot_state
        print "Robot Command Structure", self.robot_command

        # initialize structure to hold widget handles
        self.cmd_spinbox = []
        self.ramp_up_spinbox = []
        self.ramp_down_spinbox = []

        self._widget = QWidget()
        vbox = QVBoxLayout()

        # Push buttons
        hbox = QHBoxLayout()

        snap_command = QPushButton("Snap Position")
        snap_command.clicked.connect(self.snap_current_callback)
        hbox.addWidget(snap_command)

        check_limits = QPushButton("Check Limits Gains")
        check_limits.clicked.connect(self.check_limits_callback)
        hbox.addWidget(check_limits)

        apply_command = QPushButton("Send Trajectory")
        apply_command.clicked.connect(self.apply_command_callback)
        hbox.addWidget(apply_command)

        save_trajectory = QPushButton("Save Trajectory")
        save_trajectory.clicked.connect(self.save_trajectory_callback)
        hbox.addWidget(save_trajectory)

        zero_ramp = QPushButton("Zero Ramps")
        zero_ramp.clicked.connect(self.zero_ramp_callback)
        hbox.addWidget(zero_ramp)

        vbox.addLayout(hbox)

        time_hbox = QHBoxLayout()

        vbox_delay = QVBoxLayout()
        vbox_delay.addWidget(QLabel("Delay"))
        self.delay_time_spinbox = QDoubleSpinBox()
        self.delay_time_spinbox.setDecimals(5)
        self.delay_time_spinbox.setRange(0, 10.0)
        self.delay_time_spinbox.setSingleStep(0.1)
        self.delay_time_spinbox.valueChanged.connect(self.on_delay_time_value)
        self.delay_time_spinbox.setValue(self.delay_time)
        vbox_delay.addWidget(self.delay_time_spinbox)
        time_hbox.addLayout(vbox_delay)

        vbox_ramp_up = QVBoxLayout()
        vbox_ramp_up.addWidget(QLabel("Ramp Up"))
        self.ramp_up_time_spinbox = QDoubleSpinBox()
        self.ramp_up_time_spinbox.setDecimals(5)
        self.ramp_up_time_spinbox.setRange(0, 10.0)
        self.ramp_up_time_spinbox.setSingleStep(0.1)
        self.ramp_up_time_spinbox.valueChanged.connect(self.on_ramp_up_time_value)
        self.ramp_up_time_spinbox.setValue(self.ramp_up_time)
        vbox_ramp_up.addWidget(self.ramp_up_time_spinbox)
        time_hbox.addLayout(vbox_ramp_up)

        #
        vbox_dwell = QVBoxLayout()
        vbox_dwell.addWidget(QLabel("Dwell"))
        self.dwell_time_spinbox = QDoubleSpinBox()
        self.dwell_time_spinbox.setDecimals(5)
        self.dwell_time_spinbox.setRange(0, 10.0)
        self.dwell_time_spinbox.setSingleStep(0.1)
        self.dwell_time_spinbox.valueChanged.connect(self.on_dwell_time_value)
        self.dwell_time_spinbox.setValue(self.dwell_time)
        vbox_dwell.addWidget(self.dwell_time_spinbox)
        time_hbox.addLayout(vbox_dwell)

        vbox_ramp_down = QVBoxLayout()
        vbox_ramp_down.addWidget(QLabel("Down"))
        self.ramp_down_time_spinbox = QDoubleSpinBox()
        self.ramp_down_time_spinbox.setDecimals(5)
        self.ramp_down_time_spinbox.setRange(0, 10.0)
        self.ramp_down_time_spinbox.setSingleStep(0.1)
        self.ramp_down_time_spinbox.valueChanged.connect(self.on_ramp_down_time_value)
        self.ramp_down_time_spinbox.setValue(self.ramp_down_time)
        vbox_ramp_down.addWidget(self.ramp_down_time_spinbox)
        time_hbox.addLayout(vbox_ramp_down)

        vbox_hold = QVBoxLayout()
        vbox_hold.addWidget(QLabel("Hold"))
        self.hold_time_spinbox = QDoubleSpinBox()
        self.hold_time_spinbox.setDecimals(5)
        self.hold_time_spinbox.setRange(0, 10.0)
        self.hold_time_spinbox.setSingleStep(0.1)
        self.hold_time_spinbox.valueChanged.connect(self.on_hold_time_value)
        self.hold_time_spinbox.setValue(self.hold_time)
        vbox_hold.addWidget(self.hold_time_spinbox)
        time_hbox.addLayout(vbox_hold)

        vbox.addLayout(time_hbox)

        # Joints title
        title_frame = QFrame()
        title_frame.setFrameShape(QFrame.StyledPanel)
        title_frame.setFrameShadow(QFrame.Raised)

        title_hbox = QHBoxLayout()
        title_hbox.addWidget(QLabel("Joints"))
        title_hbox.addWidget(QLabel("Start"))
        title_hbox.addWidget(QLabel("Ramp Up"))
        title_hbox.addWidget(QLabel("Ramp Down"))
        title_frame.setLayout(title_hbox)
        vbox.addWidget(title_frame)

        # Define the widgets for each joint
        for i, joint in enumerate(self.chain):
            # print i,",",joint
            self.joint_widget(vbox, i)

        # add stretch at end so all GUI elements are at top of dialog
        vbox.addStretch(1)

        self._widget.setLayout(vbox)

        # Define the connections to the outside world
        self.jointSubscriber = rospy.Subscriber(self.joint_state_topic, JointState, self.stateCallbackFnc)
        self.commandPublisher = rospy.Publisher(self.trajectory_topic, JointTrajectory, queue_size=10)

        # Add the widget
        context.add_widget(self._widget)

    @Slot()
    def snap_current_callback(self):
        self.blockSignals(True)
        print "Snapping positions to actual values"
        for index, joint in enumerate(self.chain):
            for index_state, name_state in enumerate(self.robot_joint_state.name):
                if name_state == joint.name:
                    joint.position = copy.deepcopy(self.robot_joint_state.position[index_state])
                    self.cmd_spinbox[index].setValue(joint.position)
                    break

        self.blockSignals(False)

    @Slot()
    def check_limits_callback(self):
        self.blockSignals(True)
        print "Check limits callback ..."
        valid = True
        for index, joint in enumerate(self.chain):

            ramp_up = joint.position + joint.ramp_up
            ramp_down = joint.ramp_down + ramp_up

            p = self.ramp_up_spinbox[index].palette()
            if (ramp_up > joint.upper_limit) or (ramp_up < joint.lower_limit):
                print "Joint ", joint.name, " is over limit on ramp up!"
                valid = False
                p.setColor(QPalette.Window, Qt.red)  # <<<<<<<<<<<----------------- This isn't working as intended
            else:
                p.setColor(QPalette.Window, Qt.white)  # <<<<<<<<<<<----------------- This isn't working as intended
                # print joint.lower_limit," < ", ramp_up, " < ",joint.upper_limit

            self.ramp_up_spinbox[index].setPalette(p)

            if (ramp_down > joint.upper_limit) or (ramp_down < joint.lower_limit):
                print "Joint ", joint.name, " is over limit on ramp down!"
                valid = False
                p.setColor(QPalette.Window, Qt.red)  # <<<<<<<<<<<----------------- This isn't working as intended
            else:
                p.setColor(QPalette.Window, Qt.white)  # <<<<<<<<<<<----------------- This isn't working as intended
                # print joint.lower_limit," < ", ramp_down, " < ",joint.upper_limit

        if not valid:
            print "Invalid joint limits on ramp!"
        else:
            print "   Valid ramp!"

        self.blockSignals(False)
        return valid

    @Slot()
    def zero_ramp_callback(self):
        self.blockSignals(True)
        print "Zero ramps callback ..."
        for index, joint in enumerate(self.chain):
            self.ramp_up_spinbox[index].setValue(0.0)
            self.ramp_down_spinbox[index].setValue(0.0)
        self.blockSignals(False)

    @Slot()
    def apply_command_callback(self):
        self.blockSignals(True)
        print "Send trajectory"

        if self.calculate_trajectory():
            # print self.robot_command;
            self.commandPublisher.publish(self.robot_command)
        else:
            print "Trajectory calculation failure - do not publish!"

        self.blockSignals(False)

    @Slot()
    def save_trajectory_callback(self):
        self.blockSignals(True)
        print "Save gains"
        # Save data to file that we can read in
        # TODO - invalid file names

        fileName = QFileDialog.getSaveFileName()

        # if fileName[0] checks to ensure that the file dialog has not been canceled
        if fileName[0]:
            if self.calculate_trajectory():
                print self.robot_command
                newFileptr = open(fileName[0], "w")
                # Suggested format for files to make it easy to combine different outputs
                newFileptr.write("# Trajectory \n")
                newFileptr.write(self.robot_command)
                newFileptr.write("\n")
                newFileptr.close()
            else:
                print "Trajectory calculation failure - do not save!"
        else:
            print "Save cancelled."

        newFilePtr.close()

        self.blockSignals(False)

    #
    @Slot()
    def stateCallbackFnc(self, atlasState_msg):
        # Handle the state message for actual positions
        time = atlasState_msg.header.stamp.to_sec()
        if (time - self.prior_time) > 0.02:
            # Only update at 50hz
            # relay the ROS messages through a Qt Signal to switch to the GUI thread
            self.updateStateSignal.emit(atlasState_msg)
            self.prior_time = time

    # this runs in the Qt GUI thread and can therefore update the GUI
    def on_updateState(self, joint_state_msg):
        # print joint_state_msg
        self.robot_joint_state = joint_state_msg

    def on_delay_time_value(self, value):
        self.delay_time = copy.deepcopy(value)

    def on_ramp_up_time_value(self, value):
        self.ramp_up_time = copy.deepcopy(value)

    def on_ramp_down_time_value(self, value):
        self.ramp_down_time = copy.deepcopy(value)

    def on_dwell_time_value(self, value):
        self.dwell_time = copy.deepcopy(value)

    def on_hold_time_value(self, value):
        self.hold_time = copy.deepcopy(value)

    def calculate_trajectory(self):

        if not self.check_limits_callback():
            print "Invalid limits for trajectory!"
            return False

        knot_points = 1
        if self.delay_time > 0.002:
            knot_points += 1

        if self.ramp_up_time > 0.002:
            knot_points += 1

        if self.dwell_time > 0.002:
            knot_points += 1

        if self.ramp_down_time > 0.002:
            knot_points += 1

        if self.hold_time > 0.002:
            knot_points += 1

        if knot_points < 2:
            print "Invalid trajectory - knot_points = ", knot_points
            return False

        # print "Minimum knot points=",knot_points
        self.robot_command.points = []
        self.robot_command.points.append(JointTrajectoryPoint())

        ros_time_offset = rospy.Duration(0.0)
        ndx = 0
        self.robot_command.points[ndx].time_from_start = ros_time_offset
        for jnt, joint in enumerate(self.chain):
            self.robot_command.points[ndx].positions.append(joint.position)
            if self.spline_order > 1:
                self.robot_command.points[ndx].velocities.append(0.0)
            if self.spline_order > 3:
                self.robot_command.points[ndx].accelerations.append(0.0)
        ndx += 1

        if self.delay_time > 0.002:
            ros_time_offset += rospy.Duration(self.delay_time)
            self.robot_command.points.append(copy.deepcopy(self.robot_command.points[ndx - 1]))
            self.robot_command.points[ndx].time_from_start = ros_time_offset
            ndx += 1

        if self.ramp_up_time > 0.002:
            ramp_up_count = self.calculate_ramp_up_section(ndx)
            if ramp_up_count:
                ndx += ramp_up_count
                ros_time_offset += rospy.Duration(self.ramp_up_time)
            else:
                return False  # Invalid ramp calculation

        if self.dwell_time > 0.002:
            ros_time_offset += rospy.Duration(self.dwell_time)
            self.robot_command.points.append(copy.deepcopy(self.robot_command.points[ndx - 1]))
            self.robot_command.points[ndx].time_from_start = ros_time_offset
            ndx += 1

        if self.ramp_down_time > 0.002:
            ramp_dn_count = self.calculate_ramp_down_section(ndx)
            if ramp_dn_count > 0:
                ndx += ramp_dn_count
                ros_time_offset += rospy.Duration(self.ramp_down_time)
            else:
                return False  # Invalid ramp calculation

        if self.hold_time > 0.002:
            ros_time_offset += rospy.Duration(self.hold_time)
            self.robot_command.points.append(copy.deepcopy(self.robot_command.points[ndx - 1]))
            self.robot_command.points[ndx].time_from_start = ros_time_offset
            ndx += 1

        # print self.robot_command
        # print "Ndx=",ndx, " of ",knot_points, "=",len(self.robot_command.points)

        if ndx != len(self.robot_command.points):
            print "Invalid number of knot points - ignore trajectory"
            print "Ndx=", ndx, " of ", len(self.robot_command.points), " = ", knot_points
            print self.robot_command
            return False

        self.robot_command.header.stamp = rospy.Time.now() + rospy.Duration(0.1)

        return True

    def calculate_ramp_up_section(self, ndx):
        prior = self.robot_command.points[ndx - 1]

        # Create next 3 knot points
        self.robot_command.points.append(copy.deepcopy(prior))
        if self.spline_order > 1:
            # Add transition points
            self.robot_command.points.append(copy.deepcopy(prior))
            self.robot_command.points.append(copy.deepcopy(prior))
            dT0 = self.ramp_start_fraction * self.ramp_up_time
            dT1 = (self.ramp_end_fraction - self.ramp_start_fraction) * self.ramp_up_time
            dT2 = (1.0 - self.ramp_end_fraction) * self.ramp_up_time
            self.robot_command.points[ndx].time_from_start = prior.time_from_start + rospy.Duration(dT0)
            self.robot_command.points[ndx + 1].time_from_start = self.robot_command.points[
                ndx
            ].time_from_start + rospy.Duration(dT1)
            self.robot_command.points[ndx + 2].time_from_start = self.robot_command.points[
                ndx + 1
            ].time_from_start + rospy.Duration(dT2)
        else:
            dT0 = self.ramp_up_time
            self.robot_command.points[ndx].time_from_start = prior.time_from_start + rospy.Duration(dT0)

        # Now calculate the interior values for each joint individually
        for jnt, joint in enumerate(self.chain):
            if joint.ramp_up != 0.0:
                starting_position = copy.deepcopy(prior.positions[jnt])
                ending_position = starting_position + joint.ramp_up

                print "calculating ramp up for ", joint.name, " from ", starting_position, " to ", ending_position, " over ", self.ramp_up_time, " seconds"

                if self.spline_order > 1:
                    # Either quintic or cubic - either way we have transition -> linear -> transition
                    # for quintic,
                    #  Ax=b  where x=[a0 b0 c0 d0 e0 f0 e1 f1 a2 b2 c2 d2 e2 f2] with p=a*t^5 + b*t^4 + c*t^3 + d*t^2 + e*t +f for first and last, and p=e*t + f for middle
                    #  6 + 2 + 6 unknowns
                    x = self.solve_ramp_matrices(dT0, dT1, dT2, starting_position, ending_position)
                    # first interior point
                    self.robot_command.points[ndx].positions[jnt] = copy.deepcopy(x[7][0])
                    self.robot_command.points[ndx].velocities[jnt] = copy.deepcopy(x[6][0])
                    # linear segment
                    self.robot_command.points[ndx + 1].positions[jnt] = copy.deepcopy(x[13][0])
                    self.robot_command.points[ndx + 1].velocities[jnt] = copy.deepcopy(x[6][0])
                    # last interior point
                    self.robot_command.points[ndx + 2].positions[jnt] = ending_position
                    self.robot_command.points[ndx + 2].velocities[jnt] = 0.0
                else:
                    # Piecewise linear
                    self.robot_command.points[ndx].positions[jnt] = copy.deepcopy(ending_position)

                if self.spline_order > 3:
                    # Quintic spline
                    self.robot_command.points[ndx].accelerations[jnt] = 0.0
                    self.robot_command.points[ndx + 1].accelerations[jnt] = 0.0
                    self.robot_command.points[ndx + 2].accelerations[jnt] = 0.0

        if self.spline_order < 3:
            return 1

        return 3  # At least cubic and has 3 transition segments

    def calculate_ramp_down_section(self, ndx):
        prior = self.robot_command.points[ndx - 1]

        # Create next knot points
        self.robot_command.points.append(copy.deepcopy(prior))
        if self.spline_order > 1:
            # Add transition points
            self.robot_command.points.append(copy.deepcopy(prior))
            self.robot_command.points.append(copy.deepcopy(prior))
            dT0 = self.ramp_start_fraction * self.ramp_down_time
            dT1 = (self.ramp_end_fraction - self.ramp_start_fraction) * self.ramp_down_time
            dT2 = (1.0 - self.ramp_end_fraction) * self.ramp_down_time
            self.robot_command.points[ndx].time_from_start = prior.time_from_start + rospy.Duration(dT0)
            self.robot_command.points[ndx + 1].time_from_start = self.robot_command.points[
                ndx
            ].time_from_start + rospy.Duration(dT1)
            self.robot_command.points[ndx + 2].time_from_start = self.robot_command.points[
                ndx + 1
            ].time_from_start + rospy.Duration(dT2)
        else:
            dT0 = self.ramp_down_time
            self.robot_command.points[ndx].time_from_start = prior.time_from_start + rospy.Duration(dT0)

        # Now calculate the interior values for each joint individually
        for jnt, joint in enumerate(self.chain):
            if joint.ramp_up != 0.0:
                starting_position = copy.deepcopy(prior.positions[jnt])
                ending_position = starting_position + joint.ramp_down

                print "calculating ramp down for ", joint.name, " from ", starting_position, " to ", ending_position, " over ", self.ramp_down_time, " seconds"

                if self.spline_order > 1:
                    # Ax=b  where x=[a0 b0 c0 d0 e0 f0 e1 f1 a2 b2 c2 d2 e2 f2] with p=a*t^5 + b*t^4 + c*t^3 + d*t^2 + e*t +f for first and last, and p=e*t + f for middle
                    #  6 + 2 + 6 unknowns
                    x = self.solve_ramp_matrices(dT0, dT1, dT2, starting_position, ending_position)
                    # first interior point
                    self.robot_command.points[ndx].positions[jnt] = copy.deepcopy(x[7][0])
                    self.robot_command.points[ndx].velocities[jnt] = copy.deepcopy(x[6][0])
                    # linear segment
                    self.robot_command.points[ndx + 1].positions[jnt] = copy.deepcopy(x[13][0])
                    self.robot_command.points[ndx + 1].velocities[jnt] = copy.deepcopy(x[6][0])
                    # last interior point
                    self.robot_command.points[ndx + 2].positions[jnt] = ending_position
                    self.robot_command.points[ndx + 2].velocities[jnt] = 0.0
                else:
                    self.robot_command.points[ndx].positions[jnt] = copy.deepcopy(ending_position)

                if self.spline_order > 3:
                    self.robot_command.points[ndx + 2].accelerations[jnt] = 0.0
                    self.robot_command.points[ndx].accelerations[jnt] = 0.0
                    self.robot_command.points[ndx + 1].accelerations[jnt] = 0.0

        if self.spline_order < 3:
            return 1

        return 3  # At least cubic and has 3 transition segments

    def solve_ramp_matrices(self, dT0, dT1, dT2, starting_position, ending_position):

        if self.spline_order < 4:
            # assume cubic spline
            return self.solve_ramp_cubic_matrices(dT0, dT1, dT2, starting_position, ending_position)

        # This is the quintic version
        # Ax=b  where x=[a0 b0 c0 d0 e0 f0 e1 f1 a2 b2 c2 d2 e2 f2] with p=a*t^5 + b*t^4 + c*t^3 + d*t^2 + e*t +f for first and last, and p=e*t + f for middle
        #  6 + 2 + 6 = 14 unknowns
        A = np.zeros((14, 14))
        b = np.zeros((14, 1))

        # Eqn 1  - Known starting position
        A[0][5] = 1
        b[0][0] = starting_position

        # Eqn 2  - Zero velocity at start
        A[1][4] = 1.0

        # Eqn 3  - Zero acceleration at start
        A[2][3] = 2.0

        # Eqn 4  - Continuous velocity at first interior knot point
        A[3][0] = 5 * dT0 ** 4
        A[3][1] = 4 * dT0 ** 3
        A[3][2] = 3 * dT0 ** 2
        A[3][3] = 2 * dT0
        A[3][4] = 1.0
        A[3][5] = 0.0
        A[3][6] = -1.0

        # Eqn 5  - Zero acceleration at first interior knot point
        A[4][0] = 20 * dT0 ** 3
        A[4][1] = 12 * dT0 ** 2
        A[4][2] = 6 * dT0
        A[4][3] = 2
        A[4][4] = 0.0
        A[4][5] = 0.0
        A[4][6] = 0.0

        # Eqn 6  - Zero jerk at first interior knot point
        A[5][0] = 60 * dT0 ** 2
        A[5][1] = 24 * dT0
        A[5][2] = 6
        A[5][3] = 0.0
        A[5][4] = 0.0
        A[5][5] = 0.0
        A[5][6] = 0.0

        # Eqn 7  - Continuous velocity at second interior knot point
        A[6][8] = 0.0
        A[6][9] = 0.0
        A[6][10] = 0.0
        A[6][11] = 0.0
        A[6][12] = 1.0
        A[6][13] = 0.0
        A[6][6] = -1.0

        # Eqn 8  -  Zero acceleration at second interior knot point
        A[7][8] = 0.0
        A[7][9] = 0.0
        A[7][10] = 0.0
        A[7][11] = 2
        A[7][12] = 0.0
        A[7][13] = 0.0
        A[7][6] = 0.0

        # Eqn 9  - Zero jerk at second interior knot point
        A[8][8] = 0.0
        A[8][9] = 0.0
        A[8][10] = 6
        A[8][11] = 0.0
        A[8][12] = 0.0
        A[8][13] = 0.0
        A[8][6] = 0.0

        # Eqn 10 - Equal (but unknown) positions at first interior knot point
        A[9][0] = dT0 ** 5
        A[9][1] = dT0 ** 4
        A[9][2] = dT0 ** 3
        A[9][3] = dT0 ** 2
        A[9][4] = dT0
        A[9][5] = 1.0
        A[9][7] = -1.0

        # Eqn 11 - Equal (but unknown) positions at second interior knot point
        A[10][13] = 1.0
        A[10][6] = -dT1
        A[10][7] = -1.0

        # Eqn 12 - Known final position
        A[11][8] = dT2 ** 5
        A[11][9] = dT2 ** 4
        A[11][10] = dT2 ** 3
        A[11][11] = dT2 ** 2
        A[11][12] = dT2
        A[11][13] = 1.0
        b[11][0] = ending_position

        # Eqn 13 - Known final velocity = 0
        A[12][8] = 5 * dT2 ** 4
        A[12][9] = 4 * dT2 ** 3
        A[12][10] = 3 * dT2 ** 2
        A[12][11] = 2 * dT2
        A[12][12] = 1.0
        A[12][13] = 0.0

        # Eqn 14 - Known final acceleration = 0
        A[13][8] = 20 * dT2 ** 3
        A[13][9] = 12 * dT2 ** 2
        A[13][10] = 6 * dT2
        A[13][11] = 2.0
        A[13][12] = 0.0
        A[13][13] = 0.0

        params = np.linalg.solve(A, b)

        # np.set_printoptions(precision=9, suppress=True, linewidth=250)
        # print "A=",A
        # print "b=",b
        # print "condition number(A)=",np.linalg.cond(A)
        # print "x=",params

        return params

    def solve_ramp_cubic_matrices(self, dT0, dT1, dT2, starting_position, ending_position):

        # This is the cubic version
        # Quintic version: Ax=b  where x=[a0 b0 c0 d0 e0 f0 e1 f1 a2 b2 c2 d2 e2 f2] with p=a*t^5 + b*t^4 + c*t^3 + d*t^2 + e*t +f for first and last, and p=e*t + f for middle
        # Cubic   version: Ax=b  where x=[c0 d0 e0 f0 e1 f1 c2 d2 e2 f2] with p= c*t^3 + d*t^2 + e*t +f for first and last, and p=e*t + f for middle
        #
        #  4 + 2 + 4 = 10 unknowns
        A = np.zeros((10, 10))
        b = np.zeros((10, 1))

        # Eqn 1  - Known starting position
        A[0][3] = 1
        b[0][0] = starting_position

        # Eqn 2  - Zero velocity at start
        A[1][2] = 1.0

        # Eqn 3  - Continuous velocity at first interior knot point
        A[2][0] = 3 * dT0 ** 2
        A[2][1] = 2 * dT0
        A[2][2] = 1.0
        A[2][3] = 0.0
        A[2][4] = -1.0

        # Eqn 4  - Zero acceleration at first interior knot point
        A[3][0] = 6 * dT0
        A[3][1] = 2
        A[3][2] = 0.0
        A[3][3] = 0.0

        # Eqn 5  - Continuous velocity at second interior knot point
        A[4][6] = 0.0
        A[4][7] = 0.0
        A[4][8] = 1.0
        A[4][9] = 0.0
        A[4][4] = -1.0

        # Eqn 6  -  Zero acceleration at second interior knot point
        A[5][6] = 0.0
        A[5][7] = 2.0
        A[5][8] = 0.0
        A[5][9] = 0.0

        # Eqn 7 - Equal (but unknown) positions at first interior knot point
        A[6][0] = dT0 ** 3
        A[6][1] = dT0 ** 2
        A[6][2] = dT0
        A[6][3] = 1.0
        A[6][5] = -1.0

        # Eqn 8 - Equal (but unknown) positions at second interior knot point
        A[7][9] = 1.0
        A[7][4] = -dT1
        A[7][5] = -1.0

        # Eqn 9 - Known final position
        A[8][6] = dT2 ** 3
        A[8][7] = dT2 ** 2
        A[8][8] = dT2
        A[8][9] = 1.0
        b[8][0] = ending_position

        # Eqn 10 - Known final velocity = 0
        A[9][6] = 3 * dT2 ** 2
        A[9][7] = 2 * dT2
        A[9][8] = 1.0
        A[9][9] = 0.0

        params = np.linalg.solve(A, b)

        quintic_params = np.zeros((14, 1))
        quintic_params[2:8] = params[0:6]
        quintic_params[10:14] = params[6:10]

        np.set_printoptions(precision=9, suppress=True, linewidth=250)
        print "A=", A
        print "b=", b
        print "condition number(A)=", np.linalg.cond(A)
        print "x=", params
        # print "-----------------------------------------------------------------------"
        # print "--------- Cubic solve -------------------------------------------------"
        # print "cubic params=",params
        # print "quintic params = ", quintic_params
        # print "-----------------------------------------------------------------------"
        return quintic_params

    def shutdown_plugin(self):
        # Just make sure to stop timers and publishers, unsubscribe from Topics etc in the shutdown_plugin method.
        # TODO, is the above comment why the plugin gets stuck when we try to shutdown?
        print "Shutdown ..."
        rospy.sleep(0.1)
        self.jointSubscriber.unregister()
        self.commandPublisher.unregister()
        rospy.sleep(0.5)
        print "Done!"

    # Define collection of widgets for joint group
    def joint_widget(self, main_vbox, index):
        joint = self.chain[index]
        frame = QFrame()
        frame.setFrameShape(QFrame.StyledPanel)
        frame.setFrameShadow(QFrame.Raised)

        hbox = QHBoxLayout()
        # hbox.addWidget(frame)

        self.prior_time = 0.0

        robot_joint = self.robot.joints[self.joint_list[joint.name]]
        joint.lower_limit = robot_joint.limit.lower
        joint.upper_limit = robot_joint.limit.upper
        ramp_range = robot_joint.limit.upper - robot_joint.limit.lower

        print "  ", joint.name, "  limits(", joint.lower_limit, ", ", joint.upper_limit, ") range=", ramp_range

        self.cmd_spinbox.append(QDoubleSpinBox())
        self.cmd_spinbox[index].setDecimals(5)
        self.cmd_spinbox[index].setRange(joint.lower_limit, joint.upper_limit)
        self.cmd_spinbox[index].setSingleStep((robot_joint.limit.upper - robot_joint.limit.lower) / 50.0)
        self.cmd_spinbox[index].valueChanged.connect(joint.on_cmd_value)

        self.ramp_up_spinbox.append(QDoubleSpinBox())
        self.ramp_up_spinbox[index].setDecimals(5)
        self.ramp_up_spinbox[index].setRange(-ramp_range, ramp_range)
        self.ramp_up_spinbox[index].setSingleStep(ramp_range / 50.0)
        self.ramp_up_spinbox[index].valueChanged.connect(joint.on_ramp_up_value)

        self.ramp_down_spinbox.append(QDoubleSpinBox())
        self.ramp_down_spinbox[index].setDecimals(5)
        self.ramp_down_spinbox[index].setRange(-ramp_range, ramp_range)
        self.ramp_down_spinbox[index].setSingleStep(ramp_range / 50.0)
        self.ramp_down_spinbox[index].valueChanged.connect(joint.on_ramp_down_value)

        hbox.addWidget(QLabel(joint.name))
        hbox.addWidget(self.cmd_spinbox[index])
        hbox.addWidget(self.ramp_up_spinbox[index])
        hbox.addWidget(self.ramp_down_spinbox[index])

        # Add horizontal layout to frame for this joint group
        frame.setLayout(hbox)

        # Add frame to main vertical layout
        main_vbox.addWidget(frame)
class SpeakEasyGUI(QMainWindow):
    '''
    One instance of this class builds the entire sound play UI.
    Instance variable that hold widgets of interest to controllers:
    
	    - C{speechInputFld}
	    - C{onceOrRepeatDict}
	    - C{voicesRadioButtonsDict}
	    - C{recorderDict}
	    - C{programButtonDict}
	    - C{soundButtonDict}

    '''

    # ---------------------- Durations --------------------
    
    # Number of seconds a program button must be pressed to
    # go into program mode:
    PROGRAM_BUTTON_HOLD_TIME = 3.0;

    # Amount of time the program button stays in alternate look
    # to indicate that it is changing to programming mode: 

    PROGRAM_BUTTON_LOOK_CHANGE_DURATION = 0.2; # seconds

    # ---------------------- Button and Font Sizes -------------------- 

    # Minimum height of buttons:
    BUTTON_MIN_HEIGHT = 30; # pixels
    # Pushbutton minimum font size:
    BUTTON_LABEL_FONT_SIZE = 16; # pixels
    
    # Radio button minimum font size:
    RADIO_BUTTON_LABEL_FONT_SIZE = 16; # pixels
    
    EDIT_FIELD_TEXT_SIZE = 18; # pixels
    
    NUM_OF_PROGRAM_BUTTON_COLUMNS = 4;
    NUM_OF_SOUND_BUTTON_COLUMNS   = 4;
    
    # ---------------------- Names for Voices ----------------------
    
    # Official names of voices as recognized by the
    # underlying text-to-speech engines:
    voices = {'VOICE_1': 'voice_kal_diphone',  # Festival voice
              'VOICE_2': 'David',              # Cepstral voices
              'VOICE_3': 'Amy',
              'VOICE_4': 'Shouty',
              'VOICE_5': 'Whispery',
              'VOICE_6': 'Lawrence',
              'VOICE_7': 'William'
              };
        
    # ---------------------- Names for Widgets ----------------------
    
    interactionWidgets = {
                          'PLAY_ONCE': 'Play once',
                          'PLAY_REPEATEDLY': 'Play repeatedly',
                          'PLAY_REPEATEDLY_PERIOD': 'Pause between plays',
                          'VOICE_1': 'Machine',
                          'VOICE_2': 'David',
                          'VOICE_3': 'Amy',
                          'VOICE_4': 'Shout',
                          'VOICE_5': 'Whisper',
                          'VOICE_6': 'Lawrence',
                          'VOICE_7': 'William',
                          'PLAY_TEXT': 'Play Text',
                          'STOP': 'Stop',
                          'STOP_ALL' : 'Stop All',
                          
                          'SPEECH_1' : 'Speech 1',
                          'SPEECH_2' : 'Speech 2',
                          'SPEECH_3' : 'Speech 3',
                          'SPEECH_4' : 'Speech 4',
                          'SPEECH_5' : 'Speech 5',
                          'SPEECH_6' : 'Speech 6',
                          'SPEECH_7' : 'Speech 7',
                          'SPEECH_8' : 'Speech 8',
                          'SPEECH_9' : 'Speech 9',
                          'SPEECH_10' : 'Speech 10',
                          'SPEECH_11' : 'Speech 11',
                          'SPEECH_12' : 'Speech 12',
                          'SPEECH_13' : 'Speech 13',
                          'SPEECH_14' : 'Speech 14',
                          'SPEECH_15' : 'Speech 15',
                          'SPEECH_16' : 'Speech 16',
                          'SPEECH_17' : 'Speech 17',
                          'SPEECH_18' : 'Speech 18',
                          'SPEECH_19' : 'Speech 19',
                          'SPEECH_20' : 'Speech 20',
                          'SPEECH_21' : 'Speech 21',
                          'SPEECH_22' : 'Speech 22',
                          'SPEECH_23' : 'Speech 23',
                          'SPEECH_24' : 'Speech 24',
                          
                          'SOUND_1' : 'Rooster',
                          'SOUND_2' : 'Drill',
                          'SOUND_3' : 'Bull call',
                          'SOUND_4' : 'Clown horn',
                          'SOUND_5' : 'Cash register',
                          'SOUND_6' : 'Glass breaking',
                          'SOUND_7' : 'Cell door',
                          'SOUND_8' : 'Cow',
                          'SOUND_9' : 'Birds',
                          'SOUND_10' : 'Big truck',
                          'SOUND_11' : 'Seagulls',
                          'SOUND_12' : 'Lift',
                          
                          'NEW_SPEECH_SET' : 'Save speech set',
                          'PICK_SPEECH_SET' : 'Pick different speech set',
                          
                          'PLAY_LOCALLY' : 'Play locally',
                          'PLAY_AT_ROBOT' : 'Play at robot',
                          'SPEECH_MODULATION' : 'Speech modulation',
                          'PASTE' : 'Paste', 
                          'CLEAR' : 'Clear', 
                          } 
        
    # ---------------------- Application Background styling --------------------     

    # Application background:
    veryLightBlue = QColor(230, 255,255);
    stylesheetAppBG = 'QDialog {background-color: %s}' % veryLightBlue.name();
    
    defaultStylesheet = stylesheetAppBG;
    defaultStylesheetName = "Default";
    
    # ---------------------- Edit field styling -----------------

    editFieldBGColor =  QColor(12,21,109);
    editFieldTextColor = QColor(244,244,246);

    inputFldStylesheet =\
        'TextPanel {background-color: ' + editFieldBGColor.name() +\
        '; color: ' + editFieldTextColor.name() +\
        '; font-size: ' + str(EDIT_FIELD_TEXT_SIZE) + 'pt' +\
        '}';
    
    # ---------------------- Pushbutton styling -------------------- 
    
    # Button color definitions:
    recorderButtonBGColor = QColor(176,220,245); # Light blue
    recorderButtonDisabledBGColor = QColor(187,200,208); # Gray-blue
    recorderButtonTextColor = QColor(0,0,0);     # Black
    programButtonBGColor = QColor(117,150,169);  # Middle blue
    programButtonTextColor = QColor(251,247,247);# Off-white
    soundButtonBGColor = QColor(110,134,211);      # Dark blue
    soundButtonTextColor = QColor(251,247,247);  # Off-white

    # Button stylesheets:    
    recorderButtonStylesheet =\
        'QPushButton {background-color: ' + recorderButtonBGColor.name() +\
        '; color: ' + recorderButtonTextColor.name() +\
        '; font-size: ' + str(BUTTON_LABEL_FONT_SIZE) + 'px' +\
        '}';
        
    recorderButtonDisabledStylesheet =\
        'QPushButton {background-color: ' + recorderButtonDisabledBGColor.name() +\
        '; color: ' + recorderButtonTextColor.name() +\
        '; font-size: ' + str(BUTTON_LABEL_FONT_SIZE) + 'px' +\
        '}';
        
    programButtonStylesheet =\
        'QPushButton {background-color: ' + programButtonBGColor.name() +\
        '; color: ' + programButtonTextColor.name() +\
        '; font-size: ' + str(BUTTON_LABEL_FONT_SIZE) + 'px' +\
        '}';

    # Stylesheet for when program button look is temporarily
    # changed to indicate transition to programming mode:                                                          
    programButtonModeTransitionStylesheet =\
        'QPushButton {background-color: ' + editFieldBGColor.name() +\
        '; color: ' + programButtonTextColor.name() +\
        '; font-size: ' + str(BUTTON_LABEL_FONT_SIZE) + 'px' +\
        '}';
                                                          
                                                          
    soundButtonStylesheet =\
        'QPushButton {background-color: ' + soundButtonBGColor.name() +\
        '; color: ' + soundButtonTextColor.name() +\
        '; font-size: ' + str(BUTTON_LABEL_FONT_SIZE) + 'px' +\
        '}';

    # ---------------------- Radiobutton and Play Repeat Delay Spinbox styling -----------------
    
    # Radiobutton color definitions:
    playOnceRepeatButtonBGColor = QColor(121,229,230); # Very light cyan
    voicesButtonBGColor = QColor(97,164,165); # Darker cyan

    
    playOnceRepeatButtonStylesheet =\
     'font-size: ' + str(RADIO_BUTTON_LABEL_FONT_SIZE) + 'px' +\
     '; color: ' + soundButtonTextColor.name() +\
     '; background-color: ' + voicesButtonBGColor.name();
     
    playRepeatSpinboxStylesheet =\
     'font-size: ' + str(RADIO_BUTTON_LABEL_FONT_SIZE) + 'px' +\
     '; color: ' + soundButtonTextColor.name() +\
     '; background-color: ' + voicesButtonBGColor.name();

#    voiceButtonStylesheet =\
#     'font-size: ' + str(RADIO_BUTTON_LABEL_FONT_SIZE) + 'px' +\
#     '; background-color: ' + voicesButtonBGColor.name();

    voiceButtonStylesheet =\
     'font-size: ' + str(RADIO_BUTTON_LABEL_FONT_SIZE) + 'px' +\
     '; color: ' + soundButtonTextColor.name() +\
     '; background-color: ' + voicesButtonBGColor.name();

    # ---------------------- Signals -----------------
    
    hideButtonSignal  	= pyqtSignal(QPushButton); # hide the given button
    showButtonSignal  	= pyqtSignal(QPushButton); # show the hidden button

    #----------------------------------
    # Initializer 
    #--------------
        
    def __init__(self, parent=None, mirrored=True, stand_alone=False, sound_effect_labels=None):
        
        super(SpeakEasyGUI, self).__init__(parent);
        
        self.stand_alone = stand_alone;
        self.sound_effect_labels = sound_effect_labels;
        if (sound_effect_labels is None):
            raise ValueError("Must pass in non-null array of sound effect button labels.");
        
        #self.setMaximumWidth(1360);
        #self.setMaximumHeight(760);
        
            # Vertical box to hold all top level widget groups
        appLayout = QVBoxLayout();
        appWidget = QDialog();      
        appWidget.setStyleSheet(SpeakEasyGUI.defaultStylesheet);
        # Activate the window resize handle in the lower right corner
        # of the app window:
        appWidget.setSizeGripEnabled(True);
        
        self.setCentralWidget(appWidget);

        self.addTitle(appLayout);        
        self.addTxtInputFld(appLayout);
        self.buildTapeRecorderButtons(appLayout);
        self.addOnceOrRepeat_And_VoiceRadioButtons(appLayout);
        
        self.buildHorizontalDivider(appLayout);
        self.buildProgramButtons(appLayout);
        self.buildSoundButtons(appLayout);
        self.buildButtonSetControls(appLayout);
        self.buildOptionsRadioButtons(appLayout);
        
        appWidget.setLayout(appLayout);
    
        #*******self.connectEvents();    
        self.show();

    #----------------------------------
    # programButtonIterator 
    #--------------

    def programButtonIterator(self, gridLayout=None):
        
        if gridLayout is None:
            gridLayout = self.programButtonGridLayout;
        
        programButtonArray = [];
        # Collect the buttons into a flat array:
        for row in range(gridLayout.rowCount()):
            for col in range(SpeakEasyGUI.NUM_OF_PROGRAM_BUTTON_COLUMNS):
                layoutItem = gridLayout.itemAtPosition(row, col);
                if layoutItem is not None:
                    programButtonArray.append(layoutItem.widget());
        return iter(programButtonArray);

    #----------------------------------
    # replaceProgramButtons
    #--------------

    def replaceProgramButtons(self, buttonProgramsArray):
        
        programButtonObjIt = self.programButtonIterator();
        # Remove the existing program buttons from the application's
        # layout, and mark them for deletion:
        try:
            while True:
                programButtonObj = programButtonObjIt.next();
                self.programButtonGridLayout.removeWidget(programButtonObj);
                programButtonObj.deleteLater();
        except StopIteration:
            pass
        
        # Make an array of button labels from the ButtonProgram
        # instances in buttonProgramsArray:
        buttonLabelArr = [];
        for buttonProgram in buttonProgramsArray:
            buttonLabelArr.append(buttonProgram.getLabel());
            
        # No more program buttons present. Make 
        # new ones, and add them to the grid layout:
        (newProgramButtonGridLayout, self.programButtonDict) =\
            SpeakEasyGUI.buildButtonGrid(buttonLabelArr,
                                         SpeakEasyGUI.NUM_OF_PROGRAM_BUTTON_COLUMNS);
        for buttonObj in self.programButtonDict.values():
            buttonObj.setStyleSheet(SpeakEasyGUI.programButtonStylesheet);
            buttonObj.setMinimumHeight(SpeakEasyGUI.BUTTON_MIN_HEIGHT);

        # Transfer the buttons from this new gridlayout object to the
        # original layout that is embedded in the application Dialog:
        newButtonsIt = self.programButtonIterator(gridLayout=newProgramButtonGridLayout);
        try:
            while True:
                self.programButtonGridLayout.addWidget(newButtonsIt.next());
        except StopIteration:
            pass;
    
    #----------------------------------
    # connectEvents
    #--------------
    
    def connectEvents(self):
        mousePressEvent.connect(self.textAreaMouseClick);
        
    def textAreaMouseClick(self, event):
        print('Text area clicked.')
    
    
    #----------------------------------
    # connectSignalsToWidgets 
    #--------------
    
#    def connectSignalsToWidgets(self):
#        self.commChannel = CommChannel();
#        self.commChannel.hideButtonSignal.connect(SpeakEasyGUI.hideButtonHandler);
#        self.commChannel.showButtonSignal.connect(SpeakEasyGUI.showButtonHandler);


    #----------------------------------
    # addTitle 
    #--------------

    def addTitle(self,layout):
        title = QLabel("<H1>SpeakEasy</H1>");
        hbox = QHBoxLayout();
        hbox.addStretch(1);
        hbox.addWidget(title);
        hbox.addStretch(1);
        
        layout.addLayout(hbox);
        
        
    #----------------------------------
    # addTxtInputFld 
    #--------------
        
    def addTxtInputFld(self, layout):
        '''
        Creates text input field label and text field
        in a horizontal box layout. Adds that hbox layout
        to the passed-in layout.
        
        Sets instance variables:
            1. C{self.speechInputFld}
         
        @param layout: Layout object to which the label/txt-field C{hbox} is to be added.
        @type  layout: QLayout
        '''
        speechControlsLayout = QHBoxLayout();
        speechControlsLayout.addStretch(1);
        speechInputFldLabel = QLabel("<b>What to say:</b>")
        speechControlsLayout.addWidget(speechInputFldLabel);
        
        self.speechInputFld = TextPanel(numLines=5);
        self.speechInputFld.setStyleSheet(SpeakEasyGUI.inputFldStylesheet);
        self.speechInputFld.setFontPointSize(SpeakEasyGUI.EDIT_FIELD_TEXT_SIZE);
        
        speechControlsLayout.addWidget(self.speechInputFld);
        
        layout.addLayout(speechControlsLayout);
        
        # Create and hide the dialog for adding Cepstral voice modulation 
        # markup to text in the text input field:
        self.speechControls = MarkupManagementUI(textPanel=self.speechInputFld, parent=self);

    #----------------------------------
    # addVoiceRadioButtons
    #--------------
            
    def addOnceOrRepeat_And_VoiceRadioButtons(self, layout):
        '''
        Creates radio buttons for selecting whether a
        sound is to play once, or repeatedly until stopped.
        Also adds radio buttons for selecting voices.
        Places all in a horizontal box layout. Adds 
        that hbox layout to the passed-in layout.
        
        Sets instance variables:
            1. C{self.onceOrRepeatDict}
            2. C{self.voicesRadioButtonsDict}
         
        @param layout: Layout object to which the label/txt-field C{hbox} is to be added.
        @type  layout: QLayout
         '''
        
        hbox = QHBoxLayout();

        (self.onceOrRepeatGroup, onceOrRepeatButtonLayout, self.onceOrRepeatDict) =\
            self.buildRadioButtons([SpeakEasyGUI.interactionWidgets['PLAY_ONCE'], 
                                    SpeakEasyGUI.interactionWidgets['PLAY_REPEATEDLY']
                                    ],
                                   Orientation.HORIZONTAL,
                                   Alignment.LEFT,
                                   activeButtons=[SpeakEasyGUI.interactionWidgets['PLAY_ONCE']],
                                   behavior=CheckboxGroupBehavior.RADIO_BUTTONS);

        self.replayPeriodSpinBox = QDoubleSpinBox(self);
        self.replayPeriodSpinBox.setRange(0.0, 99.9); # seconds
        self.replayPeriodSpinBox.setSingleStep(0.5);
        self.replayPeriodSpinBox.setDecimals(1);
        onceOrRepeatButtonLayout.addWidget(self.replayPeriodSpinBox);
        secondsLabel = QLabel("secs delay");
        onceOrRepeatButtonLayout.addWidget(secondsLabel);
        
        # Create an array of voice radio button labels:
        voiceRadioButtonLabels = [];
        for voiceKey in SpeakEasyGUI.voices.keys():
            voiceRadioButtonLabels.append(SpeakEasyGUI.interactionWidgets[voiceKey]);
            
        (self.voicesGroup, voicesButtonLayout, self.voicesRadioButtonsDict) =\
            self.buildRadioButtons(voiceRadioButtonLabels,
                                   Orientation.HORIZONTAL,
                                   Alignment.RIGHT,
                                   activeButtons=[SpeakEasyGUI.interactionWidgets['VOICE_1']],
                                   behavior=CheckboxGroupBehavior.RADIO_BUTTONS);
                                   
        # Style all the radio buttons:
        for playFreqButton in self.onceOrRepeatDict.values():
            playFreqButton.setStyleSheet(SpeakEasyGUI.playOnceRepeatButtonStylesheet); 
        for playFreqButton in self.voicesRadioButtonsDict.values():
            playFreqButton.setStyleSheet(SpeakEasyGUI.voiceButtonStylesheet);
        #...and the replay delay spinbox:
        self.replayPeriodSpinBox.setStyleSheet(SpeakEasyGUI.playRepeatSpinboxStylesheet);
        #****** replayPeriodSpinBox styling 
                                   
        hbox.addLayout(onceOrRepeatButtonLayout);
        hbox.addStretch(1);
        hbox.addLayout(voicesButtonLayout);
        layout.addLayout(hbox);
        
    #----------------------------------
    # buildTapeRecorderButtons 
    #--------------
        
    def buildTapeRecorderButtons(self, layout):
        '''
        Creates tape recorder buttons (Play Text, Stop,etc.).
        Places all in a row, though the layout is a 
        QGridLayout. Adds QGridLayout to the passed-in 
        layout.
        
        Sets instance variables:
            1. C{self.recorderDict}
         
        @param layout: Layout object to which the label/txt-field C{QGridlayout} is to be added.
        @type  layout: QLayout
        '''
        
        (buttonGridLayout, self.recorderButtonDict) =\
            SpeakEasyGUI.buildButtonGrid([SpeakEasyGUI.interactionWidgets['PLAY_TEXT'],
                                          SpeakEasyGUI.interactionWidgets['STOP'],
                                          #SpeakEasyGUI.interactionWidgets['STOP_ALL'] # Stop button already stops all.
                                          ],
                                         2); # Two columns
        for buttonObj in self.recorderButtonDict.values():
            buttonObj.setStyleSheet(SpeakEasyGUI.recorderButtonStylesheet);
            buttonObj.setMinimumHeight(SpeakEasyGUI.BUTTON_MIN_HEIGHT);
        layout.addLayout(buttonGridLayout);                                 
         
    #----------------------------------
    # buildProgramButtons
    #--------------
        
    def buildProgramButtons(self, layout):
        '''
        Creates grid of buttons for saving sounds.
        Adds the resulting QGridLayout to the passed-in 
        layout.
        
        Sets instance variables:
            1. C{self.programButtonDict}
         
        @param layout: Layout object to which the label/txt-field C{QGridlayout} is to be added.
        @type  layout: QLayout
        '''

        buttonLabelArr = [];
        for i in range(NUM_SPEECH_PROGRAM_BUTTONS):
            key = "SPEECH_" + str(i+1);
            buttonLabelArr.append(SpeakEasyGUI.interactionWidgets[key]);
                    
        (self.programButtonGridLayout, self.programButtonDict) =\
            SpeakEasyGUI.buildButtonGrid(buttonLabelArr,
                                         SpeakEasyGUI.NUM_OF_PROGRAM_BUTTON_COLUMNS);
        for buttonObj in self.programButtonDict.values():
            buttonObj.setStyleSheet(SpeakEasyGUI.programButtonStylesheet);
            buttonObj.setMinimumHeight(SpeakEasyGUI.BUTTON_MIN_HEIGHT);
            
        layout.addLayout(self.programButtonGridLayout);                                 
        
    #----------------------------------
    # buildSoundButtons
    #--------------
        
    def buildSoundButtons(self, layout):
        '''
        Creates grid of buttons for playing canned sounds.
        Adds the resulting QGridLayout to the passed-in 
        layout.
        
        Sets instance variables:
            1. C{self.soundButtonDict}
         
        @param layout: Layout object to which the label/txt-field C{QGridlayout} is to be added.
        @type  layout: QLayout
        '''
        
        for i in range(len(self.sound_effect_labels)):
            key = "SOUND_" + str(i);
            SpeakEasyGUI.interactionWidgets[key] = self.sound_effect_labels[i];
        
        (buttonGridLayout, self.soundButtonDict) =\
            SpeakEasyGUI.buildButtonGrid(self.sound_effect_labels, SpeakEasyGUI.NUM_OF_SOUND_BUTTON_COLUMNS);
        for buttonObj in self.soundButtonDict.values():
            buttonObj.setStyleSheet(SpeakEasyGUI.soundButtonStylesheet);
            buttonObj.setMinimumHeight(SpeakEasyGUI.BUTTON_MIN_HEIGHT);
                        
        layout.addLayout(buttonGridLayout);                                 

    #----------------------------------
    # buildButtonSetControls
    #--------------

    def buildButtonSetControls(self, layout):
        
        buttonLabelArray = [self.interactionWidgets['NEW_SPEECH_SET'], self.interactionWidgets['PICK_SPEECH_SET']]; 
        # Two columns of buttons:
        (buttonGridLayout, self.speechSetButtonDict) = SpeakEasyGUI.buildButtonGrid(buttonLabelArray, 2);
        for buttonObj in self.speechSetButtonDict.values():
            buttonObj.setStyleSheet(SpeakEasyGUI.programButtonStylesheet);
            buttonObj.setMinimumHeight(SpeakEasyGUI.BUTTON_MIN_HEIGHT);
        layout.addLayout(buttonGridLayout);
        
    #----------------------------------
    # buildOptionsRadioButtons 
    #--------------
        
    def buildOptionsRadioButtons(self, layout):
        hbox = QHBoxLayout();
        (self.playLocalityGroup, playLocalityButtonLayout, self.playLocalityRadioButtonsDict) =\
            self.buildRadioButtons([SpeakEasyGUI.interactionWidgets['PLAY_LOCALLY'],
                                    SpeakEasyGUI.interactionWidgets['PLAY_AT_ROBOT']
                                    ],
                                   Orientation.HORIZONTAL,
                                   Alignment.LEFT,
                                   activeButtons=[SpeakEasyGUI.interactionWidgets[DEFAULT_PLAY_LOCATION]],
                                   behavior=CheckboxGroupBehavior.RADIO_BUTTONS);
                                   #behavior=CheckboxGroupBehavior.CHECKBOXES);
                                   
        # Style all the radio buttons:
        for playLocalityButton in self.playLocalityRadioButtonsDict.values():
            playLocalityButton.setStyleSheet(SpeakEasyGUI.voiceButtonStylesheet); 
        hbox.addLayout(playLocalityButtonLayout);
        hbox.addStretch(1);
        self.buildConvenienceButtons(hbox);
        layout.addLayout(hbox);

    #----------------------------------
    # buildConvenienceButtons
    #--------------
        
    def buildConvenienceButtons(self, layout):
        '''
        Creates buttons meant for accessibility convenience.
        Example: Paste.
        Places all in a row, though the layout is a 
        QGridLayout. Adds QGridLayout to the passed-in 
        layout.
        
        Sets instance variables:
            1. C{self.convenienceButtonDict}
         
        @param layout: Layout object to which the label/txt-field C{QGridlayout} is to be added.
        @type  layout: QLayout
        '''
        
        (buttonGridLayout, self.convenienceButtonDict) =\
            SpeakEasyGUI.buildButtonGrid([SpeakEasyGUI.interactionWidgets['SPEECH_MODULATION'],
                                          SpeakEasyGUI.interactionWidgets['PASTE'],
                                          SpeakEasyGUI.interactionWidgets['CLEAR'],
                                          ],
                                         2); # Two columns
        for buttonObj in self.convenienceButtonDict.values():
            buttonObj.setStyleSheet(SpeakEasyGUI.recorderButtonStylesheet);
            buttonObj.setMinimumHeight(SpeakEasyGUI.BUTTON_MIN_HEIGHT);
        layout.addLayout(buttonGridLayout);                                 
        
    #----------------------------------
    # buildHorizontalDivider 
    #--------------
        
    def buildHorizontalDivider(self, layout):
        frame = QFrame();
        frame.setFrameShape(QFrame.HLine);
        #*******frame.setFrameStyle(QFrame.Shadow.Sunken.value());
        frame.setLineWidth(3);
        frame.setMidLineWidth(3);
        layout.addWidget(frame);
        
        
    #----------------------------------
    # buildRadioButtons
    #--------------
        
    # Note: Whenever a button is switched on or off it emits the 
    # PySide.QtGui.QAbstractButton.toggled() signal. Connect to this 
    # signal if you want to trigger an action each time the button changes state.         
    
    def buildRadioButtons(self, 
                          labelTextArray,
                          orientation,
                          alignment,
                          activeButtons=None, 
                          behavior=CheckboxGroupBehavior.RADIO_BUTTONS):
        '''
        @param labelTextArray: Names of buttons
        @type labelTextArray: [string]
        @param orientation: Whether to arrange the buttons vertically or horizontally.
        @type orientation: Orientation
        @param alignment: whether buttons should be aligned Left/Center/Right for horizontal,
                          Top/Center/Bottom for vertical:/c
        @type  alignment: Alignment
        @param activeButtons: Name of the buttons that is to be checked initially. Or None.
        @type activeButtons: [string]
        @param behavior: Indicates whether the button group is to behave like Radio Buttons, or like Checkboxes.
        @type behavior: CheckboxGroupBehavior
        @return 
            1. The button group that contains the related buttons. Caller: ensure that 
               this object does not go out of scope. 
            2. The button layout, which callers will need to add to
               their own layouts.
            3. and a dictionary mapping button names to button objects that
               were created within this method. This dict is needed by the
               controller.
        @rtype (QButtonGroup, QLayout, dict<string,QRadioButton>).
        '''
        
        # Button control management group. It has no visible
        # representation. It allows the radio button bahavior, for instance:
        buttonGroup = QButtonGroup();
        
        if behavior == CheckboxGroupBehavior.CHECKBOXES:
            buttonGroup.setExclusive(False);
        else:
            buttonGroup.setExclusive(True);
        
        if orientation == Orientation.VERTICAL:
            buttonCompLayout = QVBoxLayout();
            # Arrange for the alignment (Part 1):
            if (alignment == Alignment.CENTER) or\
               (alignment == Alignment.BOTTOM):
                buttonCompLayout.addStretch(1);
        else:
            buttonCompLayout = QHBoxLayout();
            # Arrange for the alignment (Part 1):
            if (alignment == Alignment.CENTER) or\
               (alignment == Alignment.RIGHT):
                buttonCompLayout.addStretch(1);
            
        # Build the buttons:
        buttonDict  = {};
        
        for label in labelTextArray:
            button = QRadioButton(label);
            buttonDict[label] = button;
            # Add button to the button management group:
            buttonGroup.addButton(button);
            # Add the button to the visual group:
            buttonCompLayout.addWidget(button);
            if label in activeButtons:
                button.setChecked(True);

        if orientation == Orientation.VERTICAL:
            buttonCompLayout = QVBoxLayout();
            # Arrange for the alignment (Part 2):
            if (alignment == Alignment.CENTER) or\
               (alignment == Alignment.TOP):
                buttonCompLayout.addStretch(1);
        else:
            # Arrange for the alignment (Part 2):
            if (alignment == Alignment.CENTER) or\
               (alignment == Alignment.LEFT):
                buttonCompLayout.addStretch(1);
                        
        return (buttonGroup, buttonCompLayout, buttonDict);
    
    #----------------------------------
    # buildButtonGrid
    #--------------

    @staticmethod
    def buildButtonGrid(labelTextArray, 
                        numColumns):
        '''
        Creates a grid of QPushButton widgets. They will be 
        
        @param labelTextArray: Button labels.
        @type labelTextArray: [string]
        @param numColumns: The desired width of the button grid.
        @type numColumns: int
        @return: 1. a grid layout with the button objects inside.
                 2. a dictionary mapping button labels to button objects.
        @rtype: QGridLayout 
        '''

        buttonLayout = QGridLayout();
        
        # Compute number of button rows:
        numRows = len(labelTextArray) / numColumns;
        # If this number of rows leaves a few buttons left over (< columnNum),
        # then add a row for those:
        if (len(labelTextArray) % numColumns) > 0:
            numRows += 1;
        
        buttonDict = {}
        
        row = 0;
        col = 0;
        for buttonLabel in labelTextArray:
            button = QPushButton(buttonLabel);
            buttonDict[buttonLabel] = button;
            buttonLayout.addWidget(button, row, col);
            col += 1;
            if (col > (numColumns - 1)):
                col = 0;
                row += 1;
                
        return (buttonLayout, buttonDict);
        
    #----------------------------------
    # getNewButtonLabel 
    #--------------

    def getNewButtonLabel(self):
        '''
        Requests a new button label from the user.
        Returns None if user canceled out, or a string
        with the new button label. 
        @return: None if user canceled, else string from input field.
        @rtype: {None | string}
        
        '''
        prompt = "New label for this button:";
        dialogBox = QInputDialog(parent=self);
        dialogBox.setStyleSheet(SpeakEasyGUI.stylesheetAppBG);
        dialogBox.setLabelText(prompt);
        dialogBox.setCancelButtonText("Keep existing label");
        userChoice = dialogBox.exec_();

        if userChoice == QDialog.Accepted:
            return dialogBox.textValue();
        else:
            return None;
        
    #----------------------------------
    # playOnceChecked 
    #--------------

    def playOnceChecked(self):
        return self.onceOrRepeatDict[SpeakEasyGUI.interactionWidgets['PLAY_ONCE']].isChecked();

    #----------------------------------
    # setPlayOnceChecked 
    #--------------
    
    def setPlayOnceChecked(self):
        self.onceOrRepeatDict[SpeakEasyGUI.interactionWidgets['PLAY_ONCE']].setChecked(True);
    
    #----------------------------------
    # playRepeatedlyChecked 
    #--------------

    def playRepeatedlyChecked(self):
        return self.onceOrRepeatDict[SpeakEasyGUI.interactionWidgets['PLAY_REPEATEDLY']].isChecked();

    #----------------------------------
    # setPlayRepeatedlyChecked 
    #--------------
    
    def setPlayRepeatedlyChecked(self):
        self.onceOrRepeatDict[SpeakEasyGUI.interactionWidgets['PLAY_REPEATEDLY']].setChecked(True);
        
    #----------------------------------
    # playRepeatPeriod 
    #--------------

    def getPlayRepeatPeriod(self):
        return self.replayPeriodSpinBox.value();
    
    #----------------------------------
    # activeVoice
    #--------------

    def activeVoice(self):
        '''
        Return the official name of the voice that is currently
        checked in the UI. This is the name that will be recognized
        by the underlying text-to-speech engine(s).
        @return: Name of voice as per the SpeakEasyGUI.voices dict.
        @rtype: string
        '''
        for voiceKey in SpeakEasyGUI.voices.keys():
            if self.voicesRadioButtonsDict[SpeakEasyGUI.interactionWidgets[voiceKey]].isChecked():
                return SpeakEasyGUI.voices[voiceKey];

    #----------------------------------
    # whereToPlay 
    #--------------
    
    def whereToPlay(self):
        '''
        Returns which of the play location options is selected: Locally or Robot.
        @return: Selection of where sound and text-to-speech output is to occur.
        @rtype: PlayLocation
        '''
        if self.playLocalityRadioButtonsDict[SpeakEasyGUI.interactionWidgets['PLAY_LOCALLY']].isChecked():
            return PlayLocation.LOCALLY;
        else:
            return PlayLocation.ROBOT;
    
    #----------------------------------
    # setWhereToPlay 
    #--------------
    
    def setWhereToPlay(self, playLocation):
        '''
        Set the option radio button that determines where sound is produced,
        locally, or at the robot. No action is taken. This method merely sets
        the appropriate radio button.
        @param playLocation: PlayLocation.LOCALLY, or PlayLocation.ROBOT
        @type playLocation: PlayLocation
        '''
        
        if playLocation == PlayLocation.LOCALLY:
            radioButton = self.playLocalityRadioButtonsDict[SpeakEasyGUI.interactionWidgets['PLAY_LOCALLY']];
        else:
            radioButton = self.playLocalityRadioButtonsDict[SpeakEasyGUI.interactionWidgets['PLAY_AT_ROBOT']];
        radioButton.setChecked(True);
            
    #----------------------------------
    # setButtonLabel 
    #--------------
    
    def setButtonLabel(self, buttonObj, label):
        buttonObj.setText(label);

    #----------------------------------
    # blinkButton
    #--------------

    def blinkButton(self, buttonObj, turnOn):
        '''
        Used to make a program button blink in some way to indicate
        that it is changing into programming mode. Since this method
        is triggered by a timer thread, it cannot make any GUI changes.
        Instead, it sends a signal to have the GUI thread place the
        button into an alternative look. It then schedules a call
        to itself for a short time later. At that point it sends a
        signal to the GUI thread to return the button to its usual
        look:
        @param buttonObj: The button to be blinked
        @type  buttonObj: QPushButton
        @param turnOn:    Indicates whether to turn the button back into 
                          its normal state, or whether to make it take on
                          its alternate look. 
        @type  turnOn:    bool
        '''
        if turnOn:
            self.showButtonSignal.emit(buttonObj);
        else:
            self.hideButtonSignal.emit(buttonObj);
            self.buttonBlinkTimer = Timer(SpeakEasyGUI.PROGRAM_BUTTON_LOOK_CHANGE_DURATION, partial(self.blinkButton, buttonObj, True));
            self.buttonBlinkTimer.start();
    def __init__(self, context):
        super(JointControlWidget, self).__init__()
        self.updateStateSignal.connect(self.on_update_state)
        self.updateGhostSignal.connect(self.on_update_ghost)

        self.joint_states = JointState()
        self.ghost_joint_states = JointState()
        self._widget = context
        vbox = QVBoxLayout()

        # Define checkboxes
        radios = QWidget()
        hbox_radio = QHBoxLayout()
        self.radioGroup = QButtonGroup()
        self.radioGroup.setExclusive(True)
        self.radio_ghost_target = QRadioButton()
        self.radio_ghost_target.setText("Ghost")
        self.radioGroup.addButton(self.radio_ghost_target, 0)
        self.radio_ghost_target.setChecked(True)
        self.radio_robot_target = QRadioButton()
        self.radio_robot_target.setText("Robot")
        self.radioGroup.addButton(self.radio_robot_target, 1)
        hbox_radio.addStretch()
        hbox_radio.addWidget(self.radio_ghost_target)
        hbox_radio.addStretch()
        hbox_radio.addWidget(self.radio_robot_target)
        hbox_radio.addStretch()
        radios.setLayout(hbox_radio)
        vbox.addWidget(radios)

        duration_box = QHBoxLayout()
        duration_box.setAlignment(Qt.AlignLeft)
        duration_box.addWidget(QLabel("Trajectory duration (s):"))
        self.traj_duration_spin = QDoubleSpinBox()
        self.traj_duration_spin.setValue(1.0)
        self.traj_duration_spin.valueChanged.connect(self.on_traj_duration_changed)
        duration_box.addWidget(self.traj_duration_spin)
        self.update_controllers_buttonn = QPushButton("Update Controllers")
        self.update_controllers_buttonn.pressed.connect(self.on_update_controllers)
        duration_box.addWidget(self.update_controllers_buttonn)
        vbox.addLayout(duration_box)

        widget = QWidget()
        hbox = QHBoxLayout()


        # Left to right layout
        self.joint_control = JointControl(self, hbox)

        widget.setLayout(hbox)

        vbox.addWidget(widget)

        print "Add buttons to apply all ..."
        all_widget = QWidget()
        all_box = QHBoxLayout()

        self.snap_to_ghost_button = QPushButton("SnapAllGhost")
        self.snap_to_ghost_button.pressed.connect(self.on_snap_ghost_pressed)
        all_box.addWidget(self.snap_to_ghost_button)
        self.snap_to_current_button = QPushButton("SnapAllCurrent")
        self.snap_to_current_button.pressed.connect(self.on_snap_current_pressed)
        all_box.addWidget(self.snap_to_current_button)
        self.apply_to_robot_button = QPushButton("ApplyAllRobot")
        self.apply_to_robot_button.pressed.connect(self.on_apply_robot_pressed)
        all_box.addWidget(self.apply_to_robot_button)
        self.apply_to_robot_button = QPushButton("Apply WBC Robot")
        self.apply_to_robot_button.pressed.connect(self.on_apply_wbc_robot_pressed)
        all_box.addWidget(self.apply_to_robot_button)

        all_widget.setLayout(all_box)
        vbox.addWidget(all_widget)

        override_box = QHBoxLayout()

        self.override = QCheckBox()
        self.override.setChecked(False)
        self.override.stateChanged.connect(self.on_override_changed)
        override_box.addWidget(self.override)

        override_label = QLabel("SAFETY OVERRIDE")
        override_label.setStyleSheet('QLabel { color: red }')

        override_box.addWidget(override_label)

        override_box.addStretch()

        vbox.addLayout(override_box)

        vbox.addStretch()

        self._widget.setLayout(vbox)

        self.first_time = True

        self.stateSubscriber = rospy.Subscriber('/joint_states', JointState, self.state_callback_fnc)
        self.ghostSubscriber = rospy.Subscriber('/flor/ghost/get_joint_states', JointState, self.ghost_callback_fnc)
        self.wbc_robot_pub = rospy.Publisher('/flor/wbc_controller/joint_states', JointState, queue_size=10)

        self.time_last_update_state = time.time()
        self.time_last_update_ghost = time.time()
    def __init__(self, context):
        super(SinusoidalTrajectoryDialog, self).__init__(context)
        self.setObjectName('SinusoidalTrajectoryDialog')
        #self.updateStateSignal = Signal(object)
        self.updateStateSignal.connect(self.on_updateState)

        self.robot = URDF.from_parameter_server()
        self.joint_list = {}

    	for ndx,jnt in enumerate(self.robot.joints):
            self.joint_list[jnt.name] = ndx
            #print jnt.name, " ",self.joint_list[jnt.name]

        self.chain=[]
        self.chain_file = rospy.get_param("chain_file")
        self.chain_name = rospy.get_param("chain_name")

        yaml_file = self.chain_file+self.chain_name+"_chain.yaml"
        print yaml_file

        #define structures
        self.robot_state = JointState()
        self.robot_command = JointTrajectory()

        stream = open(yaml_file, "r")
        jointChain = yaml.load_all(stream)
        print '\n\n'
        for ndx, data in enumerate(jointChain):
            print ndx," : ", data
            self.delay_time          = data["delay_time"]
            self.amplitude           = data["amplitude"]
            self.frequency           = data["frequency"]
            self.frequency_limit     = data["frequency_limit"]
            self.iterations          = data["iterations"]
            self.joint_state_topic   = data["joint_state_topic"]
            self.trajectory_topic    = data["trajectory_topic"]

            joints = rospy.get_param(data["chain_param_name"])
            for joint in joints:
                print joint
                self.robot_state.name.append(joint)
                self.chain.append(JointData(self, joint) )
        self.robot_command.joint_names = self.robot_state.name

        stream.close()


        self.robot_state.position = [0.0]*len(self.robot_state.name)
        self.robot_state.velocity = [0.0]*len(self.robot_state.name)
        self.robot_state.effort   = [0.0]*len(self.robot_state.name)
        self.robot_joint_state = JointState()

        print "delay_time  =",self.delay_time
        print "amplitude   =",self.amplitude
        print "frequency   =",self.frequency
        print "iterations  =",self.iterations

        print "Robot State Structure",self.robot_state
        print "Robot Command Structure",self.robot_command

        # initialize structure to hold widget handles
        self.cur_position_spinbox=[]
        self.amplitude_spinbox=[]
        self.frequency_spinbox=[]
        self.iterations_spinbox=[]

        self._widget = QWidget()
        vbox = QVBoxLayout()

        # Push buttons
        hbox = QHBoxLayout()

        snap_command = QPushButton("Snap Position")
        snap_command.clicked.connect(self.snap_current_callback)
        hbox.addWidget(snap_command)

        check_limits = QPushButton("Check Limits Gains")
        check_limits.clicked.connect(self.check_limits_callback)
        hbox.addWidget(check_limits)

        apply_command = QPushButton("Send Trajectory")
        apply_command.clicked.connect(self.apply_command_callback)
        hbox.addWidget(apply_command)

        save_trajectory = QPushButton("Save Trajectory")
        save_trajectory.clicked.connect(self.save_trajectory_callback)
        hbox.addWidget(save_trajectory)

        zero_ramp = QPushButton("Zero Values")
        zero_ramp.clicked.connect(self.zero_values_callback)
        hbox.addWidget(zero_ramp)

        vbox.addLayout(hbox)

        time_hbox = QHBoxLayout()

        vbox_frequqency = QVBoxLayout()
        vbox_frequqency.addWidget(QLabel("Frequency"))
        self.frequency_spinbox = QDoubleSpinBox()
        self.frequency_spinbox.setDecimals(5)
        self.frequency_spinbox.setRange(0, self.frequency_limit)
        self.frequency_spinbox.setSingleStep(0.05)
        self.frequency_spinbox.valueChanged.connect(self.on_frequency_value)
        self.frequency_spinbox.setValue(self.frequency)
        vbox_frequqency.addWidget(self.frequency_spinbox)
        time_hbox.addLayout(vbox_frequqency)

        vbox_iterations = QVBoxLayout()
        vbox_iterations.addWidget(QLabel("Iterations"))
        self.iterations_spinbox = QDoubleSpinBox()
        self.iterations_spinbox.setDecimals(5)
        self.iterations_spinbox.setRange(0, 10)
        self.iterations_spinbox.setSingleStep(1)
        self.iterations_spinbox.valueChanged.connect(self.on_iterations_value)
        self.iterations_spinbox.setValue(self.iterations)
        vbox_iterations.addWidget(self.iterations_spinbox)
        time_hbox.addLayout(vbox_iterations)

        vbox.addLayout(time_hbox)

        # Joints title
        title_frame = QFrame()
        title_frame.setFrameShape(QFrame.StyledPanel);
        title_frame.setFrameShadow(QFrame.Raised);

        title_hbox = QHBoxLayout()
        title_hbox.addWidget(QLabel("Joints"))
        title_hbox.addWidget(QLabel("Current Position"))
        title_hbox.addWidget(QLabel("Amplitude"))
        title_frame.setLayout(title_hbox)
        vbox.addWidget(title_frame)


        # Define the widgets for each joint
        for i,joint in enumerate(self.chain):
            #print i,",",joint
            self.joint_widget( vbox, i)


        #add stretch at end so all GUI elements are at top of dialog
        vbox.addStretch(1)

        self._widget.setLayout(vbox)


        # Define the connections to the outside world
        self.jointSubscriber  = rospy.Subscriber(self.joint_state_topic, JointState, self.stateCallbackFnc)
        self.commandPublisher = rospy.Publisher(self.trajectory_topic  , JointTrajectory, queue_size=10)

        # Add the widget
        context.add_widget(self._widget)
class SinusoidalTrajectoryDialog(Plugin):
    updateStateSignal = Signal(object)

    def __init__(self, context):
        super(SinusoidalTrajectoryDialog, self).__init__(context)
        self.setObjectName('SinusoidalTrajectoryDialog')
        #self.updateStateSignal = Signal(object)
        self.updateStateSignal.connect(self.on_updateState)

        self.robot = URDF.from_parameter_server()
        self.joint_list = {}

    	for ndx,jnt in enumerate(self.robot.joints):
            self.joint_list[jnt.name] = ndx
            #print jnt.name, " ",self.joint_list[jnt.name]

        self.chain=[]
        self.chain_file = rospy.get_param("chain_file")
        self.chain_name = rospy.get_param("chain_name")

        yaml_file = self.chain_file+self.chain_name+"_chain.yaml"
        print yaml_file

        #define structures
        self.robot_state = JointState()
        self.robot_command = JointTrajectory()

        stream = open(yaml_file, "r")
        jointChain = yaml.load_all(stream)
        print '\n\n'
        for ndx, data in enumerate(jointChain):
            print ndx," : ", data
            self.delay_time          = data["delay_time"]
            self.amplitude           = data["amplitude"]
            self.frequency           = data["frequency"]
            self.frequency_limit     = data["frequency_limit"]
            self.iterations          = data["iterations"]
            self.joint_state_topic   = data["joint_state_topic"]
            self.trajectory_topic    = data["trajectory_topic"]

            joints = rospy.get_param(data["chain_param_name"])
            for joint in joints:
                print joint
                self.robot_state.name.append(joint)
                self.chain.append(JointData(self, joint) )
        self.robot_command.joint_names = self.robot_state.name

        stream.close()


        self.robot_state.position = [0.0]*len(self.robot_state.name)
        self.robot_state.velocity = [0.0]*len(self.robot_state.name)
        self.robot_state.effort   = [0.0]*len(self.robot_state.name)
        self.robot_joint_state = JointState()

        print "delay_time  =",self.delay_time
        print "amplitude   =",self.amplitude
        print "frequency   =",self.frequency
        print "iterations  =",self.iterations

        print "Robot State Structure",self.robot_state
        print "Robot Command Structure",self.robot_command

        # initialize structure to hold widget handles
        self.cur_position_spinbox=[]
        self.amplitude_spinbox=[]
        self.frequency_spinbox=[]
        self.iterations_spinbox=[]

        self._widget = QWidget()
        vbox = QVBoxLayout()

        # Push buttons
        hbox = QHBoxLayout()

        snap_command = QPushButton("Snap Position")
        snap_command.clicked.connect(self.snap_current_callback)
        hbox.addWidget(snap_command)

        check_limits = QPushButton("Check Limits Gains")
        check_limits.clicked.connect(self.check_limits_callback)
        hbox.addWidget(check_limits)

        apply_command = QPushButton("Send Trajectory")
        apply_command.clicked.connect(self.apply_command_callback)
        hbox.addWidget(apply_command)

        save_trajectory = QPushButton("Save Trajectory")
        save_trajectory.clicked.connect(self.save_trajectory_callback)
        hbox.addWidget(save_trajectory)

        zero_ramp = QPushButton("Zero Values")
        zero_ramp.clicked.connect(self.zero_values_callback)
        hbox.addWidget(zero_ramp)

        vbox.addLayout(hbox)

        time_hbox = QHBoxLayout()

        vbox_frequqency = QVBoxLayout()
        vbox_frequqency.addWidget(QLabel("Frequency"))
        self.frequency_spinbox = QDoubleSpinBox()
        self.frequency_spinbox.setDecimals(5)
        self.frequency_spinbox.setRange(0, self.frequency_limit)
        self.frequency_spinbox.setSingleStep(0.05)
        self.frequency_spinbox.valueChanged.connect(self.on_frequency_value)
        self.frequency_spinbox.setValue(self.frequency)
        vbox_frequqency.addWidget(self.frequency_spinbox)
        time_hbox.addLayout(vbox_frequqency)

        vbox_iterations = QVBoxLayout()
        vbox_iterations.addWidget(QLabel("Iterations"))
        self.iterations_spinbox = QDoubleSpinBox()
        self.iterations_spinbox.setDecimals(5)
        self.iterations_spinbox.setRange(0, 10)
        self.iterations_spinbox.setSingleStep(1)
        self.iterations_spinbox.valueChanged.connect(self.on_iterations_value)
        self.iterations_spinbox.setValue(self.iterations)
        vbox_iterations.addWidget(self.iterations_spinbox)
        time_hbox.addLayout(vbox_iterations)

        vbox.addLayout(time_hbox)

        # Joints title
        title_frame = QFrame()
        title_frame.setFrameShape(QFrame.StyledPanel);
        title_frame.setFrameShadow(QFrame.Raised);

        title_hbox = QHBoxLayout()
        title_hbox.addWidget(QLabel("Joints"))
        title_hbox.addWidget(QLabel("Current Position"))
        title_hbox.addWidget(QLabel("Amplitude"))
        title_frame.setLayout(title_hbox)
        vbox.addWidget(title_frame)


        # Define the widgets for each joint
        for i,joint in enumerate(self.chain):
            #print i,",",joint
            self.joint_widget( vbox, i)


        #add stretch at end so all GUI elements are at top of dialog
        vbox.addStretch(1)

        self._widget.setLayout(vbox)


        # Define the connections to the outside world
        self.jointSubscriber  = rospy.Subscriber(self.joint_state_topic, JointState, self.stateCallbackFnc)
        self.commandPublisher = rospy.Publisher(self.trajectory_topic  , JointTrajectory, queue_size=10)

        # Add the widget
        context.add_widget(self._widget)

    @Slot()
    def snap_current_callback(self):
        self.blockSignals(True)
        print "Snapping positions to actual values"
        for index, joint in enumerate(self.chain):
            for index_state, name_state in enumerate(self.robot_joint_state.name):
                if (name_state == joint.name):
                    joint.position = copy.deepcopy(self.robot_joint_state.position[index_state])
                    self.cur_position_spinbox[index].setValue(joint.position)
                    break

        self.blockSignals(False)

    @Slot()
    def check_limits_callback(self):
        self.blockSignals(True)
        print "Check limits callback ..."
        valid = True
        for index, joint in enumerate(self.chain):

            ramp_up   = joint.position + joint.amplitude
            ramp_down = joint.position - joint.amplitude

            p = self.amplitude_spinbox[index].palette()
            if (ramp_up > joint.upper_limit) or (ramp_up < joint.lower_limit):
                print "Joint ",joint.name, " is beyond upper limit!"
                valid=False
                p.setColor(QPalette.Window, Qt.red)  # <<<<<<<<<<<----------------- This isn't working as intended
            else:
                p.setColor(QPalette.Window, Qt.white)# <<<<<<<<<<<----------------- This isn't working as intended
                #print joint.lower_limit," < ", ramp_up, " < ",joint.upper_limit

            self.amplitude_spinbox[index].setPalette(p)

            if (ramp_down > joint.upper_limit) or (ramp_down < joint.lower_limit):
                print "Joint ",joint.name, " is beyond lower limit!"
                valid=False
                p.setColor(QPalette.Window, Qt.red)  # <<<<<<<<<<<----------------- This isn't working as intended
            else:
                p.setColor(QPalette.Window, Qt.white)# <<<<<<<<<<<----------------- This isn't working as intended
                #print joint.lower_limit," < ", ramp_down, " < ",joint.upper_limit

        if (self.frequency > self.frequency_limit or self.frequency <= 0):
            print "invalid frequency. must be between 0 and ", self.frequency_limit, ". value is: ", self.frequency
            valid = False

        if (not valid):
            print "ERROR: Invalid input!"
        else:
            print "   Valid values!"

        self.blockSignals(False)
        return valid

    @Slot()
    def zero_values_callback(self):
        self.blockSignals(True)
        print "Zero ramps callback ..."
        for index, joint in enumerate(self.chain):
            self.amplitude_spinbox[index].setValue(0.0)
        self.blockSignals(False)

    @Slot()
    def apply_command_callback(self):
        self.blockSignals(True)
        print "Send trajectory"

        if self.calculate_trajectory():
            #print self.robot_command;
            self.commandPublisher.publish(self.robot_command)
        else:
            print "Trajectory calculation failure - do not publish!"

        self.blockSignals(False)

    @Slot()
    def save_trajectory_callback(self):
        self.blockSignals(True)
        print "Save gains"
        # Save data to file that we can read in
        # TODO - invalid file names

        fileName = QFileDialog.getSaveFileName()

        #if fileName[0] checks to ensure that the file dialog has not been canceled
        if fileName[0]:
            if self.calculate_trajectory():
                print self.robot_command;
                newFileptr = open(fileName[0], 'w')
                # Suggested format for files to make it easy to combine different outputs
                newFileptr.write('# Trajectory \n')
                newFileptr.write(self.robot_command)
                newFileptr.write('\n')
                newFileptr.close()
            else:
                print "Trajectory calculation failure - do not save!"
        else:
            print "Save cancelled."

        newFilePtr.close()

        self.blockSignals(False)

    #
    @Slot()
    def stateCallbackFnc(self, atlasState_msg):
        # Handle the state message for actual positions
        time = atlasState_msg.header.stamp.to_sec()
        if ((time - self.prior_time) > 0.02):
            # Only update at 50hz
            # relay the ROS messages through a Qt Signal to switch to the GUI thread
            self.updateStateSignal.emit(atlasState_msg)
            self.prior_time = time

    # this runs in the Qt GUI thread and can therefore update the GUI
    def on_updateState(self, joint_state_msg):
        #print joint_state_msg
        self.robot_joint_state = joint_state_msg

    def on_delay_time_value(self, value):
        self.delay_time = copy.deepcopy(value)

    def on_frequency_value(self, value):
        self.frequency = copy.deepcopy(value)

    def on_iterations_value(self, value):
        self.iterations = copy.deepcopy(value)


    def calculate_trajectory(self):

        if not self.check_limits_callback():
            print "Invalid limits for trajectory!"
            return False

        knot_points = 8*self.iterations
        if (knot_points < 2):
            print "Invalid trajectory - knot_points = ",knot_points
            return False

        #print "Knot points=",knot_points
        self.robot_command.points = []
        self.robot_command.points.append(JointTrajectoryPoint())

        ndx = 0
        self.robot_command.points[ndx].time_from_start = rospy.Duration(0.0)
        for jnt, joint in enumerate(self.chain):
            self.robot_command.points[ndx].positions.append(joint.position)
            self.robot_command.points[ndx].velocities.append(0.0)
            self.robot_command.points[ndx].accelerations.append(0.0)
        ndx += 1


        dt = np.pi / (4.0 * self.frequency)
        time_offset = dt

        print "dt = ", dt
        while ndx < knot_points-1:
            self.robot_command.points.append(JointTrajectoryPoint())
            time_offset += dt
            print " time = ", time_offset
            self.robot_command.points[ndx].time_from_start = rospy.Duration(time_offset)
            for jnt, joint in enumerate(self.chain):
                self.robot_command.points[ndx].positions.append(joint.position + joint.amplitude * np.sin(self.frequency * time_offset))
                self.robot_command.points[ndx].velocities.append(self.frequency * joint.amplitude * np.cos(self.frequency * time_offset))
                self.robot_command.points[ndx].accelerations.append(-self.frequency * self.frequency * joint.amplitude * np.sin(self.frequency * time_offset))
            ndx += 1

            
        #end position
        self.robot_command.points.append(JointTrajectoryPoint())
        time_offset += dt
        self.robot_command.points[ndx].time_from_start = rospy.Duration(time_offset)
        for jnt, joint in enumerate(self.chain):
            self.robot_command.points[ndx].positions.append(joint.position)
            self.robot_command.points[ndx].velocities.append(0.0)
            self.robot_command.points[ndx].accelerations.append(0.0)

        print self.robot_command
        print "Ndx=",ndx, " of ",knot_points, "=",len(self.robot_command.points)
        if (ndx != len(self.robot_command.points)-1) or (len(self.robot_command.points) != knot_points):
            print "Invalid number of knot points - ignore trajectory"
            print self.robot_command
            return False

        self.robot_command.header.stamp = rospy.Time.now() + rospy.Duration(0.1)

        return True;



    def shutdown_plugin(self):
    #Just make sure to stop timers and publishers, unsubscribe from Topics etc in the shutdown_plugin method.
    #TODO, is the above comment why the plugin gets stuck when we try to shutdown?
        print "Shutdown ..."
        rospy.sleep(0.1)
        self.jointSubscriber.unregister()
        self.commandPublisher.unregister()
        rospy.sleep(0.5)
        print "Done!"


    # Define collection of widgets for joint group
    def joint_widget( self, main_vbox, index):
        joint = self.chain[index]
        frame = QFrame()
        frame.setFrameShape(QFrame.StyledPanel);
        frame.setFrameShadow(QFrame.Raised);

        hbox = QHBoxLayout()
        #hbox.addWidget(frame)

        self.prior_time = 0.0

        robot_joint = self.robot.joints[self.joint_list[joint.name]]
        joint.lower_limit = robot_joint.limit.lower
        joint.upper_limit = robot_joint.limit.upper
        amplitude_range = (robot_joint.limit.upper-robot_joint.limit.lower)
        frequency_range = self.frequency_limit
        iterations_range = 10000

        print "  ",joint.name, "  limits(", joint.lower_limit,", ",joint.upper_limit,") range=",amplitude_range

        self.cur_position_spinbox.append(QDoubleSpinBox())
        self.cur_position_spinbox[index].setDecimals(5)
        self.cur_position_spinbox[index].setRange(joint.lower_limit, joint.upper_limit)
        self.cur_position_spinbox[index].setSingleStep((robot_joint.limit.upper-robot_joint.limit.lower)/50.0)
        self.cur_position_spinbox[index].valueChanged.connect(joint.on_position_value)

        self.amplitude_spinbox.append(QDoubleSpinBox())
        self.amplitude_spinbox[index].setDecimals(5)
        self.amplitude_spinbox[index].setRange(-amplitude_range, amplitude_range)
        self.amplitude_spinbox[index].setSingleStep(amplitude_range/50.0)
        self.amplitude_spinbox[index].valueChanged.connect(joint.on_amplitude_value)
        self.amplitude_spinbox[index].setValue(joint.amplitude)

        hbox.addWidget(QLabel(joint.name))
        hbox.addWidget(self.cur_position_spinbox[index])
        hbox.addWidget(self.amplitude_spinbox[index])

        # Add horizontal layout to frame for this joint group
        frame.setLayout(hbox)

        # Add frame to main vertical layout
        main_vbox.addWidget(frame)
    def __init__(self, context):
        super(TrapezoidalTrajectoryDialog, self).__init__(context)
        self.setObjectName("TrapezoidalTrajectoryDialog")
        # self.updateStateSignal = Signal(object)
        self.updateStateSignal.connect(self.on_updateState)

        self.robot = URDF.from_parameter_server()
        self.joint_list = {}

        for ndx, jnt in enumerate(self.robot.joints):
            self.joint_list[jnt.name] = ndx

        self.chain = []
        self.chain_file = rospy.get_param("~chain_file")
        self.chain_name = rospy.get_param("~chain_name")

        print
        print "Define order of splines used to approximate trapezoid"
        self.spline_order = rospy.get_param("~spline_order", 5)  # 1 # 3 # 5 # linear, cubic, quintic
        if (self.spline_order == 1) or (self.spline_order == 3) or (self.spline_order == 5):
            print "Spline order=", self.spline_order
        else:
            print "Invalid spline order!  Must be 1, 3, or 5"
            print "Spline order=", self.spline_order
            sys.exit(-1)

        yaml_file = self.chain_file + self.chain_name + "_chain.yaml"
        print "Chain definition file:"
        print yaml_file
        print

        # define structures
        self.robot_state = JointState()
        self.robot_command = JointTrajectory()

        stream = open(yaml_file, "r")
        jointChain = yaml.load_all(stream)

        for ndx, data in enumerate(jointChain):
            print ndx, " : ", data
            self.delay_time = data["delay_time"]
            self.ramp_up_time = data["ramp_up_time"]
            self.dwell_time = data["dwell_time"]
            self.ramp_down_time = data["ramp_down_time"]
            self.hold_time = data["hold_time"]
            self.ramp_start_fraction = data["ramp_start_fraction"]
            self.ramp_end_fraction = data["ramp_end_fraction"]

            self.joint_state_topic = data["joint_state_topic"]
            self.trajectory_topic = data["trajectory_topic"]

            if rospy.search_param(data["chain_param_name"]):
                print "Found ", data["chain_param_name"]
            else:
                print "Failed to find the ", data["chain_param_name"], " in the parameter server!"
                sys.exit(-1)

            joint_names = rospy.get_param(data["chain_param_name"])
            for joint in joint_names:
                print joint
                self.robot_state.name.append(joint)
                self.chain.append(JointData(self, joint))

        self.robot_command.joint_names = self.robot_state.name

        stream.close()

        self.robot_state.position = [0.0] * len(self.robot_state.name)
        self.robot_state.velocity = [0.0] * len(self.robot_state.name)
        self.robot_state.effort = [0.0] * len(self.robot_state.name)
        self.robot_joint_state = JointState()

        print "delay_time    =", self.delay_time
        print "ramp_up_time  =", self.ramp_up_time
        print "dwell_time    =", self.dwell_time
        print "ramp_down_time=", self.ramp_down_time
        print "hold_time     =", self.hold_time
        print "spline order  =", self.spline_order

        if (
            (self.ramp_start_fraction < 0.001)
            or (self.ramp_end_fraction > 0.999)
            or (self.ramp_start_fraction >= self.ramp_end_fraction)
        ):
            print "Invalid ramp fractions - abort!"
            print "0.0 < ", self.ramp_start_fraction, " < ", self.ramp_end_fraction, " < 1.0"
            return

        print "Robot State Structure", self.robot_state
        print "Robot Command Structure", self.robot_command

        # initialize structure to hold widget handles
        self.cmd_spinbox = []
        self.ramp_up_spinbox = []
        self.ramp_down_spinbox = []

        self._widget = QWidget()
        vbox = QVBoxLayout()

        # Push buttons
        hbox = QHBoxLayout()

        snap_command = QPushButton("Snap Position")
        snap_command.clicked.connect(self.snap_current_callback)
        hbox.addWidget(snap_command)

        check_limits = QPushButton("Check Limits Gains")
        check_limits.clicked.connect(self.check_limits_callback)
        hbox.addWidget(check_limits)

        apply_command = QPushButton("Send Trajectory")
        apply_command.clicked.connect(self.apply_command_callback)
        hbox.addWidget(apply_command)

        save_trajectory = QPushButton("Save Trajectory")
        save_trajectory.clicked.connect(self.save_trajectory_callback)
        hbox.addWidget(save_trajectory)

        zero_ramp = QPushButton("Zero Ramps")
        zero_ramp.clicked.connect(self.zero_ramp_callback)
        hbox.addWidget(zero_ramp)

        vbox.addLayout(hbox)

        time_hbox = QHBoxLayout()

        vbox_delay = QVBoxLayout()
        vbox_delay.addWidget(QLabel("Delay"))
        self.delay_time_spinbox = QDoubleSpinBox()
        self.delay_time_spinbox.setDecimals(5)
        self.delay_time_spinbox.setRange(0, 10.0)
        self.delay_time_spinbox.setSingleStep(0.1)
        self.delay_time_spinbox.valueChanged.connect(self.on_delay_time_value)
        self.delay_time_spinbox.setValue(self.delay_time)
        vbox_delay.addWidget(self.delay_time_spinbox)
        time_hbox.addLayout(vbox_delay)

        vbox_ramp_up = QVBoxLayout()
        vbox_ramp_up.addWidget(QLabel("Ramp Up"))
        self.ramp_up_time_spinbox = QDoubleSpinBox()
        self.ramp_up_time_spinbox.setDecimals(5)
        self.ramp_up_time_spinbox.setRange(0, 10.0)
        self.ramp_up_time_spinbox.setSingleStep(0.1)
        self.ramp_up_time_spinbox.valueChanged.connect(self.on_ramp_up_time_value)
        self.ramp_up_time_spinbox.setValue(self.ramp_up_time)
        vbox_ramp_up.addWidget(self.ramp_up_time_spinbox)
        time_hbox.addLayout(vbox_ramp_up)

        #
        vbox_dwell = QVBoxLayout()
        vbox_dwell.addWidget(QLabel("Dwell"))
        self.dwell_time_spinbox = QDoubleSpinBox()
        self.dwell_time_spinbox.setDecimals(5)
        self.dwell_time_spinbox.setRange(0, 10.0)
        self.dwell_time_spinbox.setSingleStep(0.1)
        self.dwell_time_spinbox.valueChanged.connect(self.on_dwell_time_value)
        self.dwell_time_spinbox.setValue(self.dwell_time)
        vbox_dwell.addWidget(self.dwell_time_spinbox)
        time_hbox.addLayout(vbox_dwell)

        vbox_ramp_down = QVBoxLayout()
        vbox_ramp_down.addWidget(QLabel("Down"))
        self.ramp_down_time_spinbox = QDoubleSpinBox()
        self.ramp_down_time_spinbox.setDecimals(5)
        self.ramp_down_time_spinbox.setRange(0, 10.0)
        self.ramp_down_time_spinbox.setSingleStep(0.1)
        self.ramp_down_time_spinbox.valueChanged.connect(self.on_ramp_down_time_value)
        self.ramp_down_time_spinbox.setValue(self.ramp_down_time)
        vbox_ramp_down.addWidget(self.ramp_down_time_spinbox)
        time_hbox.addLayout(vbox_ramp_down)

        vbox_hold = QVBoxLayout()
        vbox_hold.addWidget(QLabel("Hold"))
        self.hold_time_spinbox = QDoubleSpinBox()
        self.hold_time_spinbox.setDecimals(5)
        self.hold_time_spinbox.setRange(0, 10.0)
        self.hold_time_spinbox.setSingleStep(0.1)
        self.hold_time_spinbox.valueChanged.connect(self.on_hold_time_value)
        self.hold_time_spinbox.setValue(self.hold_time)
        vbox_hold.addWidget(self.hold_time_spinbox)
        time_hbox.addLayout(vbox_hold)

        vbox.addLayout(time_hbox)

        # Joints title
        title_frame = QFrame()
        title_frame.setFrameShape(QFrame.StyledPanel)
        title_frame.setFrameShadow(QFrame.Raised)

        title_hbox = QHBoxLayout()
        title_hbox.addWidget(QLabel("Joints"))
        title_hbox.addWidget(QLabel("Start"))
        title_hbox.addWidget(QLabel("Ramp Up"))
        title_hbox.addWidget(QLabel("Ramp Down"))
        title_frame.setLayout(title_hbox)
        vbox.addWidget(title_frame)

        # Define the widgets for each joint
        for i, joint in enumerate(self.chain):
            # print i,",",joint
            self.joint_widget(vbox, i)

        # add stretch at end so all GUI elements are at top of dialog
        vbox.addStretch(1)

        self._widget.setLayout(vbox)

        # Define the connections to the outside world
        self.jointSubscriber = rospy.Subscriber(self.joint_state_topic, JointState, self.stateCallbackFnc)
        self.commandPublisher = rospy.Publisher(self.trajectory_topic, JointTrajectory, queue_size=10)

        # Add the widget
        context.add_widget(self._widget)
 def createEditor(self, parent, option, index):
     editor = QDoubleSpinBox(parent)
     editor.setDecimals(self._decimals)
     editor.setMaximum(self._min)
     editor.setMaximum(self._max)
     return editor
class FootstepTerrainControlDialog(Plugin):

    def __init__(self, context):
        super(FootstepTerrainControlDialog, self).__init__(context)
        self.setObjectName('FootstepTerrainControlDialog')
       
        self.request_seq = 0 
        self._widget = QWidget()
        vbox = QVBoxLayout()
        
        self.cube_vbox = QVBoxLayout()
        
        self.cube_groupbox = QGroupBox( "Terrain Box" )

        self.cube_min_hbox = QHBoxLayout()

        self.cube_min_label = QLabel("Bottom Left")
        self.cube_min_hbox.addWidget( self.cube_min_label )

        self.cube_min_x = QDoubleSpinBox()
        self.cube_min_x.setSingleStep(.01)
        self.cube_min_x.setRange(-10.0, 10.0)
        self.cube_min_x.setValue(-3.0)
        self.cube_min_hbox.addWidget(self.cube_min_x)

        self.cube_min_y = QDoubleSpinBox()
        self.cube_min_y.setSingleStep(.01)
        self.cube_min_y.setRange(-10.0, 10.0)
        self.cube_min_y.setValue(-3.0)
        self.cube_min_hbox.addWidget(self.cube_min_y)

        self.cube_min_z = QDoubleSpinBox()
        self.cube_min_z.setSingleStep(.01)
        self.cube_min_z.setRange(-10.0, 10.0)
        self.cube_min_z.setValue(-1.0)
        self.cube_min_hbox.addWidget(self.cube_min_z)

        self.cube_vbox.addLayout( self.cube_min_hbox )
        
        self.cube_max_hbox = QHBoxLayout()

        self.cube_max_label = QLabel("Top Right")
        self.cube_max_hbox.addWidget( self.cube_max_label )

        self.cube_max_x = QDoubleSpinBox()
        self.cube_max_x.setSingleStep(.01)
        self.cube_max_x.setRange(-10.0, 10.0)
        self.cube_max_x.setValue(3.0)
        self.cube_max_hbox.addWidget(self.cube_max_x)

        self.cube_max_y = QDoubleSpinBox()
        self.cube_max_y.setSingleStep(.01)
        self.cube_max_y.setRange(-10.0, 10.0)
        self.cube_max_y.setValue(3.0)
        self.cube_max_hbox.addWidget(self.cube_max_y)

        self.cube_max_z = QDoubleSpinBox()
        self.cube_max_z.setSingleStep(.01)
        self.cube_max_z.setRange(-10.0, 10.0)
        self.cube_max_z.setValue(1.0)
        self.cube_max_hbox.addWidget(self.cube_max_z)

        self.cube_vbox.addLayout( self.cube_max_hbox )
    
        self.cube_groupbox.setLayout( self.cube_vbox )
        vbox.addWidget( self.cube_groupbox )

        self.type_hbox = QHBoxLayout()
  
#        self.type_label = QLabel( "Type" )
#        self.type_hbox.addWidget( self.type_label )
#
#        self.type_combobox = QComboBox()
#
#        self.type_combobox.addItem( "Type 1" )
#        self.type_combobox.addItem( "Type 2" )       
#        self.type_combobox.addItem( "Type 3" ) 
#        
#        self.type_hbox.addWidget( self.type_combobox )
#       
#        vbox.addLayout( self.type_hbox )

        self.scan_number_hbox = QHBoxLayout()
        
        self.scan_number_label = QLabel("Number of Scans Used")
        self.scan_number_hbox.addWidget( self.scan_number_label )

        self.scan_number_val = QDoubleSpinBox()
        self.scan_number_val.setDecimals(0)
        self.scan_number_val.setRange(0,10000)
        self.scan_number_val.setValue(2000)
        self.scan_number_hbox.addWidget(self.scan_number_val)

        vbox.addLayout( self.scan_number_hbox )

#       self.use_terrain_checkbox = QCheckBox( "Use Terrain" )
#       vbox.addWidget( self.use_terrain_checkbox )


        button_hbox = QHBoxLayout()
        
#        button_get = QPushButton("Get Current Values")
#        button_hbox.addWidget( button_get )

        button_submit = QPushButton("Send Values")
        button_hbox.addWidget( button_submit)

        vbox.addLayout( button_hbox )

        vbox.addStretch(1)

        self._widget.setLayout(vbox)

        context.add_widget(self._widget)

        # publishers and subscribers
        self.terrain_pub = rospy.Publisher("/flor/terrain_classifier/generate_terrain_model", TerrainModelRequest, queue_size=10 )
        button_submit.pressed.connect(self.sendParams)

#        self.terrain_sub = self.stateSubscriber   = rospy.Subscriber('/ros_footstep_planner/terrains', FootstepPlannerParams, self.getParamCallbackFcn)
#        button_submit.pressed.connect(self.getParams)


    def shutdown_plugin(self):
        print "Shutdown ..." 
        print "Done!"
    
#    def getParams( self ):
#        msg = FootstepPlannerParams()
#
#        
#        self.terrain_pub.publish( msg ) 

    def sendParams( self ):
        msg = TerrainModelRequest()

        msg.use_default_region_request = False

        rr = EnvironmentRegionRequest()

        rr.header.seq = self.request_seq
        self.request_seq = self.request_seq+1

        rr.header.stamp = rospy.get_rostime()
        rr.header.frame_id = "/world"

        rr.bounding_box_min.x = self.cube_min_x.value()
        rr.bounding_box_min.y = self.cube_min_y.value()
        rr.bounding_box_min.z = self.cube_min_z.value()
        
        rr.bounding_box_max.x = self.cube_max_x.value()
        rr.bounding_box_max.y = self.cube_max_y.value()
        rr.bounding_box_max.z = self.cube_max_z.value()

        rr.resolution = 0
        rr.request_augment = 0

        msg.region_req = rr

        msg.aggregation_size = self.scan_number_val.value() 

        print msg
        self.terrain_pub.publish( msg ) 


    def getParamCallbackFcn(self, msg):
        print "Not Defined Yet"  
class JointControlWidget(QObject):
    updateStateSignal = Signal(object)
    updateGhostSignal = Signal(object)

    def __init__(self, context):
        super(JointControlWidget, self).__init__()
        self.updateStateSignal.connect(self.on_update_state)
        self.updateGhostSignal.connect(self.on_update_ghost)

        self.joint_states = JointState()
        self.ghost_joint_states = JointState()
        self._widget = context
        vbox = QVBoxLayout()

        # Define checkboxes
        radios = QWidget()
        hbox_radio = QHBoxLayout()
        self.radioGroup = QButtonGroup()
        self.radioGroup.setExclusive(True)
        self.radio_ghost_target = QRadioButton()
        self.radio_ghost_target.setText("Ghost")
        self.radioGroup.addButton(self.radio_ghost_target, 0)
        self.radio_ghost_target.setChecked(True)
        self.radio_robot_target = QRadioButton()
        self.radio_robot_target.setText("Robot")
        self.radioGroup.addButton(self.radio_robot_target, 1)
        hbox_radio.addStretch()
        hbox_radio.addWidget(self.radio_ghost_target)
        hbox_radio.addStretch()
        hbox_radio.addWidget(self.radio_robot_target)
        hbox_radio.addStretch()
        radios.setLayout(hbox_radio)
        vbox.addWidget(radios)

        duration_box = QHBoxLayout()
        duration_box.setAlignment(Qt.AlignLeft)
        duration_box.addWidget(QLabel("Trajectory duration (s):"))
        self.traj_duration_spin = QDoubleSpinBox()
        self.traj_duration_spin.setValue(1.0)
        self.traj_duration_spin.valueChanged.connect(self.on_traj_duration_changed)
        duration_box.addWidget(self.traj_duration_spin)
        self.update_controllers_buttonn = QPushButton("Update Controllers")
        self.update_controllers_buttonn.pressed.connect(self.on_update_controllers)
        duration_box.addWidget(self.update_controllers_buttonn)
        vbox.addLayout(duration_box)

        widget = QWidget()
        hbox = QHBoxLayout()


        # Left to right layout
        self.joint_control = JointControl(self, hbox)

        widget.setLayout(hbox)

        vbox.addWidget(widget)

        print "Add buttons to apply all ..."
        all_widget = QWidget()
        all_box = QHBoxLayout()

        self.snap_to_ghost_button = QPushButton("SnapAllGhost")
        self.snap_to_ghost_button.pressed.connect(self.on_snap_ghost_pressed)
        all_box.addWidget(self.snap_to_ghost_button)
        self.snap_to_current_button = QPushButton("SnapAllCurrent")
        self.snap_to_current_button.pressed.connect(self.on_snap_current_pressed)
        all_box.addWidget(self.snap_to_current_button)
        self.apply_to_robot_button = QPushButton("ApplyAllRobot")
        self.apply_to_robot_button.pressed.connect(self.on_apply_robot_pressed)
        all_box.addWidget(self.apply_to_robot_button)
        self.apply_to_robot_button = QPushButton("Apply WBC Robot")
        self.apply_to_robot_button.pressed.connect(self.on_apply_wbc_robot_pressed)
        all_box.addWidget(self.apply_to_robot_button)

        all_widget.setLayout(all_box)
        vbox.addWidget(all_widget)

        override_box = QHBoxLayout()

        self.override = QCheckBox()
        self.override.setChecked(False)
        self.override.stateChanged.connect(self.on_override_changed)
        override_box.addWidget(self.override)

        override_label = QLabel("SAFETY OVERRIDE")
        override_label.setStyleSheet('QLabel { color: red }')

        override_box.addWidget(override_label)

        override_box.addStretch()

        vbox.addLayout(override_box)

        vbox.addStretch()

        self._widget.setLayout(vbox)

        self.first_time = True

        self.stateSubscriber = rospy.Subscriber('/joint_states', JointState, self.state_callback_fnc)
        self.ghostSubscriber = rospy.Subscriber('/flor/ghost/get_joint_states', JointState, self.ghost_callback_fnc)
        self.wbc_robot_pub = rospy.Publisher('/flor/wbc_controller/joint_states', JointState, queue_size=10)

        self.time_last_update_state = time.time()
        self.time_last_update_ghost = time.time()

    def shutdown_plugin(self):
        # Just make sure to stop timers and publishers, unsubscribe from Topics etc in the shutdown_plugin method.
        print "Shutdown ..."
        self.stateSubscriber.unregister()
        self.ghostSubscriber.unregister()
        self.wbc_robot_pub.unregister()
        print "Done!"

    def state_callback_fnc(self, atlas_state_msg):
        self.updateStateSignal.emit(atlas_state_msg)

    def command_callback_fnc(self, atlas_command_msg):
        self.updateCommandSignal.emit(atlas_command_msg)

    def ghost_callback_fnc(self, ghost_joint_state_msg):
        self.updateGhostSignal.emit(ghost_joint_state_msg)

    def on_update_controllers(self):
        self.joint_control.update_controllers()

    def on_override_changed(self):
        if self.override.isChecked():
            print "WARNING: TURNING OFF SAFETY"
            for controller in self.joint_control.controllers:
                controller.set_override(True)
        else:
            print "Enabling safety checks"
            for controller in self.joint_control.controllers:
                controller.set_override(False)

    def on_snap_ghost_pressed(self):
        print "Snap all joint values to current ghost joint positions"
        for controller in self.joint_control.controllers:
            controller.on_snap_ghost_pressed()

    def on_snap_current_pressed(self):
        print "Snap all joint values to current joint positions"
        for controller in self.joint_control.controllers:
            controller.on_snap_current_pressed()

    def on_apply_robot_pressed(self):
        print "Send all latest joint values directly to robot"
        for controller in self.joint_control.controllers:
            controller.on_apply_robot_pressed()

    def on_traj_duration_changed(self, duration):
        self.joint_control.update_traj_duration(duration)

    def on_apply_wbc_robot_pressed(self):
        print "Send all latest joint values directly to robot as a WBC command"
        if self.first_time:
            print "Uninitialized !"
        else:
            joint_state = JointState()
            joint_state.header.stamp = rospy.Time.now()
            joint_state.name = copy.deepcopy(self.joint_states.name)
            joint_state.position = list(copy.deepcopy(self.joint_states.position))
            joint_state.velocity = list(copy.deepcopy(self.joint_states.velocity))

            # print "Positions: ",joint_state.position
            for i, name in enumerate(joint_state.name):
                # for each joint, retrieve the data from each controller to populate the WBC vectors
                for controller in self.joint_control.controllers:
                    for joint in controller.joints:
                        if joint.name == self.joint_states.name[i]:
                            print "     fetching value for " + name + " = " + str(joint.position) + "  at ndx=" + str(i)
                            joint_state.position[i] = joint.position
                            joint_state.velocity[i] = joint.velocity
                            # joint_state.effort[i]   = joint.effort

            self.wbc_robot_pub.publish(joint_state)

    def on_update_state(self, atlas_state_msg):
        if time.time() - self.time_last_update_state >= 0.2:
            self.joint_states = atlas_state_msg
            self.joint_control.update_joint_positions(self.joint_states)
            if self.first_time:
                self.joint_control.reset_current_joint_sliders()
                self.first_time = False
                self.ghost_joint_states = copy.deepcopy(atlas_state_msg)
                self.ghost_joint_states.position = list(self.ghost_joint_states.position)
            self.time_last_update_state = time.time()

    # this runs in the Qt GUI thread and can therefore update the GUI
    def on_update_ghost(self, ghost_joint_state_msg):
        if time.time() - self.time_last_update_ghost >= 0.2:
            for i, new_joint_name in enumerate(list(ghost_joint_state_msg.name)):
                name_found = False
                for j, stored_joint_name in enumerate(self.ghost_joint_states.name):
                    if new_joint_name == stored_joint_name:
                        name_found = True
                        self.ghost_joint_states.position[j] = ghost_joint_state_msg.position[i]
                if not name_found:
                    # Update the list without regard to order
                    self.ghost_joint_states.name.append(new_joint_name)
                    self.ghost_joint_states.position.append(ghost_joint_state_msg.position[i])
            self.time_last_update_ghost = time.time()
    def __init__(self, context):
        super(SensorParamControlDialog, self).__init__(context)
        self.setObjectName('SensorParamControlDialog')
        
        self._widget = QWidget()
        vbox = QVBoxLayout()


        ### Multisense ###

        ms_groupbox = QGroupBox( "Multisense" )
        
        ms_vbox = QVBoxLayout()
        
        ms_gain_hbox = QHBoxLayout()
       
        self.ms_gain_label = QLabel("Image Gain [1, 1, 8]")
        ms_gain_hbox.addWidget( self.ms_gain_label )
        
        self.ms_gain = QDoubleSpinBox()
        self.ms_gain.setSingleStep(.01)
        self.ms_gain.setRange(1,8)

        ms_gain_hbox.addWidget( self.ms_gain ) 
    
        ms_vbox.addLayout( ms_gain_hbox )       

        
        ms_exp_hbox = QHBoxLayout()
        
        self.ms_exp_auto = QCheckBox("Image Exposure [.03, 0.5]")
        ms_exp_hbox.addWidget( self.ms_exp_auto )
        
        self.ms_exp = QDoubleSpinBox()
        self.ms_exp.setSingleStep( .001 )
        self.ms_exp.setRange( .025,.5 ) 

        ms_exp_hbox.addWidget( self.ms_exp ) 
    
        ms_vbox.addLayout( ms_exp_hbox )       
        
        ms_spindle_hbox = QHBoxLayout()
        
        ms_spindle_label = QLabel("Spindle Speed [0, 5.2]")        
        
        ms_spindle_hbox.addWidget( ms_spindle_label )

        self.ms_spindle = QDoubleSpinBox()
        self.ms_spindle.setSingleStep(.01)
        self.ms_spindle.setRange( 0,15.2 )

        ms_spindle_hbox.addWidget( self.ms_spindle ) 
    
        ms_vbox.addLayout( ms_spindle_hbox )       

        ms_light_hbox = QHBoxLayout()
        
        ms_light_label = QLabel("Light Brightness")
        ms_light_hbox.addWidget(ms_light_label)

        self.ms_light = QSlider(Qt.Horizontal)
        self.ms_light.setRange(0,100)
    
        ms_light_hbox.addWidget( self.ms_light )

        ms_vbox.addLayout( ms_light_hbox )

        ms_button_hbox = QHBoxLayout()

        ms_button_hbox.addStretch(1)        

        ms_button_get = QPushButton("Get Settings")
        ms_button_get.pressed.connect(self.ms_get_callback)
        #ms_button_hbox.addWidget( ms_button_get )
        
        ms_button_set = QPushButton("Set Settings")
        ms_button_set.pressed.connect(self.ms_set_callback)
        ms_button_hbox.addWidget( ms_button_set )

        ms_vbox.addLayout( ms_button_hbox ) 

        ms_groupbox.setLayout( ms_vbox )

        vbox.addWidget( ms_groupbox )


        ### Left SA ###

        sa_left_groupbox = QGroupBox( "Left SA Camera" )
        
        sa_left_vbox = QVBoxLayout()
        
        sa_left_gain_hbox = QHBoxLayout()
        
        sa_left_gain_label = QLabel("Image Gain [0, 0, 25]")        
        
        sa_left_gain_hbox.addWidget( sa_left_gain_label )

        self.sa_left_gain = QDoubleSpinBox()
        self.sa_left_gain.setSingleStep(.01)
        self.sa_left_gain.setRange( 0, 25 )

        sa_left_gain_hbox.addWidget( self.sa_left_gain ) 
    
        sa_left_vbox.addLayout( sa_left_gain_hbox )       

        
        sa_left_exp_hbox = QHBoxLayout()
        
        sa_left_exp_label = QLabel("Image Shutter [0, 5.5, 25]")        
        
        sa_left_exp_hbox.addWidget( sa_left_exp_label )

        self.sa_left_exp = QDoubleSpinBox()
        self.sa_left_exp.setSingleStep(.01)
        self.sa_left_exp.setRange( 0, 25 )

        sa_left_exp_hbox.addWidget( self.sa_left_exp ) 
    
        sa_left_vbox.addLayout( sa_left_exp_hbox )       
        
        sa_left_button_hbox = QHBoxLayout()

        sa_left_button_hbox.addStretch(1)        

        sa_left_button_get = QPushButton("Get Settings")
        sa_left_button_get.pressed.connect(self.sa_left_get_callback)
        #sa_left_button_hbox.addWidget( sa_left_button_get )
        
        sa_left_button_set = QPushButton("Set Settings")
        sa_left_button_set.pressed.connect(self.sa_left_set_callback)
        sa_left_button_hbox.addWidget( sa_left_button_set )

        sa_left_vbox.addLayout( sa_left_button_hbox ) 

        sa_left_groupbox.setLayout( sa_left_vbox )

        vbox.addWidget(sa_left_groupbox)
        
        ### Right SA ###

        sa_right_groupbox = QGroupBox( "Right SA Camera" )
        
        sa_right_vbox = QVBoxLayout()
        
        sa_right_gain_hbox = QHBoxLayout()
        
        sa_right_gain_label = QLabel("Image Gain [0, 0, 25]")        
        
        sa_right_gain_hbox.addWidget( sa_right_gain_label )

        self.sa_right_gain = QDoubleSpinBox()
        self.sa_right_gain.setSingleStep(.01)
        self.sa_right_gain.setRange( 0, 25 )

        sa_right_gain_hbox.addWidget( self.sa_right_gain ) 
    
        sa_right_vbox.addLayout( sa_right_gain_hbox )       

        
        sa_right_exp_hbox = QHBoxLayout()
        
        sa_right_exp_label = QLabel("Image Shutter [0, 5.5, 25]")        
        
        sa_right_exp_hbox.addWidget( sa_right_exp_label )

        self.sa_right_exp = QDoubleSpinBox()
        self.sa_right_exp.setSingleStep(.01)
        self.sa_right_exp.setRange( 0, 25 )    
    
        sa_right_exp_hbox.addWidget( self.sa_right_exp ) 
    
        sa_right_vbox.addLayout( sa_right_exp_hbox )       
        
        sa_right_button_hbox = QHBoxLayout()

        sa_right_button_hbox.addStretch(1)        

        sa_right_button_get = QPushButton("Get Settings")
        sa_right_button_get.pressed.connect(self.sa_right_get_callback)
        #sa_right_button_hbox.addWidget( sa_right_button_get )
        
        sa_right_button_set = QPushButton("Set Settings")
        sa_right_button_set.pressed.connect(self.sa_right_set_callback)
        sa_right_button_hbox.addWidget( sa_right_button_set )

        sa_right_vbox.addLayout( sa_right_button_hbox ) 

        sa_right_groupbox.setLayout( sa_right_vbox )

        vbox.addWidget(sa_right_groupbox)

        vbox.addStretch(1)

        self._widget.setLayout(vbox)

        context.add_widget(self._widget)

        # publishers and subscribers

        self.ms_cam_pub = rospy.Publisher( '/multisense_sl/set_cam_parameters', Float64MultiArray, queue_size=10)
        self.ms_light_pub = rospy.Publisher( '/multisense_sl/set_light_brightness', Float64, queue_size=10)
        self.ms_spindle_pub = rospy.Publisher( '/multisense_sl/set_spindle_speed', Float64, queue_size=10)

        self.sa_left_cam_pub = rospy.Publisher( '/sa/left/set_cam_parameters', Float64MultiArray, queue_size=10)
        self.sa_right_cam_pub = rospy.Publisher( '/sa/right/set_cam_parameters', Float64MultiArray, queue_size=10)