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

magnetic_scattrelease-4.2.2ticket-1009ticket-1249
Last change on this file since 5251ec6 was 5251ec6, checked in by Paul Kienzle <pkienzle@…>, 6 years ago

improved support for py37 in sasgui

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