source: sasview/src/sas/sasgui/guiframe/data_processor.py @ ac07a3a

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.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since ac07a3a was ac07a3a, checked in by andyfaff, 7 years ago

MAINT: replace 'not x is None' by 'x is not None'

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