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

magnetic_scattrelease-4.2.2ticket-1009ticket-1249
Last change on this file since 5251ec6 was 5251ec6, checked in by Paul Kienzle <pkienzle@…>, 12 months ago

improved support for py37 in sasgui

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