예제 #1
0
파일: control.py 프로젝트: vpaeder/terapy
 def OnRunSequence(self, itm):
     """
     
         Actions initiating a sequence run.
         
         Parameters:
             itm    -    root item for sequence (wx.TreeItem)
     
     """
     # disable controls
     self.ToggleControls(False)
     
     # refresh events
     self.RefreshEvents()
     
     # initialize data tree
     stree = self.list_events.GetItemSubTree(itm, ScanEvent)
     ev = self.list_events.GetItemPyData(itm)
     meas = Measurement(stree.data.name,stree)
     meas.BuildDataTree()
     
     # check that events work with associated data
     self.plot_or_save = [False]*meas.count
     if not(self.CheckValidity(stree,meas)):
         print "Invalid event tree!"
         self.ToggleControls(True)
         return
     del self.plot_or_save
     
     # store event tree in XML format
     doc = self.BuildXMLTree(itm)
     meas.xml = doc.toprettyxml(indent="  ", newl="\n")
     
     # store system state (state of instruments before measurement)
     from terapy import hardware
     doc = hardware.get_system_state()
     meas.systemState = doc.toprettyxml(indent="  ", newl="\n")
     
     # announce measurement start
     pub.sendMessage("scan.start", inst=meas)
     
     self.scanThread = ScanThread(ev, meas)
     self.scanThread.start()
