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

magnetic_scattrelease-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249unittest-saveload
Last change on this file since cf820f5 was 20fa5fe, checked in by Stuart Prescott <stuart@…>, 7 years ago

Fix lots more typos in comments and docs

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