Package reflectometry :: Package reduction :: Module selection

Source Code for Module reflectometry.reduction.selection

  1  # This program is public domain. 
  2   
  3  import wx,os,re,time 
  4  import wx.lib.customtreectrl as tree 
  5  import wx.lib.newevent 
  6   
7 -class Datasets:
8 # TODO: Datasets is not yet used. Want a selection widget that 9 # displays available datasets and metadata. Furthermore, said 10 # selection widget should have transparent access to the various 11 # data portals, presumably reverting to portal queries to retrieve 12 # the relevant information. This will require coordination with 13 # the SNS, ISIS, NCNR, etc. to locate and query the various data 14 # servers. 15 ''' 16 A dataset is a set of files with an embedded sequence number. The 17 Dataset class gathers filenames one at a time and composes them 18 into datasets. 19 20 The sequence number is assumed to be at most three digits, and to 21 be the last three digits before the extension. The dataset includes 22 both the sequence number and the extension. 23 24 For each dataset there is a count of the number of files in the 25 dataset (max 1000) and the starting and ending sequence numbers. 26 The filenames associated with the smallest and largest sequence 27 numbers are retained so the starting and ending time can be read 28 from the directory or the file (using method latest()). The entire 29 list of files in the dataset is also available. 30 31 Only files in the set of valid extensions are considered datasets. 32 All others are classed as 'other'. 33 ''' 34 35 # The following pattern matches: 36 # name ([^.]*?), the *? meaning match as few characters as possible 37 # seq ([0-9]{1,3})?, the {1,3} matches one to three digits, ()? optional 38 # junk [^.0-9], stuff after the sequence number such as bkg 39 # ext ([.].*)?, with ()? being an optional group 40 # Note that name is empty if there is no sequence number, meaning 41 # unsequenced items will all be dumped into the same dataset. 42 pattern = re.compile('^(?P<name>[^.]*?)(?P<seq>[0-9]{1,3})?(?P<junk>[^.0-9]*?)(?P<ext>[.].*)?$') 43 #match = pattern.match('Field5T4345bkg.uxd') 44 #dict((a,match.group(a)+"") for a in ['name','seq','junk','ext']) 45
46 - class Dataset:
47 count = 0 48 files = None 49 min = 1000 50 max = -1 51 minfile = '' 52 maxfile = '' 53 time = None # Time of the highest sequence number
54 - def __init__(self): self.files = []
55 - def add(self, seq, filename):
56 if seq < self.min: 57 self.min = seq 58 self.minfile = filename 59 if seq > self.max: 60 self.max = seq 61 self.maxfile = filename 62 self.time = None 63 self.count += 1 64 self.files.append(filename)
65 - def latest(self):
66 if self.time is not None: 67 self.time = os.path.getmtime(self.maxfile) 68 return time.strformat('%Y-%m-%d',time.localtime(self.time))
69
70 - def __init__(self, extensions=None):
71 self.seen = set() 72 self.extensions = extensions 73 self.dataset = {}
74
75 - def walk(self, pattern="", recurse=False, revisit=False):
76 """ 77 Walk a file pattern adding all new files into the list 78 of available datasets. The pattern has an implicit '*' 79 at the end, thus matching all leading elements. If recurse 80 is true, then enter subdirectories. If revisit is true, 81 revisit subdirectories that have already been visited. 82 83 Note: the goal of revisit is to support refresh on the 84 list of available files, however revisiting a whole 85 subtree can be expensive. Ideally this would run in 86 a separate thread with yields to the GUI which could 87 update the list of available datasets and/or a waitbar 88 while this is happening. Yet again good interface trumps 89 clean separation? 90 """ 91 for file in glob.iglob(pattern+'*'): 92 if revisit or file in self.seen: continue 93 if recurse and os.path.isdir(file): 94 self.seen.add(file) 95 self.walk(os.path.join(file,'*')) 96 else: 97 self.add(filename) 98 pass
99
100 - def add(self, filename):
101 """ 102 Add a single file to a dataset. 103 """ 104 if filename in self.seen: return 105 self.seen.add(filename) 106 base = os.path.basename(filename) 107 match = self.pattern.match(base) 108 name = match.group('name') 109 seq = match.group('seq') 110 seq = int(seq) if seq is not None else 0 111 ext = match.group('ext') 112 if ext in self.extensions or self.extensions is None: 113 item = base+ext 114 if item not in self.dataset: 115 self.dataset[item] = Dataset() 116 self.dataset[item].add(seq,filename) 117 pass
118
119 - def __add__(self, other):
120 """ 121 Merge two datasets (e.g., from different subtrees) 122 123 Not sure yet if this is necessary --- maybe we want to enter 124 the subtrees and see what is available? 125 """ 126 pass
127 128 # Update notification for paths in path selector 129 SelectItemEvent,EVT_ITEM_SELECT = wx.lib.newevent.NewEvent() 130 ViewItemEvent,EVT_ITEM_VIEW = wx.lib.newevent.NewEvent() 131
132 -class DirTree(tree.CustomTreeCtrl):
133 ''' 134 Traditional directory tree to manage the selection of multiple files. 135 ''' 136
137 - def __init__(self, *args, **kwargs):
138 kwargs['style'] = tree.TR_DEFAULT_STYLE|tree.TR_HIDE_ROOT 139 super(DirTree, self).__init__(*args, **kwargs) 140 self.Bind(tree.EVT_TREE_ITEM_EXPANDING, self.onExpandItem) 141 #self.Bind(tree.EVT_TREE_SEL_CHANGED, self.onSelected) 142 #self.Bind(tree.EVT_TREE_ITEM_CHECKED, self.onChecked) 143 self.AddRoot('') 144 self._collapsing = False
145
146 - def root(self, path):
147 item = self.GetRootItem() 148 self.DeleteChildren(item) 149 path = os.path.abspath(path) 150 self.SetPyData(item, path) 151 #self.SetItemHasChildren(item, os.path.isdir(path)) 152 #self.Toggle(item) # Expand the path 153 self.expand(item)
154
155 - def expand(self, item):
156 path = self.GetPyData(item) 157 if not os.path.isdir(path): return 158 for file in os.listdir(path): 159 if file[0] == '.': continue 160 childpath = os.path.join(path,file) 161 check = 0 if os.path.isdir(childpath) else 1 162 child = self.AppendItem(item,file,ct_type=check) 163 self.SetPyData(child,childpath) 164 self.SetItemHasChildren(child, check==0)
165
166 - def onSelected(self, event):
167 item = event.GetItem() 168 print "selected",self.GetItemText(item)
169
170 - def onChecked(self, event):
171 item = event.GetItem() 172 print "checked",self.GetItemText(item)
173
174 - def onExpandItem(self, event):
175 item = event.GetItem() 176 if not item.HasChildren(): 177 self.expand(item)
178 179 180 # Update notification for paths in path selector 181 PathSelectedEvent,EVT_PATH_SELECTED = wx.lib.newevent.NewEvent()
182 -class PathSelector(wx.Panel):
183 """ 184 wx megawidget displaying a label, a path entry box and a button to 185 select the path using the file dialog. 186 187 Events: 188 189 self.Bind(EVT_PATH_SELECTED, self.onPath) 190 ... 191 def onPath(self, event): print "path",event.path 192 """
193 - def __init__(self, parent, *args, **kw):
194 label = kw.pop('label','Path') 195 self.validcolor = kw.pop('validcolor',wx.BLACK) 196 self.errorcolor = kw.pop('errorcolor',wx.RED) 197 path = os.path.abspath(kw.pop('path','.')) 198 super(PathSelector,self).__init__(parent, *args, **kw) 199 self.label = wx.StaticText(self, -1, label) 200 self.path = wx.TextCtrl(self, -1, path, style=wx.TE_PROCESS_ENTER) 201 self.button = wx.Button(self, -1, "...") 202 self.Bind(wx.EVT_BUTTON, self.onBrowse, self.button) 203 self.path.Bind(wx.EVT_TEXT, self.onText) 204 self.path.Bind(wx.EVT_TEXT_ENTER, self.onEnter) 205 206 sizer = wx.BoxSizer(wx.HORIZONTAL) 207 flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALL 208 border=2 209 sizer.Add(self.label, flag=flag, border=border) 210 sizer.Add(self.path, 1, flag=flag|wx.GROW|wx.SHRINK, border=2) 211 sizer.Add(self.button, flag=flag, border=2) 212 self.SetSizer(sizer)
213
214 - def GetValue(self):
215 return self.path.GetValue()
216
217 - def isvalid(self):
218 return os.path.isdir(self.path.GetValue())
219
220 - def validate(self):
221 # TODO Change the background to pink if the text is invalid; 222 # the following doesn't seem to force a repaint, even when followed 223 # by a self.path.Refresh() 224 return 225 if self.isvalid(): 226 self.path.SetForegroundColour(self.validcolor) 227 else: 228 self.path.SetForegroundColour(self.errorcolor)
229
230 - def onText(self, event):
231 # This is the text before it is changed rather than after 232 self.validate() 233 self.path.Refresh()
234
235 - def onEnter(self, event):
236 if self.isvalid(): 237 wx.PostEvent(self,PathSelectedEvent(path=self.path.GetValue()))
238
239 - def onBrowse(self, event):
240 oldpath = self.path.GetValue() 241 dlg = wx.DirDialog(self, message="Choose a directory", 242 style=wx.DD_DIR_MUST_EXIST) #, defaultPath=oldpath) 243 if dlg.ShowModal() == wx.ID_OK: 244 path = dlg.GetPath() 245 self.path.Replace(0,len(oldpath),path) 246 wx.PostEvent(self,PathSelectedEvent(path=self.path.GetValue())) 247 dlg.Destroy() 248 pass
249 250
251 -class SelectionPanel(wx.Panel):
252 - def __init__(self, *args, **kw):
253 root = kw.pop('root',None) 254 if root is None: root = '.' 255 256 super(SelectionPanel,self).__init__(*args, **kw) 257 self._path = PathSelector(self,path=root) 258 self._tree = DirTree(self) 259 self._tree.root(root) 260 self._path.Bind(EVT_PATH_SELECTED, self.onRefresh) 261 self._tree.Bind(tree.EVT_TREE_ITEM_CHECKED, self.onChecked) 262 self._tree.Bind(tree.EVT_TREE_SEL_CHANGED, self.onSelected) 263 264 sizer = wx.BoxSizer(wx.VERTICAL) 265 sizer.Add(self._path, flag=wx.EXPAND) 266 sizer.Add(self._tree, 1, flag=wx.GROW|wx.SHRINK) 267 self.SetSizer(sizer)
268
269 - def onRefresh(self, event):
270 self._tree.root(self._path.GetValue())
271
272 - def onChecked(self, event):
273 item = event.GetItem() 274 self._tree.SelectItem(item) 275 wx.PostEvent(self,SelectItemEvent(data=self._tree.GetPyData(item), 276 enabled=self._tree.IsItemChecked(item)))
277 #print "checked",self._tree.GetItemText(item) 278
279 - def onSelected(self, event):
280 item = event.GetItem() 281 if self._tree.IsSelected(item): 282 wx.PostEvent(self,ViewItemEvent(data=self._tree.GetPyData(item)))
283 #print "selected",self._tree.GetItemText(item) 284 285
286 -def demo():
287 class DemoFrame(wx.Frame): 288 def __init__(self, *args, **kwargs): 289 super(DemoFrame, self).__init__(*args, **kwargs) 290 self.selection = SelectionPanel(self,root='..') 291 #self.Bind(tree.EVT_TREE_SEL_CHANGED, self.onSelected) 292 self.selection.Bind(EVT_ITEM_SELECT, self.onChecked)
293 294 def onSelected(self, event): 295 item = event.GetItem() 296 print "selected",item 297 298 def onChecked(self, event): 299 print "checked",event.data,event.enabled 300 301 frame = DemoFrame(None) 302 frame.Show() 303 304 if __name__ == "__main__": 305 app = wx.App(False) 306 demo() 307 app.MainLoop() 308