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

Last change on this file since afb311e was 20fa5fe, checked in by Stuart Prescott <stuart@…>, 7 years ago

Fix lots more typos in comments and docs

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