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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since eb38181 was 468c253, checked in by butler, 9 years ago

Fixes image not loading in dev docs. Problem is all images are slurped
into user docs folder (not really a choice — no easy way to identify it
is a developer doc image. anyway this is the only one I think. just
adjusted path in doc to reflect way it is slurped — not beautiful but
works.

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