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

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 06aaa75d was d85c194, checked in by Piotr Rozyczko <piotr.rozyczko@…>, 9 years ago

Remaining modules refactored

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