source: sasview/sansguiframe/src/sans/guiframe/data_processor.py @ 15f68ce

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 15f68ce was 1ec979d, checked in by Jae Cho <jhjcho@…>, 13 years ago

gave sleep0.5 for error msg and send nan_to_num for nan to plottools

  • Property mode set to 100644
File size: 50.3 KB
Line 
1"""
2Implement grid used to store data
3"""
4import wx
5import numpy
6import math
7import time
8import re
9import os
10import sys
11import copy
12from wx.lib.scrolledpanel import ScrolledPanel
13import  wx.grid as  Grid
14import wx.aui
15from wx.aui import AuiNotebook as nb
16import wx.lib.sheet as sheet
17from sans.guiframe.panel_base import PanelBase
18from sans.guiframe.utils import format_number
19from sans.guiframe.events import NewPlotEvent
20from sans.guiframe.events import StatusEvent 
21from danse.common.plottools import plottables
22from sans.guiframe.dataFitting import Data1D
23
24FUNC_DICT = {"sqrt": "math.sqrt",
25             "pow": "math.sqrt"}
26
27class BatchCell:
28    """
29    Object describing a cell in  the grid.
30   
31    """
32    def __init__(self):
33        self.label = ""
34        self.value = None
35        self.col = -1
36        self.row = -1
37        self.object = []
38       
39
40def parse_string(sentence, list):
41    """
42    Return a dictionary of column label and index or row selected
43    :param sentence: String to parse
44    :param list: list of columns label
45    """
46    toks = []
47    p2 = re.compile(r'\d+')
48    p = re.compile(r'[\+\-\*\%\/]')
49    labels = p.split(sentence)
50    col_dict = {}
51    for elt in labels:
52        rang = None
53        temp_arr = []
54        for label in  list:
55            label_pos =  elt.find(label)
56            if label_pos != -1:
57                if elt.count(',') > 0:
58                    new_temp = []
59                    temp = elt.split(label)
60                    for item in temp:
61                        range_pos = item.find(":")
62                        if range_pos != -1:
63                            rang = p2.findall(item)
64                            for i in xrange(int(rang[0]), int(rang[1])+1 ):
65                                new_temp.append(i)
66                    temp_arr += new_temp
67                else:
68                    temp = elt.split(label)
69                    for item in temp:
70                        range_pos = item.find(":")
71                        if range_pos != -1:
72                            rang = p2.findall(item)
73                            for i in xrange(int(rang[0]), int(rang[1])+1 ):
74                                temp_arr.append(i)
75                col_dict[elt] = (label, temp_arr)
76    return col_dict
77
78         
79         
80class SPanel(ScrolledPanel):
81    def __init__(self, parent, *args, **kwds):
82        ScrolledPanel.__init__(self, parent , *args, **kwds)
83        self.SetupScrolling() 
84       
85class GridPage(sheet.CSheet):
86    """
87    """
88    def __init__(self, parent, panel=None):
89        """
90        """
91        sheet.CSheet.__init__(self, parent)
92       
93        self.AdjustScrollbars()
94        #self.SetLabelBackgroundColour('#DBD4D4')
95        self.uid = wx.NewId()
96        self.parent = parent
97        self.panel = panel
98        self.col_names = []
99        self.data_inputs = {}
100        self.data_outputs = {}
101        self.data = None
102        self.details = ""
103        self.file_name = None
104        self._cols = 50
105        self._rows = 51
106        self.last_selected_row = -1
107        self.last_selected_col = -1
108        self.col_width = 30
109        self.row_height = 20
110        self.max_row_touse = 0
111        self.axis_value = []
112        self.axis_label = ""
113        self.selected_cells = []
114        self.selected_cols = []
115        self.selected_rows = []
116        self.plottable_cells = []
117        self.plottable_flag = False
118        self.SetColMinimalAcceptableWidth(self.col_width)
119        self.SetRowMinimalAcceptableHeight(self.row_height)
120        self.SetNumberRows(self._cols)
121        self.SetNumberCols(self._rows)
122        self.AutoSize()
123        self.list_plot_panels = {}
124        self.default_col_width = 75
125        if self.GetNumberCols() > 0:
126            self.default_col_width =  self.GetColSize(0)
127        self.Bind(wx.grid.EVT_GRID_LABEL_LEFT_CLICK, self.on_left_click)
128        self.Bind(wx.grid.EVT_GRID_LABEL_RIGHT_CLICK, self.on_right_click)
129        self.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.on_selected_cell)
130        self.Bind(wx.grid.EVT_GRID_CMD_CELL_CHANGE, self.on_edit_cell)
131       
132       
133    def on_edit_cell(self, event):
134        """
135        """
136        row, col = event.GetRow(), event.GetCol()
137        if row > self.max_row_touse:
138            self.max_row_touse = row
139        event.Skip()
140       
141    def on_selected_cell(self, event):
142        """
143        Handler catching cell selection
144        """
145        flag = event.CmdDown() or event.ControlDown()
146        row, col = event.GetRow(), event.GetCol()
147        cell = (row, col)
148        event.Skip()
149        if not flag:
150            self.selected_cells = []
151            self.axis_value = []
152            self.axis_label = ""
153        if col >= 0:
154             self.axis_label = self.GetCellValue(0, col)
155        if cell not in self.selected_cells:
156            if row > 0:
157                self.selected_cells.append(cell)
158        else:
159            if flag:
160                self.selected_cells.remove(cell)
161        self.axis_value = []
162        for cell_row, cell_col in self.selected_cells:
163            if cell_row > 0 and cell_row < self.max_row_touse:
164                self.axis_value.append(self.GetCellValue(cell_row, cell_col))
165       
166   
167               
168    def on_left_click(self, event):
169        """
170        Catch the left click on label mouse event
171        """
172        event.Skip()
173        flag = event.CmdDown() or event.ControlDown()
174        col = event.GetCol()
175        row = event.GetRow()
176        if not flag:
177            self.selected_cols = []
178            self.selected_rows = []
179            self.selected_cells = []
180            self.axis_label = ""
181            self.plottable_list = []
182            self.plottable_cells = []
183            self.plottable_flag = False
184       
185        self.last_selected_col = col
186        self.last_selected_row = row
187        if row != -1 and row not in self.selected_rows:
188             self.selected_rows.append(row)
189             
190        if col != -1:
191            for row in range(1, self.GetNumberRows()+ 1):
192                cell = (row, col)
193                if row > 0 and row < self.max_row_touse:
194                    if cell not in self.selected_cells:
195                        self.selected_cells.append(cell)
196            self.selected_cols.append(col)
197            self.axis_value = []
198            for cell_row, cell_col in self.selected_cells:
199                self.axis_value.append(self.GetCellValue(cell_row, cell_col))
200            self.axis_label = self.GetCellValue(0, col)
201       
202    def on_right_click(self, event):
203        """
204        Catch the right click mouse
205        """
206       
207        col = event.GetCol()
208        self.selected_cols = []
209        self.selected_cols.append(col)
210        # Slicer plot popup menu
211        slicerpop = wx.Menu()
212        col_label_menu  = wx.Menu()
213        c_name = self.GetCellValue(0, col) 
214        label = "Insert column before %s " % str(c_name)
215        slicerpop.AppendSubMenu(col_label_menu , 
216                                 '&%s' % str(label), str(label))
217        col_name = [self.GetCellValue(0, c) 
218                        for c in range(self.GetNumberCols())]
219        row = 0
220        label = self.GetCellValue(row, col)
221        self.insert_col_menu(col_label_menu, label, self)
222       
223       
224        col_after_menu  = wx.Menu()
225        label = "Insert column after %s " % str(c_name)
226        slicerpop.AppendSubMenu(col_after_menu , 
227                                 '&%s' % str(label), str(label))
228        col_name = [self.GetCellValue(0, c) 
229                        for c in range(self.GetNumberCols())]
230        self.insert_after_col_menu(col_after_menu, label, self)
231       
232       
233        id = wx.NewId()   
234        hint = 'Remove selected column %s'
235        slicerpop.Append(id, '&Remove Column', hint)
236        wx.EVT_MENU(self, id, self.on_remove_column)
237       
238        pos = wx.GetMousePosition()
239        pos = self.ScreenToClient(pos)
240        self.PopupMenu(slicerpop, pos)
241        event.Skip()
242       
243    def insert_col_menu(self, menu, label, window):
244        """
245        """
246        id = wx.NewId()
247        title = "Empty"
248        hint = 'Insert empty column before %s' % str(label)
249        menu.Append(id, title, hint)
250        wx.EVT_MENU(window, id, self.on_insert_column)
251        row = 0
252        col_name = [self.GetCellValue(row, col) 
253                        for col in range(self.GetNumberCols())]
254        for c_name in self.data.keys():
255            if c_name not in col_name:
256                id = wx.NewId()
257                hint = "Insert %s column before the " % str(c_name)
258                hint += " %s column" % str(label)
259                menu.Append(id, '&%s' % str(c_name), hint)
260                wx.EVT_MENU(window, id, self.on_insert_column)
261           
262    def insert_after_col_menu(self, menu, label, window):
263        """
264        """
265        id = wx.NewId()
266        title = "Empty"
267        hint = 'Insert empty column after %s' % str(label)
268        menu.Append(id, title, hint)
269        wx.EVT_MENU(window, id, self.on_insert_after_column)
270        row = 0
271        col_name = [self.GetCellValue(row, col) 
272                        for col in range(self.GetNumberCols())]
273        for c_name in self.data.keys():
274            if c_name not in col_name:
275                id = wx.NewId()
276                hint = "Insert %s column after the " % str(c_name)
277                hint += " %s column" % str(label)
278                menu.Append(id, '&%s' % str(c_name), hint)
279                wx.EVT_MENU(window, id, self.on_insert_after_column)
280                               
281    def on_remove_column(self, event=None):
282        """
283        """
284        if self.selected_cols is not None or len(self.selected_cols) > 0:
285            col = self.selected_cols[0]
286            self.remove_column(col=col, numCols=1)
287           
288    def remove_column(self, col, numCols=1):
289        """
290        Remove column to the current grid
291        """
292        # add data to the grid   
293        row = 0
294        col_name = self.GetCellValue(row, col)
295        self.data[col_name] = []
296        for row in range(1, self.GetNumberRows() + 1):
297            if row < self.max_row_touse:
298                value = self.GetCellValue(row, col)
299                self.data[col_name].append(value)
300                for k , value_list in self.data.iteritems():
301                    if k != col_name:
302                        length = len(value_list)
303                        if length < self.max_row_touse:
304                            diff = self.max_row_touse - length
305                            for i in range(diff):
306                                self.data[k].append("")
307        self.DeleteCols(pos=col, numCols=numCols, updateLabels=True)
308           
309    def on_insert_column(self, event):
310        """
311        """
312        if self.selected_cols is not None or len(self.selected_cols) > 0:
313            col = self.selected_cols[0]
314            # add data to the grid   
315            row = 0
316            id = event.GetId()
317            col_name = event.GetEventObject().GetLabelText(id)
318            self.insert_column(col=col, col_name=col_name)
319            if  not issubclass(event.GetEventObject().__class__ , wx.Menu):
320                col += 1
321                self.selected_cols[0] += 1
322               
323    def on_insert_after_column(self, event):
324        """
325        Insert the given column after the highlighted column
326        """
327        if self.selected_cols is not None or len(self.selected_cols) > 0:
328            col = self.selected_cols[0] + 1
329            # add data to the grid   
330            row = 0
331            id = event.GetId()
332            col_name = event.GetEventObject().GetLabelText(id)
333            self.insert_column(col=col, col_name=col_name)
334            if  not issubclass(event.GetEventObject().__class__ , wx.Menu):
335                self.selected_cols[0] += 1
336           
337    def insert_column(self, col, col_name):
338        """
339        """ 
340         
341        row = 0
342        self.InsertCols(pos=col, numCols=1, updateLabels=True)
343        if col_name.strip() != "Empty":
344            self.SetCellValue(row, col, str(col_name.strip()))
345        if col_name in self.data.keys():
346            value_list = self.data[col_name]
347            cell_row =  1
348            for value in value_list:
349                label = format_number(value, high=True)
350                self.SetCellValue(cell_row, col, str(label))
351                cell_row += 1
352        self.AutoSizeColumn(col, True)
353        width = self.GetColSize(col)
354        if width < self.default_col_width:
355           self.SetColSize(col, self.default_col_width)
356        self.ForceRefresh()
357       
358    def on_set_x_axis(self, event):
359        """
360        """
361        self.panel.set_xaxis(x=self.axis_value, label=self.axis_label)
362   
363    def on_set_y_axis(self, event):
364        """
365        """
366        self.panel.set_yaxis(y=self.axis_value, label=self.axis_label)     
367           
368    def set_data(self, data_inputs, data_outputs, details, file_name):
369        """
370        Add data to the grid
371        :param data_inputs: data to use from the context menu of the grid
372        :param data_ouputs: default columns deplayed
373        """
374        self.file_name = file_name
375        self.details = details
376       
377        if data_outputs is None:
378            data_outputs = {}
379        self.data_outputs = data_outputs
380        if data_inputs is None:
381            data_inputs = {}
382        self.data_inputs = data_inputs
383        self.data = {}
384        for item in (self.data_outputs, self.data_inputs):
385            self.data.update(item)
386        if  len(self.data_outputs) > 0:
387            self._cols = self.GetNumberCols()
388            self._rows = self.GetNumberRows()
389            self.col_names = self.data_outputs.keys()
390            self.col_names.sort() 
391            nbr_user_cols = len(self.col_names)
392            #Add more columns to the grid if necessary
393            if nbr_user_cols > self._cols:
394                new_col_nbr = nbr_user_cols -  self._cols
395                self.AppendCols(new_col_nbr, True)
396            #Add more rows to the grid if necessary 
397            nbr_user_row = len(self.data_outputs.values())
398            if nbr_user_row > self._rows + 1:
399                new_row_nbr =  nbr_user_row - self._rows
400                self.AppendRows(new_row_nbr, True)
401            # add data to the grid   
402            row = 0
403            col = 0
404            cell_col = 0
405            for col_name in  self.col_names:
406                # use the first row of the grid to add user defined labels
407                self.SetCellValue(row, col, str(col_name))
408                col += 1
409                cell_row =  1
410                value_list = self.data_outputs[col_name]
411               
412                for value in value_list:
413                    label = value
414                    if issubclass(value.__class__, BatchCell):
415                        label = value.label
416                    try:
417                        float(label)
418                        label = format_number(label, high=True)
419                    except:
420                        label = str(label)
421                    self.SetCellValue(cell_row, cell_col, label)
422                    self.AutoSizeColumn(cell_col, True)
423                    width = self.GetColSize(cell_col)
424                    if width < self.default_col_width:
425                       self.SetColSize(cell_col, self.default_col_width)
426                   
427                    cell_row += 1
428                cell_col += 1
429                if cell_row > self.max_row_touse:
430                    self.max_row_touse = cell_row
431        self.ForceRefresh()
432       
433    def get_grid_view(self):
434        """
435        Return value contained in the grid
436        """
437        grid_view = {}
438        for col in xrange(self.GetNumberCols()):
439            label = self.GetCellValue(row=0, col=col) 
440            label = label.strip()
441            if label != "":
442                grid_view[label] = []
443                for row in range(1, self.max_row_touse):
444                    value = self.GetCellValue(row=row, col=col)
445                    if value != "":
446                        grid_view[label].append(value) 
447                    else:
448                        grid_view[label].append(None) 
449        return grid_view
450   
451class Notebook(nb, PanelBase):
452    """
453    ## Internal name for the AUI manager
454    window_name = "Fit panel"
455    ## Title to appear on top of the window
456    """
457    window_caption = "Notebook "
458   
459    def __init__(self, parent, manager=None, data=None, *args, **kwargs):
460        """
461        """
462        nb.__init__(self, parent, -1,
463                    style=wx.aui.AUI_NB_WINDOWLIST_BUTTON| 
464                    wx.aui.AUI_BUTTON_DOWN|
465                    wx.aui.AUI_NB_DEFAULT_STYLE|
466                    wx.CLIP_CHILDREN)
467        PanelBase.__init__(self, parent)
468        self.enable_close_button()
469        self.parent = parent
470        self.manager = manager
471        self.data = data
472        #add empty page
473        self.add_empty_page()
474       
475        self.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close_page)
476   
477    def add_empty_page(self):
478        """
479        """
480        grid = GridPage(self, panel=self.parent)
481        self.AddPage(grid, "", True)
482        pos = self.GetPageIndex(grid)
483        title = "Batch " + str(self.GetPageCount())
484        self.SetPageText(pos, title)
485        self.SetSelection(pos)
486        return grid , pos
487       
488    def enable_close_button(self):
489        """
490        display the close button on tab for more than 1 tabs else remove the
491        close button
492        """
493        if self.GetPageCount() <= 1:
494            style = self.GetWindowStyleFlag() 
495            flag = wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
496            if style & wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB == flag:
497                style = style & ~wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
498                self.SetWindowStyle(style)
499        else:
500            style = self.GetWindowStyleFlag()
501            flag = wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
502            if style & wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB != flag:
503                style |= wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
504                self.SetWindowStyle(style)
505             
506    def on_edit_axis(self):
507        """
508        Return the select cell of a given selected column
509        """
510        pos = self.GetSelection()
511        grid = self.GetPage(pos)
512        if len(grid.selected_cols) > 1:
513            msg = "Edit axis doesn't understand this selection.\n"
514            msg += "Please select only one column"
515            raise ValueError, msg
516        if len(grid.selected_cols) == 1:
517            col = grid.selected_cols[0]
518            if len(grid.selected_cells) > 0:
519                cell_row, cell_col = grid.selected_cells[0]
520                if cell_col  != col:
521                    msg = "Edit axis doesn't understand this selection.\n"
522                    msg += "Please select element of the same col"
523                    raise ValueError, msg
524        return grid.selected_cells
525       
526   
527    def get_column_labels(self):
528        """
529        return dictionary of columns labels of the current page
530        """
531        pos = self.GetSelection()
532        grid = self.GetPage(pos)
533        labels = {}
534        row = 0
535        for col in range(grid.GetNumberCols()):
536            label = grid.GetCellValue(row, col)
537            if label.strip() != "" :
538                labels[label.strip()] = col
539        return labels
540       
541    def create_axis_label(self, cell_list):
542        """
543        Receive a list of cells and  create a string presenting the selected
544        cells.
545        :param cell_list: list of tuple
546       
547        """
548        pos = self.GetSelection()
549        grid = self.GetPage(pos)
550        label = ""
551        col_name = ""
552        def create_label(col_name,  row_min=None, row_max=None):
553            """
554            """
555            result = ""
556            if row_min is not  None or row_max is not None:
557                if row_min is None:
558                    result = str(row_max) + "]"
559                elif row_max is None:
560                     result = str(col_name) + "[" + str(row_min) + ":"
561                else:
562                    result = str(col_name) +  "[" + str(row_min) + ":"
563                    result += str(col_name) + str(row_max) + "]"
564            return result
565           
566        if len(cell_list) > 0:
567            if len(cell_list) == 1:
568                 row_min, col  = cell_list[0]   
569                 col_name = grid.GetCellValue(0, col)
570                 label = create_label(col_name, row_min+1 , row_min+1)
571                 return  label,  col_name
572            else:
573                temp_list = copy.deepcopy(cell_list)
574                temp_list.sort()
575                length = len(temp_list)
576                row_min, col  = temp_list[0]   
577                row_max, _  = temp_list[length-1]
578                col_name = grid.GetCellValue(0, col)
579                index = 0
580                for row in xrange(row_min, row_max +1):
581                    if index > 0 and index < len(temp_list):
582                        new_row, _ = temp_list[index]
583                        if row != new_row:
584                            temp_list.insert(index, (None, None))
585                            if index -1 >=0:
586                                new_row, _ = temp_list[index-1]
587                                label += create_label(col_name, None, new_row +1)
588                                label += ","
589                            if index + 1 < len(temp_list):
590                                new_row, _ = temp_list[index + 1]
591                                label += create_label(col_name, new_row+1, None)
592                    if index == 0:
593                        label += create_label(col_name,  row_min+1, None)
594                    elif index == len(temp_list)-1:
595                        label += create_label(col_name, None, row_max+1)
596                    index += 1
597                return label, col_name
598   
599    def on_close_page(self, event):
600        """
601        close the page
602        """
603        if self.GetPageCount() == 1:
604            event.Veto()
605        self.enable_close_button()
606       
607    def set_data(self, data_inputs, data_outputs, details="", file_name=None):
608        if data_outputs is None or data_outputs == {}:
609            return
610       
611        for pos in range(self.GetPageCount()):
612            grid = self.GetPage(pos)
613            if grid.data is None:
614                #Found empty page
615                grid.set_data(data_inputs=data_inputs, 
616                              data_outputs=data_outputs,
617                              details=details,
618                              file_name=file_name) 
619                self.SetSelection(pos) 
620                return
621               
622        grid, pos = self.add_empty_page()
623        grid.set_data(data_inputs=data_inputs, 
624                      data_outputs=data_outputs,
625                      file_name=file_name,
626                      details=details)
627   
628    def add_column(self):
629        """
630        Append a new column to the grid
631        """
632        pos = self.GetSelection()
633        grid = self.GetPage(pos)
634        grid.AppendCols(1, True)
635       
636   
637
638
639
640class GridPanel(SPanel):
641    def __init__(self, parent, data_inputs=None,
642                 data_outputs=None, *args, **kwds):
643        SPanel.__init__(self, parent , *args, **kwds)
644       
645        self.vbox = wx.BoxSizer(wx.VERTICAL)
646       
647        self.plotting_sizer = wx.FlexGridSizer(3, 7, 10, 5)
648        self.button_sizer = wx.BoxSizer(wx.HORIZONTAL)
649        self.grid_sizer = wx.BoxSizer(wx.HORIZONTAL)
650        self.vbox.AddMany([(self.grid_sizer, 1, wx.EXPAND, 0),
651                           (wx.StaticLine(self, -1), 0, wx.EXPAND, 0),
652                           (self.plotting_sizer),
653                           (self.button_sizer)])
654        self.parent = parent
655        self._data_inputs = data_inputs
656        self._data_outputs = data_outputs
657        self.x = []
658        self.= []
659        self.x_axis_label = None
660        self.y_axis_label = None
661        self.x_axis_title = None
662        self.y_axis_title = None
663        self.x_axis_unit = None
664        self.y_axis_unit = None
665        self.view_button = None
666        self.plot_button = None
667        self.notebook = None
668       
669        self.layout_grid()
670        self.layout_plotting_area()
671        self.SetSizer(self.vbox)
672 
673    def set_xaxis(self, label="", x=None):
674        """
675        """
676        if x is None:
677            x = []
678        self.x = x
679        self.x_axis_label.SetValue("%s[:]" % str(label))
680        self.x_axis_title.SetValue(str(label))
681       
682    def set_yaxis(self, label="", y=None):
683        """
684        """
685        if y is None:
686            y = []
687        self.y = y
688        self.y_axis_label.SetValue("%s[:]" % str(label))
689        self.y_axis_title.SetValue(str(label))
690       
691    def get_plot_axis(self, col, list):
692        """
693       
694        """
695        axis = []
696        pos = self.notebook.GetSelection()
697        grid = self.notebook.GetPage(pos)
698        for row in list:
699            label = grid.GetCellValue(0, col)
700            value = grid.GetCellValue(row - 1, col).strip()
701            if value != "":
702                if label.lower().strip() == "data":
703                    axis.append(float(row - 1))
704                else:
705                    try:
706                        axis.append(float(value))
707                    except:
708                        msg = "Invalid data in row %s column %s" % (str(row),
709                                                                    str(col))
710                        wx.PostEvent(self.parent.parent, 
711                             StatusEvent(status=msg, info="error")) 
712            else:
713                axis.append(None) 
714        return axis
715   
716    def on_save_column(self, parent):
717        """
718        """
719        pos = self.notebook.GetSelection()
720        grid = self.notebook.GetPage(pos)
721        if parent is not None and  self.data is not None:
722            parent.write_batch_tofile(data=grid.data, 
723                                               file_name=path,
724                                               details=self.details)
725       
726    def on_view(self, event):
727        """
728        Get object represented buy the given cell and plot them.
729        """
730        pos = self.notebook.GetSelection()
731        grid = self.notebook.GetPage(pos)
732        title = self.notebook.GetPageText(pos)
733        if len(grid.selected_cells) == 0:
734            msg = "Highlight a Data or Chi2 column first..."
735            wx.PostEvent(self.parent.parent, 
736                             StatusEvent(status=msg, info="error")) 
737            return
738        for cell in grid.selected_cells:
739            row, col = cell
740            label_row = 0
741            label = grid.GetCellValue(label_row, col)
742            if label in grid.data:
743                values = grid.data[label]
744                if row > len(values) or row < 1:
745                    msg = "Invalid cell was chosen." 
746                    wx.PostEvent(self.parent.parent, StatusEvent(status=msg, 
747                                                                info="error"))
748                    time.sleep(0.5)
749                    continue
750                else:
751                     value = values[row -1]
752                if issubclass(value.__class__, BatchCell):
753                    if value.object is None or len(value.object) == 0:
754                        msg = "Row %s , " % str(row)
755                        msg += "Column %s is NOT " % str(label)
756                        msg += "the results of fits to view..."
757                        #raise ValueError, msg
758                        wx.PostEvent(self.parent.parent, StatusEvent(status=msg, 
759                                                                info="error")) 
760                        return
761                    for new_plot in value.object:
762                        if new_plot is None or \
763                         not issubclass(new_plot.__class__, 
764                                        plottables.Plottable):
765                            msg = "Row %s , " % str(row)
766                            msg += "Column %s is NOT " % str(label)
767                            msg += "the results of fits to view..."
768                            #raise ValueError, msg
769                            wx.PostEvent(self.parent.parent, 
770                                 StatusEvent(status=msg, info="error")) 
771                            time.sleep(0.5)
772                            continue
773                       
774                        if issubclass(new_plot.__class__, Data1D):
775                            if label in grid.list_plot_panels.keys():
776                                group_id = grid.list_plot_panels[label]
777                            else:
778                                group_id = str(grid.uid) + str(new_plot.group_id)
779                                grid.list_plot_panels[label] = group_id
780                            if group_id not in new_plot.list_group_id:
781                                new_plot.group_id = group_id
782                                new_plot.list_group_id.append(group_id)
783                        else:
784                            if label.lower() in ["data", "chi2"]:
785                                if len(grid.selected_cells) != 1:
786                                    msg = "2D View: Please select one data set"
787                                    msg += " at a time for View Results."
788                                    wx.PostEvent(self.parent.parent, 
789                                                 StatusEvent(status=msg,
790                                                              info="error"))
791                                    time.sleep(0.5) 
792                                    continue 
793                        """
794                        wx.PostEvent(self.parent.parent,
795                                     NewPlotEvent(action="clear",
796                                                  group_id=str(group_id),
797                                                  title=title))
798                        """ 
799                        wx.PostEvent(self.parent.parent, 
800                                     NewPlotEvent(plot=new_plot, 
801                                                group_id=str(new_plot.group_id),
802                                                title=title)) 
803                        msg = "Plotting the View Results  completed!"
804                        wx.PostEvent( self.parent.parent, 
805                                      StatusEvent(status=msg)) 
806                else:
807                   
808                    msg = "Row %s , " % str(row)
809                    msg += "Column %s is NOT " % str(label)
810                    msg += "the results of fits to view..."
811                    #raise ValueError, msg
812                    wx.PostEvent(self.parent.parent, 
813                         StatusEvent(status=msg, info="error")) 
814                    time.sleep(0.5)
815                    continue
816   
817       
818     
819   
820    def on_plot(self, event):
821        """
822        Evaluate the contains of textcrtl and plot result
823        """ 
824        pos = self.notebook.GetSelection()
825        grid = self.notebook.GetPage(pos)
826        column_names = {}
827        if grid is not None:
828            column_names = self.notebook.get_column_labels()
829        #evalue x
830        sentence = self.x_axis_label.GetValue()
831        try:
832            if sentence.strip() == "":
833                msg = "Select column values for x axis and y axis"
834                raise ValueError, msg
835        except:
836             wx.PostEvent(self.parent.parent, 
837                             StatusEvent(status=msg, info="error")) 
838             return
839       
840       
841        dict = parse_string(sentence, column_names.keys())
842        for tok, (col_name, list) in dict.iteritems():
843            col = column_names[col_name]
844            xaxis = self.get_plot_axis(col, list)
845            sentence = sentence.replace(tok, 
846                                        "numpy.array(%s)" % str(xaxis))
847        for key, value in FUNC_DICT.iteritems():
848            sentence = sentence.replace(key.lower(), value)
849        x = eval(sentence)
850        #evaluate y
851        sentence = self.y_axis_label.GetValue()
852        if sentence.strip() == "":
853            msg = "select value for y axis"
854            raise ValueError, msg
855        dict = parse_string(sentence, column_names.keys())
856        for tok, (col_name, list) in dict.iteritems():
857            col = column_names[col_name]
858            yaxis = self.get_plot_axis(col, list)
859            sentence = sentence.replace(tok, 
860                                        "numpy.array(%s)" % str(yaxis))
861        for key, value in FUNC_DICT.iteritems():
862            sentence = sentence.replace(key, value)
863        y = eval(sentence)
864        #plotting
865        new_plot = Data1D(x=x, y=y)
866        new_plot.id =  wx.NewId()
867        new_plot.group_id = wx.NewId()
868        title = "%s vs %s" % (self.y_axis_title.GetValue(), 
869                              self.x_axis_title.GetValue())
870        new_plot.xaxis(self.x_axis_title.GetValue(), 
871                       self.x_axis_unit.GetValue())
872        new_plot.yaxis(self.y_axis_title.GetValue(), 
873                       self.y_axis_unit.GetValue())
874        try:
875            title = self.notebook.GetPageText(pos)
876            new_plot.name = title
877            new_plot.xtransform = "x"
878            new_plot.ytransform  = "y" 
879            wx.PostEvent(self.parent.parent, 
880                        NewPlotEvent(plot=new_plot, 
881                        group_id=str(new_plot.group_id), title =title)) 
882            msg = "Plotting completed!"
883            wx.PostEvent( self.parent.parent, 
884                                      StatusEvent(status=msg))   
885        except:
886             wx.PostEvent(self.parent.parent, 
887                             StatusEvent(status=msg, info="error")) 
888
889    def layout_grid(self):
890        """
891        Draw the area related to the grid
892        """
893        self.notebook = Notebook(parent=self)
894        self.notebook.set_data(self._data_inputs, self._data_outputs)
895        self.grid_sizer.Add(self.notebook, 1, wx.EXPAND, 0)
896       
897    def layout_plotting_area(self):
898        """
899        Draw area containing options to plot
900        """
901       
902        self.x_axis_title = wx.TextCtrl(self, -1)
903        self.y_axis_title = wx.TextCtrl(self, -1)
904        self.x_axis_label = wx.TextCtrl(self, -1, size=(200, -1))
905        self.y_axis_label = wx.TextCtrl(self, -1, size=(200, -1))
906        self.x_axis_add = wx.Button(self, -1, "Add")
907        self.x_axis_add.Bind(event=wx.EVT_BUTTON, handler=self.on_edit_axis, 
908                            id=self.x_axis_add.GetId())
909        self.y_axis_add = wx.Button(self, -1, "Add")
910        self.y_axis_add.Bind(event=wx.EVT_BUTTON, handler=self.on_edit_axis, 
911                            id=self.y_axis_add.GetId())
912        self.x_axis_unit = wx.TextCtrl(self, -1)
913        self.y_axis_unit = wx.TextCtrl(self, -1)
914        self.view_button = wx.Button(self, -1, "View Results")
915        view_tip = "Highlight the data set or the Chi2 column first."
916        self.view_button.SetToolTipString(view_tip)
917        wx.EVT_BUTTON(self, self.view_button.GetId(), self.on_view)
918        self.plot_button = wx.Button(self, -1, "Plot")
919        plot_tip = "Highlight a column for each axis and \n"
920        plot_tip += "click the Add buttons first."
921        self.plot_button.SetToolTipString(plot_tip)
922        self.button_sizer.AddMany( [ (500, 30),
923                                (self.view_button, 0, wx.RIGHT|wx.BOTTOM, 10),
924                                (self.plot_button, 0, wx.RIGHT|wx.BOTTOM, 10)])
925       
926        wx.EVT_BUTTON(self, self.plot_button.GetId(), self.on_plot)
927        self.plotting_sizer.AddMany([
928                    (wx.StaticText(self, -1, 
929                                   "X-axis Label\nSelection Range"), 1,
930                      wx.TOP|wx.BOTTOM|wx.LEFT, 10),
931                    (self.x_axis_label, 1, wx.TOP|wx.BOTTOM, 10),
932                    (self.x_axis_add, 1, wx.TOP|wx.BOTTOM|wx.RIGHT, 10),
933                    (wx.StaticText(self, -1, "X-axis Label"), 1, 
934                     wx.TOP|wx.BOTTOM|wx.LEFT, 10),
935                    (self.x_axis_title, 1, wx.TOP|wx.BOTTOM, 10),
936                    (wx.StaticText(self, -1 , "X-axis Unit"), 1, 
937                     wx.TOP|wx.BOTTOM, 10),
938                    (self.x_axis_unit, 1, wx.TOP|wx.BOTTOM, 10),
939                    (wx.StaticText(self, -1, 
940                                   "Y-axis Label\nSelection Range"), 1, 
941                     wx.BOTTOM|wx.LEFT, 10),
942                    (self.y_axis_label, wx.BOTTOM, 10),
943                    (self.y_axis_add, 1, wx.BOTTOM|wx.RIGHT, 10),
944                    (wx.StaticText(self, -1, "Y-axis Label"), 1, 
945                     wx.BOTTOM|wx.LEFT, 10),
946                    (self.y_axis_title,  wx.BOTTOM, 10),
947                    (wx.StaticText(self, -1 , "Y-axis Unit"), 1, wx.BOTTOM, 10),
948                    (self.y_axis_unit, 1, wx.BOTTOM, 10),
949                      (-1, -1),
950                      (-1, -1),
951                      (-1, -1),
952                      (-1, -1),
953                      (-1, -1),
954                      (-1, -1),
955                      (-1, 1)])
956   
957    def on_edit_axis(self, event):
958        """
959        Get the selected column on  the visible grid and set values for axis
960        """
961        cell_list = self.notebook.on_edit_axis()
962        label, title = self.create_axis_label(cell_list)
963        tcrtl = event.GetEventObject()
964        if tcrtl == self.x_axis_add:
965            self.edit_axis_helper(self.x_axis_label, self.x_axis_title,
966                                   label, title)
967        elif tcrtl == self.y_axis_add:
968            self.edit_axis_helper(self.y_axis_label, self.y_axis_title,
969                                   label, title)
970           
971    def create_axis_label(self, cell_list):
972        """
973        Receive a list of cells and  create a string presenting the selected
974        cells.
975        :param cell_list: list of tuple
976       
977        """
978        if self.notebook is not None:
979            return self.notebook.create_axis_label(cell_list)
980   
981    def edit_axis_helper(self, tcrtl_label, tcrtl_title, label, title):
982        """
983        get controls to modify
984        """
985        tcrtl_label.SetValue(str(label))
986        tcrtl_title.SetValue(str(title))
987       
988    def add_column(self):
989        """
990        """
991        if self.notebook is not None:
992            self.notebook.add_column()
993       
994    def on_remove_column(self):
995        """
996        """
997        if self.notebook is not None:
998            self.notebook.on_remove_column()
999       
1000       
1001class GridFrame(wx.Frame):
1002    def __init__(self, parent=None, data_inputs=None, data_outputs=None, id=-1, 
1003                 title="Batch Window", size=(800, 500)):
1004        wx.Frame.__init__(self, parent=parent, id=id, title=title, size=size)
1005        self.parent = parent
1006        self.panel = GridPanel(self, data_inputs, data_outputs)
1007        menubar = wx.MenuBar()
1008        self.SetMenuBar(menubar)
1009       
1010        self.curr_col = None
1011        self.curr_grid = None
1012        self.curr_col_name = ""
1013        file = wx.Menu()
1014        menubar.Append(file, "&File")
1015       
1016        hint = "Open file containing batch results"
1017        open_menu = file.Append(wx.NewId(), 'Open ', hint)
1018        wx.EVT_MENU(self, open_menu.GetId(), self.on_open)
1019       
1020        hint = "Open the the current grid into excel"
1021        open_excel_menu = file.Append(wx.NewId(), 'Open with Excel', hint)
1022        wx.EVT_MENU(self, open_excel_menu.GetId(), self.open_with_excel)
1023        file.AppendSeparator()
1024        save_menu = file.Append(wx.NewId(), 'Save As', 'Save into File')
1025        wx.EVT_MENU(self, save_menu.GetId(), self.on_save_page)
1026       
1027        self.edit = wx.Menu()
1028        hint = "Insert column before the selected column"
1029        self.insert_before_menu = wx.Menu()
1030        self.insert_sub_menu = self.edit.AppendSubMenu(self.insert_before_menu, 
1031                                                      'Insert Before', hint)
1032 
1033        hint = "Remove the selected column"
1034        self.remove_menu = self.edit.Append(-1, 'Remove Column', hint)
1035        wx.EVT_MENU(self, self.remove_menu.GetId(), self.on_remove_column)
1036       
1037        self.Bind(wx.EVT_MENU_OPEN, self.on_menu_open)
1038        menubar.Append(self.edit, "&Edit")
1039        self.Bind(wx.EVT_CLOSE, self.on_close)
1040       
1041    def GetLabelText(self, id):
1042        """
1043        """
1044        for item in self.insert_before_menu.GetMenuItems():
1045            m_id = item.GetId() 
1046            if m_id == id:
1047                return item.GetLabel() 
1048   
1049    def on_remove_column(self, event):
1050        """
1051        """
1052        pos = self.panel.notebook.GetSelection()
1053        grid = self.panel.notebook.GetPage(pos)
1054        grid.on_remove_column(event=None)
1055       
1056    def on_menu_open(self, event):
1057        """
1058       
1059        """
1060        if self.edit == event.GetMenu():
1061            #get the selected column
1062            pos = self.panel.notebook.GetSelection()
1063            grid = self.panel.notebook.GetPage(pos)
1064            col_list = grid.GetSelectedCols()
1065            if len(col_list) > 0:
1066                self.remove_menu.Enable(True)
1067            else:
1068                self.remove_menu.Enable(False)
1069            if len(col_list)== 0 or len(col_list) > 1:
1070                self.insert_sub_menu.Enable(False)
1071               
1072                label = "Insert Column Before"
1073                self.insert_sub_menu.SetText(label)
1074            else:
1075                self.insert_sub_menu.Enable(True)
1076               
1077                col = col_list[0]
1078                #GetColLabelValue(self, col)
1079                col_name = grid.GetCellValue(row=0, col=col)
1080                label = "Insert Column Before " + str(col_name)
1081                self.insert_sub_menu.SetText(label)
1082                for item in self.insert_before_menu.GetMenuItems():
1083                    self.insert_before_menu.DeleteItem(item)
1084                grid.insert_col_menu(menu=self.insert_before_menu, 
1085                                     label=col_name, window=self)
1086        event.Skip()
1087       
1088 
1089       
1090    def on_save_page(self, event):
1091        """
1092        """
1093        if self.parent is not None:
1094            pos = self.panel.notebook.GetSelection()
1095            grid = self.panel.notebook.GetPage(pos)
1096            if grid.file_name is None or grid.file_name.strip() == "" or \
1097                grid.data is None or len(grid.data) == 0:
1098                name = self.panel.notebook.GetPageText(pos)
1099                msg = " %s has not data to save" % str(name)
1100                wx.PostEvent(self.parent, 
1101                             StatusEvent(status=msg, info="error")) 
1102           
1103                return
1104            reader, ext = os.path.splitext(grid.file_name)
1105            path = None
1106            if self.parent is not None: 
1107                location = os.path.dirname(grid.file_name)
1108                dlg = wx.FileDialog(self, "Save Project file",
1109                            location, grid.file_name, ext, wx.SAVE)
1110                path = None
1111                if dlg.ShowModal() == wx.ID_OK:
1112                    path = dlg.GetPath()
1113                dlg.Destroy()
1114                if path != None:
1115                    if self.parent is not None:
1116                        data = grid.get_grid_view()
1117                        self.parent.write_batch_tofile(data=data, 
1118                                               file_name=path,
1119                                               details=grid.details)
1120   
1121    def on_open(self, event):
1122        """
1123        Open file containg batch result
1124        """
1125        if self.parent is not None:
1126            self.parent.on_read_batch_tofile(event)
1127           
1128    def open_with_excel(self, event):
1129        """
1130        open excel and display batch result in Excel
1131        """
1132        if self.parent is not None:
1133            pos = self.panel.notebook.GetSelection()
1134            grid = self.panel.notebook.GetPage(pos)
1135            data = grid.get_grid_view()
1136            if grid.file_name is None or grid.file_name.strip() == "" or \
1137                grid.data is None or len(grid.data) == 0:
1138                name = self.panel.notebook.GetPageText(pos)
1139                msg = " %s has not data to open on excel" % str(name)
1140                wx.PostEvent(self.parent, 
1141                             StatusEvent(status=msg, info="error")) 
1142           
1143                return
1144            self.parent.open_with_externalapp(data=data,
1145                                               file_name=grid.file_name, 
1146                                               details=grid.details)
1147           
1148    def on_close(self, event):
1149        """
1150        """
1151        self.Hide()
1152       
1153   
1154    def on_append_column(self, event):
1155        """
1156        Append a new column to the grid
1157        """
1158        self.panel.add_column()
1159       
1160    def set_data(self, data_inputs, data_outputs, details="", file_name=None):
1161        """
1162        """
1163       
1164        self.panel.notebook.set_data(data_inputs=data_inputs, 
1165                            file_name=file_name,
1166                            details=details,
1167                            data_outputs=data_outputs)
1168     
1169     
1170class BatchOutputFrame(wx.Frame):
1171    """
1172    Allow to select where the result of batch will be displayed or stored
1173    """
1174    def __init__(self, parent, data_inputs, data_outputs, file_name="",
1175                 details="", *args, **kwds):
1176        """
1177        :param parent: Window instantiating this dialog
1178        :param result: result to display in a grid or export to an external
1179                application.
1180        """
1181        #kwds['style'] = wx.CAPTION|wx.SYSTEM_MENU
1182        wx.Frame.__init__(self, parent, *args, **kwds)
1183        self.parent = parent
1184        self.panel = wx.Panel(self)
1185        self.file_name = file_name
1186        self.details = details
1187        self.data_inputs = data_inputs
1188        self.data_outputs = data_outputs
1189        self.data = {}
1190        for item in (self.data_outputs, self.data_inputs):
1191            self.data.update(item)
1192        self.flag = 1
1193        self.SetSize((300, 200))
1194        self.local_app_selected = None
1195        self.external_app_selected = None
1196        self.save_to_file = None
1197        self._do_layout()
1198   
1199    def _do_layout(self):
1200        """
1201        Draw the content of the current dialog window
1202        """
1203        vbox = wx.BoxSizer(wx.VERTICAL)
1204        box_description = wx.StaticBox(self.panel, -1, str("Batch Outputs"))
1205        hint_sizer = wx.StaticBoxSizer(box_description, wx.VERTICAL)
1206        selection_sizer = wx.GridBagSizer(5, 5)
1207        button_sizer = wx.BoxSizer(wx.HORIZONTAL)
1208        text = "Open with %s" % self.parent.application_name
1209        self.local_app_selected = wx.RadioButton(self.panel, -1, text,
1210                                                style=wx.RB_GROUP)
1211        self.Bind(wx.EVT_RADIOBUTTON, self.onselect,
1212                    id=self.local_app_selected.GetId())
1213        text = "Open with Excel"
1214        self.external_app_selected  = wx.RadioButton(self.panel, -1, text)
1215        self.Bind(wx.EVT_RADIOBUTTON, self.onselect,
1216                    id=self.external_app_selected.GetId())
1217        text = "Save to File"
1218        self.save_to_file = wx.CheckBox(self.panel, -1, text)
1219        self.Bind(wx.EVT_CHECKBOX, self.onselect,
1220                    id=self.save_to_file.GetId())
1221        self.local_app_selected.SetValue(True)
1222        self.external_app_selected.SetValue(False)
1223        self.save_to_file.SetValue(False)
1224        button_close = wx.Button(self.panel, -1, "Close")
1225        button_close.Bind(wx.EVT_BUTTON, id=button_close.GetId(),
1226                           handler=self.on_close)
1227        button_apply = wx.Button(self.panel, -1, "Apply")
1228        button_apply.Bind(wx.EVT_BUTTON, id=button_apply.GetId(),
1229                        handler=self.on_apply)
1230        button_apply.SetFocus()
1231        hint = ""
1232        hint_sizer.Add(wx.StaticText(self.panel, -1, hint))
1233        hint_sizer.Add(selection_sizer)
1234        #draw area containing radio buttons
1235        ix = 0
1236        iy = 0
1237        selection_sizer.Add(self.local_app_selected, (iy, ix),
1238                           (1, 1), wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
1239        iy += 1
1240        selection_sizer.Add(self.external_app_selected, (iy, ix),
1241                           (1, 1), wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
1242        iy += 1
1243        selection_sizer.Add(self.save_to_file, (iy, ix),
1244                           (1, 1), wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
1245        #contruction the sizer contaning button
1246        button_sizer.Add((20, 20), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
1247
1248        button_sizer.Add(button_close, 0,
1249                        wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
1250        button_sizer.Add(button_apply, 0,
1251                                wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)
1252        vbox.Add(hint_sizer,  0, wx.EXPAND|wx.ALL, 10)
1253        vbox.Add(wx.StaticLine(self.panel, -1),  0, wx.EXPAND, 0)
1254        vbox.Add(button_sizer, 0 , wx.TOP|wx.BOTTOM, 10)
1255        self.SetSizer(vbox)
1256       
1257    def on_apply(self, event):
1258        """
1259        Get the user selection and display output to the selected application
1260        """
1261        if self.flag == 1:
1262            self.parent.open_with_localapp(data_inputs=self.data_inputs,
1263                                            data_outputs=self.data_outputs)
1264        elif self.flag == 2:
1265            self.parent.open_with_externalapp(data=self.data, 
1266                                           file_name=self.file_name,
1267                                           details=self.details)
1268    def on_close(self, event):
1269        """
1270        close the Window
1271        """
1272        self.Close()
1273       
1274    def onselect(self, event=None):
1275        """
1276        Receive event and display data into third party application
1277        or save data to file.
1278       
1279        """
1280        if self.save_to_file.GetValue():
1281            reader, ext = os.path.splitext(self.file_name)
1282            path = None
1283            location = os.getcwd()
1284            if self.parent is not None: 
1285                location = os.path.dirname(self.file_name)
1286                dlg = wx.FileDialog(self, "Save Project file",
1287                            location, self.file_name, ext, wx.SAVE)
1288                path = None
1289                if dlg.ShowModal() == wx.ID_OK:
1290                    path = dlg.GetPath()
1291                dlg.Destroy()
1292                if path != None:
1293                    if self.parent is not None and  self.data is not None:
1294                        self.parent.write_batch_tofile(data=self.data, 
1295                                               file_name=path,
1296                                               details=self.details)
1297        if self.local_app_selected.GetValue():
1298            self.flag = 1
1299        else:
1300            self.flag = 2
1301        return self.flag
1302   
1303 
1304       
1305if __name__ == "__main__":
1306    app = wx.App()
1307   
1308    try:
1309        data = {}
1310        j = 0
1311        for i in range(4):
1312            j += 1
1313            data["index"+str(i)] = [i/j, i*j, i, i+j]
1314       
1315        data_input =  copy.deepcopy(data)   
1316        data_input["index5"] = [10,20,40, 50]
1317        frame = GridFrame(data_outputs=data, data_inputs=data_input)
1318        frame.Show(True)
1319    except:
1320        print sys.exc_value
1321       
1322    app.MainLoop()
Note: See TracBrowser for help on using the repository browser.