source: sans/utils/bt5/bt5plot2/bt5plot2 @ 469

Last change on this file since 469 was 469, checked in by ajj, 14 years ago
  • Fixed handling of data directory change (plot is cleared on change)
  • Added refresh functionality to allow for the arrival of new data files
  • Added filter functionality to limit view with regex
  • Property svn:executable set to *
File size: 14.0 KB
Line 
1#!/usr/bin/env python
2
3import sys
4import os
5import re
6import matplotlib
7import numpy
8matplotlib.use('GTK')
9
10from matplotlib.figure import Figure
11from matplotlib.axes import Subplot
12from matplotlib.backends.backend_gtk import FigureCanvasGTK, NavigationToolbar
13
14import usans
15
16try:
17    import pygtk
18    pygtk.require("2.0")
19   
20except:
21    pass
22
23try:
24    import gtk
25    import gtk.glade
26except:
27    sys.exit(1)
28
29
30
31class appGui:
32   
33    TARGETS = [('STRING', gtk.TARGET_SAME_APP, 0)]
34   
35    def __init__(self):
36       
37        gladefile = "bt5plot2.glade"
38        self.windowname = "win_Main"
39        self.wTree = gtk.glade.XML(gladefile, self.windowname)
40
41        event_dic = {"on_win_Main_destroy" : gtk.main_quit,
42                     "on_quit1_activate" : gtk.main_quit,
43                     "on_set_data_dir1_activate" : self.setdatadir,
44                     "on_xaxis_loglin_activate" : self.handle_xaxis_loglin,
45                     "on_yaxis_loglin_activate" : self.handle_yaxis_loglin,
46                     "on_plot_type_activate" : self.handle_plot_type_change,
47                     "on_btn_ClearPlot_clicked" : self.handle_clearplot,
48                     "on_btn_Refresh_clicked" : self.handle_refreshlist,
49                     "on_btn_Filter_clicked" : self.handle_filter}
50    #                 "on_tv_plotlist_key_press_event" : self.handle_plotlist_keypress}
51
52        #This is a bit clunky, but never mind.
53        #Set default plottype to rate. Glade definition sets that as default active button in menu
54        self.plottype = 'rate'
55       
56        self.wTree.signal_autoconnect(event_dic)
57
58        # Set up file list
59        self.filelistview = self.wTree.get_widget("tv_filelist")
60       
61        self.filelist = gtk.ListStore(str, 'gboolean', object, object, object)
62        self.filelist.set_sort_column_id(0, gtk.SORT_ASCENDING)
63
64        # Set up filtering of file list
65        self.filter_entry = self.wTree.get_widget("ent_filter")
66        self.filter_string = []
67        self.filter_string.append(self.filter_entry.get_text())
68        self.filelistfilter = self.filelist.filter_new()
69        self.filelistfilter.set_visible_func(self.filter_filelist,self.filter_string)
70
71        self.filelistview.set_model(self.filelistfilter)
72
73        self.cellrenderertoggle = gtk.CellRendererToggle()
74        self.cellrenderertoggle.set_property('activatable', True)
75        self.cellrenderertoggle.connect("toggled", self.handle_plot_toggle, self.filelistfilter)
76   
77        self.AddFileListColumns()
78
79        #fill the file list
80        self.FillFileList(self.GetBT5DirList())
81
82        # Set up graphing widget to display xpeek data
83        self.figure = Figure(figsize=(4, 4), dpi=72)
84        self.axis = self.figure.add_subplot(111)
85        self.axis.set_yscale('log')
86        self.axis.set_aspect('auto')
87        self.axis.set_autoscale_on('True')
88        self.axis.set_xlabel('Motor position')
89        self.axis.set_ylabel('Counts')
90        self.axis.grid(True)
91       
92        self.canvas = FigureCanvasGTK(self.figure)
93        self.figure.canvas.mpl_connect('pick_event',self.handle_plot_click)
94        self.canvas.show()
95       
96        self.plotView = self.wTree.get_widget("vbox4")
97        self.plotView.pack_start(self.canvas, True, True)   
98       
99        self.metadataView = self.wTree.get_widget("tv_metadata")
100        self.mdlist = gtk.ListStore(str,str)
101       
102       
103        #self.filelistview.enable_model_drag_source( gtk.gdk.BUTTON1_MASK,
104                #                                  self.TARGETS,
105                #                                   gtk.gdk.ACTION_COPY)
106
107        #self.filelistview.connect("drag_data_get", self.dnd_data_getdata)
108       
109       
110    def AddFileListColumns(self):
111        """This function adds a column to the list view.
112        First it create the gtk.TreeViewColumn and then set
113        some needed properties"""
114                       
115        column = gtk.TreeViewColumn('Filename', gtk.CellRendererText(), text=0)
116        column.set_resizable(True)       
117        column.set_sort_column_id(0)
118        self.filelistview.append_column(column)
119
120        column = gtk.TreeViewColumn('', self.cellrenderertoggle, active=1)
121        self.filelistview.append_column(column)
122        return
123       
124    def GetBT5DirList(self):
125        """Function to parse the directory listing of the current working directory
126             and create a list of filenames that are BT5 data files"""
127             
128        dirlist = os.listdir(os.getcwd())
129       
130        bt5list = [ x for x in dirlist if (x.find('.bt5') > 0 and usans.isBT5Data(x))]
131       
132        return bt5list
133       
134   
135    def FillFileList(self, filenames):
136        self.filelist.clear()
137        for filename in filenames:
138                self.filelist.append([filename, 0, 0, 0, 0])
139        return
140           
141    def RefreshFileList(self,filenames):       
142        #print len(filenames)
143       
144        deletelist = []
145       
146        treestore = self.filelistview.get_model()
147        treestore.foreach(self.filelist_match_filename, (filenames,deletelist))
148         
149        for filename in filenames:
150            self.filelist.append([filename, 0, 0, 0, 0])
151       
152        deletelist.reverse()
153        for path in deletelist:
154            treestore.remove(treestore.get_iter(path))
155        #print len(filenames)   
156        return
157
158    def filelist_match_filename(self, model, path, iter, data):
159       
160        mval = model.get_value(iter,0)
161       
162        if mval in data[0]:
163            del data[0][data[0].index(mval)]
164        else:
165            data[1].append(path)
166           
167        return False
168   
169    def handle_refreshlist(self,widget):
170
171        self.RefreshFileList(self.GetBT5DirList())
172       
173        return
174
175    def filter_filelist(self, model, iter, data):
176       
177        if model.get_value(iter,0):       
178            match = re.match(data[0],model.get_value(iter,0))
179        else:
180            match = None
181       
182        if match is None:
183            return False
184        else:
185            return True
186       
187    def handle_filter(self,widget):
188       
189        print "Filtering"
190        del self.filter_string[:]
191        self.filter_string.append(self.filter_entry.get_text())
192        print self.filter_string[0]
193        self.filelistfilter.refilter()
194       
195        return
196
197    def setdatadir(self, widget):
198       
199        #Clear plot before selecting new folder
200        #This is a bit clunky, but it avoids a lot of pain for the moment
201       
202        self.clearplot()
203       
204        chooser = gtk.FileChooserDialog(title="Select Data Directory", action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
205                                  buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK))
206        chooser.set_default_response(gtk.RESPONSE_OK)
207        chooser.set_current_folder(os.getcwd())
208        response = chooser.run()
209        if response == gtk.RESPONSE_OK:
210            os.chdir(chooser.get_filename())
211            self.FillFileList(self.GetBT5DirList())
212        chooser.destroy()
213
214    def handle_plot_toggle(self, filter_cell, filter_path, filter_model):
215        model = filter_model.get_model()
216        path = filter_model.convert_path_to_child_path(filter_path)
217        model[path][1] = not model[path][1]
218
219        if model[path][1]:
220            #load data
221            data,metadata = usans.getBT5DataFromFile(model[path][0])
222            model[path][2] = (data,metadata)
223            #add plot
224            self.add_plot(model, path)
225        else:
226            #remove plot
227            self.remove_plot(model, path)
228        return
229
230    def add_plot(self, model, path):
231       
232        self.make_plottable_dataset(model, path, self.plottype)
233
234        if self.plottype == 'split':
235                model[path][4] = self.axis.plot(model[path][3][0],model[path][3][1], 'o',
236                                                                                         model[path][3][0],model[path][3][2], 'o',
237                                                                                         model[path][3][0],model[path][3][3], 'o',
238                                                                                         model[path][3][0],model[path][3][4], 'o',
239                                                                                         model[path][3][0],model[path][3][5], 'o')
240        else:
241            model[path][4] = self.axis.plot(model[path][3][0],model[path][3][1], 'bo', picker=5)
242       
243        self.rescale_and_redraw()
244        #self.canvas.draw()
245        return
246
247    def make_plottable_dataset(self, model, path, type):
248         
249         data,metadata = model[path][2]
250                 
251         if type == 'total':
252             #generate totals
253             xdata = []
254             ydata = []     
255             
256             mvals = data.keys()
257             mvals.sort(usans.numeric_compare)
258             for mval in mvals:
259                 xdata.append(mval)
260                 ydata.append(data[mval][1] + data[mval][2] + data[mval][4] + data[mval][5] + data[mval][6])
261             
262             model[path][3] = [xdata, ydata]
263             
264         elif type == 'rate':
265             # generate countrate
266             xdata = []
267             ydata = []
268             
269             mvals = data.keys()
270             mvals.sort(usans.numeric_compare)
271             for mval in mvals:
272                 xdata.append(mval)
273             
274             if metadata['base'] == 'TIME':
275                #Counting in TIME base, so normalize by seconds
276                cnttime = metadata['mon']
277                for mval in mvals:
278                     ydata.append((data[mval][1] + data[mval][2] + data[mval][4] + data[mval][5] + data[mval][6])/cnttime)
279             else:
280                #Must be counting in monitor base so normalize by monitor
281                moncts = metadata['mon']
282                for mval in mvals:
283                    ydata.append((data[mval][1] + data[mval][2] + data[mval][4] + data[mval][5] + data[mval][6])/cnttime)
284             
285             model[path][3] = [xdata, ydata]
286             
287         elif type == 'trans':
288             xdata = []
289             ydata = []
290             
291             mvals = data.keys()
292             mvals.sort(usans.numeric_compare)
293             for mval in mvals:
294                 xdata.append(mval)
295                 ydata.append(data[mval][3])
296             
297             model[path][3] = [xdata, ydata]             
298         
299         elif type == 'mon':
300             xdata = []
301             ydata = []
302             
303             mvals = data.keys()
304             mvals.sort(usans.numeric_compare)
305             for mval in mvals:
306                 xdata.append(mval)
307                 ydata.append(data[mval][0])
308             
309             model[path][3] = [xdata, ydata]             
310             
311         elif type == 'split':
312             xdata = []
313             ydata1 = []
314             ydata2 = []
315             ydata3 = []
316             ydata4 = []
317             ydata5 = []
318             
319             mvals = data.keys()
320             mvals.sort(usans.numeric_compare)
321             for mval in mvals:
322                 xdata.append(mval)
323                 ydata1.append(data[mval][1])   
324                 ydata2.append(data[mval][2])   
325                 ydata3.append(data[mval][4])   
326                 ydata4.append(data[mval][5])   
327                 ydata5.append(data[mval][6])   
328
329             model[path][3] = [xdata,ydata1,ydata2,ydata3,ydata4,ydata5]
330         else:
331                pass
332             
333         return
334
335    def remove_plot(self, filter_model, filter_path):
336 
337        for line in model[path][4]:
338                self.axis.lines.remove(line)
339       
340        if (len(self.axis.lines) > 0):
341                self.rescale_and_redraw()
342        else:
343            self.canvas.draw() 
344
345        return
346
347    def handle_xaxis_loglin(self, widget):
348
349
350        if (self.axis.get_xscale() == "log"):
351            self.axis.set_xscale('linear')
352        else:
353            self.axis.set_xscale('log')       
354
355
356        self.rescale_and_redraw()
357       
358        return   
359
360    def handle_yaxis_loglin(self, widget):
361
362
363        if (self.axis.get_yscale() == "log"):
364            self.axis.set_yscale('linear')
365        else:
366            self.axis.set_yscale('log')       
367
368        self.canvas.draw()
369        return
370       
371    def handle_plot_type_change(self,widget):
372               
373        if widget.get_active():
374                self.plottype = widget.get_name().split('_')[1]
375                #print self.plottype
376               
377        return
378       
379    def handle_clearplot(self,widget):
380       
381        self.clearplot()
382       
383        return
384       
385   
386    def clearplot(self):
387        model = self.filelistview.get_model().get_model()
388        iter = model.iter_children(None)
389        while iter:
390            path = model.get_path(iter)
391            if model[path][1] != 0:
392                for line in model[path][4]:
393                    self.axis.lines.remove(line)   
394                    model[path][1] = not model[path][1]         
395            iter = model.iter_next(iter)
396
397        #Remove any lines left over - shouldn't be any, but let's tidy up
398        #for line in self.axis.lines:
399             #self.axis.lines.remove(line)
400       
401        self.canvas.draw()
402        return 
403       
404    def rescale_and_redraw(self):
405
406        xdata = []
407        ydata = []
408
409        for line in self.axis.lines:
410                if self.axis.get_xscale() == 'log':
411                        xdata.extend([xval for xval in line.get_xdata() if xval > 0])
412                else:
413                        xdata.extend(line.get_xdata())
414                if self.axis.get_yscale() == 'log':
415                        ydata.extend([xval for xval in line.get_ydata() if xval > 0])
416                else:
417                        ydata.extend(line.get_ydata())
418     
419        #set limits
420        xmin = float(min(xdata))
421        xmax = float(max(xdata))
422        ymin = float(min(ydata))
423        ymax = float(max(ydata))       
424
425        #adjust for size of markers (sort of)
426        xmin = xmin - 0.1*abs(xmin)
427        xmax = xmax + 0.1*abs(xmax)
428        ymin = ymin - 0.1*abs(ymin)
429        ymax = ymax + 0.1*abs(ymax)
430               
431        self.axis.set_xlim(xmin,xmax)
432        self.axis.set_ylim(ymin,ymax)
433       
434        #self.axis.autoscale_view()
435        self.canvas.draw()
436
437        return
438
439    def handle_plot_click(self,event):
440        if isinstance(event.artist, matplotlib.lines.Line2D):
441            pickedline = event.artist
442            xdata = pickedline.get_xdata()
443            ydata = pickedline.get_ydata()
444            ind = event.ind
445            print 'Plot Click: ',zip(numpy.take(xdata,ind), numpy.take(ydata,ind))
446
447app = appGui()
448gtk.main()
Note: See TracBrowser for help on using the repository browser.