1
2
3 import wx,os,re,time
4 import wx.lib.customtreectrl as tree
5 import wx.lib.newevent
6
8
9
10
11
12
13
14
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
36
37
38
39
40
41
42 pattern = re.compile('^(?P<name>[^.]*?)(?P<seq>[0-9]{1,3})?(?P<junk>[^.0-9]*?)(?P<ext>[.].*)?$')
43
44
45
69
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
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
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
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
142
143 self.AddRoot('')
144 self._collapsing = False
145
146 - def root(self, path):
154
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
167 item = event.GetItem()
168 print "selected",self.GetItemText(item)
169
171 item = event.GetItem()
172 print "checked",self.GetItemText(item)
173
178
179
180
181 PathSelectedEvent,EVT_PATH_SELECTED = wx.lib.newevent.NewEvent()
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
216
219
221
222
223
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
232 self.validate()
233 self.path.Refresh()
234
238
240 oldpath = self.path.GetValue()
241 dlg = wx.DirDialog(self, message="Choose a directory",
242 style=wx.DD_DIR_MUST_EXIST)
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
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
271
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
278
280 item = event.GetItem()
281 if self._tree.IsSelected(item):
282 wx.PostEvent(self,ViewItemEvent(data=self._tree.GetPyData(item)))
283
284
285
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