예제 #2
0
파일: control.py 프로젝트: vpaeder/terapy
class ScanEventList(wx.Panel):
    """
    
        Scan control widget
    
    """
    def __init__(self, parent = None):
        """
        
            Initialization.
            
            Parameters:
                parent    -    parent window (wx.Window)
        
        """
        wx.Panel.__init__(self, parent)
        self.parent = parent
        self.events = []
        self.scanThread = None
        
        # event list
        self.list_events = TreeCtrl(self, -1, style=wx.TR_EDIT_LABELS|wx.TR_HAS_BUTTONS|wx.TR_HIDE_ROOT|wx.TR_SINGLE|wx.TR_LINES_AT_ROOT)
        from terapy.core.tooltip import ToolTip
        ToolTip("Double click  ->  Change properties\nRight click   ->  Menu\nShift+Left    ->  Enable/disable","Usage",self.list_events)
        self.img_list = wx.ImageList(16,16)
        self.list_events.SetMaxSize((200,-1))
        self.list_events.SetMinSize((200,-1))
        
        for x in modules:
            self.img_list.Add(x(self).get_icon())
        
        self.list_events.SetImageList(self.img_list)
        
        # controls
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.list_events, 1, wx.EXPAND|wx.TOP, 2)
        hbox = wx.BoxSizer(wx.HORIZONTAL)
        self.button_up = wx.BitmapButton(self, -1, wx.Image(icon_path + "go-up.png").ConvertToBitmap())
        self.button_down = wx.BitmapButton(self, -1, wx.Image(icon_path + "go-down.png").ConvertToBitmap())
        self.button_add = wx.BitmapButton(self, -1, wx.Image(icon_path + "list-add.png").ConvertToBitmap())
        self.button_remove = wx.BitmapButton(self, -1, wx.Image(icon_path + "list-remove.png").ConvertToBitmap())
        self.button_run = RunButton(self, -1)
        hbox.Add(self.button_up, 0, wx.EXPAND|wx.ALL, 2)
        hbox.Add(self.button_down, 0, wx.EXPAND|wx.ALL, 2)
        hbox.Add(self.button_add, 0, wx.EXPAND|wx.ALL, 2)
        hbox.Add(self.button_remove, 0, wx.EXPAND|wx.ALL, 2)
        hbox.Add(self.button_run, 0, wx.EXPAND|wx.ALL, 2)
        self.sizer.Add(hbox, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, 2)
        
        # bindings
        self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.OnEventListRightClick, self.list_events)
        self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnEventDoubleClick, self.list_events)
        self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnEditTreeLabel, self.list_events)
        self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnEndEditTreeLabel, self.list_events)
        self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnStartDrag, self.list_events)
        self.list_events.Bind(wx.EVT_RIGHT_DOWN, self.OnEventListRightClick)
        self.list_events.Bind(wx.EVT_LEFT_DOWN, self.OnEventLeftClick)
        self.Bind(wx.EVT_BUTTON, self.OnButtonUp, self.button_up)
        self.Bind(wx.EVT_BUTTON, self.OnButtonDown, self.button_down)
        self.Bind(wx.EVT_BUTTON, self.OnEventListRightClick, self.button_add)
        self.Bind(wx.EVT_BUTTON, self.OnRemoveEvent, self.button_remove)
        self.Bind(wx.EVT_BUTTON, self.OnButtonRun, self.button_run)
        pub.subscribe(self.OnStopMeasurement, "scan.after")
        pub.subscribe(self.ParseXMLFromString, "history.reload_events")

        self.list_events.SetDropTarget(EventDrop(self.OnEndDrag))
        
        self.SetAutoLayout(True)
        self.SetSizer(self.sizer)
        self.sizer.Fit(self)
        self.sizer.SetSizeHints(self)
        
        self.DefaultEvents()
        
    def ParseXMLTree(self, tree):
        """
        
            Parse given XML tree and build ScanEvent structure.
             
            Parameters:
                tree    -    minidom XML document
            
            Output:
                nested list of ScanEvent
        
        """
        ml = [x.__name__ for x in modules] # list of module names
        cn = [] # current tree branch
        for x in tree:
            if hasattr(x,'tagName'):
                if x.tagName == 'item':
                    attrs = x.attributes
                    if attrs.has_key('class'):
                        if ml.count(attrs['class'].value)>0:
                            idx = ml.index(attrs['class'].value)
                            cn.append(modules[idx](self.list_events))
                            ParseAttributes(attrs,cn[-1])
                if x.hasChildNodes():
                    cn.append(self.ParseXMLTree(x.childNodes))
        return cn
    
    def BuildXMLTree(self, itm, root=None, doc=None):
        """
        
            Build XML tree out of given TreeCtrl subtree.
            
            Parameters:
                itm    -    TreeCtrl item (wx.TreeItem)
                root   -    recipient minidom XML node 
                doc    -    recipient minidom XML document
        
        """
        if doc == None:
            from xml.dom import minidom
            doc = minidom.Document()
            croot = doc.createElement("config")
            croot.attributes["scope"] = "events"
            doc.appendChild(croot)
            root = doc.createElement("events")
            croot.appendChild(root)
        ev = self.list_events.GetItemData(itm).GetData()
        p = doc.createElement("item")
        root.appendChild(p)
        p.attributes["name"] = self.list_events.GetItemText(itm)
        p.attributes["class"] = ev.__class__.__name__
        for x in ev.config:
            p.attributes[x] = str(getattr(ev,x))
                
        if self.list_events.ItemHasChildren(itm):
            children = self.list_events.GetItemChildren(itm, ScanEvent)
            for x in children:
                self.BuildXMLTree(x, p, doc)
        return doc
    
    def CreateEventTree(self,tree,root=None):
        """
        
            Fill in TreeCtrl with given ScanEvent tree.
            
            Parameters:
                tree    -    nested list of ScanEvent
                root    -    root tree item (wx.TreeItem, root item if None)
        
        """
        if root==None:
            root = self.list_events.GetRootItem()
        nitm = root
        for x in tree:
            if isinstance(x,ScanEvent):
                nitm = self.InsertItem(x,root,False)
                self.list_events.SetItemText(nitm,x.name)
            else:
                self.CreateEventTree(x, nitm)
        
    def DefaultEvents(self):
        """
        
            Fill TreeCtrl with default events.
            Load default events from event_file if present.
        
        """
        self.list_events.DeleteAllItems()
        root = self.list_events.AddRoot("Scan events")
        if event_file==None: return # no event_file defined
        if os.path.exists(event_file):
            from xml.dom import minidom
            elist = [] 
            xmldoc = minidom.parse(event_file).getElementsByTagName('events')
            elist = self.ParseXMLTree(xmldoc)
            self.CreateEventTree(elist[0],root)

    def InsertItem(self, ev, root = None, expand=True):
        """
        
            Insert given event in tree.
            
            Parameters:
                ev        -    event to insert (ScanEvent)
                root      -    parent item (wx.TreeItem, root item if None)
                expand    -    if True, expand parent folder
        
        """
        if root == None:
            root = self.list_events.GetRootItem()
        nitm = self.list_events.AppendItem(root,ev.__extname__)
        idx = modules.index(ev.__class__)
        self.list_events.SetItemImage(nitm,idx)
        f = self.list_events.GetItemFont(nitm)
        f.SetPointSize(10)
        f.SetWeight(wx.FONTWEIGHT_BOLD)
        self.list_events.SetItemFont(nitm,f)
        self.list_events.SetItemPyData(nitm,ev)
        if expand:
            self.list_events.Expand(root)
        ev.refresh()
        ev.populate()
        return nitm
        
    def OnEditTreeLabel(self, event = None):
        """
        
            Actions triggered by edit label request.
            
            Parameters:
                event    -    wx.Event
        
        """
        itm = event.GetItem()
        ev = self.list_events.GetItemData(itm).GetData()
        if not(isinstance(ev,ScanEvent)): # if not event object, find parent event object
            while not(isinstance(ev,ScanEvent)):
                itm = self.list_events.GetItemParent(itm)
                ev = self.list_events.GetItemData(itm).GetData()
            itm0 = event.GetItem()
            propNode = self.list_events.GetItemChildren(itm, PropertyNode)[0] # get property node
            if itm0 == propNode:
                event.Veto()
                if self.list_events.IsExpanded(itm0):
                    self.list_events.Collapse(itm0)
                else:
                    self.list_events.Expand(itm0)
                return # can't edit "Properties" tag
            else: # edit with associated editor
                p = self.list_events.GetItemChildren(propNode).index(itm0)
                self.list_events.SetItemText(itm0, str(ev.propNodes[p]))
                self.list_events.GetItemData(itm0).SetData([ev,p])
                ev.edit_label(event,p)
        else: # if event object, name can be edited as is
            event.Skip()
            
    def OnEndEditTreeLabel(self, event = None):
        """
        
            Actions following label edition.
            
            Parameters:
                event    -    wx.Event
        
        """
        itm = event.GetItem()
        ev = self.list_events.GetItemData(itm).GetData()
        if isinstance(ev,list): # the edited item is a property node
            event.Veto()
            self.list_events.GetItemData(itm).SetData(None)
            ev[0].set_property(ev[1],event.GetLabel())
        elif isinstance(ev,ScanEvent):
            ev.name = event.GetLabel()
        else:
            event.Skip()
        self.list_events.SelectItem(self.list_events.GetNextSibling(itm))
        self.SetFocus()
    
    def OnEventLeftClick(self, event = None):
        """
        
            Actions triggered by left mouse button click on tree item.
            
            Parameters:
                event    -    wx.Event
        
        """
        itm = self.list_events.HitTest(event.GetPosition())[0]
        if event.ShiftDown() and itm>-1: # enable/disable shift-clicked event 
            self.OnEnableEvent(itm)
        else:
            event.Skip()
    
    def OnEventDoubleClick(self, event = None):
        """
        
            Actions triggered by left mouse button double click on tree item.
            
            Parameters:
                event    -    wx.Event
        
        """
        ev = self.list_events.GetItemData(event.GetItem()).GetData()
        if hasattr(ev,'is_root'):
            if ev.is_root:
                self.list_events.EditLabel(event.GetItem())
                return
        if hasattr(ev,'set'):
            if ev.set():
                ev.populate()
        else:
            self.list_events.EditLabel(event.GetItem())
        
    def OnEventListRightClick(self, event = None):
        """
        
            Actions triggered by right mouse button click on tree item.
            
            Parameters:
                event    -    wx.Event
        
        """
        if event.EventType == wx.EVT_BUTTON.evtType[0]:
            self.menuPosition = self.button_add.Position
        else:
            self.menuPosition = event.GetPosition()
        
        itm = self.list_events.HitTest(self.menuPosition)[0]
        menu = wx.Menu()
        menuAdd = wx.Menu()
        
        for n in range(len(modules)):
            obj = modules[n](self.list_events)
            mitem = wx.MenuItem(menuAdd,id=wx.NewId(),text=obj.__extname__)
            mitem.SetBitmap(obj.get_icon())
            if obj.is_root and not(itm.IsOk()) and obj.is_visible:
                menuAdd.AppendItem(mitem)
                self.Bind(wx.EVT_MENU, functools.partial(self.OnAddEvent,obj), id=mitem.Id)
            elif itm.IsOk() and obj.is_visible:
                ev = self.list_events.GetItemData(itm).GetData()
                if isinstance(ev,ScanEvent):
                    if (ev.is_loop or ev.is_root) and not(obj.is_root):
                        menuAdd.AppendItem(mitem)
                        self.Bind(wx.EVT_MENU, functools.partial(self.OnAddEvent,obj), id=mitem.Id)
        
        if menuAdd.GetMenuItemCount()>0:
            menu.AppendSubMenu(menuAdd,"&Add...")
        
        if self.list_events.GetItemData(self.list_events.Selection).GetData()!=None or itm.IsOk():
            mitem = menu.Append(id=wx.NewId(),text="&Remove")
            self.Bind(wx.EVT_MENU, self.OnRemoveEvent, id=mitem.Id)
            mitem = menu.Append(id=wx.NewId(),text="&Rename")
            self.Bind(wx.EVT_MENU, lambda x: self.list_events.EditLabel(self.list_events.Selection), id=mitem.Id)
        # if right-click is above an item, add enable/disable menu option
        if itm.IsOk():
            ev = self.list_events.GetItemData(itm).GetData()
            if not(hasattr(ev, "is_active")):
                return # not an editable menu item
            # fill replace options with similar items
            menuRep = wx.Menu()
            for n in range(len(modules)):
                obj = modules[n]()
                mitem = wx.MenuItem(menuRep,id=wx.NewId(),text=obj.__extname__)
                mitem.SetBitmap(obj.get_icon())
                self.Bind(wx.EVT_MENU, functools.partial(self.OnReplaceEvent,ev,obj), id=mitem.Id)
                if obj.is_visible:
                    if (ev.is_loop and obj.is_loop) or (ev.is_input and obj.is_input) or (ev.is_save and obj.is_save) or (ev.is_display and obj.is_display):
                        menuRep.AppendItem(mitem)
            if menuRep.GetMenuItemCount()>0:
                menu.AppendSubMenu(menuRep, "Replace &with...")
            
            if not(ev.is_root):
                if self.list_events.GetItemData(itm).GetData().is_active:
                    mitem = menu.Append(id=wx.NewId(), text="&Disable")
                else:
                    mitem = menu.Append(id=wx.NewId(), text="&Enable")
                self.Bind(wx.EVT_MENU, lambda x: self.OnEnableEvent(itm), id=mitem.Id)
            
        menu.AppendSeparator()
        mitem = menu.Append(id=wx.NewId(),text="&Load sequence")
        self.Bind(wx.EVT_MENU, lambda x: self.OnLoadSequence(itm), id=mitem.Id)
        
        if itm.IsOk():
            if ev.is_root:
                mitem = menu.Append(id=wx.NewId(),text="&Save sequence")
                self.Bind(wx.EVT_MENU, lambda x: self.OnSaveSequence(itm), id=mitem.Id)
                menu.AppendSeparator()
                mitem = menu.Append(id=wx.NewId(),text="&Run sequence")
                self.Bind(wx.EVT_MENU, lambda x: self.OnRunSequence(itm), id=mitem.Id)
             
        self.PopupMenu(menu)
        menu.Destroy()

    def OnLoadSequence(self, itm):
        """
        
            Actions triggered by load ScanEvent sequence request.
            
            Parameters:
                event    -    wx.Event
        
        """
        dialog = wx.FileDialog(self, "Choose input file", os.getcwd(),"", "Configuration file (*.ini)|*.ini|All files (*.*)|*.*", wx.OPEN)
        if dialog.ShowModal() == wx.ID_OK:
            from xml.dom import minidom
            root = self.list_events.GetRootItem()
            xmldoc = minidom.parse(dialog.GetPath()).getElementsByTagName('events')
            elist = self.ParseXMLTree(xmldoc)
            self.CreateEventTree(elist[0],root)
            itm = self.list_events.FindItem(elist[0][0], root)
            self.list_events.CollapseAll() # collapse tree
            self.Unfold(-1, itm) # unfold newly added sequence
            dialog.Destroy()
    
    def ParseXMLFromString(self, string):
        """
        
            Parse XML tree represented as text string and add result to tree.
            
            Parameters:
                string    -    XML tree in string format (str)
        
        """
        if not(isinstance(string,str)):
            string = string.data
        from xml.dom import minidom
        xmldoc = minidom.parseString(string).getElementsByTagName('events')
        elist = self.ParseXMLTree(xmldoc)
        root = self.list_events.GetRootItem()
        self.CreateEventTree(elist[0],root)
        itm = self.list_events.FindItem(elist[0][0], root)
        self.list_events.CollapseAll() # collapse tree
        self.Unfold(-1, itm) # unfold newly added sequence
    
    def OnSaveSequence(self, itm):
        """
        
            Actions following save sequence request for given item.
            
            Parameters:
                itm    -    tree item (wx.TreeItem)
        
        """
        dialog = wx.FileDialog(self, "Choose output file", os.getcwd(),"", "Configuration file (*.ini)|*.ini|All files (*.*)|*.*", wx.SAVE)
        if dialog.ShowModal() == wx.ID_OK:
            doc = self.BuildXMLTree(itm)
            f = open(dialog.GetPath(),'w')
            doc.writexml(f,indent="  ", addindent="  ", newl="\n")
            f.close()
            dialog.Destroy()

    def OnReplaceEvent(self, old, new, event):
        """
        
            Replace given item by new item.
            
            Parameters:
                old    -    item to replace (ScanEvent)
                new    -    item to replace with (ScanEvent)
                event  -    wx.Event
        
        """
        for x in new.config:
            try:
                setattr(new,x,getattr(old,x))
            except:
                pass
        new.host = old.host
        itm = self.list_events.FindItem(old)
        self.list_events.GetItemData(itm).SetData(new)
        new.name = new.__extname__
        self.list_events.SetItemText(itm,new.name)
        new.refresh()
        new.create_property_root()
        new.populate()
    
    def RefreshEvents(self, subtree=None):
        """
        
            Refresh given subtree.
            
            Parameters:
                subtree    -    subtree to be refreshed (SubTree, whole tree if None)
        
        """
        if subtree==None:
            subtree = self.list_events.GetItemSubTree(self.list_events.GetRootItem(),ScanEvent)
        if isinstance(subtree.data,ScanEvent):
            subtree.data.refresh()
            subtree.data.populate()
        if isinstance(subtree,SubTree):
            for x in subtree.items:
                self.RefreshEvents(x)
    
    def Unfold(self, lvl=-1, itm=None):
        """
        
            Unfold given tree item.
            
            Parameters:
                lvl    -    number of levels to unfold (int, if -1 unfold all)
                itm    -    tree item (wx.TreeItem, root item if None)
        
        """
        if lvl==0:
            return
        if itm==None:
            itm = self.list_events.GetRootItem()
        if itm.IsOk():
            self.list_events.Expand(itm)
            if self.list_events.ItemHasChildren(itm):
                for x in self.list_events.GetItemChildren(itm):
                    self.Unfold(lvl-1,x)
    
    def UnfoldSequence(self, n=0):
        """
        
            Unfold given scan sequence.
            
            Parameters:
                n    -    scan sequence index (int)
        
        """
        try:
            self.list_events.CollapseAll()
        except:
            pass
        root = self.list_events.GetRootItem()
        itm, cookie = self.list_events.GetFirstChild(root)
        while itm.IsOk():
            if self.list_events.ItemHasChildren(itm):
                try:
                    self.list_events.ExpandAllChildren(itm)
                except:
                    pass
                break
            itm, cookie = self.list_events.GetNextChild(root,cookie)
    
    def CheckValidity(self, stree, meas):
        """
        
            Check validity of given scan sequence wrt given measurement structure.
            
            Parameters:
                stree    -    scan sequence (SubTree)
                meas     -    measurement structure (Measurement)
        
        """
        is_valid = True
        if isinstance(stree.data,ScanEvent):
            if stree.data.m_id < meas.count:
                is_valid = is_valid and stree.data.check_validity(meas.data[stree.data.m_id])
                self.plot_or_save[stree.data.m_id] = (self.plot_or_save[stree.data.m_id] or stree.data.is_save or stree.data.is_display)
                if stree.data.is_input and self.plot_or_save[stree.data.m_id]:
                    return False # can't plot or save before recording data
        if isinstance(stree,SubTree):
            for x in stree.items:
                is_valid = is_valid and self.CheckValidity(x,meas)
        return is_valid
    
    def OnRunSequence(self, itm):
        """
        
            Actions initiating a sequence run.
            
            Parameters:
                itm    -    root item for sequence (wx.TreeItem)
        
        """
        # refresh events
        self.RefreshEvents()
        
        # initialize data tree
        stree = self.list_events.GetItemSubTree(itm, ScanEvent)
        ev = self.list_events.GetItemData(itm).GetData()
        meas = Measurement(stree.data.name,stree)
        meas.BuildDataTree()
        
        # check that events work with associated data
        self.plot_or_save = [False]*meas.count
        if not(self.CheckValidity(stree,meas)):
            print "Invalid event tree!"
            return
        del self.plot_or_save
        
        # store event tree in XML format
        doc = self.BuildXMLTree(itm)
        meas.xml = doc.toprettyxml(indent="  ", newl="\n")
        
        # store system state (state of instruments before measurement)
        from terapy import hardware
        doc = hardware.get_system_state()
        meas.systemState = doc.toprettyxml(indent="  ", newl="\n")
        
        # disable controls
        self.ToggleControls(False)
        
        # announce measurement start
        pub.sendMessage("scan.start", data=meas)
        
        self.scanThread = ScanThread(ev, meas)
        self.scanThread.start()
        
    def OnEnableEvent(self, itm):
        """
        
            Actions following enable/disable given scan event.
            
            Parameters:
                itm    -    tree item (wx.TreeItem)
        
        """
        ev = self.list_events.GetItemData(itm).GetData()
        ev.is_active = not(ev.is_active)
        f = self.list_events.GetItemFont(itm)
        f.SetPointSize(10)
        if ev.is_active:
            #f.SetWeight(wx.FONTWEIGHT_BOLD)
            f.SetStyle(wx.FONTSTYLE_NORMAL)
        else:
            #f.SetWeight(wx.FONTWEIGHT_NORMAL)
            f.SetStyle(wx.FONTSTYLE_ITALIC)
        self.list_events.SetItemFont(itm,f)

    def OnRemoveEvent(self, event = None):
        """
        
            Actions following tree item delete request.
            
            Parameters:
                event    -    wx.Event
        
        """
        pos = self.list_events.Selection
        if pos.IsOk():
            ev = self.list_events.GetItemPyData(pos)
            if isinstance(ev,ScanEvent): # delete only items linked with a ScanEvent object
                self.list_events.Delete(pos)
    
    def OnAddEvent(self, ev, event):
        """
        
            Actions following scan event add request.
            
            Parameters:
                ev    -    scan event to be added (ScanEvent)
                event -    wx.Event
        
        """
        if ev.set():
            itm = self.list_events.HitTest(self.menuPosition)[0]
            if not(itm.IsOk()):
                itm = self.list_events.GetRootItem()
            self.InsertItem(ev, itm)

    def OnStartDrag(self, event = None):
        """
        
            Actions following beginning of drag action.
            
            Parameters:
                event    -    wx.Event
        
        """
        pos = event.GetItem()
        ev = self.list_events.GetItemData(pos).GetData()
        if isinstance(ev,ScanEvent):
            self.drag_object = pos
            ds = wx.DropSource(self.list_events)
            p = EventDragObject()
            p.SetData(self.drag_object)
            ds.SetData(p)
            ds.DoDragDrop(flags=wx.Drag_DefaultMove)
                
    def OnEndDrag(self, x, y, data):
        """
        
            Actions following drop on TreeCtrl.
            
            Parameters:
                x,y    -    coordinates of drop action (int)
                data   -    dropped data (str)
                            Passing drag and drop data in wxpython is incovenient.
                            Alternative used here: data is stored as self.drag_object
        
        """
        ditm = self.list_events.HitTest((x,y))[0]
        if not(ditm.IsOk()): return # not dropped on a tree entry
        # store dropped item
        oitm = self.drag_object
        stree = self.list_events.GetItemSubTree(oitm)
        # target item
        ev = self.list_events.GetItemData(ditm).GetData()
        
        if not(isinstance(ev, ScanEvent)):
            # target is not a ScanEvent instance, search for previous ScanEvent entry
            titm = ditm
            while titm.IsOk():
                if self.list_events.GetPrevSibling(titm).IsOk():
                    titm = self.list_events.GetPrevSibling(titm)
                else:
                    titm = self.list_events.GetItemParent(titm)
                if isinstance(self.list_events.GetItemData(titm).GetData(), ScanEvent):
                    break
            ditm = titm
            ev = self.list_events.GetItemData(ditm).GetData() 
            
        if ev.is_loop or ev.is_root:
            # target item is a recipient item => dropped as child
            titm = self.list_events.GetLastChild(ditm)
            parent = ditm
        else:
            # item will be dropped before target
            titm = self.list_events.GetPrevSibling(ditm)
            while titm.IsOk():
                if isinstance(self.list_events.GetItemData(titm).GetData(), ScanEvent):
                    break
                titm = self.list_events.GetPrevSibling(titm)
            parent = self.list_events.GetItemParent(ditm)
            if not(titm.IsOk()):
                # previous ScanEvent item is the parent item, must find where to place
                titm = self.list_events.GetFirstChild(parent)[0]
                if not(isinstance(self.list_events.GetItemData(titm).GetData(),PropertyNode)):
                    # this ScanEvent instance doesn't have a property node
                    titm = parent
        # check that target is not a child of dropped item
        dev = self.list_events.GetItemData(parent).GetData()
        if stree.IsDataInTree(dev): return
        
        if stree.data.is_root:
            # if dropped item is root, placed before previous root item
            while titm.IsOk():
                ev = self.list_events.GetItemData(titm).GetData()
                if isinstance(ev, ScanEvent):
                    if ev.is_root:
                        break
                titm = self.list_events.GetItemParent(titm)
            titm = self.list_events.GetPrevSibling(titm)
            parent = self.list_events.GetRootItem()
            if not(titm.IsOk()):
                titm = parent

        if parent.IsOk():
            # move dropped item to the right place
            nitm = self.list_events.InsertItem(parent,titm,stree.text,stree.image)
            self.list_events.CreateItemSubTree(nitm, stree)
            self.list_events.Delete(oitm)
            self.list_events.SelectItem(nitm)

    def OnButtonUp(self, event = None):
        """
        
            Actions triggered by Up button press.
            
            Parameters:
                event    -    wx.Event
        
        """
        pos = self.list_events.Selection
        if isinstance(self.list_events.GetItemData(pos).GetData(),ScanEvent):
            self.list_events.MoveItemUp(pos,ScanEvent)

    def OnButtonDown(self, event = None):
        """
        
            Actions triggered by Down button press.
            
            Parameters:
                event    -    wx.Event
        
        """
        pos = self.list_events.Selection
        if isinstance(self.list_events.GetItemData(pos).GetData(),ScanEvent):
            self.list_events.MoveItemDown(pos,ScanEvent)

    def OnButtonRun(self, event = None):
        """
        
            Actions triggered by Run/Stop button press.
            
            Parameters:
                event    -    wx.Event
        
        """
        if self.scanThread == None:
            pos = self.list_events.GetSelection()
            ev = self.list_events.GetItemData(pos).GetData()
            while pos.IsOk():
                if hasattr(ev,'is_root'):
                    if ev.is_root:
                        break
                pos = self.list_events.GetItemParent(pos)
                ev = self.list_events.GetItemData(pos).GetData()
            if pos.IsOk():
                self.OnRunSequence(pos)
        else:
            pub.sendMessage("scan.stop")
    
    def OnStopMeasurement(self, event = None):
        """
        
            Actions following stop measurement request.
            
            Parameters:
                event    -    wx.Event
        
        """
        while(self.scanThread.is_alive()):
            sleep(0.1)
        self.scanThread = None
        # set interface to idle (non-scanning) mode
        self.ToggleControls(True) # scan event list controls
    
    def ToggleControls(self, state=True):
        """
        
            Toggle state of widgets.
            
            Parameters:
                state    -    True = idle state, False = scan running
        
        """
        self.list_events.Enable(state)
        self.button_remove.Enable(state)
        self.button_down.Enable(state)
        self.button_add.Enable(state)
        self.button_up.Enable(state)
        self.button_run.Switch(state)