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

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 8f46df7 was fce0139, checked in by smk78, 9 years ago

Changed relative path to BatchGridClassLayout?.png to resolve Sphinx
build error

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