source: sasview/src/sas/guiframe/data_processor.py @ a7aa5c7

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 a7aa5c7 was a7aa5c7, checked in by butler, 9 years ago

Final fixes (hopefully) to Batch Grid

1) Added back right click on label to allow insertion of columns
2) Added back left click on label to allow correct handling of set axix
for plots
3) Added override for sheet.CSheet.PaintBackground? which has not been
updated to account for the need to always have a dc object on a paint
event in wx.Python 3 — probably the main problem with sheet - edit was
working but generating a pile of errors behind the scenes.
4) Fixed the exception raised on insert column submenu due to removing
Edit menu. Needed to remove chunk of code or grab the wx.Menu handle.
Did later.
5) Added lots of doc strings but most are not rendering so need to
figure out why at some point - but not a release issue.

  • Property mode set to 100644
File size: 80.3 KB
Line 
1"""
2Implement grid used to store results of a batch fit.
3
4This is in Guiframe rather than fitting which is probably where it should be.
5Actually could be a generic framework implemented in fit gui module.  At this
6point however there this grid behaves independently of the fitting panel and
7only knows about information sent to it but not about the fits or fit panel and
8thus cannot feed back to the fitting panel.  This could change in the future.
9
10The organization of the classes goes as:
11#      ------------------------------------------------------------
12#      | --------------------------------------------------------  |
13#      | | ----------------------------------------------------  | |
14#      | | | ------------------------------------------------  | | |
15#      | | | | --------------------------------------------  | | | |
16#      | | | |                                               | | | |
17#      | | | |                                               | | | |
18#      | | | |     GridPage Class = the actual grid          | | | |
19#      | | | |       GridCellEditor: overides class of same  | | | |
20#      | | | |          name in the wx.CSheet module         | | | |
21#      | | | |       BatchCell:  class defines a cell within | | | |
22#      | | | |          within the grid                      | | | |
23#      | | | |                                               | | | |
24#      | | | |                                               | | | |
25#      | | | ------------------------------------------------- | | |
26#      | | |       Notebook class: contains all the GridPages  | | |
27#      | | -----              ---------------------------------| | |
28#      | |      |  Page 1     |                                  | |
29#      | |      ---------------                                  | |
30#      | |                                                       | |
31#      | |    ----------------------------------------------     | |
32#      | |                                                       | |
33#      | |    Plotting area = a standard gridsizer               | |
34#      | |     layout managed by the GridPanel class directly    | |
35#      | |                                                       | |
36#      | |    ----------------------------------------------     | |
37#      | |                                                       | |
38#      | |      GridPanel Class(SPanel(ScrolledPanel)            | |
39#      | |         contains the GridPage and the 'plotting area' | |
40#      | |         below it                                      | |
41#      | --------------------------------------------------------- |
42#      |                                                           |
43#      |            GridFrame class(wx.Frame)                      |
44#      -------------------------------------------------------------
45
46"""
47import wx
48import numpy
49import math
50import re
51import os
52import sys
53import copy
54from wx.lib.scrolledpanel import ScrolledPanel
55import wx.aui
56from wx.aui import AuiNotebook as nb
57import wx.lib.sheet as sheet
58from sas.guiframe.panel_base import PanelBase
59from sas.guiframe.events import NewPlotEvent
60from sas.guiframe.events import StatusEvent
61from sas.plottools import plottables
62from sas.guiframe.dataFitting import Data1D
63
64
65FUNC_DICT = {"sqrt": "math.sqrt",
66             "pow": "math.sqrt"}
67
68class BatchCell(object):
69    """
70    Object describing a cell in  the grid.
71
72    """
73    def __init__(self):
74        """
75        Initialize attributes of class (label, value, col, row, object)
76       
77        """
78        self.label = ""
79        self.value = None
80        self.col = -1
81        self.row = -1
82        self.object = []
83
84
85def parse_string(sentence, list):
86    """
87    Return a dictionary of column label and index or row selected
88   
89    @param sentence: String to parse
90    @param list: list of columns label
91   
92    """
93
94    p2 = re.compile(r'\d+')
95    p = re.compile(r'[\+\-\*\%\/]')
96    labels = p.split(sentence)
97    col_dict = {}
98    for elt in labels:
99        rang = None
100        temp_arr = []
101        for label in  list:
102            label_pos = elt.find(label)
103            separator_pos = label_pos + len(label)
104            if label_pos != -1 and len(elt) >= separator_pos  and\
105                elt[separator_pos] == "[":
106                # the label contain , meaning the range selected is not
107                # continuous
108                if elt.count(',') > 0:
109                    new_temp = []
110                    temp = elt.split(label)
111                    for item in temp:
112                        range_pos = item.find(":")
113                        if range_pos != -1:
114                            rang = p2.findall(item)
115                            for i in xrange(int(rang[0]), int(rang[1]) + 1):
116                                new_temp.append(i)
117                    temp_arr += new_temp
118                else:
119                    # continuous range
120                    temp = elt.split(label)
121                    for item in temp:
122                        if item.strip() != "":
123                            range_pos = item.find(":")
124                            if range_pos != -1:
125                                rang = p2.findall(item)
126                                for i in xrange(int(rang[0]), int(rang[1]) + 1):
127                                    temp_arr.append(i)
128                col_dict[elt] = (label, temp_arr)
129    return col_dict
130
131
132class SPanel(ScrolledPanel):
133    """
134    ensure proper scrolling of GridPanel
135   
136    Adds a SetupScrolling call to the normal ScrolledPanel init.   
137    GridPanel then subclasses this class
138   
139    """
140    def __init__(self, parent, *args, **kwds):
141        """
142        initialize ScrolledPanel then force a call to SetupScrolling
143
144        """
145        ScrolledPanel.__init__(self, parent, *args, **kwds)
146        self.SetupScrolling()
147
148
149class GridCellEditor(sheet.CCellEditor):
150    """
151    Custom cell editor
152   
153    This subclasses the sheet.CCellEditor (itself a subclass of
154    grid.GridCellEditor) in order to override two of its methods:
155    PaintBackrgound and EndEdit.
156   
157    This is necessary as the sheet module is broken in wx 3.0.2 and
158    improperly subclasses grid.GridCellEditor
159   
160    """
161    def __init__(self, grid):
162        """
163        Override of CCellEditor init. Runs the grid.GridCellEditor init code
164
165        """
166        super(GridCellEditor, self).__init__(grid)
167
168    def PaintBackground(self, dc, rect, attr):
169        """
170        Overrides wx.sheet.CCellEditor.PaintBackground which incorrectly calls
171        the base class method.
172       
173        In wx3.0 all paint objects must explicitly
174        have a wxPaintDC (Device Context) object.  Thus the paint event which
175        generates a call to this method provides such a DC object and the
176        base class in grid expects to receive that object.  sheet was apparently
177        not updated to reflect this and hence fails.  This could thus
178        become obsolete in a future bug fix of wxPython.
179       
180        Apart from adding a dc variable in the list of arguments in the def
181        and in the call to the base class the rest of this method is copied
182        as is from sheet.CCellEditor.PaintBackground
183       
184        @param dc: the wxDC object for the paint
185        -------
186        Draws the part of the cell not occupied by the edit control.  The
187        base class version just fills it with background colour from the
188        attribute.
189
190        NOTE: There is no need to override this if you don't need
191        to do something out of the ordinary.
192
193        """
194        # Call base class method.
195        DC = dc
196        super(sheet.CCellEditor,self).PaintBackground(DC, rect, attr)
197
198    def EndEdit(self, row, col, grid, previous):
199        """
200        Commit editing the current cell. Returns True if the value has changed.
201       
202        @param previous: previous value in the cell
203       
204        """
205        changed = False                             # Assume value not changed
206        val = self._tc.GetValue()                   # Get value in edit control
207        if val != self._startValue:                 # Compare
208            changed = True                          # If different then changed is True
209            grid.GetTable().SetValue(row, col, val) # Update the table
210        self._startValue = ''                       # Clear the class' start value
211        self._tc.SetValue('')                       # Clear contents of the edit control
212        return changed
213
214
215class GridPage(sheet.CSheet):
216    """
217    Class that receives the results of a batch fit.
218   
219    GridPage displays the received results in a wx.grid using sheet.  This is
220    then used by GridPanel and GridFrame to present the full GUI.
221   
222    """
223    def __init__(self, parent, panel=None):
224        """
225        Initialize
226       
227        Initialize all the attributes of GridPage, and the events. include
228        the init stuff from sheet.CSheet as well.
229       
230        """
231        #sheet.CSheet.__init__(self, parent)
232       
233        # The following is the __init__ from CSheet. ##########################
234        # We re-write it here because the class is broken in wx 3.0,
235        # such that the cell editor is not able to receive the right
236        # number of parameters when it is called. The only way to
237        # pick a different cell editor is apparently to re-write the __init__.
238        wx.grid.Grid.__init__(self, parent, -1)
239
240        # Init variables
241        self._lastCol = -1              # Init last cell column clicked
242        self._lastRow = -1              # Init last cell row clicked
243        self._selected = None           # Init range currently selected
244                                        # Map string datatype to default renderer/editor
245        self.RegisterDataType(wx.grid.GRID_VALUE_STRING,
246                              wx.grid.GridCellStringRenderer(),
247                              GridCellEditor(self))
248
249        self.CreateGrid(4, 3)           # By default start with a 4 x 3 grid
250        self.SetColLabelSize(18)        # Default sizes and alignment
251        self.SetRowLabelSize(50)
252        self.SetRowLabelAlignment(wx.ALIGN_RIGHT, wx.ALIGN_BOTTOM)
253        self.SetColSize(0, 75)          # Default column sizes
254        self.SetColSize(1, 75)
255        self.SetColSize(2, 75)
256
257        # Sink events
258        self.Bind(wx.grid.EVT_GRID_RANGE_SELECT, self.OnRangeSelect)
259        self.Bind(wx.grid.EVT_GRID_ROW_SIZE, self.OnRowSize)
260        self.Bind(wx.grid.EVT_GRID_COL_SIZE, self.OnColSize)
261        self.Bind(wx.grid.EVT_GRID_SELECT_CELL, self.OnGridSelectCell)
262        # NOTE: the following bind to standard sheet methods that are
263        # overriden in this subclassn - actually we have currently
264        # disabled the on_context_menu that would override the OnRightClick
265        self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.OnCellChange)
266        self.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.OnLeftClick)
267        self.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK, self.OnRightClick)
268        #self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.OnLeftDoubleClick)
269        # This ends the __init__ section for CSheet. ##########################
270
271
272
273        # The following events must be bound even if CSheet is working
274        # properly and does not need the above re-implementation of the
275        # CSheet init method.  Basically these override any intrinsic binding
276        self.Bind(wx.grid.EVT_GRID_LABEL_RIGHT_CLICK, self.on_right_click)
277        self.Bind(wx.grid.EVT_GRID_LABEL_LEFT_CLICK, self.on_left_click)       
278
279        self.AdjustScrollbars()
280        #self.SetLabelBackgroundColour('#DBD4D4')
281        self.uid = wx.NewId()
282        self.parent = parent
283        self.panel = panel
284        self.col_names = []
285        self.data_inputs = {}
286        self.data_outputs = {}
287        self.data = None
288        self.details = ""
289        self.file_name = None
290        self._cols = 50
291        self._rows = 3001
292        self.last_selected_row = -1
293        self.last_selected_col = -1
294        self.col_width = 30
295        self.row_height = 20
296        self.max_row_touse = 0
297        self.axis_value = []
298        self.axis_label = ""
299        self.selected_cells = []
300        self.selected_cols = []
301        self.selected_rows = []
302        self.plottable_cells = []
303        self.plottable_flag = False
304        self.SetColMinimalAcceptableWidth(self.col_width)
305        self.SetRowMinimalAcceptableHeight(self.row_height)
306        self.SetNumberRows(self._rows)
307        self.SetNumberCols(self._cols)
308        color = self.parent.GetBackgroundColour()
309        for col in range(self._cols):
310            self.SetCellBackgroundColour(0, col, color)
311        self.AutoSize()
312        self.list_plot_panels = {}
313        self.default_col_width = 75
314        self.EnableEditing(True)
315        if self.GetNumberCols() > 0:
316            self.default_col_width = self.GetColSize(0)
317        # We have moved these to the top of the init section with the
318        # rest of the grid event bindings from the sheet init when
319        # appropriate
320        #self.Bind(wx.grid.EVT_GRID_LABEL_LEFT_CLICK, self.on_left_click)
321        #self.Bind(wx.grid.EVT_GRID_LABEL_RIGHT_CLICK, self.on_right_click)
322        #self.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.on_selected_cell)
323        #self.Bind(wx.grid.EVT_GRID_CMD_CELL_CHANGE, self.on_edit_cell)
324        #self.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK, self.onContextMenu)
325
326    def OnLeftClick(self, event):
327        """
328        Overrides sheet.CSheet.OnLefClick.
329       
330        Processes when a cell is selected by left clicking on that cell. First
331        process the base Sheet method then the current class specific method
332         
333        """
334        sheet.CSheet.OnLeftClick(self, event)
335        self.on_selected_cell(event)
336       
337
338    def OnCellChange(self, event):
339        """
340        Overrides sheet.CSheet.OnCellChange. 
341       
342        Processes when a cell has been edited by a cell editor. Checks for the
343        edited row being outside the max row to use attribute and if so updates
344        the last row.  Then calls the base handler using skip.
345
346        """
347        row, _ = event.GetRow(), event.GetCol()
348        if row > self.max_row_touse:
349            self.max_row_touse = row
350        if self.data == None:
351            self.data = {}
352        event.Skip()
353
354    def on_selected_cell(self, event):
355        """
356        Handler catching cell selection.
357       
358        Called after calling base 'on left click' method.
359
360        """
361
362        flag = event.CmdDown() or event.ControlDown()
363        flag_shift = event.ShiftDown()
364        row, col = event.GetRow(), event.GetCol()
365        cell = (row, col)
366        if not flag and not flag_shift:
367            self.selected_cols = []
368            self.selected_rows = []
369            self.selected_cells = []
370            self.axis_label = ""
371            self.axis_value = []
372            self.plottable_list = []
373            self.plottable_cells = []
374            self.plottable_flag = False
375        self.last_selected_col = col
376        self.last_selected_row = row
377        if col >= 0:
378            if flag:
379                label_row = row
380            else:
381                label_row = 0
382            self.axis_label = self.GetCellValue(label_row, col)
383            self.selected_cols.append(col)
384        if flag_shift:
385            if not self.selected_rows:
386                min_r = 1
387            else:
388                min_r = min(self.selected_rows)
389            for row_s in range(min_r, row + 1):
390                cel = (row_s, col)
391                if cel not in self.selected_cells:
392                    if row > 0:
393                        self.selected_cells.append(cel)
394                        self.selected_rows.append(row)
395            for row_s in self.selected_rows:
396                cel = (row_s, col)
397                if row_s > row:
398                    try:
399                        self.selected_cells.remove(cel)
400                    except:
401                        pass
402                    try:
403                        self.selected_rows.remove(row_s)
404                    except:
405                        pass
406        elif flag:
407            if cell not in self.selected_cells:
408                if row > 0:
409                    self.selected_cells.append(cell)
410                    self.selected_rows.append(row)
411            else:
412                try:
413                    self.selected_cells.remove(cell)
414                except:
415                    pass
416                try:
417                    self.selected_rows.remove(row)
418                except:
419                    pass
420        else:
421            self.selected_cells.append(cell)
422            self.selected_rows.append(row)
423        self.axis_value = []
424        for cell_row, cell_col in self.selected_cells:
425            if cell_row > 0 and cell_row < self.max_row_touse:
426                self.axis_value.append(self.GetCellValue(cell_row, cell_col))
427        event.Skip()
428
429    def on_left_click(self, event):
430        """
431        Is triggered when the left mouse button is clicked while the mouse
432        is hovering over the column 'label.'
433       
434        This processes the information on the selected column: the column name
435        (in row 0 of column) and the range of cells with a valid value to be
436        used by the GridPanel set_axis methods.
437       
438        """
439       
440        flag = event.CmdDown() or event.ControlDown()
441
442        col = event.GetCol()
443        row = event.GetRow()
444
445        if not flag:
446            self.selected_cols = []
447            self.selected_rows = []
448            self.selected_cells = []
449            self.axis_label = ""
450            self.axis_value = []
451            self.plottable_list = []
452            self.plottable_cells = []
453            self.plottable_flag = False
454
455        self.last_selected_col = col
456        self.last_selected_row = row
457        if row != -1 and row not in self.selected_rows:
458            self.selected_rows.append(row)
459
460        if col != -1:
461            for row in range(1, self.GetNumberRows() + 1):
462                cell = (row, col)
463                if row > 0 and row < self.max_row_touse:
464                    if cell not in self.selected_cells:
465                        self.selected_cells.append(cell)
466                    else:
467                        if flag:
468                            self.selected_cells.remove(cell)
469            self.selected_cols.append(col)
470            self.axis_value = []
471            for cell_row, cell_col in self.selected_cells:
472                val = self.GetCellValue(cell_row, cell_col)
473                if not val:
474                    self.axis_value.append(self.GetCellValue(cell_row, cell_col))
475            self.axis_label = self.GetCellValue(0, col)
476            if not self.axis_label:
477                self.axis_label = " "
478        event.Skip()
479
480    def on_right_click(self, event):
481        """
482        Is triggered when the right mouse button is clicked while the mouse
483        is hovering over the column 'label.'
484       
485        This brings up a context menu that allows the deletion of the column,
486        or the insertion of a new column either to the right or left of the
487        current column.  If inserting a new column can insert a blank column or
488        choose a number of hidden columns.  By default all the error parameters
489        are in hidden columns so as to save space on the grid.  Also any other
490        intrinsic variables stored with the data such as Temperature, pressure,
491        time etc can be used to populate this menu.
492       
493        """
494       
495        col = event.GetCol()
496        row = event.GetRow()
497        # Ignore the index column
498        if col < 0 or row != -1:
499            return
500        self.selected_cols = []
501        self.selected_cols.append(col)
502        # Slicer plot popup menu
503        slicerpop = wx.Menu()
504        col_label_menu = wx.Menu()
505        c_name = self.GetCellValue(0, col)
506        label = "Insert column before %s " % str(c_name)
507        slicerpop.AppendSubMenu(col_label_menu, '&%s' % str(label), str(label))
508        row = 0
509        label = self.GetCellValue(row, col)
510        self.insert_col_menu(col_label_menu, label, self)
511
512        col_after_menu = wx.Menu()
513        label = "Insert column after %s " % str(c_name)
514        slicerpop.AppendSubMenu(col_after_menu, '&%s' % str(label), str(label))
515        self.insert_after_col_menu(col_after_menu, label, self)
516
517        wx_id = wx.NewId()
518        hint = 'Remove selected column %s'
519        slicerpop.Append(wx_id, '&Remove Column', hint)
520        wx.EVT_MENU(self, wx_id, self.on_remove_column)
521
522        pos = wx.GetMousePosition()
523        pos = self.ScreenToClient(pos)
524        self.PopupMenu(slicerpop, pos)
525        event.Skip()
526
527    def insert_col_menu(self, menu, label, window):
528        """
529        method called to populate the 'insert column before current column'
530        submenu.
531       
532        """
533       
534        if self.data is None:
535            return
536        id = wx.NewId()
537        title = "Empty"
538        hint = 'Insert empty column before %s' % str(label)
539        menu.Append(id, title, hint)
540        wx.EVT_MENU(window, id, self.on_insert_column)
541        row = 0
542        col_name = [self.GetCellValue(row, col) for col in range(self.GetNumberCols())]
543        for c_name in self.data.keys():
544            if c_name not in col_name and self.data[c_name]:
545                wx_id = wx.NewId()
546                hint = "Insert %s column before the " % str(c_name)
547                hint += " %s column" % str(label)
548                menu.Append(wx_id, '&%s' % str(c_name), hint)
549                wx.EVT_MENU(window, wx_id, self.on_insert_column)
550
551    def insert_after_col_menu(self, menu, label, window):
552        """
553        Method called to populate the 'insert column after current column'
554        submenu
555       
556        """
557       
558        if self.data is None:
559            return
560        wx_id = wx.NewId()
561        title = "Empty"
562        hint = 'Insert empty column after %s' % str(label)
563        menu.Append(wx_id, title, hint)
564        wx.EVT_MENU(window, wx_id, self.on_insert_after_column)
565        row = 0
566        col_name = [self.GetCellValue(row, col)
567                        for col in range(self.GetNumberCols())]
568        for c_name in self.data.keys():
569            if c_name not in col_name and self.data[c_name]:
570                wx_id = wx.NewId()
571                hint = "Insert %s column after the " % str(c_name)
572                hint += " %s column" % str(label)
573                menu.Append(wx_id, '&%s' % str(c_name), hint)
574                wx.EVT_MENU(window, wx_id, self.on_insert_after_column)
575
576    def on_remove_column(self, event=None):
577        """
578        Called when user chooses remove from the column right click menu
579        Checks the columnn exists then calls the remove_column method
580       
581        """
582       
583        if self.selected_cols is not None or len(self.selected_cols) > 0:
584            col = self.selected_cols[0]
585            self.remove_column(col=col, numCols=1)
586
587    def remove_column(self, col, numCols=1):
588        """
589        Remove the col column from the current grid
590       
591        """
592       
593        # add data to the grid   
594        row = 0
595        col_name = self.GetCellValue(row, col)
596        self.data[col_name] = []
597        for row in range(1, self.GetNumberRows() + 1):
598            if row < self.max_row_touse:
599                value = self.GetCellValue(row, col)
600                self.data[col_name].append(value)
601                for k, value_list in self.data.iteritems():
602                    if k != col_name:
603                        length = len(value_list)
604                        if length < self.max_row_touse:
605                            diff = self.max_row_touse - length
606                            for i in range(diff):
607                                self.data[k].append("")
608        self.DeleteCols(pos=col, numCols=numCols, updateLabels=True)
609
610    def on_insert_column(self, event):
611        """
612        Called when user chooses insert 'column before' submenu
613        of the column context menu obtained when right clicking on a given
614        column header.
615       
616        Sets up to insert column into the current grid before the current
617        highlighted column location and sets up what to populate that column
618        with.  Then calls insert_column method to actually do the insertion.
619       
620        """
621       
622        if self.selected_cols is not None or len(self.selected_cols) > 0:
623            col = self.selected_cols[0]
624            # add data to the grid
625            wx_id = event.GetId()
626            col_name = event.GetEventObject().GetLabelText(wx_id)
627            self.insert_column(col=col, col_name=col_name)
628            if  not issubclass(event.GetEventObject().__class__, wx.Menu):
629                col += 1
630                self.selected_cols[0] += 1
631
632    def on_insert_after_column(self, event):
633        """
634        Called when user chooses insert 'column after' submenu
635        of the column context menu obtained when right clicking on a given
636        column header.
637       
638        Sets up to insert column into the current grid after the current
639        highlighted column location and sets up what to populate that column
640        with.  Then calls insert_column method to actually do the insertion.
641       
642        """
643       
644        if self.selected_cols is not None or len(self.selected_cols) > 0:
645            col = self.selected_cols[0] + 1
646            # add data to the grid
647            wx_id = event.GetId()
648            col_name = event.GetEventObject().GetLabelText(wx_id)
649            self.insert_column(col=col, col_name=col_name)
650            if  not issubclass(event.GetEventObject().__class__, wx.Menu):
651                self.selected_cols[0] += 1
652
653    def insert_column(self, col, col_name):
654        """
655        Insert column at position col with data[col_name] into the current
656        grid.
657       
658        """
659       
660        row = 0
661        self.InsertCols(pos=col, numCols=1, updateLabels=True)
662        if col_name.strip() != "Empty":
663            self.SetCellValue(row, col, str(col_name.strip()))
664        if col_name in self.data.keys():
665            value_list = self.data[col_name]
666            cell_row = 1
667            for value in value_list:
668                label = value#format_number(value, high=True)
669                self.SetCellValue(cell_row, col, str(label))
670                cell_row += 1
671        self.AutoSizeColumn(col, True)
672        width = self.GetColSize(col)
673        if width < self.default_col_width:
674            self.SetColSize(col, self.default_col_width)
675        color = self.parent.GetBackgroundColour()
676        self.SetCellBackgroundColour(0, col, color)
677        self.ForceRefresh()
678
679    def on_set_x_axis(self, event):
680        """
681        Just calls the panel version of the method
682       
683        """
684       
685        self.panel.set_xaxis(x=self.axis_value, label=self.axis_label)
686
687    def on_set_y_axis(self, event):
688        """
689        Just calls the panel version of the method
690       
691        """
692        self.panel.set_yaxis(y=self.axis_value, label=self.axis_label)
693
694    def set_data(self, data_inputs, data_outputs, details, file_name):
695        """
696        Add data to the grid
697       
698        @param data_inputs: data to use from the context menu of the grid
699        @param data_ouputs: default columns deplayed
700       
701        """
702       
703        self.file_name = file_name
704        self.details = details
705
706        if data_outputs is None:
707            data_outputs = {}
708        self.data_outputs = data_outputs
709        if data_inputs is None:
710            data_inputs = {}
711        self.data_inputs = data_inputs
712        self.data = {}
713        for item in (self.data_outputs, self.data_inputs):
714            self.data.update(item)
715
716        if  len(self.data_outputs) > 0:
717            self._cols = self.GetNumberCols()
718            self._rows = self.GetNumberRows()
719            self.col_names = self.data_outputs.keys()
720            self.col_names.sort()
721            nbr_user_cols = len(self.col_names)
722            #Add more columns to the grid if necessary
723            if nbr_user_cols > self._cols:
724                new_col_nbr = nbr_user_cols - self._cols + 1
725                self.AppendCols(new_col_nbr, True)
726            #Add more rows to the grid if necessary
727            nbr_user_row = len(self.data_outputs.values()[0])
728            if nbr_user_row > self._rows + 1:
729                new_row_nbr = nbr_user_row - self._rows + 1
730                self.AppendRows(new_row_nbr, True)
731            # add data to the grid
732            wx.CallAfter(self.set_grid_values)
733        self.ForceRefresh()
734
735    def set_grid_values(self):
736        """
737        Set the values in grids
738       
739        """
740       
741        # add data to the grid
742        row = 0
743        col = 0
744        cell_col = 0
745        for col_name in  self.col_names:
746            # use the first row of the grid to add user defined labels
747            self.SetCellValue(row, col, str(col_name))
748            col += 1
749            cell_row = 1
750            value_list = self.data_outputs[col_name]
751
752            for value in value_list:
753                label = value
754                if issubclass(value.__class__, BatchCell):
755                    label = value.label
756                try:
757                    float(label)
758                    label = str(label)#format_number(label, high=True)
759                except:
760                    label = str(label)
761                self.SetCellValue(cell_row, cell_col, label)
762                self.AutoSizeColumn(cell_col, True)
763                width = self.GetColSize(cell_col)
764                if width < self.default_col_width:
765                    self.SetColSize(cell_col, self.default_col_width)
766
767                cell_row += 1
768            cell_col += 1
769            if cell_row > self.max_row_touse:
770                self.max_row_touse = cell_row
771
772    def get_grid_view(self):
773        """
774        Return value contained in the grid
775       
776        """
777       
778        grid_view = {}
779        for col in xrange(self.GetNumberCols()):
780            label = self.GetCellValue(row=0, col=col)
781            label = label.strip()
782            if label != "":
783                grid_view[label] = []
784                for row in range(1, self.max_row_touse):
785                    value = self.GetCellValue(row=row, col=col)
786                    if value != "":
787                        grid_view[label].append(value)
788                    else:
789                        grid_view[label].append(None)
790        return grid_view
791
792    def get_nofrows(self):
793        """
794        Return number of total rows
795       
796        """
797        return self._rows
798
799    def onContextMenu(self, event):
800        """
801        Method to handle cell right click context menu.
802       
803        THIS METHOD IS NOT CURRENTLY USED.  It is designed to provide a
804        cell pop up context by right clicking on a cell and gives the
805        option to cut, paste, and clear. This will probably be removed in
806        future versions and is being superceded by more traditional cut and
807        paste options.
808       
809        """
810       
811        wx_id = wx.NewId()
812        c_menu = wx.Menu()
813        copy_menu = c_menu.Append(wx_id, '&Copy', 'Copy the selected cells')
814        wx.EVT_MENU(self, wx_id, self.on_copy)
815
816        wx_id = wx.NewId()
817        c_menu.Append(wx_id, '&Paste', 'Paste the selected cells')
818        wx.EVT_MENU(self, wx_id, self.on_paste)
819
820        wx_id = wx.NewId()
821        clear_menu = c_menu.Append(wx_id, '&Clear', 'Clear the selected cells')
822        wx.EVT_MENU(self, wx_id, self.on_clear)
823
824        # enable from flag
825        has_selection = False
826        selected_cel = self.selected_cells
827        if len(selected_cel) > 0:
828            _row, _col = selected_cel[0]
829            has_selection = self.IsInSelection(_row, _col)
830        if len(self.selected_cols) > 0:
831            has_selection = True
832        if len(self.selected_rows) > 0:
833            has_selection = True
834        copy_menu.Enable(has_selection)
835        clear_menu.Enable(has_selection)
836        try:
837            # mouse event pos
838            pos_evt = event.GetPosition()
839            self.PopupMenu(c_menu, pos_evt)
840        except:
841            return
842
843    def on_copy(self, event):
844        """
845        Called when copy is chosen from cell right click context menu
846
847        THIS METHOD IS NOT CURRENTLY USED.  it is part of right click cell
848        context menu which is being removed. This will probably be removed in
849        future versions and is being superceded by more traditional cut and
850        paste options
851
852        """
853
854        self.Copy()
855
856    def on_paste(self, event):
857        """
858        Called when paste is chosen from cell right click context menu
859       
860        THIS METHOD IS NOT CURRENTLY USED.  it is part of right click cell
861        context menu which is being removed. This will probably be removed in
862        future versions and is being superceded by more traditional cut and
863        paste options
864
865        """
866
867        if self.data == None:
868            self.data = {}
869        if self.file_name == None:
870            self.file_name = 'copied_data'
871        self.Paste()
872
873    def on_clear(self, event):
874        """
875        Called when clear cell is chosen from cell right click context menu
876
877        THIS METHOD IS NOT CURRENTLY USED.  it is part of right click cell
878        context menu which is being removed. This will probably be removed in
879        future versions and is being superceded by more traditional cut and
880        paste options
881
882        """
883
884        self.Clear()
885
886class Notebook(nb, PanelBase):
887    """
888    ## Internal name for the AUI manager
889    window_name = "Fit panel"
890    ## Title to appear on top of the window
891   
892    """
893   
894    window_caption = "Notebook "
895
896    def __init__(self, parent, manager=None, data=None, *args, **kwargs):
897        """
898        """
899        nb.__init__(self, parent, -1,
900                    style=wx.aui.AUI_NB_WINDOWLIST_BUTTON |
901                    wx.aui.AUI_BUTTON_DOWN |
902                    wx.aui.AUI_NB_DEFAULT_STYLE |
903                    wx.CLIP_CHILDREN)
904        PanelBase.__init__(self, parent)
905        self.gpage_num = 1
906        self.enable_close_button()
907        self.parent = parent
908        self.manager = manager
909        self.data = data
910        #add empty page
911        self.add_empty_page()
912        self.pageClosedEvent = wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSE
913        self.Bind(self.pageClosedEvent, self.on_close_page)
914
915    def add_empty_page(self):
916        """
917        """
918        grid = GridPage(self, panel=self.parent)
919        self.AddPage(grid, "", True)
920        pos = self.GetPageIndex(grid)
921        title = "Table" + str(self.gpage_num)
922        self.SetPageText(pos, title)
923        self.SetSelection(pos)
924        self.enable_close_button()
925        self.gpage_num += 1
926        return grid, pos
927
928    def enable_close_button(self):
929        """
930        display the close button on the tab if more than 1 tab exits.
931        Otherwise remove the close button
932
933        """
934
935        if self.GetPageCount() <= 1:
936            style = self.GetWindowStyleFlag()
937            flag = wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
938            if style & wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB == flag:
939                style = style & ~wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
940                self.SetWindowStyle(style)
941        else:
942            style = self.GetWindowStyleFlag()
943            flag = wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
944            if style & wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB != flag:
945                style |= wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
946                self.SetWindowStyle(style)
947
948    def on_edit_axis(self):
949        """
950        Return the select cell range from a given selected column. Checks that
951        all cells are from the same column
952
953        """
954
955        pos = self.GetSelection()
956        grid = self.GetPage(pos)
957        #grid.selected_cols = [grid.GetSelectedRows()]#
958        if len(grid.selected_cols) >= 1:
959            col = grid.selected_cols[0]
960            for c in grid.selected_cols:
961                if c != col:
962                    msg = "Edit axis doesn't understand this selection.\n"
963                    msg += "Please select only one column"
964                    raise ValueError, msg
965            for (_, cell_col) in grid.selected_cells:
966                if cell_col != col:
967                    msg = "Cannot use cells from different columns for "
968                    msg += "this operation.\n"
969                    msg += "Please select elements of the same col.\n"
970                    raise ValueError, msg
971
972            # Finally check the highlighted cell if any cells missing
973            self.get_highlighted_row(True)
974        else:
975            msg = "No item selected.\n"
976            msg += "Please select only one column or one cell"
977            raise ValueError, msg
978        return grid.selected_cells
979
980    def get_highlighted_row(self, is_number=True):
981        """
982        Add highlight rows
983
984        """
985
986        pos = self.GetSelection()
987        grid = self.GetPage(pos)
988        col = grid.selected_cols[0]
989        # Finally check the highlighted cell if any cells missing
990        for row in range(grid.get_nofrows()):
991            if grid.IsInSelection(row, col):
992                cel = (row, col)
993                if row < 1 and not is_number:
994                    continue
995                # empty cell
996                if not grid.GetCellValue(row, col).lstrip().rstrip():
997                    if cel in grid.selected_cells:
998                        grid.selected_cells.remove(cel)
999                    continue
1000                if is_number:
1001                    try:
1002                        float(grid.GetCellValue(row, col))
1003                    except:
1004                        # non numeric cell
1005                        if cel in grid.selected_cells:
1006                            grid.selected_cells.remove(cel)
1007                        continue
1008                if cel not in grid.selected_cells:
1009                    grid.selected_cells.append(cel)
1010
1011    def get_column_labels(self):
1012        """
1013        return dictionary of columns labels on the current page
1014
1015        """
1016
1017        pos = self.GetSelection()
1018        grid = self.GetPage(pos)
1019        labels = {}
1020        for col in range(grid.GetNumberCols()):
1021            label = grid.GetColLabelValue(int(col))
1022            if label.strip() != "":
1023                labels[label.strip()] = col
1024        return labels
1025
1026    def create_axis_label(self, cell_list):
1027        """
1028        Receive a list of cells and  create a string presenting the selected
1029        cells that can be used as data for one axis of a plot.
1030       
1031        @param cell_list: list of tuple
1032
1033        """
1034        pos = self.GetSelection()
1035        grid = self.GetPage(pos)
1036        label = ""
1037        col_name = ""
1038        def create_label(col_name, row_min=None, row_max=None):
1039            """
1040            """
1041            result = " "
1042            if row_min is not  None or row_max is not None:
1043                if row_min is None:
1044                    result = str(row_max) + "]"
1045                elif row_max is None:
1046                    result = str(col_name) + "[" + str(row_min) + ":"
1047                else:
1048                    result = str(col_name) + "[" + str(row_min) + ":"
1049                    result += str(row_max) + "]"
1050            return str(result)
1051
1052        if len(cell_list) > 0:
1053            if len(cell_list) == 1:
1054                row_min, col = cell_list[0]
1055                col_name = grid.GetColLabelValue(int(col))
1056
1057                col_title = grid.GetCellValue(0, col)
1058                label = create_label(col_name, row_min + 1, row_min + 1)
1059                return  label, col_title
1060            else:
1061                temp_list = copy.deepcopy(cell_list)
1062                temp_list.sort()
1063                length = len(temp_list)
1064                row_min, col = temp_list[0]
1065                row_max, _ = temp_list[length - 1]
1066                col_name = grid.GetColLabelValue(int(col))
1067                col_title = grid.GetCellValue(0, col)
1068
1069                index = 0
1070                for row in xrange(row_min, row_max + 1):
1071                    if index > 0 and index < len(temp_list):
1072                        new_row, _ = temp_list[index]
1073                        if row != new_row:
1074                            temp_list.insert(index, (None, None))
1075                            if index - 1 >= 0:
1076                                new_row, _ = temp_list[index - 1]
1077                                if not new_row == None and new_row != ' ':
1078                                    label += create_label(col_name, None,
1079                                                          int(new_row) + 1)
1080                                else:
1081                                    label += "]"
1082                                label += ","
1083                            if index + 1 < len(temp_list):
1084                                new_row, _ = temp_list[index + 1]
1085                                if not new_row == None:
1086                                    label += create_label(col_name,
1087                                                          int(new_row) + 1, None)
1088                    if row_min != None and row_max != None:
1089                        if index == 0:
1090                            label += create_label(col_name,
1091                                                  int(row_min) + 1, None)
1092                        elif index == len(temp_list) - 1:
1093                            label += create_label(col_name, None,
1094                                                  int(row_max) + 1)
1095                    index += 1
1096                # clean up the list
1097                label_out = ''
1098                for item in label.split(','):
1099                    if item.split(":")[1] == "]":
1100                        continue
1101                    else:
1102                        label_out += item + ","
1103
1104                return label_out, col_title
1105
1106    def on_close_page(self, event):
1107        """
1108        close the page
1109
1110        """
1111
1112        if self.GetPageCount() == 1:
1113            event.Veto()
1114        wx.CallAfter(self.enable_close_button)
1115
1116    def set_data(self, data_inputs, data_outputs, details="", file_name=None):
1117        """
1118        """
1119        if data_outputs is None or data_outputs == {}:
1120            return
1121        inputs, outputs = self.get_odered_results(data_inputs, data_outputs)
1122        for pos in range(self.GetPageCount()):
1123            grid = self.GetPage(pos)
1124            if grid.data is None:
1125                #Found empty page
1126                grid.set_data(data_inputs=inputs,
1127                              data_outputs=outputs,
1128                              details=details,
1129                              file_name=file_name)
1130                self.SetSelection(pos)
1131                return
1132
1133        grid, pos = self.add_empty_page()
1134        grid.set_data(data_inputs=inputs,
1135                      data_outputs=outputs,
1136                      file_name=file_name,
1137                      details=details)
1138
1139    def get_odered_results(self, inputs, outputs=None):
1140        """
1141        Order a list of 'inputs.' Used to sort rows and columns to present
1142        in batch results grid.
1143
1144        """
1145
1146        # Let's re-order the data from the keys in 'Data' name.
1147        if outputs == None:
1148            return
1149        try:
1150            # For outputs from batch
1151            to_be_sort = [str(item.label) for item in outputs['Data']]
1152        except:
1153            # When inputs are from an external file
1154            return inputs, outputs
1155        inds = numpy.lexsort((to_be_sort, to_be_sort))
1156        for key in outputs.keys():
1157            key_list = outputs[key]
1158            temp_key = [item for item in key_list]
1159            for ind in inds:
1160                temp_key[ind] = key_list[inds[ind]]
1161            outputs[key] = temp_key
1162        for key in inputs.keys():
1163            key_list = inputs[key]
1164            if len(key_list) == len(inds):
1165                temp_key = [item for item in key_list]
1166                for ind in inds:
1167                    temp_key[ind] = key_list[inds[ind]]
1168                inputs[key] = temp_key
1169            else:
1170                inputs[key] = []
1171
1172        return inputs, outputs
1173
1174    def add_column(self):
1175        """
1176        Append a new column to the grid
1177
1178        """
1179
1180        # I Believe this is no longer used now that we have removed the
1181        # edit menu from the menubar - PDB July 12, 2015
1182        pos = self.GetSelection()
1183        grid = self.GetPage(pos)
1184        grid.AppendCols(1, True)
1185
1186    def on_remove_column(self):
1187        """
1188        Remove the selected column from the grid
1189
1190        """
1191        # I Believe this is no longer used now that we have removed the
1192        # edit menu from the menubar - PDB July 12, 2015
1193        pos = self.GetSelection()
1194        grid = self.GetPage(pos)
1195        grid.on_remove_column(event=None)
1196
1197class GridPanel(SPanel):
1198    """
1199    A ScrolledPanel class that contains the grid sheet as well as a number of
1200    widgets to create interesting plots and buttons for help etc.
1201
1202    """
1203
1204    def __init__(self, parent, data_inputs=None,
1205                 data_outputs=None, *args, **kwds):
1206        """
1207        Initialize the GridPanel
1208
1209        """
1210
1211        SPanel.__init__(self, parent, *args, **kwds)
1212
1213        self.vbox = wx.BoxSizer(wx.VERTICAL)
1214
1215        self.plotting_sizer = wx.FlexGridSizer(3, 7, 10, 5)
1216        self.button_sizer = wx.BoxSizer(wx.HORIZONTAL)
1217        self.grid_sizer = wx.BoxSizer(wx.HORIZONTAL)
1218        self.vbox.AddMany([(self.grid_sizer, 1, wx.EXPAND, 0),
1219                           (wx.StaticLine(self, -1), 0, wx.EXPAND, 0),
1220                           (self.plotting_sizer),
1221                           (self.button_sizer, 0, wx.BOTTOM, 10)])
1222        self.parent = parent
1223        self._data_inputs = data_inputs
1224        self._data_outputs = data_outputs
1225        self.x = []
1226        self.y = []
1227        self.dy = []
1228        self.x_axis_label = None
1229        self.y_axis_label = None
1230        self.dy_axis_label = None
1231        self.x_axis_title = None
1232        self.y_axis_title = None
1233        self.x_axis_unit = None
1234        self.y_axis_unit = None
1235        self.view_button = None
1236        self.plot_button = None
1237        self.notebook = None
1238        self.plot_num = 1
1239
1240        self.layout_grid()
1241        self.layout_plotting_area()
1242        self.SetSizer(self.vbox)
1243
1244    def set_xaxis(self, label="", x=None):
1245        """
1246        """
1247        if x is None:
1248            x = []
1249        self.x = x
1250        self.x_axis_label.SetValue("%s[:]" % str(label))
1251        self.x_axis_title.SetValue(str(label))
1252
1253    def set_yaxis(self, label="", y=None):
1254        """
1255        """
1256        if y is None:
1257            y = []
1258        self.y = y
1259        self.y_axis_label.SetValue("%s[:]" % str(label))
1260        self.y_axis_title.SetValue(str(label))
1261
1262    def set_dyaxis(self, label="", dy=None):
1263        """
1264        """
1265        if dy is None:
1266            dy = []
1267        self.dy = dy
1268        self.dy_axis_label.SetValue("%s[:]" % str(label))
1269
1270    def get_plot_axis(self, col, list):
1271        """
1272        """
1273        axis = []
1274        pos = self.notebook.GetSelection()
1275        grid = self.notebook.GetPage(pos)
1276        for row in list:
1277            label = grid.GetCellValue(0, col)
1278            value = grid.GetCellValue(row - 1, col).strip()
1279            if value != "":
1280                if label.lower().strip() == "data":
1281                    axis.append(float(row - 1))
1282                else:
1283                    try:
1284                        axis.append(float(value))
1285                    except:
1286                        msg = "Invalid data in row %s column %s" % (str(row), str(col))
1287                        wx.PostEvent(self.parent.parent,
1288                                     StatusEvent(status=msg, info="error"))
1289                        return None
1290            else:
1291                axis.append(None)
1292        return axis
1293
1294    def on_view(self, event):
1295        """
1296        Get object represented by the given cells and plot them.  Basically
1297        plot the colum in y vs the column in x.
1298
1299        """
1300
1301        pos = self.notebook.GetSelection()
1302        grid = self.notebook.GetPage(pos)
1303        title = self.notebook.GetPageText(pos)
1304        self.notebook.get_highlighted_row(False)
1305        if len(grid.selected_cells) == 0:
1306            msg = "Highlight a Data or Chi2 column first..."
1307            wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info="error"))
1308            return
1309        elif len(grid.selected_cells) > 20:
1310            msg = "Too many data (> 20) to plot..."
1311            msg += "\n Please select no more than 20 data."
1312            wx.MessageDialog(self, msg, 'Plotting', wx.OK)
1313            wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info="error"))
1314            return
1315
1316        for cell in grid.selected_cells:
1317            row, col = cell
1318            label_row = 0
1319            label = grid.GetCellValue(label_row, col)
1320            if label in grid.data:
1321                values = grid.data[label]
1322                if row > len(values) or row < 1:
1323                    msg = "Invalid cell was chosen."
1324                    wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info="error"))
1325                    continue
1326                else:
1327                    value = values[row - 1]
1328                if issubclass(value.__class__, BatchCell):
1329                    if value.object is None or len(value.object) == 0:
1330                        msg = "Row %s , " % str(row)
1331                        msg += "Column %s is NOT " % str(label)
1332                        msg += "the results of fits to view..."
1333                        wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info="error"))
1334                        return
1335                    for new_plot in value.object:
1336                        if new_plot is None or \
1337                         not issubclass(new_plot.__class__,
1338                                        plottables.Plottable):
1339                            msg = "Row %s , " % str(row)
1340                            msg += "Column %s is NOT " % str(label)
1341                            msg += "the results of fits to view..."
1342                            wx.PostEvent(self.parent.parent,
1343                                         StatusEvent(status=msg, info="error"))
1344                            return
1345                        if issubclass(new_plot.__class__, Data1D):
1346                            if label in grid.list_plot_panels.keys():
1347                                group_id = grid.list_plot_panels[label]
1348                            else:
1349                                group_id = str(new_plot.group_id) + str(grid.uid)
1350                                grid.list_plot_panels[label] = group_id
1351                            if group_id not in new_plot.list_group_id:
1352                                new_plot.group_id = group_id
1353                                new_plot.list_group_id.append(group_id)
1354                        else:
1355                            if label.lower() in ["data", "chi2"]:
1356                                if len(grid.selected_cells) != 1:
1357                                    msg = "2D View: Please select one data set"
1358                                    msg += " at a time for View Fit Results."
1359                                    wx.PostEvent(self.parent.parent,
1360                                                 StatusEvent(status=msg, info="error"))
1361                                    return
1362
1363                        wx.PostEvent(self.parent.parent,
1364                                     NewPlotEvent(plot=new_plot,
1365                                                  group_id=str(new_plot.group_id),
1366                                                  title=title))
1367                        msg = "Plotting the View Fit Results  completed!"
1368                        wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
1369                else:
1370                    msg = "Row %s , " % str(row)
1371                    msg += "Column %s is NOT " % str(label)
1372                    msg += "the results of fits to view..."
1373                    wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info="error"))
1374                    return
1375
1376    def on_plot(self, event):
1377        """
1378        Evaluate the contains of textcrtl and plot result
1379
1380        """
1381
1382        pos = self.notebook.GetSelection()
1383        grid = self.notebook.GetPage(pos)
1384        column_names = {}
1385        if grid is not None:
1386            column_names = self.notebook.get_column_labels()
1387        #evaluate x
1388        sentence = self.x_axis_label.GetValue()
1389        try:
1390            if sentence.strip() == "":
1391                msg = "Select column values for x axis"
1392                raise ValueError, msg
1393        except:
1394            msg = "X axis value error."
1395            wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info="error"))
1396            return
1397        dict = parse_string(sentence, column_names.keys())
1398
1399        try:
1400            sentence = self.get_sentence(dict, sentence, column_names)
1401            x = eval(sentence)
1402        except:
1403            msg = "Need a proper x-range."
1404            wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info="error"))
1405            return
1406        #evaluate y
1407        sentence = self.y_axis_label.GetValue()
1408        try:
1409            if sentence.strip() == "":
1410                msg = "select value for y axis"
1411                raise ValueError, msg
1412        except:
1413            msg = "Y axis value error."
1414            wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info="error"))
1415            return
1416        dict = parse_string(sentence, column_names.keys())
1417        try:
1418            sentence = self.get_sentence(dict, sentence, column_names)
1419            y = eval(sentence)
1420        except:
1421            msg = "Need a proper y-range."
1422            wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info="error"))
1423            return
1424        #evaluate y
1425        sentence = self.dy_axis_label.GetValue()
1426        dy = None
1427        if sentence.strip() != "":
1428            dict = parse_string(sentence, column_names.keys())
1429            sentence = self.get_sentence(dict, sentence, column_names)
1430            try:
1431                dy = eval(sentence)
1432            except:
1433                msg = "Need a proper dy-range."
1434                wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info="error"))
1435                return
1436        if len(x) != len(y) or (len(x) == 0 or len(y) == 0):
1437            msg = "Need same length for X and Y axis and both greater than 0"
1438            msg += " to plot.\n"
1439            msg += "Got X length = %s, Y length = %s" % (str(len(x)), str(len(y)))
1440            wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info="error"))
1441            return
1442        if dy != None and (len(y) != len(dy)):
1443            msg = "Need same length for Y and dY axis and both greater than 0"
1444            msg += " to plot.\n"
1445            msg += "Got Y length = %s, dY length = %s" % (str(len(y)), str(len(dy)))
1446            wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info="error"))
1447            return
1448        if dy == None:
1449            dy = numpy.zeros(len(y))
1450        #plotting
1451        new_plot = Data1D(x=x, y=y, dy=dy)
1452        new_plot.id = wx.NewId()
1453        new_plot.is_data = False
1454        new_plot.group_id = wx.NewId()
1455        y_title = self.y_axis_title.GetValue()
1456        x_title = self.x_axis_title.GetValue()
1457        title = "%s_vs_%s" % (y_title, x_title)
1458        new_plot.xaxis(x_title, self.x_axis_unit.GetValue())
1459        new_plot.yaxis(y_title, self.y_axis_unit.GetValue())
1460        try:
1461            title = y_title.strip()
1462            title += "_" + self.notebook.GetPageText(pos)
1463            title += "_" + str(self.plot_num)
1464            self.plot_num += 1
1465            new_plot.name = title
1466            new_plot.xtransform = "x"
1467            new_plot.ytransform = "y"
1468            wx.PostEvent(self.parent.parent,
1469                         NewPlotEvent(plot=new_plot,
1470                                      group_id=str(new_plot.group_id), title=title))
1471            msg = "Plotting completed!"
1472            wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
1473            self.parent.parent.update_theory(data_id=new_plot.id, theory=new_plot)
1474        except:
1475            wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info="error"))
1476
1477    def on_help(self, event):
1478        """
1479        Bring up the Batch Grid Panel Usage Documentation whenever
1480        the HELP button is clicked.
1481
1482        Calls DocumentationWindow with the path of the location within the
1483        documentation tree (after /doc/ ....".  Note that when using old
1484        versions of Wx (before 2.9) and thus not the release version of
1485        installers, the help comes up at the top level of the file as
1486        webbrowser does not pass anything past the # to the browser when it is
1487        running "file:///...."
1488
1489        @param evt: Triggers on clicking the help button
1490
1491        """
1492
1493        #import documentation window here to avoid circular imports
1494        #if put at top of file with rest of imports.
1495        from documentation_window import DocumentationWindow
1496
1497        _TreeLocation = "user/perspectives/fitting/fitting_help.html"
1498        _PageAnchor = "#batch-fit-mode"
1499        _doc_viewer = DocumentationWindow(self, -1, _TreeLocation, _PageAnchor,
1500                                          "Batch Mode Help")
1501
1502    def get_sentence(self, dict, sentence, column_names):
1503        """
1504        Get sentence from dict
1505
1506        """
1507
1508        for tok, (col_name, list) in dict.iteritems():
1509            col = column_names[col_name]
1510            axis = self.get_plot_axis(col, list)
1511            if axis == None:
1512                return None
1513            sentence = sentence.replace(tok, "numpy.array(%s)" % str(axis))
1514        for key, value in FUNC_DICT.iteritems():
1515            sentence = sentence.replace(key.lower(), value)
1516        return sentence
1517
1518    def layout_grid(self):
1519        """
1520        Draw the area related to the grid by adding it as the first element
1521        in the panel's grid_sizer
1522
1523        """
1524
1525        self.notebook = Notebook(parent=self)
1526        self.notebook.set_data(self._data_inputs, self._data_outputs)
1527        self.grid_sizer.Add(self.notebook, 1, wx.EXPAND, 0)
1528
1529    def layout_plotting_area(self):
1530        """
1531        Add the area containing all the plot options, buttons etc to a plotting
1532        area sizer to later be added to the top level grid_sizer
1533
1534        """
1535
1536        view_description = wx.StaticBox(self, -1, 'Plot Fits/Residuals')
1537        note = "To plot the fits (or residuals), click the 'View Fits' button"
1538        note += "\n after highlighting the Data names (or Chi2 values)."
1539        note_text = wx.StaticText(self, -1, note)
1540        boxsizer1 = wx.StaticBoxSizer(view_description, wx.HORIZONTAL)
1541        self.x_axis_title = wx.TextCtrl(self, -1)
1542        self.y_axis_title = wx.TextCtrl(self, -1)
1543        self.x_axis_label = wx.TextCtrl(self, -1, size=(200, -1))
1544        self.y_axis_label = wx.TextCtrl(self, -1, size=(200, -1))
1545        self.dy_axis_label = wx.TextCtrl(self, -1, size=(200, -1))
1546        self.x_axis_add = wx.Button(self, -1, "Add")
1547        self.x_axis_add.Bind(event=wx.EVT_BUTTON, handler=self.on_edit_axis,
1548                             id=self.x_axis_add.GetId())
1549        self.y_axis_add = wx.Button(self, -1, "Add")
1550        self.y_axis_add.Bind(event=wx.EVT_BUTTON, handler=self.on_edit_axis,
1551                             id=self.y_axis_add.GetId())
1552        self.dy_axis_add = wx.Button(self, -1, "Add")
1553        self.dy_axis_add.Bind(event=wx.EVT_BUTTON, handler=self.on_edit_axis,
1554                              id=self.dy_axis_add.GetId())
1555        self.x_axis_unit = wx.TextCtrl(self, -1)
1556        self.y_axis_unit = wx.TextCtrl(self, -1)
1557        self.view_button = wx.Button(self, -1, "View Fits")
1558        view_tip = "Highlight the data set or the Chi2 column first."
1559        self.view_button.SetToolTipString(view_tip)
1560        wx.EVT_BUTTON(self, self.view_button.GetId(), self.on_view)
1561        self.plot_button = wx.Button(self, -1, "Plot")
1562        plot_tip = "Highlight a column for each axis and \n"
1563        plot_tip += "click the Add buttons first."
1564
1565        self.plot_button.SetToolTipString(plot_tip)
1566
1567        self.help_button = wx.Button(self, -1, "HELP")
1568        self.help_button.SetToolTipString("Get Help for Batch Mode")
1569        self.help_button.Bind(wx.EVT_BUTTON, self.on_help)
1570
1571        boxsizer1.AddMany([(note_text, 0, wx.LEFT, 10),
1572                           (self.view_button, 0, wx.LEFT | wx.RIGHT, 10)])
1573        self.button_sizer.AddMany([(boxsizer1, 0,
1574                                    wx.LEFT | wx.RIGHT | wx.BOTTOM, 10),
1575                                   (self.plot_button, 0,
1576                                    wx.LEFT | wx.TOP | wx.BOTTOM, 12),
1577                                   (self.help_button,0, 
1578                                    wx.LEFT | wx.TOP | wx.BOTTOM, 12)])
1579
1580        wx.EVT_BUTTON(self, self.plot_button.GetId(), self.on_plot)
1581        self.plotting_sizer.AddMany(\
1582                   [(wx.StaticText(self, -1, "X-axis Label\nSelection Range"), 1,
1583                     wx.TOP | wx.BOTTOM | wx.LEFT, 10),
1584                    (self.x_axis_label, 1, wx.TOP | wx.BOTTOM, 10),
1585                    (self.x_axis_add, 1, wx.TOP | wx.BOTTOM | wx.RIGHT, 10),
1586                    (wx.StaticText(self, -1, "X-axis Label"), 1, wx.TOP | wx.BOTTOM | wx.LEFT, 10),
1587                    (self.x_axis_title, 1, wx.TOP | wx.BOTTOM, 10),
1588                    (wx.StaticText(self, -1, "X-axis Unit"), 1, wx.TOP | wx.BOTTOM, 10),
1589                    (self.x_axis_unit, 1, wx.TOP | wx.BOTTOM, 10),
1590                    (wx.StaticText(self, -1, "Y-axis Label\nSelection Range"), 1,
1591                     wx.BOTTOM | wx.LEFT, 10),
1592                    (self.y_axis_label, wx.BOTTOM, 10),
1593                    (self.y_axis_add, 1, wx.BOTTOM | wx.RIGHT, 10),
1594                    (wx.StaticText(self, -1, "Y-axis Label"), 1,
1595                     wx.BOTTOM | wx.LEFT, 10),
1596                    (self.y_axis_title, wx.BOTTOM, 10),
1597                    (wx.StaticText(self, -1, "Y-axis Unit"), 1, wx.BOTTOM, 10),
1598                    (self.y_axis_unit, 1, wx.BOTTOM, 10),
1599                    (wx.StaticText(self, -1, "dY-Bar (Optional)\nSelection Range"),
1600                     1, wx.BOTTOM | wx.LEFT, 10),
1601                    (self.dy_axis_label, wx.BOTTOM, 10),
1602                    (self.dy_axis_add, 1, wx.BOTTOM | wx.RIGHT, 10),
1603                    (-1, -1),
1604                    (-1, -1),
1605                    (-1, -1),
1606                    (-1, 1)])
1607
1608    def on_edit_axis(self, event):
1609        """
1610        Get the selected column on  the visible grid and set values for axis
1611
1612        """
1613
1614        try:
1615            cell_list = self.notebook.on_edit_axis()
1616            label, title = self.create_axis_label(cell_list)
1617        except:
1618            msg = str(sys.exc_value)
1619            wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info="error"))
1620            return
1621        tcrtl = event.GetEventObject()
1622        if tcrtl == self.x_axis_add:
1623            self.edit_axis_helper(self.x_axis_label, self.x_axis_title, label, title)
1624        elif tcrtl == self.y_axis_add:
1625            self.edit_axis_helper(self.y_axis_label, self.y_axis_title, label, title)
1626        elif tcrtl == self.dy_axis_add:
1627            self.edit_axis_helper(self.dy_axis_label, None, label, None)
1628
1629    def create_axis_label(self, cell_list):
1630        """
1631        Receive a list of cells and  create a string presenting the selected
1632        cells.
1633
1634        @param cell_list: list of tuple
1635
1636        """
1637
1638        if self.notebook is not None:
1639            return self.notebook.create_axis_label(cell_list)
1640
1641    def edit_axis_helper(self, tcrtl_label, tcrtl_title, label, title):
1642        """
1643        get controls to modify
1644
1645        """
1646
1647        if label != None:
1648            tcrtl_label.SetValue(str(label))
1649        if title != None:
1650            tcrtl_title.SetValue(str(title))
1651
1652    def add_column(self):
1653        """
1654        """
1655        # I Believe this is no longer used now that we have removed the
1656        # edit menu from the menubar - PDB July 12, 2015
1657        if self.notebook is not None:
1658            self.notebook.add_column()
1659
1660    def on_remove_column(self):
1661        """
1662        """
1663        # I Believe this is no longer used now that we have removed the
1664        # edit menu from the menubar - PDB July 12, 2015
1665        if self.notebook is not None:
1666            self.notebook.on_remove_column()
1667
1668
1669class GridFrame(wx.Frame):
1670    """
1671    The main wx.Frame for the batch results grid
1672
1673    """
1674
1675    def __init__(self, parent=None, data_inputs=None, data_outputs=None, id=-1,
1676                 title="Batch Fitting Results Panel", size=(800, 500)):
1677        """
1678        Initialize the Frame
1679       
1680        """
1681
1682        wx.Frame.__init__(self, parent=parent, id=id, title=title, size=size)
1683        self.parent = parent
1684        self.panel = GridPanel(self, data_inputs, data_outputs)
1685        menubar = wx.MenuBar()
1686        self.SetMenuBar(menubar)
1687
1688        self.curr_col = None
1689        self.curr_grid = None
1690        self.curr_col_name = ""
1691        self.file = wx.Menu()
1692        menubar.Append(self.file, "&File")
1693
1694        hint = "Open file containing batch results"
1695        open_menu = self.file.Append(wx.NewId(), 'Open ', hint)
1696        wx.EVT_MENU(self, open_menu.GetId(), self.on_open)
1697
1698        hint = "Open the the current grid into excel"
1699        self.open_excel_menu = self.file.Append(wx.NewId(), 'Open with Excel', hint)
1700        wx.EVT_MENU(self, self.open_excel_menu.GetId(), self.open_with_excel)
1701        self.file.AppendSeparator()
1702        self.save_menu = self.file.Append(wx.NewId(), 'Save As', 'Save into File')
1703        wx.EVT_MENU(self, self.save_menu.GetId(), self.on_save_page)
1704
1705        # We need to grab a WxMenu handle here, otherwise the next one to grab
1706        # the handle will be treated as the Edit Menu handle when checking in
1707        # on_menu_open event handler and thus raise an exception when it hits an
1708        # unitialized object.  Alternative is to comment out that whole section
1709        # in on_menu_open, but that would make it more difficult to undo the
1710        # hidding of the menu.   PDB  July 12, 2015.
1711        #
1712        # To enable the Edit menubar comment out next line and uncomment the
1713        # following line.
1714        self.edit = wx.Menu()
1715        #self.add_edit_menu()
1716
1717        self.Bind(wx.EVT_MENU_OPEN, self.on_menu_open)
1718        self.Bind(wx.EVT_CLOSE, self.on_close)
1719
1720    def add_edit_menu(self, menubar):
1721        """
1722        populates the edit menu on the menubar.  Not activated as of SasView
1723        3.1.0
1724
1725        """
1726        self.edit = wx.Menu()
1727
1728        add_table_menu = self.edit.Append(-1, 'New Table',
1729                                          'Add a New Table')
1730        self.edit.AppendSeparator()
1731        wx.EVT_MENU(self, add_table_menu.GetId(), self.add_table)
1732
1733        self.copy_menu = self.edit.Append(-1, 'Copy',
1734                                          'Copy the selected cells')
1735        wx.EVT_MENU(self, self.copy_menu.GetId(), self.on_copy)
1736        self.paste_menu = self.edit.Append(-1, 'Paste',
1737                                           'Paste the selected Cells')
1738        wx.EVT_MENU(self, self.paste_menu.GetId(), self.on_paste)
1739        self.clear_menu = self.edit.Append(-1, 'Clear',
1740                                           'Clear the selected Cells')
1741        wx.EVT_MENU(self, self.clear_menu.GetId(), self.on_clear)
1742
1743        self.edit.AppendSeparator()
1744        hint = "Insert column before the selected column"
1745        self.insert_before_menu = wx.Menu()
1746        self.insertb_sub_menu = self.edit.AppendSubMenu(self.insert_before_menu,
1747                                                        'Insert Before', hint)
1748        hint = "Insert column after the selected column"
1749        self.insert_after_menu = wx.Menu()
1750        self.inserta_sub_menu = self.edit.AppendSubMenu(self.insert_after_menu,
1751                                                        'Insert After', hint)
1752        hint = "Remove the selected column"
1753        self.remove_menu = self.edit.Append(-1, 'Remove Column', hint)
1754        wx.EVT_MENU(self, self.remove_menu.GetId(), self.on_remove_column)
1755        menubar.Append(self.edit, "&Edit")
1756
1757    def on_copy(self, event):
1758        """
1759        On Copy from the Edit menu item on the menubar 
1760
1761        """
1762        # I Believe this is no longer used now that we have removed the
1763        # edit menu from the menubar - PDB July 12, 2015
1764        if event != None:
1765            event.Skip()
1766        pos = self.panel.notebook.GetSelection()
1767        grid = self.panel.notebook.GetPage(pos)
1768        grid.Copy()
1769
1770    def on_paste(self, event):
1771        """
1772        On Paste from the Edit menu item on the menubar
1773
1774        """
1775        # I Believe this is no longer used now that we have removed the
1776        # edit menu from the menubar - PDB July 12, 2015
1777        if event != None:
1778            event.Skip()
1779        pos = self.panel.notebook.GetSelection()
1780        grid = self.panel.notebook.GetPage(pos)
1781        grid.on_paste(None)
1782
1783    def on_clear(self, event):
1784        """
1785        On Clear from the Edit menu item on the menubar
1786
1787        """
1788        # I Believe this is no longer used now that we have removed the
1789        # edit menu from the menubar - PDB July 12, 2015
1790        pos = self.panel.notebook.GetSelection()
1791        grid = self.panel.notebook.GetPage(pos)
1792        grid.Clear()
1793
1794    def GetLabelText(self, id):
1795        """
1796        Get Label Text
1797
1798        """
1799        for item in self.insert_before_menu.GetMenuItems():
1800            m_id = item.GetId()
1801            if m_id == id:
1802                return item.GetLabel()
1803
1804    def on_remove_column(self, event):
1805        """
1806        On remove column from the Edit menu Item on the menubar
1807
1808        """
1809        # I Believe this is no longer used now that we have removed the
1810        # edit menu from the menubar - PDB July 12, 2015
1811        pos = self.panel.notebook.GetSelection()
1812        grid = self.panel.notebook.GetPage(pos)
1813        grid.on_remove_column(event=None)
1814
1815    def on_menu_open(self, event):
1816        """
1817        On menu open
1818
1819        """
1820        if self.file == event.GetMenu():
1821            pos = self.panel.notebook.GetSelection()
1822            grid = self.panel.notebook.GetPage(pos)
1823            has_data = (grid.data != None and grid.data != {})
1824            self.open_excel_menu.Enable(has_data)
1825            self.save_menu.Enable(has_data)
1826
1827        if self.edit == event.GetMenu():
1828            #get the selected column
1829            pos = self.panel.notebook.GetSelection()
1830            grid = self.panel.notebook.GetPage(pos)
1831            col_list = grid.GetSelectedCols()
1832            has_selection = False
1833            selected_cel = grid.selected_cells
1834            if len(selected_cel) > 0:
1835                _row, _col = selected_cel[0]
1836                has_selection = grid.IsInSelection(_row, _col)
1837            if len(grid.selected_cols) > 0:
1838                has_selection = True
1839            if len(grid.selected_rows) > 0:
1840                has_selection = True
1841            self.copy_menu.Enable(has_selection)
1842            self.clear_menu.Enable(has_selection)
1843
1844            if len(col_list) > 0:
1845                self.remove_menu.Enable(True)
1846            else:
1847                self.remove_menu.Enable(False)
1848            if len(col_list) == 0 or len(col_list) > 1:
1849                self.insertb_sub_menu.Enable(False)
1850                self.inserta_sub_menu.Enable(False)
1851                label = "Insert Column Before"
1852                self.insertb_sub_menu.SetText(label)
1853                label = "Insert Column After"
1854                self.inserta_sub_menu.SetText(label)
1855            else:
1856                self.insertb_sub_menu.Enable(True)
1857                self.inserta_sub_menu.Enable(True)
1858
1859                col = col_list[0]
1860                col_name = grid.GetCellValue(row=0, col=col)
1861                label = "Insert Column Before " + str(col_name)
1862                self.insertb_sub_menu.SetText(label)
1863                for item in self.insert_before_menu.GetMenuItems():
1864                    self.insert_before_menu.DeleteItem(item)
1865                grid.insert_col_menu(menu=self.insert_before_menu,
1866                                     label=col_name, window=self)
1867                label = "Insert Column After " + str(col_name)
1868                self.inserta_sub_menu.SetText(label)
1869                for item in self.insert_after_menu.GetMenuItems():
1870                    self.insert_after_menu.DeleteItem(item)
1871                grid.insert_after_col_menu(menu=self.insert_after_menu,
1872                                           label=col_name, window=self)
1873        event.Skip()
1874
1875
1876
1877    def on_save_page(self, event):
1878        """
1879        Saves data in grid to a csv file.
1880       
1881        At this time only the columns displayed get saved.  Thus any error
1882        bars not inserted before saving will not be saved in the file
1883
1884        """
1885
1886        if self.parent is not None:
1887            pos = self.panel.notebook.GetSelection()
1888            grid = self.panel.notebook.GetPage(pos)
1889            if grid.file_name is None or grid.file_name.strip() == "" or \
1890                grid.data is None or len(grid.data) == 0:
1891                name = self.panel.notebook.GetPageText(pos)
1892                msg = " %s has not data to save" % str(name)
1893                wx.PostEvent(self.parent,
1894                             StatusEvent(status=msg, info="error"))
1895
1896                return
1897            reader, ext = os.path.splitext(grid.file_name)
1898            path = None
1899            if self.parent is not None:
1900                location = os.path.dirname(grid.file_name)
1901                dlg = wx.FileDialog(self, "Save Project file",
1902                                    location, grid.file_name, ext, wx.SAVE)
1903                path = None
1904                if dlg.ShowModal() == wx.ID_OK:
1905                    path = dlg.GetPath()
1906                dlg.Destroy()
1907                if path != None:
1908                    if self.parent is not None:
1909                        data = grid.get_grid_view()
1910                        self.parent.write_batch_tofile(data=data,
1911                                                       file_name=path,
1912                                                       details=grid.details)
1913
1914    def on_open(self, event):
1915        """
1916        Open file containing batch result
1917
1918        """
1919
1920        if self.parent is not None:
1921            self.parent.on_read_batch_tofile(self)
1922
1923    def open_with_excel(self, event):
1924        """
1925        open excel and display batch result in Excel
1926
1927        """
1928
1929        if self.parent is not None:
1930            pos = self.panel.notebook.GetSelection()
1931            grid = self.panel.notebook.GetPage(pos)
1932            data = grid.get_grid_view()
1933            if grid.file_name is None or grid.file_name.strip() == "" or \
1934                grid.data is None or len(grid.data) == 0:
1935                name = self.panel.notebook.GetPageText(pos)
1936                msg = " %s has not data to open on excel" % str(name)
1937                wx.PostEvent(self.parent,
1938                             StatusEvent(status=msg, info="error"))
1939
1940                return
1941            self.parent.open_with_externalapp(data=data,
1942                                              file_name=grid.file_name,
1943                                              details=grid.details)
1944
1945    def on_close(self, event):
1946        """
1947        """
1948        self.Hide()
1949
1950    def on_append_column(self, event):
1951        """
1952        Append a new column to the grid
1953
1954        """
1955        self.panel.add_column()
1956
1957    def set_data(self, data_inputs, data_outputs, details="", file_name=None):
1958        """
1959        Set data
1960
1961        """
1962        self.panel.notebook.set_data(data_inputs=data_inputs,
1963                                     file_name=file_name,
1964                                     details=details,
1965                                     data_outputs=data_outputs)
1966
1967    def add_table(self, event):
1968        """
1969        Add a new table
1970
1971        """
1972        # DO not event.Skip(): it will make 2 pages
1973        self.panel.notebook.add_empty_page()
1974
1975class BatchOutputFrame(wx.Frame):
1976    """
1977    Allow to select where the result of batch will be displayed or stored
1978
1979    """
1980    def __init__(self, parent, data_inputs, data_outputs, file_name="",
1981                 details="", *args, **kwds):
1982        """
1983        Initialize dialog
1984
1985        @param parent: Window instantiating this dialog
1986        @param result: result to display in a grid or export to an external
1987                application.
1988
1989        """
1990
1991        #kwds['style'] = wx.CAPTION|wx.SYSTEM_MENU
1992        wx.Frame.__init__(self, parent, *args, **kwds)
1993        self.parent = parent
1994        self.panel = wx.Panel(self)
1995        self.file_name = file_name
1996        self.details = details
1997        self.data_inputs = data_inputs
1998        self.data_outputs = data_outputs
1999        self.data = {}
2000        for item in (self.data_outputs, self.data_inputs):
2001            self.data.update(item)
2002        self.flag = 1
2003        self.SetSize((300, 200))
2004        self.local_app_selected = None
2005        self.external_app_selected = None
2006        self.save_to_file = None
2007        self._do_layout()
2008
2009    def _do_layout(self):
2010        """
2011        Draw the content of the current dialog window
2012
2013        """
2014
2015        vbox = wx.BoxSizer(wx.VERTICAL)
2016        box_description = wx.StaticBox(self.panel, -1, str("Batch Outputs"))
2017        hint_sizer = wx.StaticBoxSizer(box_description, wx.VERTICAL)
2018        selection_sizer = wx.GridBagSizer(5, 5)
2019        button_sizer = wx.BoxSizer(wx.HORIZONTAL)
2020        text = "Open with %s" % self.parent.application_name
2021        self.local_app_selected = wx.RadioButton(self.panel, -1, text, style=wx.RB_GROUP)
2022        self.Bind(wx.EVT_RADIOBUTTON, self.onselect,
2023                  id=self.local_app_selected.GetId())
2024        text = "Open with Excel"
2025        self.external_app_selected = wx.RadioButton(self.panel, -1, text)
2026        self.Bind(wx.EVT_RADIOBUTTON, self.onselect, id=self.external_app_selected.GetId())
2027        text = "Save to File"
2028        self.save_to_file = wx.CheckBox(self.panel, -1, text)
2029        self.Bind(wx.EVT_CHECKBOX, self.onselect, id=self.save_to_file.GetId())
2030        self.local_app_selected.SetValue(True)
2031        self.external_app_selected.SetValue(False)
2032        self.save_to_file.SetValue(False)
2033        button_close = wx.Button(self.panel, -1, "Close")
2034        button_close.Bind(wx.EVT_BUTTON, id=button_close.GetId(), handler=self.on_close)
2035        button_apply = wx.Button(self.panel, -1, "Apply")
2036        button_apply.Bind(wx.EVT_BUTTON, id=button_apply.GetId(), handler=self.on_apply)
2037        button_apply.SetFocus()
2038        hint = ""
2039        hint_sizer.Add(wx.StaticText(self.panel, -1, hint))
2040        hint_sizer.Add(selection_sizer)
2041        #draw area containing radio buttons
2042        ix = 0
2043        iy = 0
2044        selection_sizer.Add(self.local_app_selected, (iy, ix),
2045                            (1, 1), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
2046        iy += 1
2047        selection_sizer.Add(self.external_app_selected, (iy, ix),
2048                            (1, 1), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
2049        iy += 1
2050        selection_sizer.Add(self.save_to_file, (iy, ix),
2051                            (1, 1), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
2052        #contruction the sizer contaning button
2053        button_sizer.Add((20, 20), 1, wx.EXPAND | wx.ADJUST_MINSIZE, 0)
2054
2055        button_sizer.Add(button_close, 0,
2056                         wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
2057        button_sizer.Add(button_apply, 0,
2058                         wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 10)
2059        vbox.Add(hint_sizer, 0, wx.EXPAND | wx.ALL, 10)
2060        vbox.Add(wx.StaticLine(self.panel, -1), 0, wx.EXPAND, 0)
2061        vbox.Add(button_sizer, 0, wx.TOP | wx.BOTTOM, 10)
2062        self.SetSizer(vbox)
2063
2064    def on_apply(self, event):
2065        """
2066        Get the user selection and display output to the selected application
2067
2068        """
2069
2070        if self.flag == 1:
2071            self.parent.open_with_localapp(data_inputs=self.data_inputs,
2072                                           data_outputs=self.data_outputs)
2073        elif self.flag == 2:
2074            self.parent.open_with_externalapp(data=self.data,
2075                                              file_name=self.file_name,
2076                                              details=self.details)
2077    def on_close(self, event):
2078        """
2079        close the Window
2080
2081        """
2082
2083        self.Close()
2084
2085    def onselect(self, event=None):
2086        """
2087        Receive event and display data into third party application
2088        or save data to file.
2089
2090        """
2091        if self.save_to_file.GetValue():
2092            _, ext = os.path.splitext(self.file_name)
2093            path = None
2094            location = os.getcwd()
2095            if self.parent is not None:
2096                location = os.path.dirname(self.file_name)
2097                dlg = wx.FileDialog(self, "Save Project file",
2098                                    location, self.file_name, ext, wx.SAVE)
2099                path = None
2100                if dlg.ShowModal() == wx.ID_OK:
2101                    path = dlg.GetPath()
2102                dlg.Destroy()
2103                if path != None:
2104                    if self.parent is not None and  self.data is not None:
2105                        self.parent.write_batch_tofile(data=self.data,
2106                                                       file_name=path,
2107                                                       details=self.details)
2108        if self.local_app_selected.GetValue():
2109            self.flag = 1
2110        else:
2111            self.flag = 2
2112        return self.flag
2113
2114
2115
2116if __name__ == "__main__":
2117    app = wx.App()
2118
2119    try:
2120        data = {}
2121        j = 0
2122        for i in range(4):
2123            j += 1
2124            data["index" + str(i)] = [i / j, i * j, i, i + j]
2125
2126        data_input = copy.deepcopy(data)
2127        data_input["index5"] = [10, 20, 40, 50]
2128        frame = GridFrame(data_outputs=data, data_inputs=data_input)
2129        frame.Show(True)
2130    except:
2131        print sys.exc_value
2132
2133    app.MainLoop()
Note: See TracBrowser for help on using the repository browser.