Changeset 760 for moxy/trunk


Ignore:
Timestamp:
Jan 7, 2012 2:50:40 PM (13 years ago)
Author:
jemian
Message:

subpanels refactored into callable classes

File:
1 edited

Legend:

Unmodified
Added
Removed
  • TabularUnified moxy/trunk/src/moxy/vc_axis.py

    r757 r760  
    7070            "Do you really want to close this application?",
    7171            "Confirm Exit", wx.OK|wx.CANCEL|wx.ICON_QUESTION)
    72         result = wx.ID_OK               # development
     72        #result = wx.ID_OK               # development
    7373        result = dlg.ShowModal()        # production
    7474        dlg.Destroy()
     
    9898
    9999        wx.Panel.__init__(self, id=wx.ID_ANY, parent=parent)
    100         self.SetToolTipString( self.axis.val.pvname )
     100        self.SetToolTipString( u'SingleAxisPanel' )
    101101
    102102        # tabbed notebook: Operate | Setup
    103103        self.notebook = wx.Notebook( self )
    104104       
    105         # TODO: make each page (panel) a separate class so each can be called from vc_set.py
    106         operate_panel = self._Make_Operate_Panel( self.notebook )
    107         setup_panel = self._Make_Setup_Panel( self.notebook )
    108 
    109         self.notebook.AddPage( operate_panel, "operate" )
    110         self.notebook.AddPage( setup_panel, "setup" )
     105        self.operate_panel = SingleAxisOperatePanel(self.notebook,
     106                                axis, show_buttons,
     107                                handler = None)
     108        self.setup_panel = SingleAxisSetupPanel(self.notebook,
     109                                axis, show_buttons,
     110                                operate_panel = self.operate_panel,
     111                                handler = None)
     112
     113        self.notebook.AddPage( self.operate_panel, "operate" )
     114        self.notebook.AddPage( self.setup_panel, "setup" )
    111115       
    112116        sizer = wx.BoxSizer( orient=wx.VERTICAL )
     
    114118        self.SetSizer( sizer )
    115119
    116         self.connect()
    117         self._Set_All_PvWidgetBackgroundColours()
    118    
    119     def _Make_Operate_Panel(self, parent):
    120         ''' Create (and return) a panel to operate this axis '''
    121         # TODO: make into separate class so can be callable from vc_set.py
    122         panel = wx.Panel( parent )
    123 
     120        self.operate_panel.connect()
     121        self.setup_panel._Set_All_PvWidgetBackgroundColours()
     122
     123
     124class SingleAxisOperatePanel(wx.Panel):
     125    '''
     126    Show the widgets to operate a single motion axis in a panel.
     127   
     128    Some buttons (Connect, Disconnect, ...) are optional
     129    and can be suppressed by adding  ``show_buttons = False``
     130   
     131    :param obj parent: panel or frame that contains this panel
     132    :param axis: set of PVs that describe the operation of this axis
     133    :type axis: m_axis.SingleAxis object
     134    :param bool show_buttons: suppress display of certain buttons
     135    :param obj handler: callback routine for TextCtrl widget accept events
     136    '''
     137   
     138    def __init__(self, parent, axis, show_buttons = True, handler = None):
     139        self.parent = parent
     140        self.axis = axis
     141        self.show_buttons = show_buttons
     142        self.handler = {True: handler,
     143                        False: self.callback}[handler is not None]
     144        self.connected = False
     145
     146        wx.Panel.__init__(self, id=wx.ID_ANY, parent=parent)
     147        self.SetToolTipString( self.axis.val.pvname )
     148        self._init_panel(self)
     149   
     150    def _init_panel(self, panel):
    124151        description_sizer = self._Make_Description_Sizer( panel )
    125152        position_sizer = self._Make_Positions_Sizer( panel )
    126         self.stopButton = self._Make_My_Button(panel,
    127               label=u'Stop', name='stopButton',
    128               tip=u'Stop this axis from moving',
    129               binding=self.doStopButton)
     153        self.stopButton = Make_My_Button(panel,
     154                                         label=u'Stop',
     155                                         name='stopButton',
     156                                         tip=u'Stop this axis from moving',
     157                                         binding=self.doStopButton)
    130158        self.stopButton.SetBackgroundColour('red')
    131159        self.stopButton.SetForegroundColour('yellow')
     
    138166        sizer.AddSizer(position_sizer, 1, flag=wx.EXPAND)
    139167        sizer.AddSizer(self.stopButton, 0, flag=wx.EXPAND)
    140 
    141         return panel
    142168
    143169    def _Make_Description_Sizer(self, parent):
     
    178204        self.w_readback.SetToolTipString(u'readback value')
    179205   
    180         self.w_target = self._Make_My_TextEntry(parent, '',
    181                 False, u'target value', self.doConfigureHandler)
     206        self.w_target = Make_My_TextEntry(parent, '',
     207                False, u'target value', self.doTargetEntry)
    182208       
    183209        self.w_status = wx.lib.stattext.GenStaticText(parent, wx.ID_ANY, 'status')
     
    199225        return sbs
    200226   
    201     def _Make_Setup_Panel(self, parent):
    202         ''' Create (and return) a panel to configure this axis '''
    203         # TODO: make into separate class so can be callable from vc_set.py
    204         panel = wx.Panel( parent )
    205 
     227    def doTargetEntry(self, event):
     228        ''' [Enter] was pressed in target field, need to tell axis to move '''
     229        obj = event.EventObject
     230        if obj == self.w_target:
     231            if self.axis.val.channel.connected:
     232                s = obj.GetValue()
     233                try:
     234                    value = float( s )
     235                    self.axis.val.channel.put( value )
     236                except ValueError:
     237                    msg = '''"%s" is not a floating point number!''' % s
     238                    AcknowledgeDialog( msg )
     239
     240    def doStopButton(self, event):
     241        '''stop this axis from moving'''
     242        self.axis.stopMotion()
     243
     244    def SetStatus(self, value):
     245        '''describe what's what'''
     246        #wx.CallAfter(self.w_status.SetLabel, value)
     247        print "status: ", value
     248
     249    def connect(self):
     250        '''connect the PVs of this axis with EPICS'''
     251        if not self.connected:
     252            self.axis.connect(ext_handler=self.callback)
     253            self.SetStatus( 'connected' )
     254            #self.w_name.SetEditable(False)
     255            self.w_target.SetEditable(True)
     256            self.connected = True
     257            self.gst_name.SetLabel( self.axis.name )
     258            self.SetToolTipString( self.axis.val.pvname )
     259
     260    def disconnect(self):
     261        '''connect the PVs of this axis from EPICS'''
     262        if self.connected:
     263            self.axis.disconnect()
     264            self.SetStatus( 'disconnected' )
     265            #self.w_name.SetEditable(True)
     266            self.w_target.SetEditable(False)
     267            self.connected = False
     268
     269    def SetVAL(self, value):
     270        '''put the value into the widget text'''
     271        wx.CallAfter(self.w_target.SetValue, str(value) )
     272
     273    def SetRBV(self, value):
     274        '''put the value into the widget text'''
     275        wx.CallAfter(self.w_readback.SetLabel, str(value) )
     276
     277    def SetEGU(self, value):
     278        '''put the value into the widget text'''
     279        wx.CallAfter(self.w_egu.SetLabel, value)
     280
     281    def SetDESC(self, value):
     282        '''put the value into the widget text'''
     283        wx.CallAfter(self.w_desc.SetLabel, value)
     284
     285    def SetDMOV(self, value):
     286        '''set the widget background color based on moving state'''
     287        moving = not value
     288        color = {False: COLOR_NOT_MOVING, True: COLOR_MOVING}[moving]
     289        wx.CallAfter(self.w_target.SetBackgroundColour, color )
     290        wx.CallAfter(self.w_readback.SetBackgroundColour, color )
     291
     292    def SetSTOP(self, value):
     293        '''a no-op'''
     294        pass
     295   
     296    def callback(self, **kw):
     297        "PyEPICS CA monitor callback"
     298        #print kw
     299        if 'conn' in kw.keys():
     300            print "connection event", kw
     301            choices = {True: 'connected event received',
     302                       False: 'disconnect received, IOC is unavailable?'}
     303            self.SetStatus( choices[ kw['conn'] ] )
     304        else:
     305            #print "PV update event"
     306            if 'field' in kw:
     307                #print kw['field']
     308                {'VAL': self.SetVAL,
     309                 'RBV': self.SetRBV,
     310                 'EGU': self.SetEGU,
     311                 'DESC': self.SetDESC,
     312                 'DMOV': self.SetDMOV,
     313                 'STOP': self.SetSTOP,
     314                }[ kw['field'] ]( kw['value'] )
     315
     316
     317class SingleAxisSetupPanel(wx.Panel):
     318    '''
     319    Show the widgets to setup and configure a single motion axis in a panel.
     320   
     321    Some buttons (Connect, Disconnect, ...) are optional
     322    and can be suppressed by adding  ``show_buttons = False``
     323   
     324    :param obj parent: panel or frame that contains this panel
     325    :param axis: set of PVs that describe the operation of this axis
     326    :type axis: m_axis.SingleAxis object
     327    :param bool show_buttons: suppress display of certain buttons
     328    :param obj handler: callback routine for TextCtrl widget accept events
     329    :param obj operate_panel: SingleAxisOperatePanel object to use with this class
     330    '''
     331   
     332    def __init__(self, parent, axis, show_buttons = True, handler = None, operate_panel = None):
     333        self.parent = parent
     334        self.axis = axis
     335        self.show_buttons = show_buttons
     336        self.handler = handler
     337        self.operate_panel = operate_panel
     338
     339        wx.Panel.__init__(self, id=wx.ID_ANY, parent=parent)
     340        self.SetToolTipString( self.axis.val.pvname )
     341        self._init_panel(self)
     342   
     343    def _init_panel(self, panel):
    206344        if self.show_buttons:
    207345            connect_buttons = self._Make_Connect_Buttons_Sizer( panel )
     
    216354            sizer.AddSizer(accept_buttons, 0, flag=wx.EXPAND)
    217355            sizer.AddSizer(connect_buttons, 0, flag=wx.EXPAND)
    218        
    219         return panel
    220356
    221357    def _Make_Configure_Sizer(self, parent):
     
    232368        label_STOP = wx.lib.stattext.GenStaticText(parent, wx.ID_ANY, 'STOP')
    233369       
    234         self.w_name = self._Make_My_TextEntry(parent, self.axis.name,
     370        self.w_name = Make_My_TextEntry(parent, self.axis.name,
    235371                        True, u'local name for this axis',
    236372                        self.doConfigureHandler)
     
    241377        self.w_isMotor.Bind(wx.EVT_CHECKBOX, self.doCheckboxClick)
    242378       
    243         self.w_VAL_pv = self._Make_My_TextEntry(parent, self.axis.val.pvname,
     379        self.w_VAL_pv = Make_My_TextEntry(parent, self.axis.val.pvname,
    244380                            True, u'EPICS PV name for target position',
    245381                            self.doConfigureHandler)
    246         self.w_RBV_pv = self._Make_My_TextEntry(parent, self.axis.rbv.pvname,
     382        self.w_RBV_pv = Make_My_TextEntry(parent, self.axis.rbv.pvname,
    247383                            True, u'EPICS PV name for readback position',
    248384                            self.doConfigureHandler)
    249         self.w_EGU_pv = self._Make_My_TextEntry(parent, self.axis.egu.pvname,
     385        self.w_EGU_pv = Make_My_TextEntry(parent, self.axis.egu.pvname,
    250386                            True, u'EPICS PV name for engineering units',
    251387                            self.doConfigureHandler)
    252         self.w_DESC_pv = self._Make_My_TextEntry(parent, self.axis.desc.pvname,
     388        self.w_DESC_pv = Make_My_TextEntry(parent, self.axis.desc.pvname,
    253389                            True, u'EPICS PV name for description',
    254390                            self.doConfigureHandler)
    255         self.w_DMOV_pv = self._Make_My_TextEntry(parent, self.axis.dmov.pvname,
     391        self.w_DMOV_pv = Make_My_TextEntry(parent, self.axis.dmov.pvname,
    256392                            True, u'EPICS PV name for motion is done',
    257393                            self.doConfigureHandler)
    258         self.w_STOP_pv = self._Make_My_TextEntry(parent, self.axis.stop.pvname,
     394        self.w_STOP_pv = Make_My_TextEntry(parent, self.axis.stop.pvname,
    259395                            True, u'EPICS PV name for STOP moving command',
    260396                            self.doConfigureHandler)
     
    267403        sbs.Add(fgs, 0, wx.EXPAND|wx.ALIGN_CENTRE|wx.ALL, 5)
    268404
    269         fgs.Add(label_name, flag=wx.EXPAND|wx.GROW)
    270         fgs.Add(self.w_name, flag=wx.EXPAND|wx.GROW)
    271         fgs.Add(label_VAL, flag=wx.EXPAND|wx.GROW)
    272         fgs.Add(self.w_VAL_pv, flag=wx.EXPAND|wx.GROW)
    273         fgs.Add(label_isMotor, flag=wx.EXPAND|wx.GROW)
    274         fgs.Add(self.w_isMotor, flag=wx.EXPAND|wx.GROW)
    275         fgs.Add(label_RBV, flag=wx.EXPAND|wx.GROW)
    276         fgs.Add(self.w_RBV_pv, flag=wx.EXPAND|wx.GROW)
    277         fgs.Add(label_EGU, flag=wx.EXPAND|wx.GROW)
    278         fgs.Add(self.w_EGU_pv, flag=wx.EXPAND|wx.GROW)
    279         fgs.Add(label_DESC, flag=wx.EXPAND|wx.GROW)
    280         fgs.Add(self.w_DESC_pv, flag=wx.EXPAND|wx.GROW)
    281         fgs.Add(label_DMOV, flag=wx.EXPAND|wx.GROW)
    282         fgs.Add(self.w_DMOV_pv, flag=wx.EXPAND|wx.GROW)
    283         fgs.Add(label_STOP, flag=wx.EXPAND|wx.GROW)
    284         fgs.Add(self.w_STOP_pv, flag=wx.EXPAND|wx.GROW)
     405        for obj in (label_name,     self.w_name,
     406                    label_VAL,      self.w_VAL_pv,
     407                    label_isMotor,  self.w_isMotor,
     408                    label_RBV,      self.w_RBV_pv,
     409                    label_EGU,      self.w_EGU_pv,
     410                    label_DESC,     self.w_DESC_pv,
     411                    label_DMOV,     self.w_DMOV_pv,
     412                    label_STOP,     self.w_STOP_pv
     413                    ):
     414            fgs.Add(obj, flag=wx.EXPAND|wx.GROW)
    285415
    286416        return sbs
    287417   
    288     def _Make_My_TextEntry(self, parent, value='',
    289                            editable=True, tooltip='', handler=None):
    290         '''
    291         Create and return a TextCtrl object
    292        
    293         :param obj parent: widget panel that will contain this TextCtrl
    294         :param str|float value: initial value to set in the entry field
    295         :param bool editable: should the entry field be editable?
    296         :param str tooltip: short description of this field
    297         :param obj handler: None or method to be called when [Enter] is pressed in this field
    298         :return: wx.TextCtrl object
    299         '''
    300         # TODO: decide the best trigger for _Make_My_TextEntry
    301         # see doConfigureHandler() for choices
    302         style = wx.TE_PROCESS_ENTER
    303         entry = wx.TextCtrl(parent=parent, id=wx.ID_ANY, style=style)
    304         entry.SetEditable( editable )
    305         if value is not None:
    306             entry.SetValue( str(value) )
    307         entry.SetToolTipString( tooltip )
    308         if handler is not None:
    309             entry.Bind(wx.EVT_TEXT_ENTER, handler)
    310         return entry
    311    
    312418    def _Make_Connect_Buttons_Sizer(self, parent):
    313419        '''
    314420        build a buttons row and place it in a sizer, return the sizer
    315421        '''
    316         self.connectButton = self._Make_My_Button(parent,
     422        self.connectButton = Make_My_Button(parent,
    317423              label=u'Connect', name='connectButton',
    318424              tip=u'Connect this axis with EPICS',
    319425              binding=self.doConnnectButton)
    320         self.disconnectButton = self._Make_My_Button(parent,
     426        self.disconnectButton = Make_My_Button(parent,
    321427              label=u'Disconnect', name='disconnectButton',
    322428              tip=u'Disconnect this axis from EPICS',
     
    332438        build a buttons row and place it in a sizer, return the sizer
    333439        '''
    334         self.acceptButton = self._Make_My_Button(parent,
     440        self.acceptButton = Make_My_Button(parent,
    335441              label=u'Accept', name='acceptButton',
    336442              tip=u'Accept the new PV names',
    337443              binding=self.doAcceptButton)
    338         self.revertButton = self._Make_My_Button(parent,
     444        self.revertButton = Make_My_Button(parent,
    339445              label=u'Revert', name='revertButton',
    340446              tip=u'Revert to previous PV names',
     
    345451        sizer.AddWindow(self.revertButton, proportion=1, flag=wx.EXPAND)
    346452        return sizer
    347        
    348     def _Make_My_Button(self, parent, label, name, tip, binding):
    349         '''
    350         Create a button and bind it to a method.
    351         Return the button object
    352         '''
    353         button = wx.lib.buttons.GenButton(id=wx.ID_ANY,
    354               label=label, name=name, parent=parent,)
    355         button.SetToolTipString(tip)
    356         button.Bind(wx.EVT_BUTTON, binding)
    357         return button
    358 
    359     def doStopButton(self, event):
    360         '''stop this axis from moving'''
    361         self.axis.stopMotion()
    362453
    363454    def doConnnectButton(self, event):
    364455        '''user clicked button to connect'''
    365         self.connect()
     456        self.operate_panel.connect()
    366457
    367458    def doDisconnnectButton(self, event):
    368459        '''user clicked button to disconnect'''
    369         self.disconnect()
     460        self.operate_panel.disconnect()
     461   
     462    def doAcceptButton(self, event):
     463        '''user clicked button to accept PV names'''
     464        # filter out any cases why we should not accept
     465        result = self._Verify_Fields_Before_Accepting()
     466        if result is not None:
     467            AcknowledgeDialog( result )
     468            return
     469
     470        self.operate_panel.disconnect()
     471        self.axis.name = self.w_name.GetValue()
     472        self.axis.val.pvname = self.w_VAL_pv.GetValue()
     473        self.axis.rbv.pvname = self.w_RBV_pv.GetValue()
     474        self.axis.egu.pvname = self.w_EGU_pv.GetValue()
     475        self.axis.desc.pvname = self.w_DESC_pv.GetValue()
     476        self.axis.dmov.pvname = self.w_DMOV_pv.GetValue()
     477        self.axis.stop.pvname = self.w_STOP_pv.GetValue()
     478        self.axis.isMotorRecord = self.w_isMotor.GetValue()
     479        self.operate_panel.connect()
     480        self.operate_panel.SetVAL( self.axis.val.channel.get(as_string = True) )
     481
     482    def doRevertButton(self, event):
     483        '''user clicked button to revert to existing PV name'''
     484        self.w_VAL_pv.SetValue( self.axis.val.pvname )
     485        self.w_RBV_pv.SetValue( self.axis.rbv.pvname )
     486        self.w_EGU_pv.SetValue( self.axis.egu.pvname )
     487        self.w_DESC_pv.SetValue( self.axis.desc.pvname )
     488        self.w_DMOV_pv.SetValue( self.axis.dmov.pvname )
     489        self.w_STOP_pv.SetValue( self.axis.stop.pvname )
     490        self.w_isMotor.SetValue( self.axis.isMotorRecord )
     491        self.w_name.SetValue( self.operate_panel.gst_name.GetLabel() )
     492        self._Set_All_PvWidgetBackgroundColours()
     493   
     494    def doCheckboxClick(self, event):
     495        ''' isMotorRecord checkbox was clicked '''
     496        #print "isMotorRecord checkbox was clicked"
     497        pass
     498
     499    def doConfigureHandler(self, event):
     500        '''
     501        User pressed [Enter] key in a configure panel TextCtrl.
     502        Indicate with background color if the given PV name can be connected.
     503       
     504        Consider the best binding/trigger to call ``doConfigureHandler()``.
     505        For now, we use choice #1.
     506        Choices include:
     507       
     508        # [Enter] key (as used in ``wxmtxy`` tool)
     509        # wx.EVT_TEXT events after a time delay
     510        '''
     511        obj = event.EventObject
     512        if obj in (self.w_VAL_pv,
     513                                 self.w_RBV_pv,
     514                                 self.w_EGU_pv,
     515                                 self.w_DESC_pv,
     516                                 self.w_DMOV_pv,
     517                                 self.w_STOP_pv):
     518            self._SetPvWidgetBackgroundColour( obj )
     519
     520    def _SetPvWidgetBackgroundColour(self, obj):
     521        '''
     522        Decide the background color of a PV widget
     523        based on whether or not a PV connection can be
     524        established from this client
     525        and set the background color of that widget.
     526        IF a connection attempt with a given PV name string
     527        does not succeed within ``CONNECT_TEST_TIMEOUT`` seconds,
     528        the PV will be considered as unavailable.
     529       
     530        =======================  =========================================
     531        color                    meaning
     532        =======================  =========================================
     533        COLOR_PV_NOT_DEFINED     no string given for PV name
     534        COLOR_PV_OK              string given can be connected as a PV
     535        COLOR_PV_NOT_OK          PV connection timed out for string given
     536        =======================  =========================================
     537        '''
     538        pv = obj.GetValue().strip()
     539        if len(pv) == 0:
     540            color = COLOR_PV_NOT_DEFINED
     541        else:
     542            valid = m_pv.pvIsAvailable( pv, CONNECT_TEST_TIMEOUT )
     543            color = {True: COLOR_PV_OK, False: COLOR_PV_NOT_OK}[valid]
     544        wx.CallAfter( obj.SetBackgroundColour, color )
     545
     546    def _Set_All_PvWidgetBackgroundColours(self):
     547        '''
     548        Walk through all the PV name widgets and check if they
     549        can be connected.  Set the background color of each widget
     550        accordingly.
     551        '''
     552        for obj in (self.w_VAL_pv,
     553                   self.w_RBV_pv,
     554                   self.w_EGU_pv,
     555                   self.w_DESC_pv,
     556                   self.w_DMOV_pv,
     557                   self.w_STOP_pv):
     558            self._SetPvWidgetBackgroundColour( obj )
    370559
    371560    def _Verify_Fields_Before_Accepting(self):
     
    412601                    return '''cannot connect to PV "%s"''' % val_pv
    413602        return None
    414    
    415     def doAcceptButton(self, event):
    416         '''user clicked button to accept PV names'''
    417         # filter out any cases why we should not accept
    418         result = self._Verify_Fields_Before_Accepting()
    419         if result is not None:
    420             self.AcknowledgeDialog( result )
    421             return
    422 
    423         self.disconnect()
    424         self.axis.val.pvname = self.w_VAL_pv.GetValue()
    425         self.axis.rbv.pvname = self.w_RBV_pv.GetValue()
    426         self.axis.egu.pvname = self.w_EGU_pv.GetValue()
    427         self.axis.desc.pvname = self.w_DESC_pv.GetValue()
    428         self.axis.dmov.pvname = self.w_DMOV_pv.GetValue()
    429         self.axis.stop.pvname = self.w_STOP_pv.GetValue()
    430         self.axis.isMotorRecord = self.w_isMotor.GetValue()
    431         self.connect()
    432         self.SetVAL( self.axis.val.channel.get(as_string = True) )
    433         self.gst_name.SetLabel( self.w_name.GetValue() )
    434 
    435     def doRevertButton(self, event):
    436         '''user clicked button to revert to existing PV name'''
    437         self.w_VAL_pv.SetValue( self.axis.val.pvname )
    438         self.w_RBV_pv.SetValue( self.axis.rbv.pvname )
    439         self.w_EGU_pv.SetValue( self.axis.egu.pvname )
    440         self.w_DESC_pv.SetValue( self.axis.desc.pvname )
    441         self.w_DMOV_pv.SetValue( self.axis.dmov.pvname )
    442         self.w_STOP_pv.SetValue( self.axis.stop.pvname )
    443         self.w_isMotor.SetValue( self.axis.isMotorRecord )
    444         self.w_name.SetValue( self.gst_name.GetLabel() )
    445         self._Set_All_PvWidgetBackgroundColours()
    446    
    447     def doCheckboxClick(self, event):
    448         ''' isMotorRecord checkbox was clicked '''
    449         #print "isMotorRecord checkbox was clicked"
    450         pass
    451 
    452     def doConfigureHandler(self, event):
    453         '''user pressed [Enter] key in a configure panel TextCtrl'''
    454         # TODO: decide the best trigger for doConfigureHandler
    455         # choices are [Enter] key or wx.EVT_TEXT events after a time delay
    456         # [Enter] was used in wxmtxy tool
    457         obj = event.EventObject
    458         if obj in (self.w_VAL_pv,
    459                                  self.w_RBV_pv,
    460                                  self.w_EGU_pv,
    461                                  self.w_DESC_pv,
    462                                  self.w_DMOV_pv,
    463                                  self.w_STOP_pv):
    464             self._SetPvWidgetBackgroundColour( obj )
    465         else:
    466             if obj == self.w_target:
    467                 if self.axis.val.channel.connected:
    468                     s = obj.GetValue()
    469                     try:
    470                         value = float( s )
    471                         self.axis.val.channel.put( value )
    472                     except ValueError:
    473                         msg = '''"%s" is not a floating point number!''' % s
    474                         self.AcknowledgeDialog( msg )
    475             elif obj == self.w_name:
    476                 self.gst_name.SetLabel( obj.GetValue() )
    477    
    478     def AcknowledgeDialog(self, msg):
    479         ''' present a modal dialog box with a message to be acknowledged '''
    480         dlg = wx.MessageDialog(self, msg,
    481                     "Acknowledge", wx.OK|wx.ICON_EXCLAMATION)
    482         dlg.ShowModal()
    483         dlg.Destroy()
    484 
    485     def _SetPvWidgetBackgroundColour(self, obj):
    486         '''
    487         Decide the background color of a PV widget
    488         based on whether or not a PV connection can be
    489         established from this client
    490         and set the background color of that widget.
    491         IF a connection attempt with a given PV name string
    492         does not succeed within ``CONNECT_TEST_TIMEOUT`` seconds,
    493         the PV will be considered as unavailable.
    494        
    495         =======================  =========================================
    496         color                    meaning
    497         =======================  =========================================
    498         COLOR_PV_NOT_DEFINED     no string given for PV name
    499         COLOR_PV_OK              string given can be connected as a PV
    500         COLOR_PV_NOT_OK          PV connection timed out for string given
    501         =======================  =========================================
    502         '''
    503         pv = obj.GetValue().strip()
    504         if len(pv) == 0:
    505             color = COLOR_PV_NOT_DEFINED
    506         else:
    507             valid = m_pv.pvIsAvailable( pv, CONNECT_TEST_TIMEOUT )
    508             color = {True: COLOR_PV_OK, False: COLOR_PV_NOT_OK}[valid]
    509         wx.CallAfter( obj.SetBackgroundColour, color )
    510 
    511     def _Set_All_PvWidgetBackgroundColours(self):
    512         '''
    513         Walk through all the PV name widgets and check if they
    514         can be connected.  Set the background color of each widget
    515         accordingly.
    516         '''
    517         for obj in (self.w_VAL_pv,
    518                    self.w_RBV_pv,
    519                    self.w_EGU_pv,
    520                    self.w_DESC_pv,
    521                    self.w_DMOV_pv,
    522                    self.w_STOP_pv):
    523             self._SetPvWidgetBackgroundColour( obj )
    524 
    525     def connect(self):
    526         '''connect the PVs of this axis with EPICS'''
    527         if not self.connected:
    528             self.axis.connect(ext_handler=self.callback)
    529             self.SetStatus( 'connected' )
    530             #self.w_name.SetEditable(False)
    531             self.w_target.SetEditable(True)
    532             self.connected = True
    533 
    534     def disconnect(self):
    535         '''connect the PVs of this axis from EPICS'''
    536         if self.connected:
    537             self.axis.disconnect()
    538             self.SetStatus( 'disconnected' )
    539             #self.w_name.SetEditable(True)
    540             self.w_target.SetEditable(False)
    541             self.connected = False
    542 
    543     def SetVAL(self, value):
    544         '''put the value into the widget text'''
    545         wx.CallAfter(self.w_target.SetValue, str(value) )
    546 
    547     def SetRBV(self, value):
    548         '''put the value into the widget text'''
    549         wx.CallAfter(self.w_readback.SetLabel, str(value) )
    550 
    551     def SetEGU(self, value):
    552         '''put the value into the widget text'''
    553         wx.CallAfter(self.w_egu.SetLabel, value)
    554 
    555     def SetDESC(self, value):
    556         '''put the value into the widget text'''
    557         wx.CallAfter(self.w_desc.SetLabel, value)
    558 
    559     def SetDMOV(self, value):
    560         '''set the widget background color based on moving state'''
    561         moving = not value
    562         color = {False: COLOR_NOT_MOVING, True: COLOR_MOVING}[moving]
    563         wx.CallAfter(self.w_target.SetBackgroundColour, color )
    564         wx.CallAfter(self.w_readback.SetBackgroundColour, color )
    565 
    566     def SetSTOP(self, value):
    567         '''a no-op'''
    568         pass
    569 
    570     def SetStatus(self, value):
    571         '''describe what's what'''
    572         wx.CallAfter(self.w_status.SetLabel, value)
    573    
    574     def callback(self, **kw):
    575         "PyEPICS CA monitor callback"
    576         #print kw
    577         if 'conn' in kw.keys():
    578             print "connection event", kw
    579             choices = {True: 'connected event received',
    580                        False: 'disconnect received, IOC is unavailable?'}
    581             self.SetStatus( choices[ kw['conn'] ] )
    582         else:
    583             #print "PV update event"
    584             if 'field' in kw:
    585                 #print kw['field']
    586                 {'VAL': self.SetVAL,
    587                  'RBV': self.SetRBV,
    588                  'EGU': self.SetEGU,
    589                  'DESC': self.SetDESC,
    590                  'DMOV': self.SetDMOV,
    591                  'STOP': self.SetSTOP,
    592                 }[ kw['field'] ]( kw['value'] )
    593603
    594604
    595605# - - - - - - - - - - - - - - - - - - methods
     606
     607
     608   
     609def AcknowledgeDialog(parent, msg):
     610    ''' present a modal dialog box with a message to be acknowledged '''
     611    dlg = wx.MessageDialog(parent, msg,
     612                "Acknowledge", wx.OK|wx.ICON_EXCLAMATION)
     613    dlg.ShowModal()
     614    dlg.Destroy()
     615
     616
     617def Make_My_Button(parent, label, name, tip, binding):
     618    '''
     619    Create a button and bind it to a method.
     620    Return the button object
     621    '''
     622    button = wx.lib.buttons.GenButton(id=wx.ID_ANY,
     623          label=label, name=name, parent=parent,)
     624    button.SetToolTipString(tip)
     625    button.Bind(wx.EVT_BUTTON, binding)
     626    return button
     627
     628
     629def Make_My_TextEntry(parent, value='',
     630                       editable=True, tooltip='', handler=None):
     631    '''
     632    Create and return a TextCtrl object.
     633    [Enter] key event is bound to widget to call handler.
     634   
     635    :param obj parent: widget panel that will contain this TextCtrl
     636    :param str|float value: initial value to set in the entry field
     637    :param bool editable: should the entry field be editable?
     638    :param str tooltip: short description of this field
     639    :param obj handler: None or method to be called when [Enter] is pressed in this field
     640    :return: wx.TextCtrl object
     641    '''
     642    style = wx.TE_PROCESS_ENTER
     643    entry = wx.TextCtrl(parent=parent, id=wx.ID_ANY, style=style)
     644    entry.SetEditable( editable )
     645    if value is not None:
     646        entry.SetValue( str(value) )
     647    entry.SetToolTipString( tooltip )
     648    if handler is not None:
     649        entry.Bind(wx.EVT_TEXT_ENTER, handler)
     650    return entry
    596651
    597652
Note: See TracChangeset for help on using the changeset viewer.