Changeset 1509 for branch/logging/log.py


Ignore:
Timestamp:
Sep 29, 2014 2:31:04 PM (7 years ago)
Author:
toby
Message:

logging refactored, and much cleaner\!

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branch/logging/log.py

    r1505 r1509  
    33import GSASIIgrid as G2gd
    44MenuBindingLookup = {}
     5'Lookup table for Menu buttons'
     6ButtonBindingLookup = {}
     7'Lookup table for button objects'
    58G2logList = [None]
    69'Contains a list of logged actions; first item is ignored'
    7 LogInfo = {'Logging':False, 'Tree':None, 'G2frame':None}
     10LogInfo = {'Logging':False, 'Tree':None, 'G2frame':None, 'LastPaintAction':None}
    811'Contains a dict with values that are needed in the module'
    912
     13# TODO:
     14# might want to save the last displayed screen with some objects to make sure
     15# the commands are executed in a sensible order
     16
     17# Problem: checks on Logging not set properly (still!)
     18
    1019debug = True
    11 
    12 # TODO:
    13 ### Note: no provinance info for histogram instrument parameters: need to remove insVal etc.
    14 
    1520#===========================================================================
    1621# objects for logging variables
     
    2328    return s
    2429
     30# Define logging objects. These store information on events in a manner that can be
     31# pickled and saved -- direct references to wx objects is not allowed.
     32# each object should define:
     33#     __init__: stores the information needed to log & later recreate the action
     34#     __str__ : shows a nice ASCII string for each action
     35#     Replay:   recreates the action when the log is played
     36# optional
     37#     Repaint:  redisplays the current window
     38
    2539class VarLogEntry(object):
    2640    'object that tracks changes to a variable'
     
    3751            treeList[1] = 'Phase #'+str(treeList[1]+1)
    3852        return 'Variable change: Key(s)= '+_l2s(self.indexRefs)+' to value='+str(self.value)
     53    def Replay(self):
     54        'Perform a Variable Change action, when read from the log'
     55        parentId = LogInfo['Tree'].root
     56        for i,treeitem in enumerate(self.treeRefs):
     57            if i == 0 and type(treeitem) is tuple:
     58                treeitem = LogInfo['Tree'].ConvertRelativeHistNum(*treeitem)
     59            item, cookie = LogInfo['Tree'].GetFirstChild(parentId)
     60            while item:
     61                if LogInfo['Tree'].GetItemText(item) == treeitem:
     62                    parentId = item
     63                    break
     64                else:
     65                    item, cookie = LogInfo['Tree'].GetNextChild(parentId, cookie)
     66            else:
     67                raise Exception("Tree item not found for "+str(self))
     68        # get the inner most data array
     69        data = LogInfo['Tree'].GetItemPyData(item)
     70        for item in self.indexRefs[:-1]:
     71            data = data[item]
     72        # set the value
     73        data[self.indexRefs[-1]] = self.value
    3974
    4075class MenuLogEntry(object):
     
    4984                if l: l += ' -> '
    5085                l += item
    51         if debug: print 'Logging menu command: '+l
     86            print 'Logging menu command: '+l
    5287    def __str__(self):
    5388        return 'Menu press: From '+_l2s(self.menulabellist,'/')
     89    def Replay(self):
     90        'Perform a Menu item action when read from the log'
     91        key = ''
     92        for item in self.menulabellist:
     93            if key: key += '+'
     94            key += item
     95        if MenuBindingLookup.get(key):
     96            handler,id,menuobj = MenuBindingLookup[key]
     97            MyEvent = wx.CommandEvent(wx.EVT_MENU.typeId, id)
     98            MyEvent.SetEventObject(menuobj)
     99            handler(MyEvent)
     100        else:
     101            raise Exception('No binding for menu item '+key)       
    54102           
    55103class TabLogEntry(object):
     
    61109    def __str__(self):
    62110        return 'Tab press: Tab='+_l2s([self.tablabel])+' on window labeled '+str(self.wintitle)
     111    def Repaint(self):
     112        if debug: print 'Repaint'
     113        saveval = LogInfo['LastPaintAction']
     114        self.Replay()
     115        LogInfo['LastPaintAction'] = saveval
     116    def Replay(self):
     117        'Perform a Tab press action when read from the log'
     118        wintitle = self.wintitle
     119        tabname = self.tablabel
     120        LogInfo['LastPaintAction'] = self
     121        if LogInfo['Tree'].G2frame.dataFrame.GetTitle() != wintitle:
     122            print LogInfo['Tree'].G2frame.dataFrame.GetTitle(),' != ',wintitle
     123            raise Exception('tab in wrong window')
     124        for PageNum in range(LogInfo['Tree'].G2frame.dataDisplay.GetPageCount()):
     125            if tabname == LogInfo['Tree'].G2frame.dataDisplay.GetPageText(PageNum):
     126                LogInfo['Tree'].G2frame.dataDisplay.SetSelection(PageNum)
     127                return
     128        else:
     129            print tabname,'not in',[
     130                LogInfo['Tree'].G2frame.dataDisplay.GetPageText(PageNum) for
     131                PageNum in range(LogInfo['Tree'].G2frame.dataDisplay.GetPageCount())]
     132            raise Exception('tab not found')
    63133
    64134class TreeLogEntry(object):
     
    74144            treeList[1] = 'Phase #'+str(treeList[1]+1)
    75145        return 'Tree item pressed: '+_l2s(treeList)
     146    def Repaint(self):
     147        if debug: print 'Repaint'
     148        saveval = LogInfo['LastPaintAction']
     149        LogInfo['Tree'].SelectItem(LogInfo['Tree'].root) # need to select something else
     150        wx.Yield()
     151        self.Replay()
     152        LogInfo['LastPaintAction'] = saveval
     153    def Replay(self):
     154        'Perform a Tree press action when read from the log'
     155        LogInfo['LastPaintAction'] = self
     156        parent = LogInfo['Tree'].root
     157        for i,txt in enumerate(self.treeItemList):
     158            if i == 0 and type(txt) is tuple:
     159                txt = LogInfo['Tree'].ConvertRelativeHistNum(*txt)
     160            elif i == 1 and type(txt) is int and self.treeItemList[0] == "Phases":
     161                txt = LogInfo['Tree'].ConvertRelativePhaseNum(txt)
     162            item = G2gd.GetPatternTreeItemId(LogInfo['Tree'].G2frame,parent,txt)
     163            if not item:
     164                print 'Not found',txt
     165                return
     166            else:
     167                parent = item
     168        else:
     169            LogInfo['Tree'].SelectItem(item)
     170
     171class ButtonLogEntry(object):
     172    'Object to track button press'
     173    def __init__(self,locationcode,label):
     174        self.locationcode = locationcode
     175        self.label = label
     176        if debug: print 'Logging '+label+' button press in '+locationcode
     177    def __str__(self):
     178        return 'Press of '+self.label+' button in '+self.locationcode
     179    def Replay(self):
     180        key = self.locationcode + '+' + self.label
     181        if ButtonBindingLookup.get(key):
     182            btn = ButtonBindingLookup[key]
     183            clickEvent = wx.CommandEvent(wx.EVT_BUTTON.typeId, btn.GetId())
     184            clickEvent.SetEventObject(btn)
     185            #btn.GetEventHandler().ProcessEvent(clickEvent)
     186            btn.handler(clickEvent)
    76187           
    77188def _wrapper(func):
     
    166277        return self.obj.__str__() + " : " + str(self.treeRefs) + ',' + str(self.indexRefs)
    167278
    168 #===========================================================================
    169 class G2TreeCtrl(wx.TreeCtrl):
    170     '''Create a wrapper around the standard TreeCtrl so we can "wrap"
    171     various events.
    172    
    173     This logs when a tree item is selected (in :meth:`onSelectionChanged`)
    174 
    175     This also wraps lists and dicts pulled out of the tree to track where
    176     they were retrieved from.
    177     '''
    178     def __init__(self,parent=None,*args,**kwargs):
    179         super(self.__class__,self).__init__(parent=parent,*args,**kwargs)
    180         LogInfo['G2frame'] = self.G2frame = parent.GetParent()
    181         self.root = self.AddRoot('Loaded Data: ')
    182         self.SelectionChanged = None
    183         self.repaintAction = None
    184         LogInfo['Tree'] = self
    185 
    186     def _getTreeItemsList(self,item):
    187         '''Get the full tree hierarchy from a reference to a tree item.
    188         Note that this effectively hard-codes phase and histogram names in the
    189         returned list. We may want to make these names relative in the future.
    190         '''
    191         textlist = [self.GetItemText(item)]
    192         parent = self.GetItemParent(item)
    193         while parent:
    194             if parent == self.root: break
    195             textlist.insert(0,self.GetItemText(parent))
    196             parent = self.GetItemParent(parent)
    197         return textlist
    198 
    199     def onSelectionChanged(self,event):
    200         '''Log each press on a tree item here.
    201         '''
    202         if self.SelectionChanged:
    203             textlist = self._getTreeItemsList(event.GetItem())
    204             if LogInfo['Logging'] and event.GetItem() != self.root:
    205                 textlist[0] = self.GetRelativeHistNum(textlist[0])
    206                 if textlist[0] == "Phases" and len(textlist) > 1:
    207                     textlist[1] = self.GetRelativePhaseNum(textlist[1])
    208                 G2logList.append(TreeLogEntry(textlist))
    209             self.SelectionChanged(event)
    210 
    211     def Bind(self,eventtype,handler,*args,**kwargs):
    212         '''Override the Bind() function so that page change events can be trapped
    213         '''
    214         if eventtype == wx.EVT_TREE_SEL_CHANGED:
    215             self.SelectionChanged = handler
    216             wx.TreeCtrl.Bind(self,eventtype,self.onSelectionChanged)
    217             return
    218         wx.TreeCtrl.Bind(self,eventtype,handler,*args,**kwargs)
    219 
    220     def GetItemPyData(self,*args,**kwargs):
    221         '''Override the standard method to wrap the contents
    222         so that the source can be tracked
    223         '''
    224         data = super(self.__class__,self).GetItemPyData(*args,**kwargs)
    225         textlist = self._getTreeItemsList(args[0])
    226         if type(data) is dict:
    227             return dictLogged(data,textlist)
    228         if type(data) is list:
    229             return listLogged(data,textlist)
    230         if type(data) is tuple: #N.B. tuples get converted to lists
    231             return listLogged(list(data),textlist)
    232         return data
    233 
    234     def ClearDataRepaint(self):
    235         self.repaintAction = None
    236 
    237     def GetRelativeHistNum(self,histname):
    238         '''Returns list with a histogram type and a relative number for that
    239         histogram, or the original string if not a histogram
    240         '''
    241         histtype = histname.split()[0]
    242         if histtype != histtype.upper(): # histograms (only) have a keyword all in caps
    243             return histname
    244         item, cookie = self.GetFirstChild(self.root)
    245         i = 0
    246         while item:
    247             itemtext = self.GetItemText(item)
    248             if itemtext == histname:
    249                 return histtype,i
    250             elif itemtext.split()[0] == histtype:
    251                 i += 1
    252             item, cookie = self.GetNextChild(self.root, cookie)
    253         else:
    254             raise Exception("Histogram not found: "+histname)
    255 
    256     def ConvertRelativeHistNum(self,histtype,histnum):
    257         '''Converts a histogram type and relative histogram number to a
    258         histogram name in the current project
    259         '''
    260         item, cookie = self.GetFirstChild(self.root)
    261         i = 0
    262         while item:
    263             itemtext = self.GetItemText(item)
    264             if itemtext.split()[0] == histtype:
    265                 if i == histnum: return itemtext
    266                 i += 1
    267             item, cookie = self.GetNextChild(self.root, cookie)
    268         else:
    269             raise Exception("Histogram #'+str(histnum)+' of type "+histtype+' not found')
    270        
    271     def GetRelativePhaseNum(self,phasename):
    272         '''Returns a phase number if the string matches a phase name
    273         or else returns the original string
    274         '''
    275         item, cookie = self.GetFirstChild(self.root)
    276         while item:
    277             itemtext = self.GetItemText(item)
    278             if itemtext == "Phases":
    279                 parent = item
    280                 item, cookie = self.GetFirstChild(parent)
    281                 i = 0
    282                 while item:
    283                     itemtext = self.GetItemText(item)
    284                     if itemtext == phasename:
    285                         return i
    286                     item, cookie = self.GetNextChild(parent, cookie)
    287                     i += 1
    288                 else:
    289                     return phasename # not a phase name
    290             item, cookie = self.GetNextChild(self.root, cookie)
    291         else:
    292             raise Exception("No phases found ")
    293 
    294     def ConvertRelativePhaseNum(self,phasenum):
    295         '''Converts relative phase number to a phase name in
    296         the current project
    297         '''
    298         item, cookie = self.GetFirstChild(self.root)
    299         while item:
    300             itemtext = self.GetItemText(item)
    301             if itemtext == "Phases":
    302                 parent = item
    303                 item, cookie = self.GetFirstChild(parent)
    304                 i = 0
    305                 while item:
    306                     if i == phasenum:
    307                         return self.GetItemText(item)
    308                     item, cookie = self.GetNextChild(parent, cookie)
    309                     i += 1
    310                 else:
    311                     raise Exception("Phase "+str(phasenum)+" not found")
    312             item, cookie = self.GetNextChild(self.root, cookie)
    313         else:
    314             raise Exception("No phases found ")
    315                
    316     def RepaintDataWindow(self):
    317         item = self.repaintAction
    318         if isinstance(item,TreeLogEntry):
    319             self.SelectItem(self.root) # need to select something else
    320             self.ReplayTreePress(item)
    321         elif isinstance(item,TabLogEntry):
    322             self.ReplayTabPress(item)
    323            
    324     def ReplayLogItem(self,item):
    325         'Execute an action taken from a log file entry'
    326         if isinstance(item,MenuLogEntry):
    327             self.ReplayMenuCommand(item)
    328         elif isinstance(item,TreeLogEntry):
    329             self.ReplayTreePress(item)
    330             self.repaintAction = item
    331         elif isinstance(item,VarLogEntry):
    332             self.ReplayVariableChange(item)
    333         elif isinstance(item,TabLogEntry):
    334             self.ReplayTabPress(item)
    335             self.repaintAction = item
    336         else:
    337             raise Exception("Unknown object in log: "+str(type(item))+": "
    338                             +str(item))
    339        
    340     def ReplayTreePress(self,logitem):
    341         'Perform a Tree press action when read from the log'
    342         parent = self.root
    343         for i,txt in enumerate(logitem.treeItemList):
    344             if i == 0 and type(txt) is tuple:
    345                 txt = self.ConvertRelativeHistNum(*txt)
    346             elif i == 1 and type(txt) is int and logitem.treeItemList[0] == "Phases":
    347                 txt = self.ConvertRelativePhaseNum(txt)
    348             item = G2gd.GetPatternTreeItemId(self.G2frame,parent,txt)
    349             if not item:
    350                 print 'Not found',txt
    351                 return
    352             else:
    353                 parent = item
    354         else:
    355             self.SelectItem(item)
    356                
    357     def ReplayTabPress(self,logitem):
    358         'Perform a Tab press action when read from the log'
    359         wintitle = logitem.wintitle
    360         tabname = logitem.tablabel
    361         if self.G2frame.dataFrame.GetTitle() != wintitle:
    362             print self.G2frame.dataFrame.GetTitle(),' != ',wintitle
    363             raise Exception('tab in wrong window')
    364         for PageNum in range(self.G2frame.dataDisplay.GetPageCount()):
    365             if tabname == self.G2frame.dataDisplay.GetPageText(PageNum):
    366                 self.G2frame.dataDisplay.SetSelection(PageNum)
    367                 return
    368         else:
    369             print tabname,'not in',[
    370                 self.G2frame.dataDisplay.GetPageText(PageNum) for
    371                 PageNum in range(self.G2frame.dataDisplay.GetPageCount())]
    372             raise Exception('tab not found')
    373     def ReplayVariableChange(self,logitem):
    374         'Perform a Variable Change action, when read from the log'
    375         parentId = self.root
    376         for i,treeitem in enumerate(logitem.treeRefs):
    377             if i == 0 and type(treeitem) is tuple:
    378                 treeitem = self.ConvertRelativeHistNum(*treeitem)
    379             item, cookie = self.GetFirstChild(parentId)
    380             while item:
    381                 if self.GetItemText(item) == treeitem:
    382                     parentId = item
    383                     break
    384                 else:
    385                     item, cookie = self.GetNextChild(parentId, cookie)
    386             else:
    387                 raise Exception("Tree item not found for "+str(logitem))
    388         # get the inner most data array
    389         data = super(self.__class__,self).GetItemPyData(item)
    390         for item in logitem.indexRefs[:-1]:
    391             data = data[item]
    392         # set the value
    393         data[logitem.indexRefs[-1]] = logitem.value
    394     def ReplayMenuCommand(self,logitem):
    395         'Perform a Menu item action when read from the log'
    396         key = ''
    397         for item in logitem.menulabellist:
    398             if key: key += '+'
    399             key += item
    400         if MenuBindingLookup.get(key):
    401             MenuBindingLookup[key](None)
    402         else:
    403             raise Exception('No binding for menu item '+key)       
    404        
    405279#===========================================================================
    406280# variable tracking
     
    446320           |->itemlabel                 |-> sublabel(s)               |-> menulabel
    447321           
    448     :returns: a list containing all the labels (or None)           
     322    :returns: a list containing all the labels and the menuitem object
     323       or None if the menu object will not be cataloged.
    449324    '''
    450325    # don't worry about help menuitems
     
    488363        #raise Exception('debug2: error tracing menuitem')
    489364        return
    490     return menuLabelList
     365    return menuLabelList,menuitem
    491366
    492367def SaveMenuCommand(id,G2frame,handler):
    493368    '''Creates a table of menu items and their pseudo-bindings
    494369    '''
    495     menuLabelList = _getmenuinfo(id,G2frame,handler)
    496     if not menuLabelList: return
     370    menuinfo = _getmenuinfo(id,G2frame,handler)
     371    if not menuinfo: return
     372    menuLabelList,menuobj = menuinfo
    497373    key = ''
    498374    for item in menuLabelList:
    499375        if key: key += '+'
    500376        key += item
    501     MenuBindingLookup[key] = handler
     377    MenuBindingLookup[key] = [handler,id,menuobj]
    502378    return menuLabelList
    503379
     
    515391        if LogInfo['Logging']:
    516392            G2logList.append(MenuLogEntry(menuLabelList))
    517         MenuBindingLookup[key](event)
     393        handler = MenuBindingLookup[key][0]
     394        handler(event)
    518395    else:
    519396        print 'Error no binding for menu command',menuLabelList,'id=',id
     
    536413    return LogInfo['Logging']
    537414
     415def OnReplayPress(event):
     416    'execute one or more commands when the replay button is pressed'
     417    clb = LogInfo['clb']
     418    dlg = clb.GetTopLevelParent()
     419    sels = sorted(clb.GetSelections())
     420    if not sels:
     421        dlg1 = wx.MessageDialog(dlg,
     422            'Select one or more items in the list box to replay',
     423            'No selection actions',
     424            wx.OK)
     425        dlg1.CenterOnParent()
     426        dlg1.ShowModal()
     427        dlg1.Destroy()
     428        return
     429    logstat = ShowLogStatus()
     430    if logstat: LogOff()
     431    if debug: print 70*'='
     432    for i in sels:
     433        i += 1
     434        item = G2logList[i]
     435        if debug: print 'replaying',item
     436        item.Replay()
     437        wx.Yield()
     438    if i >= len(G2logList)-1:
     439         dlg.EndModal(wx.ID_OK)
     440    else:
     441        clb.DeselectAll()
     442        clb.SetSelection(i)
     443        if logstat: LogOn()
     444    if debug: print 70*'='
     445    # if the last command did not display a window, repaint it in
     446    # case something on that window changed.
     447    if item != LogInfo['LastPaintAction'] and hasattr(LogInfo['LastPaintAction'],'Repaint'):
     448        LogInfo['LastPaintAction'].Repaint()
     449     
    538450def ReplayLog(event):
    539     'replay the logged actions (needs to be a wx.widget)'
    540    
    541     LogInfo['G2frame'].OnMacroRecordStatus(None,False) # turn off recording
    542     LogInfo['Tree'].ClearDataRepaint()
    543     print 70*'='
    544     print 'Performing logged actions:'
     451    'replay the logged actions'
     452    LogInfo['LastPaintAction'] = None # clear the pointed to the last data window
     453    # is this really needed? -- probably not.
     454    commandList = []
    545455    for item in G2logList:
    546456        if item: # skip over 1st item in list (None)
    547             print 'replaying',item
    548             LogInfo['Tree'].ReplayLogItem(item)
    549             wx.Yield()
    550     # do repaint here
    551     LogInfo['Tree'].RepaintDataWindow()
    552     print 70*'='
    553    
    554    
    555    
     457            commandList.append(str(item))
     458    if not commandList:
     459        dlg = wx.MessageDialog(LogInfo['Tree'],
     460            'No actions found in log to replay',
     461            'Empty Log',
     462            wx.OK)
     463        dlg.CenterOnParent()
     464        dlg.ShowModal()
     465        dlg.Destroy()
     466        return
     467    dlg = wx.Dialog(LogInfo['Tree'],wx.ID_ANY,'Replay actions from log',
     468        style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.CENTRE)
     469    mainSizer = wx.BoxSizer(wx.VERTICAL)
     470    mainSizer.Add((5,5))
     471    clb = wx.ListBox(dlg, wx.ID_ANY, (30,100), wx.DefaultSize, commandList,
     472                     style=wx.LB_EXTENDED)
     473    LogInfo['clb'] = clb
     474    mainSizer.Add(clb,1,wx.EXPAND,1)
     475    mainSizer.Add((5,5))
     476    btn = wx.Button(dlg, wx.ID_ANY,'Replay selected')
     477    btn.Bind(wx.EVT_BUTTON,OnReplayPress)
     478    mainSizer.Add(btn,0,wx.ALIGN_CENTER,0)
     479    btnsizer = wx.StdDialogButtonSizer()
     480    OKbtn = wx.Button(dlg, wx.ID_OK,'Close')
     481    #OKbtn = wx.Button(dlg, wx.ID_CLOSE)
     482    OKbtn.SetDefault()
     483    OKbtn.Bind(wx.EVT_BUTTON,lambda event: dlg.EndModal(wx.ID_OK))
     484    btnsizer.AddButton(OKbtn)
     485    btnsizer.Realize()
     486    mainSizer.Add((-1,5),1,wx.EXPAND,1)
     487    mainSizer.Add(btnsizer,0,wx.ALIGN_CENTER,0)
     488    mainSizer.Add((-1,5))
     489    dlg.SetSizer(mainSizer)
     490    dlg.CenterOnParent()
     491    clb.SetSelection(0)
     492    dlg.ShowModal()
     493    dlg.Destroy()
     494    return
     495
    556496LogOn() # for debug
Note: See TracChangeset for help on using the changeset viewer.